Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(294)

Side by Side Diff: content/common/gpu/media/android_video_encode_accelerator.cc

Issue 74563002: AndroidVideoEncodeAccelerator is born! (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 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/logging.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/metrics/histogram.h"
11 #include "content/common/gpu/gpu_channel.h"
12 #include "gpu/command_buffer/service/gles2_cmd_decoder.h"
13 #include "media/base/android/media_codec_bridge.h"
14 #include "media/base/bitstream_buffer.h"
15 #include "media/base/limits.h"
16 #include "media/video/picture.h"
17 #include "third_party/libyuv/include/libyuv/convert_from.h"
18 #include "ui/gl/android/scoped_java_surface.h"
19 #include "ui/gl/gl_bindings.h"
20
21 using media::MediaCodecBridge;
22 using media::VideoCodecBridge;
23 using media::VideoFrame;
24
25 namespace content {
26
27 enum {
28 // Subset of MediaCodecInfo.CodecCapabilities.
xhwang 2013/11/19 00:11:25 Should this be a MediaCodecBridge constant? It'd b
Ami GONE FROM CHROMIUM 2013/11/21 22:59:07 Maybe; I think the real answer is to convert Media
29 kColorFormatYUV420SemiPlanar = 21,
xhwang 2013/11/19 00:11:25 ditto about enum naming style
Ami GONE FROM CHROMIUM 2013/11/21 22:59:07 Done.
30 };
31
32 // Helper macros for dealing with failure. If |result| evaluates false, emit
33 // |log| to DLOG(ERROR), register |error| with the client, and return.
34 #define RETURN_ON_FAILURE(result, log, error) \
35 do { \
36 if (!(result)) { \
37 DLOG(ERROR) << log; \
38 if (client_ptr_factory_.GetWeakPtr()) { \
39 client_ptr_factory_.GetWeakPtr()->NotifyError(error); \
40 client_ptr_factory_.InvalidateWeakPtrs(); \
41 } \
42 return; \
43 } \
44 } while (0)
45
46 static inline const base::TimeDelta EncodePollDelay() {
47 return base::TimeDelta::FromMilliseconds(10);
xhwang 2013/11/19 00:11:25 // arbitrary choice?
Ami GONE FROM CHROMIUM 2013/11/21 22:59:07 Done.
48 }
49
50 static inline const base::TimeDelta NoWaitTimeOut() {
51 return base::TimeDelta::FromMicroseconds(0);
52 }
53
54 AndroidVideoEncodeAccelerator::AndroidVideoEncodeAccelerator(
55 media::VideoEncodeAccelerator::Client* client)
56 : client_ptr_factory_(client),
57 num_buffers_at_codec_(0),
58 num_output_buffers_(-1),
59 output_buffers_capacity_(0),
60 last_set_bitrate_(0) {}
61
62 AndroidVideoEncodeAccelerator::~AndroidVideoEncodeAccelerator() {
63 DCHECK(thread_checker_.CalledOnValidThread());
64 }
65
66 // static
67 std::vector<media::VideoEncodeAccelerator::SupportedProfile>
68 AndroidVideoEncodeAccelerator::GetSupportedProfiles() {
69 std::vector<MediaCodecBridge::CodecsInfo> codecs_info =
70 MediaCodecBridge::GetCodecsInfo();
71
72 std::vector<SupportedProfile> profiles;
73 for (size_t i = 0; i < codecs_info.size(); ++i) {
74 const MediaCodecBridge::CodecsInfo& info = codecs_info[i];
75 if (!info.is_encoder || info.codecs != "vp8" ||
76 VideoCodecBridge::IsKnownUnaccelerated(media::kCodecVP8, true)) {
xhwang 2013/11/19 00:11:25 Comment that we are only looking for "hardware vp8
Ami GONE FROM CHROMIUM 2013/11/21 22:59:07 Done.
77 continue;
78 }
79 SupportedProfile profile;
80 profile.profile = media::VP8PROFILE_MAIN;
81 profile.max_resolution.SetSize(1920, 1088);
82 profile.max_framerate.numerator = 30;
xhwang 2013/11/19 00:11:25 Where are the max resolution and size coming from?
Ami GONE FROM CHROMIUM 2013/11/21 22:59:07 I don't think it's worth hoisting these to named c
83 profile.max_framerate.denominator = 1;
84 profiles.push_back(profile);
85 }
86 return profiles;
87 }
88
89 void AndroidVideoEncodeAccelerator::Initialize(
90 VideoFrame::Format format, const gfx::Size& input_visible_size,
91 media::VideoCodecProfile output_profile, uint32 initial_bitrate) {
92 DVLOG(3) << "AndroidVideoEncodeAccelerator::Initialize: format: " << format
93 << ", input_visible_size: " << input_visible_size.ToString()
94 << ", output_profile: " << output_profile
95 << ", initial_bitrate: " << initial_bitrate;
96 DCHECK(!media_codec_);
97 DCHECK(thread_checker_.CalledOnValidThread());
98
99 RETURN_ON_FAILURE(media::MediaCodecBridge::IsAvailable() &&
100 media::MediaCodecBridge::SupportsSetParameters() &&
101 format == VideoFrame::I420 &&
102 output_profile == media::VP8PROFILE_MAIN,
103 "Unexpected combo: " << format << ", " << output_profile,
104 kInvalidArgumentError);
105
106 last_set_bitrate_ = initial_bitrate;
107
108 // Only consider using MediaCodec if it's likely backed by hardware.
109 RETURN_ON_FAILURE(
110 !media::VideoCodecBridge::IsKnownUnaccelerated(media::kCodecVP8, true),
111 "No HW support", kPlatformFailureError);
112
113 // TODO(fischman): when there is more HW out there with different color-space
114 // support, this should turn into a negotiation with the codec for supported
115 // formats. For now we use the only format supported by the only available
116 // HW.
117 media_codec_.reset(media::VideoCodecBridge::CreateEncoder(
118 media::kCodecVP8, input_visible_size, initial_bitrate, kInitialFramerate,
119 kIFrameInterval, kColorFormatYUV420SemiPlanar));
120
121 RETURN_ON_FAILURE(media_codec_, "Failed to create/start the codec: "
122 << input_visible_size.ToString(),
123 kPlatformFailureError);
124
125 base::MessageLoop::current()->PostTask(
126 FROM_HERE,
127 base::Bind(&VideoEncodeAccelerator::Client::NotifyInitializeDone,
128 client_ptr_factory_.GetWeakPtr()));
129
130 num_output_buffers_ = media_codec_->GetOutputBuffersCount();
131 output_buffers_capacity_ = media_codec_->GetOutputBuffersCapacity();
132 base::MessageLoop::current()->PostTask(
133 FROM_HERE,
134 base::Bind(&VideoEncodeAccelerator::Client::RequireBitstreamBuffers,
135 client_ptr_factory_.GetWeakPtr(), num_output_buffers_,
136 input_visible_size, output_buffers_capacity_));
137 }
138
139 void AndroidVideoEncodeAccelerator::MaybeStartIOTimer() {
140 if (!io_timer_.IsRunning() &&
141 (num_buffers_at_codec_ > 0 || !pending_frames_.empty())) {
142 io_timer_.Start(FROM_HERE, EncodePollDelay(), this,
143 &AndroidVideoEncodeAccelerator::DoIOTask);
144 }
145 }
146
147 void AndroidVideoEncodeAccelerator::MaybeStopIOTimer() {
148 if (io_timer_.IsRunning() &&
149 (num_buffers_at_codec_ == 0 && pending_frames_.empty())) {
150 io_timer_.Stop();
151 }
152 }
153
154 void AndroidVideoEncodeAccelerator::Encode(
155 const scoped_refptr<VideoFrame>& frame, bool force_keyframe) {
156 DVLOG(3) << "AndroidVideoEncodeAccelerator::Encode: " << force_keyframe;
xhwang 2013/11/19 00:11:25 Here and below, will __FUNCTION__ work for you?
Ami GONE FROM CHROMIUM 2013/11/21 22:59:07 __PRETTY_FUNCTION__, even, since we're guaranteed
157 DCHECK(thread_checker_.CalledOnValidThread());
158 RETURN_ON_FAILURE(frame->format() == VideoFrame::I420, "Unexpected format",
159 kInvalidArgumentError);
160
161 // MediaCodec doesn't have a way to specify stride for non-Packed formats, so
162 // we insist on being called with packed frames and no cropping :(
163 RETURN_ON_FAILURE(frame->row_bytes(VideoFrame::kYPlane) ==
164 frame->stride(VideoFrame::kYPlane) &&
165 frame->row_bytes(VideoFrame::kUPlane) ==
166 frame->stride(VideoFrame::kUPlane) &&
167 frame->row_bytes(VideoFrame::kVPlane) ==
168 frame->stride(VideoFrame::kVPlane) &&
169 gfx::Rect(frame->coded_size()) == frame->visible_rect(),
170 "Non-packed frame, or visible rect != coded size",
171 kInvalidArgumentError);
172
173 pending_frames_.push(MakeTuple(frame, force_keyframe, base::Time::Now()));
174 DoIOTask();
175 }
176
177 void AndroidVideoEncodeAccelerator::UseOutputBitstreamBuffer(
xhwang 2013/11/19 00:11:25 nit comment for the interface: "Use" is more like
Ami GONE FROM CHROMIUM 2013/11/21 22:59:07 "Provide" in the decode API is "request", so... ;)
178 const media::BitstreamBuffer& buffer) {
179 DVLOG(3) << "AndroidVideoEncodeAccelerator::UseOutputBitstreamBuffer: "
180 << "bitstream_buffer_id=" << buffer.id();
181 DCHECK(thread_checker_.CalledOnValidThread());
182 RETURN_ON_FAILURE(buffer.size() >= media_codec_->GetOutputBuffersCapacity(),
183 "Output buffers too small!", kInvalidArgumentError);
184 available_bitstream_buffers_.push_back(buffer);
185 DoIOTask();
186 }
187
188 void AndroidVideoEncodeAccelerator::RequestEncodingParametersChange(
189 uint32 bitrate, uint32 framerate) {
190 DVLOG(3) << "AndroidVideoEncodeAccelerator::RequestEncodingParametersChange: "
191 << "bitrate: " << bitrate << ", framerate: " << framerate;
192 DCHECK(thread_checker_.CalledOnValidThread());
193 if (bitrate != last_set_bitrate_) {
194 last_set_bitrate_ = bitrate;
195 media_codec_->SetVideoBitrate(bitrate);
196 }
197 // Note: Android's MediaCodec doesn't allow mid-stream adjustments to
198 // framerate, so we ignore that here. This is OK because Android only uses
199 // the framerate value from MediaFormat during configure() as a proxy for
200 // bitrate, and we set that explicitly.
xhwang 2013/11/19 00:11:25 Will we hit unused variable for |framerate| in rel
Ami GONE FROM CHROMIUM 2013/11/21 22:59:07 Unused warnings don't apply to function parameters
201 }
202
203 void AndroidVideoEncodeAccelerator::Destroy() {
204 DVLOG(3) << "AndroidVideoEncodeAccelerator::Destroy";
205 DCHECK(thread_checker_.CalledOnValidThread());
206 client_ptr_factory_.InvalidateWeakPtrs();
207 if (media_codec_) {
208 if (io_timer_.IsRunning()) io_timer_.Stop();
209 media_codec_->Stop();
210 }
211 delete this;
212 }
213
214 void AndroidVideoEncodeAccelerator::DoIOTask() {
215 QueueInput();
216 DequeueOutput();
217 MaybeStartIOTimer();
218 MaybeStopIOTimer();
219 }
220
221 void AndroidVideoEncodeAccelerator::QueueInput() {
222 if (!client_ptr_factory_.GetWeakPtr() || pending_frames_.empty()) return;
xhwang 2013/11/19 00:11:25 nit: I like your use of if-return-in-one-line in m
Ami GONE FROM CHROMIUM 2013/11/21 22:59:07 I obey clang-format. Wrapped now.
223
224 int input_buf_index = 0;
225 media::MediaCodecStatus status =
226 media_codec_->DequeueInputBuffer(NoWaitTimeOut(), &input_buf_index);
227 if (status != media::MEDIA_CODEC_OK) {
228 DCHECK(status == media::MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER ||
229 status == media::MEDIA_CODEC_ERROR);
230 RETURN_ON_FAILURE(status != media::MEDIA_CODEC_ERROR, "MediaCodec error",
231 kPlatformFailureError);
232 return;
233 }
234
235 const PendingFrames::value_type& input = pending_frames_.front();
236 if (input.b) {
xhwang 2013/11/19 00:11:25 I have to go to the .h file to check what "b" is.
Ami GONE FROM CHROMIUM 2013/11/21 22:59:07 Done, except I refuse to use bool& instead of bool
237 // Ideally MediaCodec would honor BUFFER_FLAG_SYNC_FRAME so we could
238 // indicate this in the QueueInputBuffer() call below and guarantee _this_
239 // frame be encoded as a key frame, but sadly that flag is ignored.
240 // Instead, we request a key frame "soon".
241 media_codec_->RequestKeyFrameSoon();
242 }
243 scoped_refptr<VideoFrame> frame = input.a;
244
245 uint8* buffer = NULL;
246 size_t capacity = 0;
247 media_codec_->GetInputBuffer(input_buf_index, &buffer, &capacity);
248
249 size_t queued_size =
250 frame->AllocationSize(VideoFrame::I420, frame->coded_size());
xhwang 2013/11/19 00:11:25 VideoFrame::AllocationSize()?
Ami GONE FROM CHROMIUM 2013/11/21 22:59:07 Done.
251 RETURN_ON_FAILURE(capacity >= queued_size,
252 "Failed to get input buffer: " << input_buf_index,
253 kPlatformFailureError);
254
255 uint8* dst_y = buffer;
256 int dst_stride_y = frame->stride(VideoFrame::kYPlane);
257 uint8* dst_uv = buffer + frame->stride(VideoFrame::kYPlane) *
258 frame->rows(VideoFrame::kYPlane);
259 int dst_stride_uv = frame->stride(VideoFrame::kUPlane) * 2;
260 // Why NV12? Because kColorFormatYUV420SemiPlanar. See comment at other
261 // mention of that constant.
262 bool converted =
263 !libyuv::I420ToNV12(
264 frame->data(VideoFrame::kYPlane), frame->stride(VideoFrame::kYPlane),
265 frame->data(VideoFrame::kUPlane), frame->stride(VideoFrame::kUPlane),
266 frame->data(VideoFrame::kVPlane), frame->stride(VideoFrame::kVPlane),
267 dst_y, dst_stride_y, dst_uv, dst_stride_uv,
268 frame->coded_size().width(), frame->coded_size().height());
269 RETURN_ON_FAILURE(converted, "Failed to I420ToNV12!", kPlatformFailureError);
270
271 fake_input_timestamp_ += base::TimeDelta::FromMicroseconds(1);
272 status = media_codec_->QueueInputBuffer(input_buf_index, NULL, queued_size,
273 fake_input_timestamp_);
274 UMA_HISTOGRAM_TIMES("Media.AVEA.InputQueueTime", base::Time::Now() - input.c);
275 RETURN_ON_FAILURE(status == media::MEDIA_CODEC_OK,
276 "Failed to QueueInputBuffer: " << status,
277 kPlatformFailureError);
278 ++num_buffers_at_codec_;
279 pending_frames_.pop();
280 }
281
282 bool AndroidVideoEncodeAccelerator::DoOutputBuffersSuffice() {
283 // If this returns false ever, then the VEA::Client interface will need to
284 // grow a DismissBitstreamBuffer() call, and VEA::Client impls will have to be
285 // prepared to field multiple requests to RequireBitstreamBuffers().
286 int count = media_codec_->GetOutputBuffersCount();
287 size_t capacity = media_codec_->GetOutputBuffersCapacity();
288 bool ret = media_codec_->GetOutputBuffers() && count <= num_output_buffers_ &&
289 capacity <= output_buffers_capacity_;
290 if (!ret) {
291 LOG(ERROR) << "Need more/bigger buffers; before: " << num_output_buffers_
xhwang 2013/11/19 00:11:25 Use LOG_IF(ERROR, !ret)?
Ami GONE FROM CHROMIUM 2013/11/21 22:59:07 Oh wow I never use LOG_IF. Done.
292 << "x" << output_buffers_capacity_ << ", now: " << count << "x"
293 << capacity;
294 }
295 UMA_HISTOGRAM_BOOLEAN("Media.AVEA.OutputBuffersSuffice", ret);
296 return ret;
297 }
298
299 void AndroidVideoEncodeAccelerator::DequeueOutput() {
300 if (!client_ptr_factory_.GetWeakPtr() ||
301 available_bitstream_buffers_.empty() || num_buffers_at_codec_ == 0) {
302 return;
303 }
304
305 int32 buf_index = 0;
306 size_t offset = 0;
307 size_t size = 0;
308 bool key_frame = false;
309 do {
310 media::MediaCodecStatus status = media_codec_->DequeueOutputBuffer(
311 NoWaitTimeOut(), &buf_index, &offset, &size, NULL, NULL, &key_frame);
312 switch (status) {
313 case media::MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER:
314 return;
315
316 case media::MEDIA_CODEC_ERROR:
317 RETURN_ON_FAILURE(false, "Codec error", kPlatformFailureError);
318 // Unreachable because of previous statement, but included for clarity.
319 return;
320
321 case media::MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: // Fall-through.
322 case media::MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED:
323 RETURN_ON_FAILURE(DoOutputBuffersSuffice(),
324 "Bitstream now requires more/larger buffers",
325 kPlatformFailureError);
326 break;
327
328 case media::MEDIA_CODEC_OK:
329 DCHECK_GE(buf_index, 0);
330 break;
331
332 default:
333 NOTREACHED();
334 break;
335 }
336 } while (buf_index < 0);
xhwang 2013/11/19 00:11:25 In what case would buf_index < 0?
Ami GONE FROM CHROMIUM 2013/11/21 22:59:07 In all of the cases where status!=MEDIA_CODEC_OK.
337
338 media::BitstreamBuffer bitstream_buffer = available_bitstream_buffers_.back();
339 available_bitstream_buffers_.pop_back();
340 scoped_ptr<base::SharedMemory> shm(
341 new base::SharedMemory(bitstream_buffer.handle(), false));
342 RETURN_ON_FAILURE(shm->Map(bitstream_buffer.size()), "Failed to map SHM",
343 kPlatformFailureError);
344 RETURN_ON_FAILURE(size <= shm->mapped_size(),
345 "Encoded buffer too large: " << size << ">"
346 << shm->mapped_size(),
347 kPlatformFailureError);
348
349 media_codec_->CopyFromOutputBuffer(buf_index, offset, shm->memory(), size);
350 media_codec_->ReleaseOutputBuffer(buf_index, false);
351 --num_buffers_at_codec_;
352
353 UMA_HISTOGRAM_COUNTS_10000("Media.AVEA.EncodedBufferSizeKB", size / 1024);
354 base::MessageLoop::current()->PostTask(
355 FROM_HERE,
356 base::Bind(&VideoEncodeAccelerator::Client::BitstreamBufferReady,
357 client_ptr_factory_.GetWeakPtr(), bitstream_buffer.id(), size,
358 key_frame));
359 }
360
361 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698