• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "content/common/gpu/media/android_video_encode_accelerator.h"
6 
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/logging.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/metrics/histogram.h"
12 #include "content/common/gpu/gpu_channel.h"
13 #include "content/public/common/content_switches.h"
14 #include "gpu/command_buffer/service/gles2_cmd_decoder.h"
15 #include "media/base/android/media_codec_bridge.h"
16 #include "media/base/bitstream_buffer.h"
17 #include "media/base/limits.h"
18 #include "media/video/picture.h"
19 #include "third_party/libyuv/include/libyuv/convert_from.h"
20 #include "ui/gl/android/scoped_java_surface.h"
21 #include "ui/gl/gl_bindings.h"
22 
23 using media::MediaCodecBridge;
24 using media::VideoCodecBridge;
25 using media::VideoFrame;
26 
27 namespace content {
28 
29 enum {
30   // Subset of MediaCodecInfo.CodecCapabilities.
31   COLOR_FORMAT_YUV420_SEMIPLANAR = 21,
32 };
33 
34 // Helper macros for dealing with failure.  If |result| evaluates false, emit
35 // |log| to DLOG(ERROR), register |error| with the client, and return.
36 #define RETURN_ON_FAILURE(result, log, error)                 \
37   do {                                                        \
38     if (!(result)) {                                          \
39       DLOG(ERROR) << log;                                     \
40       if (client_ptr_factory_.GetWeakPtr()) {                 \
41         client_ptr_factory_.GetWeakPtr()->NotifyError(error); \
42         client_ptr_factory_.InvalidateWeakPtrs();             \
43       }                                                       \
44       return;                                                 \
45     }                                                         \
46   } while (0)
47 
48 // Because MediaCodec is thread-hostile (must be poked on a single thread) and
49 // has no callback mechanism (b/11990118), we must drive it by polling for
50 // complete frames (and available input buffers, when the codec is fully
51 // saturated).  This function defines the polling delay.  The value used is an
52 // arbitrary choice that trades off CPU utilization (spinning) against latency.
53 // Mirrors android_video_decode_accelerator.cc::DecodePollDelay().
EncodePollDelay()54 static inline const base::TimeDelta EncodePollDelay() {
55   // An alternative to this polling scheme could be to dedicate a new thread
56   // (instead of using the ChildThread) to run the MediaCodec, and make that
57   // thread use the timeout-based flavor of MediaCodec's dequeue methods when it
58   // believes the codec should complete "soon" (e.g. waiting for an input
59   // buffer, or waiting for a picture when it knows enough complete input
60   // pictures have been fed to saturate any internal buffering).  This is
61   // speculative and it's unclear that this would be a win (nor that there's a
62   // reasonably device-agnostic way to fill in the "believes" above).
63   return base::TimeDelta::FromMilliseconds(10);
64 }
65 
NoWaitTimeOut()66 static inline const base::TimeDelta NoWaitTimeOut() {
67   return base::TimeDelta::FromMicroseconds(0);
68 }
69 
AndroidVideoEncodeAccelerator(media::VideoEncodeAccelerator::Client * client)70 AndroidVideoEncodeAccelerator::AndroidVideoEncodeAccelerator(
71     media::VideoEncodeAccelerator::Client* client)
72     : client_ptr_factory_(client),
73       num_buffers_at_codec_(0),
74       num_output_buffers_(-1),
75       output_buffers_capacity_(0),
76       last_set_bitrate_(0) {}
77 
~AndroidVideoEncodeAccelerator()78 AndroidVideoEncodeAccelerator::~AndroidVideoEncodeAccelerator() {
79   DCHECK(thread_checker_.CalledOnValidThread());
80 }
81 
82 // static
83 std::vector<media::VideoEncodeAccelerator::SupportedProfile>
GetSupportedProfiles()84 AndroidVideoEncodeAccelerator::GetSupportedProfiles() {
85   std::vector<MediaCodecBridge::CodecsInfo> codecs_info =
86       MediaCodecBridge::GetCodecsInfo();
87 
88   std::vector<SupportedProfile> profiles;
89 
90   const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
91   if (cmd_line->HasSwitch(switches::kDisableWebRtcHWEncoding))
92     return profiles;
93 
94   for (size_t i = 0; i < codecs_info.size(); ++i) {
95     const MediaCodecBridge::CodecsInfo& info = codecs_info[i];
96     if (info.direction != media::MEDIA_CODEC_ENCODER || info.codecs != "vp8" ||
97         VideoCodecBridge::IsKnownUnaccelerated(media::kCodecVP8,
98                                                media::MEDIA_CODEC_ENCODER)) {
99       // We're only looking for a HW VP8 encoder.
100       continue;
101     }
102     SupportedProfile profile;
103     profile.profile = media::VP8PROFILE_MAIN;
104     // Wouldn't it be nice if MediaCodec exposed the maximum capabilities of the
105     // encoder?  Sure would be.  Too bad it doesn't.  So we hard-code some
106     // reasonable defaults.
107     profile.max_resolution.SetSize(1920, 1088);
108     profile.max_framerate.numerator = 30;
109     profile.max_framerate.denominator = 1;
110     profiles.push_back(profile);
111   }
112   return profiles;
113 }
114 
Initialize(VideoFrame::Format format,const gfx::Size & input_visible_size,media::VideoCodecProfile output_profile,uint32 initial_bitrate)115 void AndroidVideoEncodeAccelerator::Initialize(
116     VideoFrame::Format format,
117     const gfx::Size& input_visible_size,
118     media::VideoCodecProfile output_profile,
119     uint32 initial_bitrate) {
120   DVLOG(3) << __PRETTY_FUNCTION__ << " format: " << format
121            << ", input_visible_size: " << input_visible_size.ToString()
122            << ", output_profile: " << output_profile
123            << ", initial_bitrate: " << initial_bitrate;
124   DCHECK(!media_codec_);
125   DCHECK(thread_checker_.CalledOnValidThread());
126 
127   RETURN_ON_FAILURE(media::MediaCodecBridge::IsAvailable() &&
128                         media::MediaCodecBridge::SupportsSetParameters() &&
129                         format == VideoFrame::I420 &&
130                         output_profile == media::VP8PROFILE_MAIN,
131                     "Unexpected combo: " << format << ", " << output_profile,
132                     kInvalidArgumentError);
133 
134   last_set_bitrate_ = initial_bitrate;
135 
136   // Only consider using MediaCodec if it's likely backed by hardware.
137   RETURN_ON_FAILURE(!media::VideoCodecBridge::IsKnownUnaccelerated(
138                          media::kCodecVP8, media::MEDIA_CODEC_ENCODER),
139                     "No HW support",
140                     kPlatformFailureError);
141 
142   // TODO(fischman): when there is more HW out there with different color-space
143   // support, this should turn into a negotiation with the codec for supported
144   // formats.  For now we use the only format supported by the only available
145   // HW.
146   media_codec_.reset(
147       media::VideoCodecBridge::CreateEncoder(media::kCodecVP8,
148                                              input_visible_size,
149                                              initial_bitrate,
150                                              INITIAL_FRAMERATE,
151                                              IFRAME_INTERVAL,
152                                              COLOR_FORMAT_YUV420_SEMIPLANAR));
153 
154   RETURN_ON_FAILURE(
155       media_codec_,
156       "Failed to create/start the codec: " << input_visible_size.ToString(),
157       kPlatformFailureError);
158 
159   base::MessageLoop::current()->PostTask(
160       FROM_HERE,
161       base::Bind(&VideoEncodeAccelerator::Client::NotifyInitializeDone,
162                  client_ptr_factory_.GetWeakPtr()));
163 
164   num_output_buffers_ = media_codec_->GetOutputBuffersCount();
165   output_buffers_capacity_ = media_codec_->GetOutputBuffersCapacity();
166   base::MessageLoop::current()->PostTask(
167       FROM_HERE,
168       base::Bind(&VideoEncodeAccelerator::Client::RequireBitstreamBuffers,
169                  client_ptr_factory_.GetWeakPtr(),
170                  num_output_buffers_,
171                  input_visible_size,
172                  output_buffers_capacity_));
173 }
174 
MaybeStartIOTimer()175 void AndroidVideoEncodeAccelerator::MaybeStartIOTimer() {
176   if (!io_timer_.IsRunning() &&
177       (num_buffers_at_codec_ > 0 || !pending_frames_.empty())) {
178     io_timer_.Start(FROM_HERE,
179                     EncodePollDelay(),
180                     this,
181                     &AndroidVideoEncodeAccelerator::DoIOTask);
182   }
183 }
184 
MaybeStopIOTimer()185 void AndroidVideoEncodeAccelerator::MaybeStopIOTimer() {
186   if (io_timer_.IsRunning() &&
187       (num_buffers_at_codec_ == 0 && pending_frames_.empty())) {
188     io_timer_.Stop();
189   }
190 }
191 
Encode(const scoped_refptr<VideoFrame> & frame,bool force_keyframe)192 void AndroidVideoEncodeAccelerator::Encode(
193     const scoped_refptr<VideoFrame>& frame,
194     bool force_keyframe) {
195   DVLOG(3) << __PRETTY_FUNCTION__ << ": " << force_keyframe;
196   DCHECK(thread_checker_.CalledOnValidThread());
197   RETURN_ON_FAILURE(frame->format() == VideoFrame::I420,
198                     "Unexpected format",
199                     kInvalidArgumentError);
200 
201   // MediaCodec doesn't have a way to specify stride for non-Packed formats, so
202   // we insist on being called with packed frames and no cropping :(
203   RETURN_ON_FAILURE(frame->row_bytes(VideoFrame::kYPlane) ==
204                             frame->stride(VideoFrame::kYPlane) &&
205                         frame->row_bytes(VideoFrame::kUPlane) ==
206                             frame->stride(VideoFrame::kUPlane) &&
207                         frame->row_bytes(VideoFrame::kVPlane) ==
208                             frame->stride(VideoFrame::kVPlane) &&
209                         gfx::Rect(frame->coded_size()) == frame->visible_rect(),
210                     "Non-packed frame, or visible rect != coded size",
211                     kInvalidArgumentError);
212 
213   pending_frames_.push(MakeTuple(frame, force_keyframe, base::Time::Now()));
214   DoIOTask();
215 }
216 
UseOutputBitstreamBuffer(const media::BitstreamBuffer & buffer)217 void AndroidVideoEncodeAccelerator::UseOutputBitstreamBuffer(
218     const media::BitstreamBuffer& buffer) {
219   DVLOG(3) << __PRETTY_FUNCTION__ << ": bitstream_buffer_id=" << buffer.id();
220   DCHECK(thread_checker_.CalledOnValidThread());
221   RETURN_ON_FAILURE(buffer.size() >= media_codec_->GetOutputBuffersCapacity(),
222                     "Output buffers too small!",
223                     kInvalidArgumentError);
224   available_bitstream_buffers_.push_back(buffer);
225   DoIOTask();
226 }
227 
RequestEncodingParametersChange(uint32 bitrate,uint32 framerate)228 void AndroidVideoEncodeAccelerator::RequestEncodingParametersChange(
229     uint32 bitrate,
230     uint32 framerate) {
231   DVLOG(3) << __PRETTY_FUNCTION__ << ": bitrate: " << bitrate
232            << ", framerate: " << framerate;
233   DCHECK(thread_checker_.CalledOnValidThread());
234   if (bitrate != last_set_bitrate_) {
235     last_set_bitrate_ = bitrate;
236     media_codec_->SetVideoBitrate(bitrate);
237   }
238   // Note: Android's MediaCodec doesn't allow mid-stream adjustments to
239   // framerate, so we ignore that here.  This is OK because Android only uses
240   // the framerate value from MediaFormat during configure() as a proxy for
241   // bitrate, and we set that explicitly.
242 }
243 
Destroy()244 void AndroidVideoEncodeAccelerator::Destroy() {
245   DVLOG(3) << __PRETTY_FUNCTION__;
246   DCHECK(thread_checker_.CalledOnValidThread());
247   client_ptr_factory_.InvalidateWeakPtrs();
248   if (media_codec_) {
249     if (io_timer_.IsRunning())
250       io_timer_.Stop();
251     media_codec_->Stop();
252   }
253   delete this;
254 }
255 
DoIOTask()256 void AndroidVideoEncodeAccelerator::DoIOTask() {
257   QueueInput();
258   DequeueOutput();
259   MaybeStartIOTimer();
260   MaybeStopIOTimer();
261 }
262 
QueueInput()263 void AndroidVideoEncodeAccelerator::QueueInput() {
264   if (!client_ptr_factory_.GetWeakPtr() || pending_frames_.empty())
265     return;
266 
267   int input_buf_index = 0;
268   media::MediaCodecStatus status =
269       media_codec_->DequeueInputBuffer(NoWaitTimeOut(), &input_buf_index);
270   if (status != media::MEDIA_CODEC_OK) {
271     DCHECK(status == media::MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER ||
272            status == media::MEDIA_CODEC_ERROR);
273     RETURN_ON_FAILURE(status != media::MEDIA_CODEC_ERROR,
274                       "MediaCodec error",
275                       kPlatformFailureError);
276     return;
277   }
278 
279   const PendingFrames::value_type& input = pending_frames_.front();
280   bool is_key_frame = input.b;
281   if (is_key_frame) {
282     // Ideally MediaCodec would honor BUFFER_FLAG_SYNC_FRAME so we could
283     // indicate this in the QueueInputBuffer() call below and guarantee _this_
284     // frame be encoded as a key frame, but sadly that flag is ignored.
285     // Instead, we request a key frame "soon".
286     media_codec_->RequestKeyFrameSoon();
287   }
288   scoped_refptr<VideoFrame> frame = input.a;
289 
290   uint8* buffer = NULL;
291   size_t capacity = 0;
292   media_codec_->GetInputBuffer(input_buf_index, &buffer, &capacity);
293 
294   size_t queued_size =
295       VideoFrame::AllocationSize(VideoFrame::I420, frame->coded_size());
296   RETURN_ON_FAILURE(capacity >= queued_size,
297                     "Failed to get input buffer: " << input_buf_index,
298                     kPlatformFailureError);
299 
300   uint8* dst_y = buffer;
301   int dst_stride_y = frame->stride(VideoFrame::kYPlane);
302   uint8* dst_uv = buffer + frame->stride(VideoFrame::kYPlane) *
303                                frame->rows(VideoFrame::kYPlane);
304   int dst_stride_uv = frame->stride(VideoFrame::kUPlane) * 2;
305   // Why NV12?  Because COLOR_FORMAT_YUV420_SEMIPLANAR.  See comment at other
306   // mention of that constant.
307   bool converted = !libyuv::I420ToNV12(frame->data(VideoFrame::kYPlane),
308                                        frame->stride(VideoFrame::kYPlane),
309                                        frame->data(VideoFrame::kUPlane),
310                                        frame->stride(VideoFrame::kUPlane),
311                                        frame->data(VideoFrame::kVPlane),
312                                        frame->stride(VideoFrame::kVPlane),
313                                        dst_y,
314                                        dst_stride_y,
315                                        dst_uv,
316                                        dst_stride_uv,
317                                        frame->coded_size().width(),
318                                        frame->coded_size().height());
319   RETURN_ON_FAILURE(converted, "Failed to I420ToNV12!", kPlatformFailureError);
320 
321   fake_input_timestamp_ += base::TimeDelta::FromMicroseconds(1);
322   status = media_codec_->QueueInputBuffer(
323       input_buf_index, NULL, queued_size, fake_input_timestamp_);
324   UMA_HISTOGRAM_TIMES("Media.AVEA.InputQueueTime", base::Time::Now() - input.c);
325   RETURN_ON_FAILURE(status == media::MEDIA_CODEC_OK,
326                     "Failed to QueueInputBuffer: " << status,
327                     kPlatformFailureError);
328   ++num_buffers_at_codec_;
329   pending_frames_.pop();
330 }
331 
DoOutputBuffersSuffice()332 bool AndroidVideoEncodeAccelerator::DoOutputBuffersSuffice() {
333   // If this returns false ever, then the VEA::Client interface will need to
334   // grow a DismissBitstreamBuffer() call, and VEA::Client impls will have to be
335   // prepared to field multiple requests to RequireBitstreamBuffers().
336   int count = media_codec_->GetOutputBuffersCount();
337   size_t capacity = media_codec_->GetOutputBuffersCapacity();
338   bool ret = media_codec_->GetOutputBuffers() && count <= num_output_buffers_ &&
339              capacity <= output_buffers_capacity_;
340   LOG_IF(ERROR, !ret) << "Need more/bigger buffers; before: "
341                       << num_output_buffers_ << "x" << output_buffers_capacity_
342                       << ", now: " << count << "x" << capacity;
343   UMA_HISTOGRAM_BOOLEAN("Media.AVEA.OutputBuffersSuffice", ret);
344   return ret;
345 }
346 
DequeueOutput()347 void AndroidVideoEncodeAccelerator::DequeueOutput() {
348   if (!client_ptr_factory_.GetWeakPtr() ||
349       available_bitstream_buffers_.empty() || num_buffers_at_codec_ == 0) {
350     return;
351   }
352 
353   int32 buf_index = 0;
354   size_t offset = 0;
355   size_t size = 0;
356   bool key_frame = false;
357   do {
358     media::MediaCodecStatus status = media_codec_->DequeueOutputBuffer(
359         NoWaitTimeOut(), &buf_index, &offset, &size, NULL, NULL, &key_frame);
360     switch (status) {
361       case media::MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER:
362         return;
363 
364       case media::MEDIA_CODEC_ERROR:
365         RETURN_ON_FAILURE(false, "Codec error", kPlatformFailureError);
366         // Unreachable because of previous statement, but included for clarity.
367         return;
368 
369       case media::MEDIA_CODEC_OUTPUT_FORMAT_CHANGED:  // Fall-through.
370       case media::MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED:
371         RETURN_ON_FAILURE(DoOutputBuffersSuffice(),
372                           "Bitstream now requires more/larger buffers",
373                           kPlatformFailureError);
374         break;
375 
376       case media::MEDIA_CODEC_OK:
377         DCHECK_GE(buf_index, 0);
378         break;
379 
380       default:
381         NOTREACHED();
382         break;
383     }
384   } while (buf_index < 0);
385 
386   media::BitstreamBuffer bitstream_buffer = available_bitstream_buffers_.back();
387   available_bitstream_buffers_.pop_back();
388   scoped_ptr<base::SharedMemory> shm(
389       new base::SharedMemory(bitstream_buffer.handle(), false));
390   RETURN_ON_FAILURE(shm->Map(bitstream_buffer.size()),
391                     "Failed to map SHM",
392                     kPlatformFailureError);
393   RETURN_ON_FAILURE(size <= shm->mapped_size(),
394                     "Encoded buffer too large: " << size << ">"
395                                                  << shm->mapped_size(),
396                     kPlatformFailureError);
397 
398   media_codec_->CopyFromOutputBuffer(buf_index, offset, shm->memory(), size);
399   media_codec_->ReleaseOutputBuffer(buf_index, false);
400   --num_buffers_at_codec_;
401 
402   UMA_HISTOGRAM_COUNTS_10000("Media.AVEA.EncodedBufferSizeKB", size / 1024);
403   base::MessageLoop::current()->PostTask(
404       FROM_HERE,
405       base::Bind(&VideoEncodeAccelerator::Client::BitstreamBufferReady,
406                  client_ptr_factory_.GetWeakPtr(),
407                  bitstream_buffer.id(),
408                  size,
409                  key_frame));
410 }
411 
412 }  // namespace content
413