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

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

Issue 215783002: Fix an issue that audio and video may run out of sync (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: addressing acolwell's comments Created 6 years, 8 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 | Annotate | Revision Log
« no previous file with comments | « media/base/android/media_decoder_job.h ('k') | media/base/android/media_source_player.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 "media/base/android/media_decoder_job.h" 5 #include "media/base/android/media_decoder_job.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/callback_helpers.h" 8 #include "base/callback_helpers.h"
9 #include "base/debug/trace_event.h" 9 #include "base/debug/trace_event.h"
10 #include "base/message_loop/message_loop_proxy.h" 10 #include "base/message_loop/message_loop_proxy.h"
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
73 73
74 base::Closure done_cb = base::ResetAndReturn(&on_data_received_cb_); 74 base::Closure done_cb = base::ResetAndReturn(&on_data_received_cb_);
75 75
76 // If this data request is for the inactive chunk, or |on_data_received_cb_| 76 // If this data request is for the inactive chunk, or |on_data_received_cb_|
77 // was set to null by ClearData() or Release(), do nothing. 77 // was set to null by ClearData() or Release(), do nothing.
78 if (done_cb.is_null()) 78 if (done_cb.is_null())
79 return; 79 return;
80 80
81 if (stop_decode_pending_) { 81 if (stop_decode_pending_) {
82 DCHECK(is_decoding()); 82 DCHECK(is_decoding());
83 OnDecodeCompleted(MEDIA_CODEC_STOPPED, kNoTimestamp(), 0); 83 OnDecodeCompleted(MEDIA_CODEC_STOPPED, kNoTimestamp(), kNoTimestamp());
84 return; 84 return;
85 } 85 }
86 86
87 done_cb.Run(); 87 done_cb.Run();
88 } 88 }
89 89
90 void MediaDecoderJob::Prefetch(const base::Closure& prefetch_cb) { 90 void MediaDecoderJob::Prefetch(const base::Closure& prefetch_cb) {
91 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 91 DCHECK(ui_task_runner_->BelongsToCurrentThread());
92 DCHECK(on_data_received_cb_.is_null()); 92 DCHECK(on_data_received_cb_.is_null());
93 DCHECK(decode_cb_.is_null()); 93 DCHECK(decode_cb_.is_null());
(...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after
268 268
269 RequestCurrentChunkIfEmpty(); 269 RequestCurrentChunkIfEmpty();
270 const AccessUnit& access_unit = CurrentAccessUnit(); 270 const AccessUnit& access_unit = CurrentAccessUnit();
271 // If the first access unit is a config change, request the player to dequeue 271 // If the first access unit is a config change, request the player to dequeue
272 // the input buffer again so that it can request config data. 272 // the input buffer again so that it can request config data.
273 if (access_unit.status == DemuxerStream::kConfigChanged) { 273 if (access_unit.status == DemuxerStream::kConfigChanged) {
274 ui_task_runner_->PostTask(FROM_HERE, 274 ui_task_runner_->PostTask(FROM_HERE,
275 base::Bind(&MediaDecoderJob::OnDecodeCompleted, 275 base::Bind(&MediaDecoderJob::OnDecodeCompleted,
276 base::Unretained(this), 276 base::Unretained(this),
277 MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER, 277 MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER,
278 kNoTimestamp(), 278 kNoTimestamp(), kNoTimestamp()));
279 0));
280 return; 279 return;
281 } 280 }
282 281
283 decoder_task_runner_->PostTask(FROM_HERE, base::Bind( 282 decoder_task_runner_->PostTask(FROM_HERE, base::Bind(
284 &MediaDecoderJob::DecodeInternal, base::Unretained(this), 283 &MediaDecoderJob::DecodeInternal, base::Unretained(this),
285 access_unit, 284 access_unit,
286 start_time_ticks, start_presentation_timestamp, needs_flush_, 285 start_time_ticks, start_presentation_timestamp, needs_flush_,
287 media::BindToCurrentLoop(base::Bind( 286 media::BindToCurrentLoop(base::Bind(
288 &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this))))); 287 &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this)))));
289 needs_flush_ = false; 288 needs_flush_ = false;
290 } 289 }
291 290
292 void MediaDecoderJob::DecodeInternal( 291 void MediaDecoderJob::DecodeInternal(
293 const AccessUnit& unit, 292 const AccessUnit& unit,
294 base::TimeTicks start_time_ticks, 293 base::TimeTicks start_time_ticks,
295 base::TimeDelta start_presentation_timestamp, 294 base::TimeDelta start_presentation_timestamp,
296 bool needs_flush, 295 bool needs_flush,
297 const MediaDecoderJob::DecoderCallback& callback) { 296 const MediaDecoderJob::DecoderCallback& callback) {
298 DVLOG(1) << __FUNCTION__; 297 DVLOG(1) << __FUNCTION__;
299 DCHECK(decoder_task_runner_->BelongsToCurrentThread()); 298 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
300 TRACE_EVENT0("media", __FUNCTION__); 299 TRACE_EVENT0("media", __FUNCTION__);
301 300
302 if (needs_flush) { 301 if (needs_flush) {
303 DVLOG(1) << "DecodeInternal needs flush."; 302 DVLOG(1) << "DecodeInternal needs flush.";
304 input_eos_encountered_ = false; 303 input_eos_encountered_ = false;
305 output_eos_encountered_ = false; 304 output_eos_encountered_ = false;
306 MediaCodecStatus reset_status = media_codec_bridge_->Reset(); 305 MediaCodecStatus reset_status = media_codec_bridge_->Reset();
307 if (MEDIA_CODEC_OK != reset_status) { 306 if (MEDIA_CODEC_OK != reset_status) {
308 callback.Run(reset_status, kNoTimestamp(), 0); 307 callback.Run(reset_status, kNoTimestamp(), kNoTimestamp());
309 return; 308 return;
310 } 309 }
311 } 310 }
312 311
313 // Once output EOS has occurred, we should not be asked to decode again. 312 // Once output EOS has occurred, we should not be asked to decode again.
314 // MediaCodec has undefined behavior if similarly asked to decode after output 313 // MediaCodec has undefined behavior if similarly asked to decode after output
315 // EOS. 314 // EOS.
316 DCHECK(!output_eos_encountered_); 315 DCHECK(!output_eos_encountered_);
317 316
318 // For aborted access unit, just skip it and inform the player. 317 // For aborted access unit, just skip it and inform the player.
319 if (unit.status == DemuxerStream::kAborted) { 318 if (unit.status == DemuxerStream::kAborted) {
320 // TODO(qinmin): use a new enum instead of MEDIA_CODEC_STOPPED. 319 // TODO(qinmin): use a new enum instead of MEDIA_CODEC_STOPPED.
321 callback.Run(MEDIA_CODEC_STOPPED, kNoTimestamp(), 0); 320 callback.Run(MEDIA_CODEC_STOPPED, kNoTimestamp(), kNoTimestamp());
322 return; 321 return;
323 } 322 }
324 323
325 if (skip_eos_enqueue_) { 324 if (skip_eos_enqueue_) {
326 if (unit.end_of_stream || unit.data.empty()) { 325 if (unit.end_of_stream || unit.data.empty()) {
327 input_eos_encountered_ = true; 326 input_eos_encountered_ = true;
328 output_eos_encountered_ = true; 327 output_eos_encountered_ = true;
329 callback.Run(MEDIA_CODEC_OUTPUT_END_OF_STREAM, kNoTimestamp(), 0); 328 callback.Run(MEDIA_CODEC_OUTPUT_END_OF_STREAM, kNoTimestamp(),
329 kNoTimestamp());
330 return; 330 return;
331 } 331 }
332 332
333 skip_eos_enqueue_ = false; 333 skip_eos_enqueue_ = false;
334 } 334 }
335 335
336 MediaCodecStatus input_status = MEDIA_CODEC_INPUT_END_OF_STREAM; 336 MediaCodecStatus input_status = MEDIA_CODEC_INPUT_END_OF_STREAM;
337 if (!input_eos_encountered_) { 337 if (!input_eos_encountered_) {
338 input_status = QueueInputBuffer(unit); 338 input_status = QueueInputBuffer(unit);
339 if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM) { 339 if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM) {
340 input_eos_encountered_ = true; 340 input_eos_encountered_ = true;
341 } else if (input_status != MEDIA_CODEC_OK) { 341 } else if (input_status != MEDIA_CODEC_OK) {
342 callback.Run(input_status, kNoTimestamp(), 0); 342 callback.Run(input_status, kNoTimestamp(), kNoTimestamp());
343 return; 343 return;
344 } 344 }
345 } 345 }
346 346
347 int buffer_index = 0; 347 int buffer_index = 0;
348 size_t offset = 0; 348 size_t offset = 0;
349 size_t size = 0; 349 size_t size = 0;
350 base::TimeDelta presentation_timestamp; 350 base::TimeDelta presentation_timestamp;
351 351
352 base::TimeDelta timeout = base::TimeDelta::FromMilliseconds( 352 base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(
353 kMediaCodecTimeoutInMilliseconds); 353 kMediaCodecTimeoutInMilliseconds);
354 354
355 MediaCodecStatus status = 355 MediaCodecStatus status =
356 media_codec_bridge_->DequeueOutputBuffer(timeout, 356 media_codec_bridge_->DequeueOutputBuffer(timeout,
357 &buffer_index, 357 &buffer_index,
358 &offset, 358 &offset,
359 &size, 359 &size,
360 &presentation_timestamp, 360 &presentation_timestamp,
361 &output_eos_encountered_, 361 &output_eos_encountered_,
362 NULL); 362 NULL);
363 363
364 if (status != MEDIA_CODEC_OK) { 364 if (status != MEDIA_CODEC_OK) {
365 if (status == MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED && 365 if (status == MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED &&
366 !media_codec_bridge_->GetOutputBuffers()) { 366 !media_codec_bridge_->GetOutputBuffers()) {
367 status = MEDIA_CODEC_ERROR; 367 status = MEDIA_CODEC_ERROR;
368 } 368 }
369 callback.Run(status, kNoTimestamp(), 0); 369 callback.Run(status, kNoTimestamp(), kNoTimestamp());
370 return; 370 return;
371 } 371 }
372 372
373 // TODO(xhwang/qinmin): This logic is correct but strange. Clean it up. 373 // TODO(xhwang/qinmin): This logic is correct but strange. Clean it up.
374 if (output_eos_encountered_) 374 if (output_eos_encountered_)
375 status = MEDIA_CODEC_OUTPUT_END_OF_STREAM; 375 status = MEDIA_CODEC_OUTPUT_END_OF_STREAM;
376 else if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM) 376 else if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM)
377 status = MEDIA_CODEC_INPUT_END_OF_STREAM; 377 status = MEDIA_CODEC_INPUT_END_OF_STREAM;
378 378
379 bool render_output = presentation_timestamp >= preroll_timestamp_ && 379 bool render_output = presentation_timestamp >= preroll_timestamp_ &&
380 (status != MEDIA_CODEC_OUTPUT_END_OF_STREAM || size != 0u); 380 (status != MEDIA_CODEC_OUTPUT_END_OF_STREAM || size != 0u);
381 base::TimeDelta time_to_render; 381 base::TimeDelta time_to_render;
382 DCHECK(!start_time_ticks.is_null()); 382 DCHECK(!start_time_ticks.is_null());
383 if (render_output && ComputeTimeToRender()) { 383 if (render_output && ComputeTimeToRender()) {
384 time_to_render = presentation_timestamp - (base::TimeTicks::Now() - 384 time_to_render = presentation_timestamp - (base::TimeTicks::Now() -
385 start_time_ticks + start_presentation_timestamp); 385 start_time_ticks + start_presentation_timestamp);
386 } 386 }
387 387
388 if (time_to_render > base::TimeDelta()) { 388 if (time_to_render > base::TimeDelta()) {
389 decoder_task_runner_->PostDelayedTask( 389 decoder_task_runner_->PostDelayedTask(
390 FROM_HERE, 390 FROM_HERE,
391 base::Bind(&MediaDecoderJob::ReleaseOutputBuffer, 391 base::Bind(&MediaDecoderJob::ReleaseOutputBuffer,
392 weak_factory_.GetWeakPtr(), 392 weak_factory_.GetWeakPtr(),
393 buffer_index, 393 buffer_index,
394 size, 394 size,
395 render_output, 395 render_output,
396 base::Bind(callback, status, presentation_timestamp)), 396 presentation_timestamp,
397 base::Bind(callback, status)),
397 time_to_render); 398 time_to_render);
398 return; 399 return;
399 } 400 }
400 401
401 // TODO(qinmin): The codec is lagging behind, need to recalculate the 402 // TODO(qinmin): The codec is lagging behind, need to recalculate the
402 // |start_presentation_timestamp_| and |start_time_ticks_| in 403 // |start_presentation_timestamp_| and |start_time_ticks_| in
403 // media_source_player.cc. 404 // media_source_player.cc.
404 DVLOG(1) << "codec is lagging behind :" << time_to_render.InMicroseconds(); 405 DVLOG(1) << "codec is lagging behind :" << time_to_render.InMicroseconds();
405 if (render_output) { 406 if (render_output) {
406 // The player won't expect a timestamp smaller than the 407 // The player won't expect a timestamp smaller than the
407 // |start_presentation_timestamp|. However, this could happen due to decoder 408 // |start_presentation_timestamp|. However, this could happen due to decoder
408 // errors. 409 // errors.
409 presentation_timestamp = std::max( 410 presentation_timestamp = std::max(
410 presentation_timestamp, start_presentation_timestamp); 411 presentation_timestamp, start_presentation_timestamp);
411 } else { 412 } else {
412 presentation_timestamp = kNoTimestamp(); 413 presentation_timestamp = kNoTimestamp();
413 } 414 }
414 ReleaseOutputCompletionCallback completion_callback = base::Bind( 415 ReleaseOutputCompletionCallback completion_callback = base::Bind(
415 callback, status, presentation_timestamp); 416 callback, status);
416 ReleaseOutputBuffer(buffer_index, size, render_output, completion_callback); 417 ReleaseOutputBuffer(buffer_index, size, render_output, presentation_timestamp,
418 completion_callback);
417 } 419 }
418 420
419 void MediaDecoderJob::OnDecodeCompleted( 421 void MediaDecoderJob::OnDecodeCompleted(
420 MediaCodecStatus status, base::TimeDelta presentation_timestamp, 422 MediaCodecStatus status, base::TimeDelta current_presentation_timestamp,
421 size_t audio_output_bytes) { 423 base::TimeDelta max_presentation_timestamp) {
422 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 424 DCHECK(ui_task_runner_->BelongsToCurrentThread());
423 425
424 if (destroy_pending_) { 426 if (destroy_pending_) {
425 DVLOG(1) << __FUNCTION__ << " : completing pending deletion"; 427 DVLOG(1) << __FUNCTION__ << " : completing pending deletion";
426 delete this; 428 delete this;
427 return; 429 return;
428 } 430 }
429 431
430 DCHECK(!decode_cb_.is_null()); 432 DCHECK(!decode_cb_.is_null());
431 433
432 // If output was queued for rendering, then we have completed prerolling. 434 // If output was queued for rendering, then we have completed prerolling.
433 if (presentation_timestamp != kNoTimestamp()) 435 if (current_presentation_timestamp != kNoTimestamp())
434 prerolling_ = false; 436 prerolling_ = false;
435 437
436 switch (status) { 438 switch (status) {
437 case MEDIA_CODEC_OK: 439 case MEDIA_CODEC_OK:
438 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER: 440 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER:
439 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: 441 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED:
440 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: 442 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED:
441 case MEDIA_CODEC_OUTPUT_END_OF_STREAM: 443 case MEDIA_CODEC_OUTPUT_END_OF_STREAM:
442 if (!input_eos_encountered_) 444 if (!input_eos_encountered_)
443 access_unit_index_[current_demuxer_data_index_]++; 445 access_unit_index_[current_demuxer_data_index_]++;
444 break; 446 break;
445 447
446 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: 448 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER:
447 case MEDIA_CODEC_INPUT_END_OF_STREAM: 449 case MEDIA_CODEC_INPUT_END_OF_STREAM:
448 case MEDIA_CODEC_NO_KEY: 450 case MEDIA_CODEC_NO_KEY:
449 case MEDIA_CODEC_STOPPED: 451 case MEDIA_CODEC_STOPPED:
450 case MEDIA_CODEC_ERROR: 452 case MEDIA_CODEC_ERROR:
451 // Do nothing. 453 // Do nothing.
452 break; 454 break;
453 }; 455 };
454 456
455 stop_decode_pending_ = false; 457 stop_decode_pending_ = false;
456 base::ResetAndReturn(&decode_cb_).Run(status, presentation_timestamp, 458 base::ResetAndReturn(&decode_cb_).Run(
457 audio_output_bytes); 459 status, current_presentation_timestamp, max_presentation_timestamp);
458 } 460 }
459 461
460 const AccessUnit& MediaDecoderJob::CurrentAccessUnit() const { 462 const AccessUnit& MediaDecoderJob::CurrentAccessUnit() const {
461 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 463 DCHECK(ui_task_runner_->BelongsToCurrentThread());
462 DCHECK(HasData()); 464 DCHECK(HasData());
463 int index = NoAccessUnitsRemainingInChunk(true) ? 465 int index = NoAccessUnitsRemainingInChunk(true) ?
464 inactive_demuxer_data_index() : current_demuxer_data_index_; 466 inactive_demuxer_data_index() : current_demuxer_data_index_;
465 return received_data_[index].access_units[access_unit_index_[index]]; 467 return received_data_[index].access_units[access_unit_index_[index]];
466 } 468 }
467 469
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
501 } 503 }
502 504
503 void MediaDecoderJob::InitializeReceivedData() { 505 void MediaDecoderJob::InitializeReceivedData() {
504 for (size_t i = 0; i < 2; ++i) { 506 for (size_t i = 0; i < 2; ++i) {
505 received_data_[i] = DemuxerData(); 507 received_data_[i] = DemuxerData();
506 access_unit_index_[i] = 0; 508 access_unit_index_[i] = 0;
507 } 509 }
508 } 510 }
509 511
510 } // namespace media 512 } // namespace media
OLDNEW
« no previous file with comments | « media/base/android/media_decoder_job.h ('k') | media/base/android/media_source_player.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698