android_video_encode_accelerator.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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
23using media::MediaCodecBridge;
24using media::VideoCodecBridge;
25using media::VideoFrame;
26
27namespace content {
28
29enum {
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().
54static 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
66static inline const base::TimeDelta NoWaitTimeOut() {
67  return base::TimeDelta::FromMicroseconds(0);
68}
69
70AndroidVideoEncodeAccelerator::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
78AndroidVideoEncodeAccelerator::~AndroidVideoEncodeAccelerator() {
79  DCHECK(thread_checker_.CalledOnValidThread());
80}
81
82// static
83std::vector<media::VideoEncodeAccelerator::SupportedProfile>
84AndroidVideoEncodeAccelerator::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
115void 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
175void 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
185void AndroidVideoEncodeAccelerator::MaybeStopIOTimer() {
186  if (io_timer_.IsRunning() &&
187      (num_buffers_at_codec_ == 0 && pending_frames_.empty())) {
188    io_timer_.Stop();
189  }
190}
191
192void 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
217void 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
228void 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
244void 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
256void AndroidVideoEncodeAccelerator::DoIOTask() {
257  QueueInput();
258  DequeueOutput();
259  MaybeStartIOTimer();
260  MaybeStopIOTimer();
261}
262
263void 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
332bool 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
347void 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