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

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 wolenetz's comment 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(), 0, 0,
77 base::TimeTicks());
77 return; 78 return;
78 } 79 }
79 80
80 if (!done_cb.is_null()) 81 if (!done_cb.is_null())
81 done_cb.Run(); 82 done_cb.Run();
82 } 83 }
83 84
84 void MediaDecoderJob::Prefetch(const base::Closure& prefetch_cb) { 85 void MediaDecoderJob::Prefetch(const base::Closure& prefetch_cb) {
85 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 86 DCHECK(ui_task_runner_->BelongsToCurrentThread());
86 DCHECK(on_data_received_cb_.is_null()); 87 DCHECK(on_data_received_cb_.is_null());
(...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after
263 RequestCurrentChunkIfEmpty(); 264 RequestCurrentChunkIfEmpty();
264 const AccessUnit& access_unit = CurrentAccessUnit(); 265 const AccessUnit& access_unit = CurrentAccessUnit();
265 // If the first access unit is a config change, request the player to dequeue 266 // 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. 267 // the input buffer again so that it can request config data.
267 if (access_unit.status == DemuxerStream::kConfigChanged) { 268 if (access_unit.status == DemuxerStream::kConfigChanged) {
268 ui_task_runner_->PostTask(FROM_HERE, 269 ui_task_runner_->PostTask(FROM_HERE,
269 base::Bind(&MediaDecoderJob::OnDecodeCompleted, 270 base::Bind(&MediaDecoderJob::OnDecodeCompleted,
270 base::Unretained(this), 271 base::Unretained(this),
271 MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER, 272 MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER,
272 kNoTimestamp(), 273 kNoTimestamp(),
273 0)); 274 0, 0, base::TimeTicks()));
274 return; 275 return;
275 } 276 }
276 277
277 decoder_task_runner_->PostTask(FROM_HERE, base::Bind( 278 decoder_task_runner_->PostTask(FROM_HERE, base::Bind(
278 &MediaDecoderJob::DecodeInternal, base::Unretained(this), 279 &MediaDecoderJob::DecodeInternal, base::Unretained(this),
279 access_unit, 280 access_unit,
280 start_time_ticks, start_presentation_timestamp, needs_flush_, 281 start_time_ticks, start_presentation_timestamp, needs_flush_,
281 media::BindToCurrentLoop(base::Bind( 282 media::BindToCurrentLoop(base::Bind(
282 &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this))))); 283 &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this)))));
283 needs_flush_ = false; 284 needs_flush_ = false;
284 } 285 }
285 286
286 void MediaDecoderJob::DecodeInternal( 287 void MediaDecoderJob::DecodeInternal(
287 const AccessUnit& unit, 288 const AccessUnit& unit,
288 base::TimeTicks start_time_ticks, 289 base::TimeTicks start_time_ticks,
289 base::TimeDelta start_presentation_timestamp, 290 base::TimeDelta start_presentation_timestamp,
290 bool needs_flush, 291 bool needs_flush,
291 const MediaDecoderJob::DecoderCallback& callback) { 292 const MediaDecoderJob::DecoderCallback& callback) {
292 DVLOG(1) << __FUNCTION__; 293 DVLOG(1) << __FUNCTION__;
293 DCHECK(decoder_task_runner_->BelongsToCurrentThread()); 294 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
294 TRACE_EVENT0("media", __FUNCTION__); 295 TRACE_EVENT0("media", __FUNCTION__);
295 296
296 if (needs_flush) { 297 if (needs_flush) {
297 DVLOG(1) << "DecodeInternal needs flush."; 298 DVLOG(1) << "DecodeInternal needs flush.";
298 input_eos_encountered_ = false; 299 input_eos_encountered_ = false;
299 output_eos_encountered_ = false; 300 output_eos_encountered_ = false;
300 MediaCodecStatus reset_status = media_codec_bridge_->Reset(); 301 MediaCodecStatus reset_status = media_codec_bridge_->Reset();
301 if (MEDIA_CODEC_OK != reset_status) { 302 if (MEDIA_CODEC_OK != reset_status) {
302 callback.Run(reset_status, kNoTimestamp(), 0); 303 callback.Run(reset_status, kNoTimestamp(), 0, 0, base::TimeTicks());
303 return; 304 return;
304 } 305 }
305 } 306 }
306 307
307 // Once output EOS has occurred, we should not be asked to decode again. 308 // 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 309 // MediaCodec has undefined behavior if similarly asked to decode after output
309 // EOS. 310 // EOS.
310 DCHECK(!output_eos_encountered_); 311 DCHECK(!output_eos_encountered_);
311 312
312 // For aborted access unit, just skip it and inform the player. 313 // For aborted access unit, just skip it and inform the player.
313 if (unit.status == DemuxerStream::kAborted) { 314 if (unit.status == DemuxerStream::kAborted) {
314 // TODO(qinmin): use a new enum instead of MEDIA_CODEC_STOPPED. 315 // TODO(qinmin): use a new enum instead of MEDIA_CODEC_STOPPED.
315 callback.Run(MEDIA_CODEC_STOPPED, kNoTimestamp(), 0); 316 callback.Run(MEDIA_CODEC_STOPPED, kNoTimestamp(), 0, 0, base::TimeTicks());
316 return; 317 return;
317 } 318 }
318 319
319 if (skip_eos_enqueue_) { 320 if (skip_eos_enqueue_) {
320 if (unit.end_of_stream || unit.data.empty()) { 321 if (unit.end_of_stream || unit.data.empty()) {
321 input_eos_encountered_ = true; 322 input_eos_encountered_ = true;
322 output_eos_encountered_ = true; 323 output_eos_encountered_ = true;
323 callback.Run(MEDIA_CODEC_OUTPUT_END_OF_STREAM, kNoTimestamp(), 0); 324 callback.Run(MEDIA_CODEC_OUTPUT_END_OF_STREAM, kNoTimestamp(), 0, 0,
325 base::TimeTicks());
324 return; 326 return;
325 } 327 }
326 328
327 skip_eos_enqueue_ = false; 329 skip_eos_enqueue_ = false;
328 } 330 }
329 331
330 MediaCodecStatus input_status = MEDIA_CODEC_INPUT_END_OF_STREAM; 332 MediaCodecStatus input_status = MEDIA_CODEC_INPUT_END_OF_STREAM;
331 if (!input_eos_encountered_) { 333 if (!input_eos_encountered_) {
332 input_status = QueueInputBuffer(unit); 334 input_status = QueueInputBuffer(unit);
333 if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM) { 335 if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM) {
334 input_eos_encountered_ = true; 336 input_eos_encountered_ = true;
335 } else if (input_status != MEDIA_CODEC_OK) { 337 } else if (input_status != MEDIA_CODEC_OK) {
336 callback.Run(input_status, kNoTimestamp(), 0); 338 callback.Run(input_status, kNoTimestamp(), 0, 0, base::TimeTicks());
337 return; 339 return;
338 } 340 }
339 } 341 }
340 342
341 int buffer_index = 0; 343 int buffer_index = 0;
342 size_t offset = 0; 344 size_t offset = 0;
343 size_t size = 0; 345 size_t size = 0;
344 base::TimeDelta presentation_timestamp; 346 base::TimeDelta presentation_timestamp;
345 347
346 base::TimeDelta timeout = base::TimeDelta::FromMilliseconds( 348 base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(
347 kMediaCodecTimeoutInMilliseconds); 349 kMediaCodecTimeoutInMilliseconds);
348 350
349 MediaCodecStatus status = 351 MediaCodecStatus status =
350 media_codec_bridge_->DequeueOutputBuffer(timeout, 352 media_codec_bridge_->DequeueOutputBuffer(timeout,
351 &buffer_index, 353 &buffer_index,
352 &offset, 354 &offset,
353 &size, 355 &size,
354 &presentation_timestamp, 356 &presentation_timestamp,
355 &output_eos_encountered_, 357 &output_eos_encountered_,
356 NULL); 358 NULL);
357 359
358 if (status != MEDIA_CODEC_OK) { 360 if (status != MEDIA_CODEC_OK) {
359 if (status == MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED && 361 if (status == MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED &&
360 !media_codec_bridge_->GetOutputBuffers()) { 362 !media_codec_bridge_->GetOutputBuffers()) {
361 status = MEDIA_CODEC_ERROR; 363 status = MEDIA_CODEC_ERROR;
362 } 364 }
363 callback.Run(status, kNoTimestamp(), 0); 365 callback.Run(status, kNoTimestamp(), 0, 0, base::TimeTicks());
364 return; 366 return;
365 } 367 }
366 368
367 // TODO(xhwang/qinmin): This logic is correct but strange. Clean it up. 369 // TODO(xhwang/qinmin): This logic is correct but strange. Clean it up.
368 if (output_eos_encountered_) 370 if (output_eos_encountered_)
369 status = MEDIA_CODEC_OUTPUT_END_OF_STREAM; 371 status = MEDIA_CODEC_OUTPUT_END_OF_STREAM;
370 else if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM) 372 else if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM)
371 status = MEDIA_CODEC_INPUT_END_OF_STREAM; 373 status = MEDIA_CODEC_INPUT_END_OF_STREAM;
372 374
373 bool render_output = presentation_timestamp >= preroll_timestamp_ && 375 bool render_output = presentation_timestamp >= preroll_timestamp_ &&
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
405 } else { 407 } else {
406 presentation_timestamp = kNoTimestamp(); 408 presentation_timestamp = kNoTimestamp();
407 } 409 }
408 ReleaseOutputCompletionCallback completion_callback = base::Bind( 410 ReleaseOutputCompletionCallback completion_callback = base::Bind(
409 callback, status, presentation_timestamp); 411 callback, status, presentation_timestamp);
410 ReleaseOutputBuffer(buffer_index, size, render_output, completion_callback); 412 ReleaseOutputBuffer(buffer_index, size, render_output, 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 presentation_timestamp,
415 size_t audio_output_bytes) { 417 size_t audio_output_bytes, int64 audio_head_position,
418 base::TimeTicks audio_render_time) {
416 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 419 DCHECK(ui_task_runner_->BelongsToCurrentThread());
417 420
418 if (destroy_pending_) { 421 if (destroy_pending_) {
419 DVLOG(1) << __FUNCTION__ << " : completing pending deletion"; 422 DVLOG(1) << __FUNCTION__ << " : completing pending deletion";
420 delete this; 423 delete this;
421 return; 424 return;
422 } 425 }
423 426
424 DCHECK(!decode_cb_.is_null()); 427 DCHECK(!decode_cb_.is_null());
425 428
(...skipping 14 matching lines...) Expand all
440 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: 443 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER:
441 case MEDIA_CODEC_INPUT_END_OF_STREAM: 444 case MEDIA_CODEC_INPUT_END_OF_STREAM:
442 case MEDIA_CODEC_NO_KEY: 445 case MEDIA_CODEC_NO_KEY:
443 case MEDIA_CODEC_STOPPED: 446 case MEDIA_CODEC_STOPPED:
444 case MEDIA_CODEC_ERROR: 447 case MEDIA_CODEC_ERROR:
445 // Do nothing. 448 // Do nothing.
446 break; 449 break;
447 }; 450 };
448 451
449 stop_decode_pending_ = false; 452 stop_decode_pending_ = false;
450 base::ResetAndReturn(&decode_cb_).Run(status, presentation_timestamp, 453 base::ResetAndReturn(&decode_cb_).Run(
451 audio_output_bytes); 454 status, presentation_timestamp, audio_output_bytes, audio_head_position,
455 audio_render_time);
452 } 456 }
453 457
454 const AccessUnit& MediaDecoderJob::CurrentAccessUnit() const { 458 const AccessUnit& MediaDecoderJob::CurrentAccessUnit() const {
455 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 459 DCHECK(ui_task_runner_->BelongsToCurrentThread());
456 DCHECK(HasData()); 460 DCHECK(HasData());
457 int index = NoAccessUnitsRemainingInChunk(true) ? 461 int index = NoAccessUnitsRemainingInChunk(true) ?
458 inactive_demuxer_data_index() : current_demuxer_data_index_; 462 inactive_demuxer_data_index() : current_demuxer_data_index_;
459 return received_data_[index].access_units[access_unit_index_[index]]; 463 return received_data_[index].access_units[access_unit_index_[index]];
460 } 464 }
461 465
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
495 } 499 }
496 500
497 void MediaDecoderJob::InitializeReceivedData() { 501 void MediaDecoderJob::InitializeReceivedData() {
498 for (size_t i = 0; i < 2; ++i) { 502 for (size_t i = 0; i < 2; ++i) {
499 received_data_[i] = DemuxerData(); 503 received_data_[i] = DemuxerData();
500 access_unit_index_[i] = 0; 504 access_unit_index_[i] = 0;
501 } 505 }
502 } 506 }
503 507
504 } // namespace media 508 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698