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

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
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 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
66 return; 66 return;
67 } 67 }
68 68
69 size_t next_demuxer_data_index = inactive_demuxer_data_index(); 69 size_t next_demuxer_data_index = inactive_demuxer_data_index();
70 received_data_[next_demuxer_data_index] = data; 70 received_data_[next_demuxer_data_index] = data;
71 access_unit_index_[next_demuxer_data_index] = 0; 71 access_unit_index_[next_demuxer_data_index] = 0;
72 is_requesting_demuxer_data_ = false; 72 is_requesting_demuxer_data_ = false;
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 if (stop_decode_pending_) { 75 if (stop_decode_pending_) {
76 OnDecodeCompleted(MEDIA_CODEC_STOPPED, kNoTimestamp(), 0); 76 OnDecodeCompleted(MEDIA_CODEC_STOPPED, kNoTimestamp(), kNoTimestamp());
77 return; 77 return;
78 } 78 }
79 79
80 if (!done_cb.is_null()) 80 if (!done_cb.is_null())
81 done_cb.Run(); 81 done_cb.Run();
82 } 82 }
83 83
84 void MediaDecoderJob::Prefetch(const base::Closure& prefetch_cb) { 84 void MediaDecoderJob::Prefetch(const base::Closure& prefetch_cb) {
85 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 85 DCHECK(ui_task_runner_->BelongsToCurrentThread());
86 DCHECK(on_data_received_cb_.is_null()); 86 DCHECK(on_data_received_cb_.is_null());
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after
262 262
263 RequestCurrentChunkIfEmpty(); 263 RequestCurrentChunkIfEmpty();
264 const AccessUnit& access_unit = CurrentAccessUnit(); 264 const AccessUnit& access_unit = CurrentAccessUnit();
265 // If the first access unit is a config change, request the player to dequeue 265 // If the first access unit is a config change, request the player to dequeue
266 // the input buffer again so that it can request config data. 266 // the input buffer again so that it can request config data.
267 if (access_unit.status == DemuxerStream::kConfigChanged) { 267 if (access_unit.status == DemuxerStream::kConfigChanged) {
268 ui_task_runner_->PostTask(FROM_HERE, 268 ui_task_runner_->PostTask(FROM_HERE,
269 base::Bind(&MediaDecoderJob::OnDecodeCompleted, 269 base::Bind(&MediaDecoderJob::OnDecodeCompleted,
270 base::Unretained(this), 270 base::Unretained(this),
271 MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER, 271 MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER,
272 kNoTimestamp(), 272 kNoTimestamp(), kNoTimestamp()));
273 0));
274 return; 273 return;
275 } 274 }
276 275
277 decoder_task_runner_->PostTask(FROM_HERE, base::Bind( 276 decoder_task_runner_->PostTask(FROM_HERE, base::Bind(
278 &MediaDecoderJob::DecodeInternal, base::Unretained(this), 277 &MediaDecoderJob::DecodeInternal, base::Unretained(this),
279 access_unit, 278 access_unit,
280 start_time_ticks, start_presentation_timestamp, needs_flush_, 279 start_time_ticks, start_presentation_timestamp, needs_flush_,
281 media::BindToCurrentLoop(base::Bind( 280 media::BindToCurrentLoop(base::Bind(
282 &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this))))); 281 &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this)))));
283 needs_flush_ = false; 282 needs_flush_ = false;
284 } 283 }
285 284
286 void MediaDecoderJob::DecodeInternal( 285 void MediaDecoderJob::DecodeInternal(
287 const AccessUnit& unit, 286 const AccessUnit& unit,
288 base::TimeTicks start_time_ticks, 287 base::TimeTicks start_time_ticks,
289 base::TimeDelta start_presentation_timestamp, 288 base::TimeDelta start_presentation_timestamp,
290 bool needs_flush, 289 bool needs_flush,
291 const MediaDecoderJob::DecoderCallback& callback) { 290 const MediaDecoderJob::DecoderCallback& callback) {
292 DVLOG(1) << __FUNCTION__; 291 DVLOG(1) << __FUNCTION__;
293 DCHECK(decoder_task_runner_->BelongsToCurrentThread()); 292 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
294 TRACE_EVENT0("media", __FUNCTION__); 293 TRACE_EVENT0("media", __FUNCTION__);
295 294
296 if (needs_flush) { 295 if (needs_flush) {
297 DVLOG(1) << "DecodeInternal needs flush."; 296 DVLOG(1) << "DecodeInternal needs flush.";
298 input_eos_encountered_ = false; 297 input_eos_encountered_ = false;
299 output_eos_encountered_ = false; 298 output_eos_encountered_ = false;
300 MediaCodecStatus reset_status = media_codec_bridge_->Reset(); 299 MediaCodecStatus reset_status = media_codec_bridge_->Reset();
301 if (MEDIA_CODEC_OK != reset_status) { 300 if (MEDIA_CODEC_OK != reset_status) {
302 callback.Run(reset_status, kNoTimestamp(), 0); 301 callback.Run(reset_status, kNoTimestamp(), kNoTimestamp());
303 return; 302 return;
304 } 303 }
305 } 304 }
306 305
307 // Once output EOS has occurred, we should not be asked to decode again. 306 // Once output EOS has occurred, we should not be asked to decode again.
308 // MediaCodec has undefined behavior if similarly asked to decode after output 307 // MediaCodec has undefined behavior if similarly asked to decode after output
309 // EOS. 308 // EOS.
310 DCHECK(!output_eos_encountered_); 309 DCHECK(!output_eos_encountered_);
311 310
312 // For aborted access unit, just skip it and inform the player. 311 // For aborted access unit, just skip it and inform the player.
313 if (unit.status == DemuxerStream::kAborted) { 312 if (unit.status == DemuxerStream::kAborted) {
314 // TODO(qinmin): use a new enum instead of MEDIA_CODEC_STOPPED. 313 // TODO(qinmin): use a new enum instead of MEDIA_CODEC_STOPPED.
315 callback.Run(MEDIA_CODEC_STOPPED, kNoTimestamp(), 0); 314 callback.Run(MEDIA_CODEC_STOPPED, kNoTimestamp(), kNoTimestamp());
316 return; 315 return;
317 } 316 }
318 317
319 if (skip_eos_enqueue_) { 318 if (skip_eos_enqueue_) {
320 if (unit.end_of_stream || unit.data.empty()) { 319 if (unit.end_of_stream || unit.data.empty()) {
321 input_eos_encountered_ = true; 320 input_eos_encountered_ = true;
322 output_eos_encountered_ = true; 321 output_eos_encountered_ = true;
323 callback.Run(MEDIA_CODEC_OUTPUT_END_OF_STREAM, kNoTimestamp(), 0); 322 callback.Run(MEDIA_CODEC_OUTPUT_END_OF_STREAM, kNoTimestamp(),
323 kNoTimestamp());
324 return; 324 return;
325 } 325 }
326 326
327 skip_eos_enqueue_ = false; 327 skip_eos_enqueue_ = false;
328 } 328 }
329 329
330 MediaCodecStatus input_status = MEDIA_CODEC_INPUT_END_OF_STREAM; 330 MediaCodecStatus input_status = MEDIA_CODEC_INPUT_END_OF_STREAM;
331 if (!input_eos_encountered_) { 331 if (!input_eos_encountered_) {
332 input_status = QueueInputBuffer(unit); 332 input_status = QueueInputBuffer(unit);
333 if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM) { 333 if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM) {
334 input_eos_encountered_ = true; 334 input_eos_encountered_ = true;
335 } else if (input_status != MEDIA_CODEC_OK) { 335 } else if (input_status != MEDIA_CODEC_OK) {
336 callback.Run(input_status, kNoTimestamp(), 0); 336 callback.Run(input_status, kNoTimestamp(), kNoTimestamp());
337 return; 337 return;
338 } 338 }
339 } 339 }
340 340
341 int buffer_index = 0; 341 int buffer_index = 0;
342 size_t offset = 0; 342 size_t offset = 0;
343 size_t size = 0; 343 size_t size = 0;
344 base::TimeDelta presentation_timestamp; 344 base::TimeDelta presentation_timestamp;
345 345
346 base::TimeDelta timeout = base::TimeDelta::FromMilliseconds( 346 base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(
347 kMediaCodecTimeoutInMilliseconds); 347 kMediaCodecTimeoutInMilliseconds);
348 348
349 MediaCodecStatus status = 349 MediaCodecStatus status =
350 media_codec_bridge_->DequeueOutputBuffer(timeout, 350 media_codec_bridge_->DequeueOutputBuffer(timeout,
351 &buffer_index, 351 &buffer_index,
352 &offset, 352 &offset,
353 &size, 353 &size,
354 &presentation_timestamp, 354 &presentation_timestamp,
355 &output_eos_encountered_, 355 &output_eos_encountered_,
356 NULL); 356 NULL);
357 357
358 if (status != MEDIA_CODEC_OK) { 358 if (status != MEDIA_CODEC_OK) {
359 if (status == MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED && 359 if (status == MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED &&
360 !media_codec_bridge_->GetOutputBuffers()) { 360 !media_codec_bridge_->GetOutputBuffers()) {
361 status = MEDIA_CODEC_ERROR; 361 status = MEDIA_CODEC_ERROR;
362 } 362 }
363 callback.Run(status, kNoTimestamp(), 0); 363 callback.Run(status, kNoTimestamp(), kNoTimestamp());
364 return; 364 return;
365 } 365 }
366 366
367 // TODO(xhwang/qinmin): This logic is correct but strange. Clean it up. 367 // TODO(xhwang/qinmin): This logic is correct but strange. Clean it up.
368 if (output_eos_encountered_) 368 if (output_eos_encountered_)
369 status = MEDIA_CODEC_OUTPUT_END_OF_STREAM; 369 status = MEDIA_CODEC_OUTPUT_END_OF_STREAM;
370 else if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM) 370 else if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM)
371 status = MEDIA_CODEC_INPUT_END_OF_STREAM; 371 status = MEDIA_CODEC_INPUT_END_OF_STREAM;
372 372
373 bool render_output = presentation_timestamp >= preroll_timestamp_ && 373 bool render_output = presentation_timestamp >= preroll_timestamp_ &&
374 (status != MEDIA_CODEC_OUTPUT_END_OF_STREAM || size != 0u); 374 (status != MEDIA_CODEC_OUTPUT_END_OF_STREAM || size != 0u);
375 base::TimeDelta time_to_render; 375 base::TimeDelta time_to_render;
376 DCHECK(!start_time_ticks.is_null()); 376 DCHECK(!start_time_ticks.is_null());
377 if (render_output && ComputeTimeToRender()) { 377 if (render_output && ComputeTimeToRender()) {
378 time_to_render = presentation_timestamp - (base::TimeTicks::Now() - 378 time_to_render = presentation_timestamp - (base::TimeTicks::Now() -
379 start_time_ticks + start_presentation_timestamp); 379 start_time_ticks + start_presentation_timestamp);
380 } 380 }
381 381
382 if (time_to_render > base::TimeDelta()) { 382 if (time_to_render > base::TimeDelta()) {
383 decoder_task_runner_->PostDelayedTask( 383 decoder_task_runner_->PostDelayedTask(
384 FROM_HERE, 384 FROM_HERE,
385 base::Bind(&MediaDecoderJob::ReleaseOutputBuffer, 385 base::Bind(&MediaDecoderJob::ReleaseOutputBuffer,
386 weak_factory_.GetWeakPtr(), 386 weak_factory_.GetWeakPtr(),
387 buffer_index, 387 buffer_index,
388 size, 388 size,
389 render_output, 389 render_output,
390 base::Bind(callback, status, presentation_timestamp)), 390 presentation_timestamp,
391 base::Bind(callback, status)),
391 time_to_render); 392 time_to_render);
392 return; 393 return;
393 } 394 }
394 395
395 // TODO(qinmin): The codec is lagging behind, need to recalculate the 396 // TODO(qinmin): The codec is lagging behind, need to recalculate the
396 // |start_presentation_timestamp_| and |start_time_ticks_| in 397 // |start_presentation_timestamp_| and |start_time_ticks_| in
397 // media_source_player.cc. 398 // media_source_player.cc.
398 DVLOG(1) << "codec is lagging behind :" << time_to_render.InMicroseconds(); 399 DVLOG(1) << "codec is lagging behind :" << time_to_render.InMicroseconds();
399 if (render_output) { 400 if (render_output) {
400 // The player won't expect a timestamp smaller than the 401 // The player won't expect a timestamp smaller than the
401 // |start_presentation_timestamp|. However, this could happen due to decoder 402 // |start_presentation_timestamp|. However, this could happen due to decoder
402 // errors. 403 // errors.
403 presentation_timestamp = std::max( 404 presentation_timestamp = std::max(
404 presentation_timestamp, start_presentation_timestamp); 405 presentation_timestamp, start_presentation_timestamp);
405 } else { 406 } else {
406 presentation_timestamp = kNoTimestamp(); 407 presentation_timestamp = kNoTimestamp();
407 } 408 }
408 ReleaseOutputCompletionCallback completion_callback = base::Bind( 409 ReleaseOutputCompletionCallback completion_callback = base::Bind(
409 callback, status, presentation_timestamp); 410 callback, status);
410 ReleaseOutputBuffer(buffer_index, size, render_output, completion_callback); 411 ReleaseOutputBuffer(buffer_index, size, render_output, presentation_timestamp,
412 completion_callback);
411 } 413 }
412 414
413 void MediaDecoderJob::OnDecodeCompleted( 415 void MediaDecoderJob::OnDecodeCompleted(
414 MediaCodecStatus status, base::TimeDelta presentation_timestamp, 416 MediaCodecStatus status, base::TimeDelta current_presentation_timestamp,
415 size_t audio_output_bytes) { 417 base::TimeDelta max_presentation_timestamp) {
416 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 418 DCHECK(ui_task_runner_->BelongsToCurrentThread());
417 419
418 if (destroy_pending_) { 420 if (destroy_pending_) {
419 DVLOG(1) << __FUNCTION__ << " : completing pending deletion"; 421 DVLOG(1) << __FUNCTION__ << " : completing pending deletion";
420 delete this; 422 delete this;
421 return; 423 return;
422 } 424 }
423 425
424 DCHECK(!decode_cb_.is_null()); 426 DCHECK(!decode_cb_.is_null());
425 427
426 // If output was queued for rendering, then we have completed prerolling. 428 // If output was queued for rendering, then we have completed prerolling.
427 if (presentation_timestamp != kNoTimestamp()) 429 if (current_presentation_timestamp != kNoTimestamp())
428 prerolling_ = false; 430 prerolling_ = false;
429 431
430 switch (status) { 432 switch (status) {
431 case MEDIA_CODEC_OK: 433 case MEDIA_CODEC_OK:
432 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER: 434 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER:
433 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: 435 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED:
434 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: 436 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED:
435 case MEDIA_CODEC_OUTPUT_END_OF_STREAM: 437 case MEDIA_CODEC_OUTPUT_END_OF_STREAM:
436 if (!input_eos_encountered_) 438 if (!input_eos_encountered_)
437 access_unit_index_[current_demuxer_data_index_]++; 439 access_unit_index_[current_demuxer_data_index_]++;
438 break; 440 break;
439 441
440 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: 442 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER:
441 case MEDIA_CODEC_INPUT_END_OF_STREAM: 443 case MEDIA_CODEC_INPUT_END_OF_STREAM:
442 case MEDIA_CODEC_NO_KEY: 444 case MEDIA_CODEC_NO_KEY:
443 case MEDIA_CODEC_STOPPED: 445 case MEDIA_CODEC_STOPPED:
444 case MEDIA_CODEC_ERROR: 446 case MEDIA_CODEC_ERROR:
445 // Do nothing. 447 // Do nothing.
446 break; 448 break;
447 }; 449 };
448 450
449 stop_decode_pending_ = false; 451 stop_decode_pending_ = false;
450 base::ResetAndReturn(&decode_cb_).Run(status, presentation_timestamp, 452 base::ResetAndReturn(&decode_cb_).Run(
451 audio_output_bytes); 453 status, current_presentation_timestamp, max_presentation_timestamp);
452 } 454 }
453 455
454 const AccessUnit& MediaDecoderJob::CurrentAccessUnit() const { 456 const AccessUnit& MediaDecoderJob::CurrentAccessUnit() const {
455 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 457 DCHECK(ui_task_runner_->BelongsToCurrentThread());
456 DCHECK(HasData()); 458 DCHECK(HasData());
457 int index = NoAccessUnitsRemainingInChunk(true) ? 459 int index = NoAccessUnitsRemainingInChunk(true) ?
458 inactive_demuxer_data_index() : current_demuxer_data_index_; 460 inactive_demuxer_data_index() : current_demuxer_data_index_;
459 return received_data_[index].access_units[access_unit_index_[index]]; 461 return received_data_[index].access_units[access_unit_index_[index]];
460 } 462 }
461 463
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
495 } 497 }
496 498
497 void MediaDecoderJob::InitializeReceivedData() { 499 void MediaDecoderJob::InitializeReceivedData() {
498 for (size_t i = 0; i < 2; ++i) { 500 for (size_t i = 0; i < 2; ++i) {
499 received_data_[i] = DemuxerData(); 501 received_data_[i] = DemuxerData();
500 access_unit_index_[i] = 0; 502 access_unit_index_[i] = 0;
501 } 503 }
502 } 504 }
503 505
504 } // namespace media 506 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698