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

Side by Side Diff: media/base/android/media_codec_decoder.cc

Issue 1176993005: Audio and video decoders for MediaCodecPlayer (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressed Min's comments Created 5 years, 6 months 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
OLDNEW
(Empty)
1 // Copyright 2015 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 "media/base/android/media_codec_decoder.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback_helpers.h"
10 #include "base/logging.h"
11 #include "media/base/android/media_codec_bridge.h"
12
13 namespace media {
14
15 namespace {
16
17 // Stop requesting new data in the kPrefetching state when the queue size
18 // reaches this limit.
19 const int kPrefetchLimit = 8;
20
21 // Request new data in the kRunning state if the queue size is less than this.
22 const int kPlaybackLowLimit = 4;
23
24 // Posting delay of the next frame processing, in milliseconds
25 const int kNextFrameDelay = 2;
26
27 // Timeout for dequeuing an input buffer from MediaCodec in milliseconds.
28 const int kInputBufferTimeout = 20;
29
30 // Timeout for dequeuing an output buffer from MediaCodec in milliseconds.
31 const int kOutputBufferTimeout = 20;
32 }
33
34 MediaCodecDecoder::MediaCodecDecoder(
35 const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
36 const base::Closure& request_data_cb,
37 const base::Closure& starvation_cb,
38 const base::Closure& stop_done_cb,
39 const base::Closure& error_cb,
40 const char* decoder_thread_name)
41 : media_task_runner_(media_task_runner),
42 decoder_thread_(decoder_thread_name),
43 request_data_cb_(request_data_cb),
44 starvation_cb_(starvation_cb),
45 stop_done_cb_(stop_done_cb),
46 error_cb_(error_cb),
47 state_(kStopped),
48 eos_enqueued_(false),
49 completed_(false),
50 last_frame_posted_(false),
51 weak_factory_(this) {
52 DCHECK(media_task_runner_->BelongsToCurrentThread());
53
54 DVLOG(1) << "Decoder::Decoder() " << decoder_thread_name;
55
56 weak_this_ = weak_factory_.GetWeakPtr();
57
58 internal_error_cb_ = base::Bind(&MediaCodecDecoder::OnCodecError, weak_this_);
59 }
60
61 MediaCodecDecoder::~MediaCodecDecoder() {
62 DCHECK(media_task_runner_->BelongsToCurrentThread());
63
64 DVLOG(1) << "Decoder::~Decoder()";
65
66 // NB: ReleaseDecoderResources() is virtual
67 ReleaseDecoderResources();
68 }
69
70 void MediaCodecDecoder::ReleaseDecoderResources() {
71 DCHECK(media_task_runner_->BelongsToCurrentThread());
72
73 DVLOG(1) << class_name() << "::" << __FUNCTION__;
74
75 decoder_thread_.Stop(); // synchronous
76 state_ = kStopped;
77 media_codec_bridge_.reset();
78 }
79
80 void MediaCodecDecoder::Flush() {
81 DCHECK(media_task_runner_->BelongsToCurrentThread());
82
83 DVLOG(1) << class_name() << "::" << __FUNCTION__;
84
85 DCHECK_EQ(GetState(), kStopped);
wolenetz 2015/06/19 23:28:41 nit: (not strong feeling here) Why not just DCHECK
Tima Vaisburd 2015/06/20 02:32:30 When I was writing this I imagined that IsStopped(
wolenetz 2015/06/22 21:41:21 no strong feeling. as-is is fine.
86
87 eos_enqueued_ = false;
88 completed_ = false;
89 au_queue_.Flush();
90
91 if (media_codec_bridge_) {
92 // MediaCodecBridge::Reset() performs MediaCodecBridge.flush()
93 MediaCodecStatus flush_status = media_codec_bridge_->Reset();
94 if (flush_status != MEDIA_CODEC_OK) {
95 DVLOG(0) << class_name() << "::" << __FUNCTION__
96 << "MediaCodecBridge::Reset() failed";
97 media_task_runner_->PostTask(FROM_HERE, internal_error_cb_);
98 }
99 }
100 }
101
102 void MediaCodecDecoder::ReleaseMediaCodec() {
103 DCHECK(media_task_runner_->BelongsToCurrentThread());
104
105 DVLOG(1) << class_name() << "::" << __FUNCTION__;
106
107 media_codec_bridge_.reset();
108 }
109
110 bool MediaCodecDecoder::IsPrefetchingOrPlaying() const {
111 DCHECK(media_task_runner_->BelongsToCurrentThread());
112
113 base::AutoLock lock(state_lock_);
114 return state_ == kPrefetching || state_ == kRunning;
115 }
116
117 bool MediaCodecDecoder::IsStopped() const {
118 DCHECK(media_task_runner_->BelongsToCurrentThread());
119
120 return GetState() == kStopped;
121 }
122
123 bool MediaCodecDecoder::IsCompleted() const {
124 DCHECK(media_task_runner_->BelongsToCurrentThread());
125
126 return completed_;
127 }
128
129 base::android::ScopedJavaLocalRef<jobject> MediaCodecDecoder::GetMediaCrypto() {
130 base::android::ScopedJavaLocalRef<jobject> media_crypto;
131
132 // TODO(timav): implement DRM.
133 // drm_bridge_ is not implemented
134 // if (drm_bridge_)
135 // media_crypto = drm_bridge_->GetMediaCrypto();
136 return media_crypto;
137 }
138
139 void MediaCodecDecoder::Prefetch(const base::Closure& prefetch_done_cb) {
140 DCHECK(media_task_runner_->BelongsToCurrentThread());
141
142 DVLOG(1) << class_name() << "::" << __FUNCTION__;
143
144 DCHECK(GetState() == kStopped);
145
146 prefetch_done_cb_ = prefetch_done_cb;
147
148 SetState(kPrefetching);
149 PrefetchNextChunk();
150 }
151
152 MediaCodecDecoder::ConfigStatus MediaCodecDecoder::Configure() {
153 DCHECK(media_task_runner_->BelongsToCurrentThread());
154
155 DVLOG(1) << class_name() << "::" << __FUNCTION__;
156
157 if (GetState() == kError) {
158 DVLOG(0) << class_name() << "::" << __FUNCTION__ << ": wrong state kError";
159 return CONFIG_FAILURE;
160 }
161
162 // Here I assume that OnDemuxerConfigsAvailable won't come
wolenetz 2015/06/19 23:28:41 aside: This is probably a valid assumption. (Confi
163 // in the middle of demuxer data.
164
165 if (media_codec_bridge_) {
166 DVLOG(1) << class_name() << "::" << __FUNCTION__
167 << ": reconfiguration is not required, ignoring";
168 return CONFIG_OK;
169 }
170
171 return ConfigureInternal();
172 }
173
174 bool MediaCodecDecoder::Start(base::TimeDelta current_time) {
175 DCHECK(media_task_runner_->BelongsToCurrentThread());
176
177 DVLOG(1) << class_name() << "::" << __FUNCTION__
178 << " current_time:" << current_time;
179
180 DecoderState state = GetState();
181 if (state == kRunning) {
182 DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": already started";
183 return true; // already started
184 }
185
186 if (state != kPrefetched) {
187 DVLOG(0) << class_name() << "::" << __FUNCTION__ << ": wrong state "
188 << AsString(state) << " ignoring";
189 return false;
190 }
191
192 if (!media_codec_bridge_) {
193 DVLOG(0) << class_name() << "::" << __FUNCTION__
194 << ": not configured, ignoring";
195 return false;
196 }
197
198 DCHECK(!decoder_thread_.IsRunning());
199
200 // We only synchronize video stream.
201 // When audio is present, the |current_time| is audio time.
202 SynchronizePTSWithTime(current_time);
203
204 last_frame_posted_ = false;
205
206 // Start the decoder thread
207 if (!decoder_thread_.Start()) {
208 DVLOG(1) << class_name() << "::" << __FUNCTION__
209 << ": cannot start decoder thread";
210 return false;
211 }
212
213 SetState(kRunning);
214
215 decoder_thread_.task_runner()->PostTask(
216 FROM_HERE,
217 base::Bind(&MediaCodecDecoder::ProcessNextFrame, base::Unretained(this)));
218
219 return true;
220 }
221
222 void MediaCodecDecoder::SyncStop() {
223 DCHECK(media_task_runner_->BelongsToCurrentThread());
224
225 DVLOG(1) << class_name() << "::" << __FUNCTION__;
226
227 if (GetState() == kError) {
228 DVLOG(0) << class_name() << "::" << __FUNCTION__
229 << ": wrong state kError, ignoring";
230 return;
231 }
232
233 // After this method returns, decoder thread will not be running.
234
235 decoder_thread_.Stop(); // synchronous
236 state_ = kStopped;
237
238 // Shall we move |delayed_buffers_| from VideoDecoder to Decoder class?
239 ReleaseDelayedBuffers();
240 }
241
242 void MediaCodecDecoder::RequestToStop() {
243 DCHECK(media_task_runner_->BelongsToCurrentThread());
244
245 DVLOG(1) << class_name() << "::" << __FUNCTION__;
246
247 DecoderState state = GetState();
248 switch (state) {
249 case kError:
250 DVLOG(0) << class_name() << "::" << __FUNCTION__
251 << ": wrong state kError, ignoring";
252 break;
253 case kRunning:
254 SetState(kStopping);
255 break;
256 case kStopping:
257 break; // ignore
258 case kStopped:
259 case kPrefetching:
260 case kPrefetched:
261 // There is nothing to wait for, we can sent nofigication right away.
262 DCHECK(!decoder_thread_.IsRunning());
263 SetState(kStopped);
264 media_task_runner_->PostTask(FROM_HERE, stop_done_cb_);
265 break;
266 default:
267 NOTREACHED();
268 break;
269 }
270 }
271
272 void MediaCodecDecoder::OnLastFrameRendered(bool completed) {
273 DCHECK(media_task_runner_->BelongsToCurrentThread());
274
275 DVLOG(1) << class_name() << "::" << __FUNCTION__
276 << " completed:" << completed;
277
278 decoder_thread_.Stop(); // synchronous
279 state_ = kStopped;
280 completed_ = completed;
281
282 media_task_runner_->PostTask(FROM_HERE, stop_done_cb_);
283 }
284
285 void MediaCodecDecoder::OnDemuxerDataAvailable(const DemuxerData& data) {
286 DCHECK(media_task_runner_->BelongsToCurrentThread());
287
288 DVLOG(2) << class_name() << "::" << __FUNCTION__
289 << " #AUs:" << data.access_units.size()
290 << " #Configs:" << data.demuxer_configs.size();
291 #if !defined(NDEBUG)
292 for (const auto& unit : data.access_units)
293 DVLOG(2) << class_name() << "::" << __FUNCTION__ << " au: " << unit;
294 #endif
295
296 au_queue_.PushBack(data);
297
298 if (state_ == kPrefetching)
299 PrefetchNextChunk();
300 }
301
302 void MediaCodecDecoder::CheckLastFrame(bool eos_encountered,
303 bool has_delayed_tasks) {
304 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread());
305
306 bool last_frame_when_stopping = GetState() == kStopping && !has_delayed_tasks;
307
308 if (last_frame_when_stopping || eos_encountered) {
309 media_task_runner_->PostTask(
310 FROM_HERE, base::Bind(&MediaCodecDecoder::OnLastFrameRendered,
311 weak_this_, eos_encountered));
312 last_frame_posted_ = true;
313 }
314 }
315
316 void MediaCodecDecoder::OnCodecError() {
317 DCHECK(media_task_runner_->BelongsToCurrentThread());
318
319 SetState(kError);
320 error_cb_.Run();
321 }
322
323 void MediaCodecDecoder::PrefetchNextChunk() {
324 DCHECK(media_task_runner_->BelongsToCurrentThread());
325
326 DVLOG(1) << class_name() << "::" << __FUNCTION__;
327
328 AccessUnitQueue::Info au_info = au_queue_.GetInfo();
329
330 if (eos_enqueued_ || au_info.length >= kPrefetchLimit || au_info.has_eos) {
331 // We are done prefetching
332 SetState(kPrefetched);
333 DVLOG(1) << class_name() << "::" << __FUNCTION__ << " posting PrefetchDone";
334 media_task_runner_->PostTask(FROM_HERE,
335 base::ResetAndReturn(&prefetch_done_cb_));
336 return;
337 }
338
339 request_data_cb_.Run();
340 }
341
342 void MediaCodecDecoder::ProcessNextFrame() {
343 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread());
344
345 DVLOG(2) << class_name() << "::" << __FUNCTION__;
346
347 DecoderState state = GetState();
348
349 if (state != kRunning && state != kStopping) {
350 DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": not running";
351 return;
352 }
353
354 if (state == kStopping) {
355 if (NumDelayedRenderTasks() == 0 && !last_frame_posted_) {
356 DVLOG(1) << class_name() << "::" << __FUNCTION__
357 << ": kStopping, posting OnLastFrameRendered";
358 media_task_runner_->PostTask(
359 FROM_HERE, base::Bind(&MediaCodecDecoder::OnLastFrameRendered,
360 weak_this_, false));
361 last_frame_posted_ = true;
362 }
363
364 // We can stop processing, the |au_queue_| and MediaCodec queues can freeze.
365 // We only need to let finish the delayed rendering tasks.
366 return;
367 }
368
369 DCHECK(state == kRunning);
370
371 if (!EnqueueInputBuffer())
372 return;
373
374 bool eos_encountered = false;
375 if (!DepleteOutputBufferQueue(&eos_encountered))
376 return;
377
378 if (eos_encountered) {
379 DVLOG(1) << class_name() << "::" << __FUNCTION__
380 << " EOS dequeued, stopping frame processing";
381 return;
382 }
383
384 // We need a small delay if we want to stop this thread by
385 // decoder_thread_.Stop() reliably.
386 // The decoder thread message loop processes all pending
387 // (but not delayed) tasks before it can quit; without a delay
388 // the message loop might be forever processing the pendng tasks.
389 decoder_thread_.task_runner()->PostDelayedTask(
390 FROM_HERE,
391 base::Bind(&MediaCodecDecoder::ProcessNextFrame, base::Unretained(this)),
392 base::TimeDelta::FromMilliseconds(kNextFrameDelay));
wolenetz 2015/06/19 23:28:41 aside: might this prevent (really high) high frame
Tima Vaisburd 2015/06/20 02:32:30 I haven't seen this so far. I also reduced kNextFr
wolenetz 2015/06/22 21:41:21 Acknowledged.
393 }
394
395 // Returns false if we should stop decoding process. Right now
396 // it happens if we got MediaCodec error or detected starvation.
397 bool MediaCodecDecoder::EnqueueInputBuffer() {
398 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread());
399
400 DVLOG(2) << class_name() << "::" << __FUNCTION__;
401
402 if (eos_enqueued_) {
403 DVLOG(1) << class_name() << "::" << __FUNCTION__
404 << ": eos_enqueued, returning";
405 return true; // Nothing to do
406 }
407
408 // Keep the number pending video frames low, ideally maintaining
409 // the same audio and video duration after stop request
410 if (NumDelayedRenderTasks() > 1) {
411 DVLOG(2) << class_name() << "::" << __FUNCTION__ << ": # delayed buffers ("
412 << NumDelayedRenderTasks() << ") exceeds 1, returning";
413 return true; // Nothing to do
414 }
415
416 // Get the next frame from the queue and the queue info
417
418 AccessUnitQueue::Info au_info = au_queue_.GetInfo();
419
420 // Request the data from Demuxer
421 if (au_info.length <= kPlaybackLowLimit && !au_info.has_eos)
422 media_task_runner_->PostTask(FROM_HERE, request_data_cb_);
423
424 // Get the next frame from the queue
425
426 if (!au_info.length) {
427 // Report starvation and return, Start() will be called again later.
428 DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": starvation detected";
429 media_task_runner_->PostTask(FROM_HERE, starvation_cb_);
430 return false;
431 }
432
433 if (au_info.configs) {
434 DVLOG(1) << class_name() << "::" << __FUNCTION__
435 << ": received new configs, not implemented";
436 // post an error for now?
437 media_task_runner_->PostTask(FROM_HERE, internal_error_cb_);
438 return false;
439 }
440
441 // Dequeue input buffer
442
443 base::TimeDelta timeout =
444 base::TimeDelta::FromMilliseconds(kInputBufferTimeout);
445 int index = -1;
446 MediaCodecStatus status =
447 media_codec_bridge_->DequeueInputBuffer(timeout, &index);
448
449 DVLOG(2) << class_name() << ":: DequeueInputBuffer index:" << index;
450
451 switch (status) {
452 case MEDIA_CODEC_ERROR:
453 DVLOG(0) << class_name() << "::" << __FUNCTION__
454 << ": MEDIA_CODEC_ERROR DequeueInputBuffer failed";
455 media_task_runner_->PostTask(FROM_HERE, internal_error_cb_);
456 return false;
457
458 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER:
459 return true;
460
461 default:
462 break;
463 }
464
465 // We got the buffer
466 DCHECK_EQ(status, MEDIA_CODEC_OK);
467 DCHECK_GE(index, 0);
468
469 const AccessUnit* unit = au_info.front_unit;
470 DCHECK(unit);
471
472 if (unit->is_end_of_stream) {
473 DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": QueueEOS";
474 media_codec_bridge_->QueueEOS(index);
475 eos_enqueued_ = true;
476 return true;
477 }
478
479 DVLOG(2) << class_name() << ":: QueueInputBuffer pts:" << unit->timestamp;
480
481 status = media_codec_bridge_->QueueInputBuffer(
482 index, &unit->data[0], unit->data.size(), unit->timestamp);
483
484 if (status == MEDIA_CODEC_ERROR) {
485 DVLOG(0) << class_name() << "::" << __FUNCTION__
486 << ": MEDIA_CODEC_ERROR: QueueInputBuffer failed";
487 media_task_runner_->PostTask(FROM_HERE, internal_error_cb_);
488 return false;
489 }
490
491 // Have successfully queued input buffer, go to next access unit.
492 au_queue_.Advance();
493 return true;
494 }
495
496 // Returns false if there was MediaCodec error.
497 bool MediaCodecDecoder::DepleteOutputBufferQueue(bool* eos_encountered) {
wolenetz 2015/06/19 23:28:41 aside: I like this naming :)
Tima Vaisburd 2015/06/20 02:32:30 M-mm.. wrong choice? I meant "process till the que
wolenetz 2015/06/22 21:41:21 Hmm. Deplete[...] is better IMO than Process[...].
498 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread());
499
500 DVLOG(2) << class_name() << "::" << __FUNCTION__;
501
502 int buffer_index = 0;
503 size_t offset = 0;
504 size_t size = 0;
505 base::TimeDelta pts;
506 MediaCodecStatus status;
507
508 base::TimeDelta timeout =
509 base::TimeDelta::FromMilliseconds(kOutputBufferTimeout);
510
511 // Extract all output buffers that are available.
512 // Usually there will be only one, but sometimes it is preceeded by
513 // MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED or MEDIA_CODEC_OUTPUT_FORMAT_CHANGED.
514 do {
515 status = media_codec_bridge_->DequeueOutputBuffer(
516 timeout, &buffer_index, &offset, &size, &pts, eos_encountered, nullptr);
517
518 // Reset the timeout to 0 for the subsequent DequeueOutputBuffer() calls
519 // to quickly break the loop after we got all currently available buffers.
520 timeout = base::TimeDelta::FromMilliseconds(0);
521
522 switch (status) {
523 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED:
524 // Output buffers are replaced in MediaCodecBridge, nothing to do.
525 break;
526
527 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED:
528 DVLOG(2) << class_name() << "::" << __FUNCTION__
529 << " MEDIA_CODEC_OUTPUT_FORMAT_CHANGED";
530 OnOutputFormatChanged();
531 break;
532
533 case MEDIA_CODEC_OK:
534 // We got the decoded frame
535 Render(buffer_index, size, true, pts, *eos_encountered);
536 break;
537
538 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER:
539 // Nothing to do.
540 break;
541
542 case MEDIA_CODEC_ERROR:
543 DVLOG(0) << class_name() << "::" << __FUNCTION__
544 << ": MEDIA_CODEC_ERROR from DequeueOutputBuffer";
545 media_task_runner_->PostTask(FROM_HERE, internal_error_cb_);
546 break;
547
548 default:
549 NOTREACHED();
550 break;
551 }
552
553 } while (status != MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER &&
554 status != MEDIA_CODEC_ERROR && !*eos_encountered);
555
556 return status != MEDIA_CODEC_ERROR;
557 }
558
559 MediaCodecDecoder::DecoderState MediaCodecDecoder::GetState() const {
560 base::AutoLock lock(state_lock_);
561 return state_;
562 }
563
564 void MediaCodecDecoder::SetState(DecoderState state) {
565 DVLOG(1) << class_name() << "::" << __FUNCTION__ << " " << state;
566
567 base::AutoLock lock(state_lock_);
568 state_ = state;
569 }
570
571 #undef RETURN_STRING
572 #define RETURN_STRING(x) \
573 case x: \
574 return #x;
575
576 const char* MediaCodecDecoder::AsString(DecoderState state) {
577 switch (state) {
578 RETURN_STRING(kStopped);
579 RETURN_STRING(kPrefetching);
580 RETURN_STRING(kPrefetched);
581 RETURN_STRING(kRunning);
582 RETURN_STRING(kStopping);
583 RETURN_STRING(kError);
584 default:
585 return "Unknown DecoderState";
586 }
587 }
588
589 #undef RETURN_STRING
590
591 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698