| 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 "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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 |
| OLD | NEW |