OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/common/gpu/media/android_video_encode_accelerator.h" | 5 #include "content/common/gpu/media/android_video_encode_accelerator.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/command_line.h" | 8 #include "base/command_line.h" |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/message_loop/message_loop.h" | 10 #include "base/message_loop/message_loop.h" |
(...skipping 15 matching lines...) Expand all Loading... |
26 | 26 |
27 namespace content { | 27 namespace content { |
28 | 28 |
29 enum { | 29 enum { |
30 // Subset of MediaCodecInfo.CodecCapabilities. | 30 // Subset of MediaCodecInfo.CodecCapabilities. |
31 COLOR_FORMAT_YUV420_SEMIPLANAR = 21, | 31 COLOR_FORMAT_YUV420_SEMIPLANAR = 21, |
32 }; | 32 }; |
33 | 33 |
34 // Helper macros for dealing with failure. If |result| evaluates false, emit | 34 // Helper macros for dealing with failure. If |result| evaluates false, emit |
35 // |log| to DLOG(ERROR), register |error| with the client, and return. | 35 // |log| to DLOG(ERROR), register |error| with the client, and return. |
36 #define RETURN_ON_FAILURE(result, log, error) \ | 36 #define RETURN_ON_FAILURE(result, log, error) \ |
37 do { \ | 37 do { \ |
38 if (!(result)) { \ | 38 if (!(result)) { \ |
39 DLOG(ERROR) << log; \ | 39 DLOG(ERROR) << log; \ |
40 if (client_ptr_factory_.GetWeakPtr()) { \ | 40 if (client_ptr_factory_->GetWeakPtr()) { \ |
41 client_ptr_factory_.GetWeakPtr()->NotifyError(error); \ | 41 client_ptr_factory_->GetWeakPtr()->NotifyError(error); \ |
42 client_ptr_factory_.InvalidateWeakPtrs(); \ | 42 client_ptr_factory_.reset(); \ |
43 } \ | 43 } \ |
44 return; \ | 44 return; \ |
45 } \ | 45 } \ |
46 } while (0) | 46 } while (0) |
47 | 47 |
48 // Because MediaCodec is thread-hostile (must be poked on a single thread) and | 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 | 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 | 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 | 51 // saturated). This function defines the polling delay. The value used is an |
52 // arbitrary choice that trades off CPU utilization (spinning) against latency. | 52 // arbitrary choice that trades off CPU utilization (spinning) against latency. |
53 // Mirrors android_video_decode_accelerator.cc::DecodePollDelay(). | 53 // Mirrors android_video_decode_accelerator.cc::DecodePollDelay(). |
54 static inline const base::TimeDelta EncodePollDelay() { | 54 static inline const base::TimeDelta EncodePollDelay() { |
55 // An alternative to this polling scheme could be to dedicate a new thread | 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 | 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 | 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 | 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 | 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 | 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 | 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). | 62 // reasonably device-agnostic way to fill in the "believes" above). |
63 return base::TimeDelta::FromMilliseconds(10); | 63 return base::TimeDelta::FromMilliseconds(10); |
64 } | 64 } |
65 | 65 |
66 static inline const base::TimeDelta NoWaitTimeOut() { | 66 static inline const base::TimeDelta NoWaitTimeOut() { |
67 return base::TimeDelta::FromMicroseconds(0); | 67 return base::TimeDelta::FromMicroseconds(0); |
68 } | 68 } |
69 | 69 |
70 AndroidVideoEncodeAccelerator::AndroidVideoEncodeAccelerator( | 70 AndroidVideoEncodeAccelerator::AndroidVideoEncodeAccelerator() |
71 media::VideoEncodeAccelerator::Client* client) | 71 : num_buffers_at_codec_(0), |
72 : client_ptr_factory_(client), | |
73 num_buffers_at_codec_(0), | |
74 num_output_buffers_(-1), | 72 num_output_buffers_(-1), |
75 output_buffers_capacity_(0), | 73 output_buffers_capacity_(0), |
76 last_set_bitrate_(0) {} | 74 last_set_bitrate_(0) {} |
77 | 75 |
78 AndroidVideoEncodeAccelerator::~AndroidVideoEncodeAccelerator() { | 76 AndroidVideoEncodeAccelerator::~AndroidVideoEncodeAccelerator() { |
79 DCHECK(thread_checker_.CalledOnValidThread()); | 77 DCHECK(thread_checker_.CalledOnValidThread()); |
80 } | 78 } |
81 | 79 |
82 // static | 80 // static |
83 std::vector<media::VideoEncodeAccelerator::SupportedProfile> | 81 std::vector<media::VideoEncodeAccelerator::SupportedProfile> |
(...skipping 27 matching lines...) Expand all Loading... |
111 profile.max_framerate.denominator = 1; | 109 profile.max_framerate.denominator = 1; |
112 profiles.push_back(profile); | 110 profiles.push_back(profile); |
113 } | 111 } |
114 return profiles; | 112 return profiles; |
115 } | 113 } |
116 | 114 |
117 void AndroidVideoEncodeAccelerator::Initialize( | 115 void AndroidVideoEncodeAccelerator::Initialize( |
118 VideoFrame::Format format, | 116 VideoFrame::Format format, |
119 const gfx::Size& input_visible_size, | 117 const gfx::Size& input_visible_size, |
120 media::VideoCodecProfile output_profile, | 118 media::VideoCodecProfile output_profile, |
121 uint32 initial_bitrate) { | 119 uint32 initial_bitrate, |
| 120 Client* client) { |
122 DVLOG(3) << __PRETTY_FUNCTION__ << " format: " << format | 121 DVLOG(3) << __PRETTY_FUNCTION__ << " format: " << format |
123 << ", input_visible_size: " << input_visible_size.ToString() | 122 << ", input_visible_size: " << input_visible_size.ToString() |
124 << ", output_profile: " << output_profile | 123 << ", output_profile: " << output_profile |
125 << ", initial_bitrate: " << initial_bitrate; | 124 << ", initial_bitrate: " << initial_bitrate; |
126 DCHECK(!media_codec_); | 125 DCHECK(!media_codec_); |
127 DCHECK(thread_checker_.CalledOnValidThread()); | 126 DCHECK(thread_checker_.CalledOnValidThread()); |
128 | 127 |
| 128 client_ptr_factory_.reset(new base::WeakPtrFactory<Client>(client)); |
| 129 |
129 RETURN_ON_FAILURE(media::MediaCodecBridge::IsAvailable() && | 130 RETURN_ON_FAILURE(media::MediaCodecBridge::IsAvailable() && |
130 media::MediaCodecBridge::SupportsSetParameters() && | 131 media::MediaCodecBridge::SupportsSetParameters() && |
131 format == VideoFrame::I420 && | 132 format == VideoFrame::I420 && |
132 output_profile == media::VP8PROFILE_MAIN, | 133 output_profile == media::VP8PROFILE_MAIN, |
133 "Unexpected combo: " << format << ", " << output_profile, | 134 "Unexpected combo: " << format << ", " << output_profile, |
134 kInvalidArgumentError); | 135 kInvalidArgumentError); |
135 | 136 |
136 last_set_bitrate_ = initial_bitrate; | 137 last_set_bitrate_ = initial_bitrate; |
137 | 138 |
138 // Only consider using MediaCodec if it's likely backed by hardware. | 139 // Only consider using MediaCodec if it's likely backed by hardware. |
(...skipping 15 matching lines...) Expand all Loading... |
154 COLOR_FORMAT_YUV420_SEMIPLANAR)); | 155 COLOR_FORMAT_YUV420_SEMIPLANAR)); |
155 | 156 |
156 RETURN_ON_FAILURE( | 157 RETURN_ON_FAILURE( |
157 media_codec_, | 158 media_codec_, |
158 "Failed to create/start the codec: " << input_visible_size.ToString(), | 159 "Failed to create/start the codec: " << input_visible_size.ToString(), |
159 kPlatformFailureError); | 160 kPlatformFailureError); |
160 | 161 |
161 base::MessageLoop::current()->PostTask( | 162 base::MessageLoop::current()->PostTask( |
162 FROM_HERE, | 163 FROM_HERE, |
163 base::Bind(&VideoEncodeAccelerator::Client::NotifyInitializeDone, | 164 base::Bind(&VideoEncodeAccelerator::Client::NotifyInitializeDone, |
164 client_ptr_factory_.GetWeakPtr())); | 165 client_ptr_factory_->GetWeakPtr())); |
165 | 166 |
166 num_output_buffers_ = media_codec_->GetOutputBuffersCount(); | 167 num_output_buffers_ = media_codec_->GetOutputBuffersCount(); |
167 output_buffers_capacity_ = media_codec_->GetOutputBuffersCapacity(); | 168 output_buffers_capacity_ = media_codec_->GetOutputBuffersCapacity(); |
168 base::MessageLoop::current()->PostTask( | 169 base::MessageLoop::current()->PostTask( |
169 FROM_HERE, | 170 FROM_HERE, |
170 base::Bind(&VideoEncodeAccelerator::Client::RequireBitstreamBuffers, | 171 base::Bind(&VideoEncodeAccelerator::Client::RequireBitstreamBuffers, |
171 client_ptr_factory_.GetWeakPtr(), | 172 client_ptr_factory_->GetWeakPtr(), |
172 num_output_buffers_, | 173 num_output_buffers_, |
173 input_visible_size, | 174 input_visible_size, |
174 output_buffers_capacity_)); | 175 output_buffers_capacity_)); |
175 } | 176 } |
176 | 177 |
177 void AndroidVideoEncodeAccelerator::MaybeStartIOTimer() { | 178 void AndroidVideoEncodeAccelerator::MaybeStartIOTimer() { |
178 if (!io_timer_.IsRunning() && | 179 if (!io_timer_.IsRunning() && |
179 (num_buffers_at_codec_ > 0 || !pending_frames_.empty())) { | 180 (num_buffers_at_codec_ > 0 || !pending_frames_.empty())) { |
180 io_timer_.Start(FROM_HERE, | 181 io_timer_.Start(FROM_HERE, |
181 EncodePollDelay(), | 182 EncodePollDelay(), |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
239 } | 240 } |
240 // Note: Android's MediaCodec doesn't allow mid-stream adjustments to | 241 // Note: Android's MediaCodec doesn't allow mid-stream adjustments to |
241 // framerate, so we ignore that here. This is OK because Android only uses | 242 // framerate, so we ignore that here. This is OK because Android only uses |
242 // the framerate value from MediaFormat during configure() as a proxy for | 243 // the framerate value from MediaFormat during configure() as a proxy for |
243 // bitrate, and we set that explicitly. | 244 // bitrate, and we set that explicitly. |
244 } | 245 } |
245 | 246 |
246 void AndroidVideoEncodeAccelerator::Destroy() { | 247 void AndroidVideoEncodeAccelerator::Destroy() { |
247 DVLOG(3) << __PRETTY_FUNCTION__; | 248 DVLOG(3) << __PRETTY_FUNCTION__; |
248 DCHECK(thread_checker_.CalledOnValidThread()); | 249 DCHECK(thread_checker_.CalledOnValidThread()); |
249 client_ptr_factory_.InvalidateWeakPtrs(); | 250 client_ptr_factory_.reset(); |
250 if (media_codec_) { | 251 if (media_codec_) { |
251 if (io_timer_.IsRunning()) | 252 if (io_timer_.IsRunning()) |
252 io_timer_.Stop(); | 253 io_timer_.Stop(); |
253 media_codec_->Stop(); | 254 media_codec_->Stop(); |
254 } | 255 } |
255 delete this; | 256 delete this; |
256 } | 257 } |
257 | 258 |
258 void AndroidVideoEncodeAccelerator::DoIOTask() { | 259 void AndroidVideoEncodeAccelerator::DoIOTask() { |
259 QueueInput(); | 260 QueueInput(); |
260 DequeueOutput(); | 261 DequeueOutput(); |
261 MaybeStartIOTimer(); | 262 MaybeStartIOTimer(); |
262 MaybeStopIOTimer(); | 263 MaybeStopIOTimer(); |
263 } | 264 } |
264 | 265 |
265 void AndroidVideoEncodeAccelerator::QueueInput() { | 266 void AndroidVideoEncodeAccelerator::QueueInput() { |
266 if (!client_ptr_factory_.GetWeakPtr() || pending_frames_.empty()) | 267 if (!client_ptr_factory_->GetWeakPtr() || pending_frames_.empty()) |
267 return; | 268 return; |
268 | 269 |
269 int input_buf_index = 0; | 270 int input_buf_index = 0; |
270 media::MediaCodecStatus status = | 271 media::MediaCodecStatus status = |
271 media_codec_->DequeueInputBuffer(NoWaitTimeOut(), &input_buf_index); | 272 media_codec_->DequeueInputBuffer(NoWaitTimeOut(), &input_buf_index); |
272 if (status != media::MEDIA_CODEC_OK) { | 273 if (status != media::MEDIA_CODEC_OK) { |
273 DCHECK(status == media::MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER || | 274 DCHECK(status == media::MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER || |
274 status == media::MEDIA_CODEC_ERROR); | 275 status == media::MEDIA_CODEC_ERROR); |
275 RETURN_ON_FAILURE(status != media::MEDIA_CODEC_ERROR, | 276 RETURN_ON_FAILURE(status != media::MEDIA_CODEC_ERROR, |
276 "MediaCodec error", | 277 "MediaCodec error", |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
340 bool ret = media_codec_->GetOutputBuffers() && count <= num_output_buffers_ && | 341 bool ret = media_codec_->GetOutputBuffers() && count <= num_output_buffers_ && |
341 capacity <= output_buffers_capacity_; | 342 capacity <= output_buffers_capacity_; |
342 LOG_IF(ERROR, !ret) << "Need more/bigger buffers; before: " | 343 LOG_IF(ERROR, !ret) << "Need more/bigger buffers; before: " |
343 << num_output_buffers_ << "x" << output_buffers_capacity_ | 344 << num_output_buffers_ << "x" << output_buffers_capacity_ |
344 << ", now: " << count << "x" << capacity; | 345 << ", now: " << count << "x" << capacity; |
345 UMA_HISTOGRAM_BOOLEAN("Media.AVEA.OutputBuffersSuffice", ret); | 346 UMA_HISTOGRAM_BOOLEAN("Media.AVEA.OutputBuffersSuffice", ret); |
346 return ret; | 347 return ret; |
347 } | 348 } |
348 | 349 |
349 void AndroidVideoEncodeAccelerator::DequeueOutput() { | 350 void AndroidVideoEncodeAccelerator::DequeueOutput() { |
350 if (!client_ptr_factory_.GetWeakPtr() || | 351 if (!client_ptr_factory_->GetWeakPtr() || |
351 available_bitstream_buffers_.empty() || num_buffers_at_codec_ == 0) { | 352 available_bitstream_buffers_.empty() || num_buffers_at_codec_ == 0) { |
352 return; | 353 return; |
353 } | 354 } |
354 | 355 |
355 int32 buf_index = 0; | 356 int32 buf_index = 0; |
356 size_t offset = 0; | 357 size_t offset = 0; |
357 size_t size = 0; | 358 size_t size = 0; |
358 bool key_frame = false; | 359 bool key_frame = false; |
359 do { | 360 do { |
360 media::MediaCodecStatus status = media_codec_->DequeueOutputBuffer( | 361 media::MediaCodecStatus status = media_codec_->DequeueOutputBuffer( |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
398 kPlatformFailureError); | 399 kPlatformFailureError); |
399 | 400 |
400 media_codec_->CopyFromOutputBuffer(buf_index, offset, shm->memory(), size); | 401 media_codec_->CopyFromOutputBuffer(buf_index, offset, shm->memory(), size); |
401 media_codec_->ReleaseOutputBuffer(buf_index, false); | 402 media_codec_->ReleaseOutputBuffer(buf_index, false); |
402 --num_buffers_at_codec_; | 403 --num_buffers_at_codec_; |
403 | 404 |
404 UMA_HISTOGRAM_COUNTS_10000("Media.AVEA.EncodedBufferSizeKB", size / 1024); | 405 UMA_HISTOGRAM_COUNTS_10000("Media.AVEA.EncodedBufferSizeKB", size / 1024); |
405 base::MessageLoop::current()->PostTask( | 406 base::MessageLoop::current()->PostTask( |
406 FROM_HERE, | 407 FROM_HERE, |
407 base::Bind(&VideoEncodeAccelerator::Client::BitstreamBufferReady, | 408 base::Bind(&VideoEncodeAccelerator::Client::BitstreamBufferReady, |
408 client_ptr_factory_.GetWeakPtr(), | 409 client_ptr_factory_->GetWeakPtr(), |
409 bitstream_buffer.id(), | 410 bitstream_buffer.id(), |
410 size, | 411 size, |
411 key_frame)); | 412 key_frame)); |
412 } | 413 } |
413 | 414 |
414 } // namespace content | 415 } // namespace content |
OLD | NEW |