| 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(), kNoTimestamp()); | 83 OnDecodeCompleted(MEDIA_CODEC_STOPPED, kNoTimestamp(), 0); |
| 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(), kNoTimestamp())); | 278 kNoTimestamp(), |
| 279 0)); |
| 279 return; | 280 return; |
| 280 } | 281 } |
| 281 | 282 |
| 282 decoder_task_runner_->PostTask(FROM_HERE, base::Bind( | 283 decoder_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 283 &MediaDecoderJob::DecodeInternal, base::Unretained(this), | 284 &MediaDecoderJob::DecodeInternal, base::Unretained(this), |
| 284 access_unit, | 285 access_unit, |
| 285 start_time_ticks, start_presentation_timestamp, needs_flush_, | 286 start_time_ticks, start_presentation_timestamp, needs_flush_, |
| 286 media::BindToCurrentLoop(base::Bind( | 287 media::BindToCurrentLoop(base::Bind( |
| 287 &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this))))); | 288 &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this))))); |
| 288 needs_flush_ = false; | 289 needs_flush_ = false; |
| 289 } | 290 } |
| 290 | 291 |
| 291 void MediaDecoderJob::DecodeInternal( | 292 void MediaDecoderJob::DecodeInternal( |
| 292 const AccessUnit& unit, | 293 const AccessUnit& unit, |
| 293 base::TimeTicks start_time_ticks, | 294 base::TimeTicks start_time_ticks, |
| 294 base::TimeDelta start_presentation_timestamp, | 295 base::TimeDelta start_presentation_timestamp, |
| 295 bool needs_flush, | 296 bool needs_flush, |
| 296 const MediaDecoderJob::DecoderCallback& callback) { | 297 const MediaDecoderJob::DecoderCallback& callback) { |
| 297 DVLOG(1) << __FUNCTION__; | 298 DVLOG(1) << __FUNCTION__; |
| 298 DCHECK(decoder_task_runner_->BelongsToCurrentThread()); | 299 DCHECK(decoder_task_runner_->BelongsToCurrentThread()); |
| 299 TRACE_EVENT0("media", __FUNCTION__); | 300 TRACE_EVENT0("media", __FUNCTION__); |
| 300 | 301 |
| 301 if (needs_flush) { | 302 if (needs_flush) { |
| 302 DVLOG(1) << "DecodeInternal needs flush."; | 303 DVLOG(1) << "DecodeInternal needs flush."; |
| 303 input_eos_encountered_ = false; | 304 input_eos_encountered_ = false; |
| 304 output_eos_encountered_ = false; | 305 output_eos_encountered_ = false; |
| 305 MediaCodecStatus reset_status = media_codec_bridge_->Reset(); | 306 MediaCodecStatus reset_status = media_codec_bridge_->Reset(); |
| 306 if (MEDIA_CODEC_OK != reset_status) { | 307 if (MEDIA_CODEC_OK != reset_status) { |
| 307 callback.Run(reset_status, kNoTimestamp(), kNoTimestamp()); | 308 callback.Run(reset_status, kNoTimestamp(), 0); |
| 308 return; | 309 return; |
| 309 } | 310 } |
| 310 } | 311 } |
| 311 | 312 |
| 312 // Once output EOS has occurred, we should not be asked to decode again. | 313 // Once output EOS has occurred, we should not be asked to decode again. |
| 313 // MediaCodec has undefined behavior if similarly asked to decode after output | 314 // MediaCodec has undefined behavior if similarly asked to decode after output |
| 314 // EOS. | 315 // EOS. |
| 315 DCHECK(!output_eos_encountered_); | 316 DCHECK(!output_eos_encountered_); |
| 316 | 317 |
| 317 // For aborted access unit, just skip it and inform the player. | 318 // For aborted access unit, just skip it and inform the player. |
| 318 if (unit.status == DemuxerStream::kAborted) { | 319 if (unit.status == DemuxerStream::kAborted) { |
| 319 // TODO(qinmin): use a new enum instead of MEDIA_CODEC_STOPPED. | 320 // TODO(qinmin): use a new enum instead of MEDIA_CODEC_STOPPED. |
| 320 callback.Run(MEDIA_CODEC_STOPPED, kNoTimestamp(), kNoTimestamp()); | 321 callback.Run(MEDIA_CODEC_STOPPED, kNoTimestamp(), 0); |
| 321 return; | 322 return; |
| 322 } | 323 } |
| 323 | 324 |
| 324 if (skip_eos_enqueue_) { | 325 if (skip_eos_enqueue_) { |
| 325 if (unit.end_of_stream || unit.data.empty()) { | 326 if (unit.end_of_stream || unit.data.empty()) { |
| 326 input_eos_encountered_ = true; | 327 input_eos_encountered_ = true; |
| 327 output_eos_encountered_ = true; | 328 output_eos_encountered_ = true; |
| 328 callback.Run(MEDIA_CODEC_OUTPUT_END_OF_STREAM, kNoTimestamp(), | 329 callback.Run(MEDIA_CODEC_OUTPUT_END_OF_STREAM, kNoTimestamp(), 0); |
| 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(), kNoTimestamp()); | 342 callback.Run(input_status, kNoTimestamp(), 0); |
| 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(), kNoTimestamp()); | 369 callback.Run(status, kNoTimestamp(), 0); |
| 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 presentation_timestamp, | 396 base::Bind(callback, status, presentation_timestamp)), |
| 397 base::Bind(callback, status)), | |
| 398 time_to_render); | 397 time_to_render); |
| 399 return; | 398 return; |
| 400 } | 399 } |
| 401 | 400 |
| 402 // TODO(qinmin): The codec is lagging behind, need to recalculate the | 401 // TODO(qinmin): The codec is lagging behind, need to recalculate the |
| 403 // |start_presentation_timestamp_| and |start_time_ticks_| in | 402 // |start_presentation_timestamp_| and |start_time_ticks_| in |
| 404 // media_source_player.cc. | 403 // media_source_player.cc. |
| 405 DVLOG(1) << "codec is lagging behind :" << time_to_render.InMicroseconds(); | 404 DVLOG(1) << "codec is lagging behind :" << time_to_render.InMicroseconds(); |
| 406 if (render_output) { | 405 if (render_output) { |
| 407 // The player won't expect a timestamp smaller than the | 406 // The player won't expect a timestamp smaller than the |
| 408 // |start_presentation_timestamp|. However, this could happen due to decoder | 407 // |start_presentation_timestamp|. However, this could happen due to decoder |
| 409 // errors. | 408 // errors. |
| 410 presentation_timestamp = std::max( | 409 presentation_timestamp = std::max( |
| 411 presentation_timestamp, start_presentation_timestamp); | 410 presentation_timestamp, start_presentation_timestamp); |
| 412 } else { | 411 } else { |
| 413 presentation_timestamp = kNoTimestamp(); | 412 presentation_timestamp = kNoTimestamp(); |
| 414 } | 413 } |
| 415 ReleaseOutputCompletionCallback completion_callback = base::Bind( | 414 ReleaseOutputCompletionCallback completion_callback = base::Bind( |
| 416 callback, status); | 415 callback, status, presentation_timestamp); |
| 417 ReleaseOutputBuffer(buffer_index, size, render_output, presentation_timestamp, | 416 ReleaseOutputBuffer(buffer_index, size, render_output, completion_callback); |
| 418 completion_callback); | |
| 419 } | 417 } |
| 420 | 418 |
| 421 void MediaDecoderJob::OnDecodeCompleted( | 419 void MediaDecoderJob::OnDecodeCompleted( |
| 422 MediaCodecStatus status, base::TimeDelta current_presentation_timestamp, | 420 MediaCodecStatus status, base::TimeDelta presentation_timestamp, |
| 423 base::TimeDelta max_presentation_timestamp) { | 421 size_t audio_output_bytes) { |
| 424 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 422 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
| 425 | 423 |
| 426 if (destroy_pending_) { | 424 if (destroy_pending_) { |
| 427 DVLOG(1) << __FUNCTION__ << " : completing pending deletion"; | 425 DVLOG(1) << __FUNCTION__ << " : completing pending deletion"; |
| 428 delete this; | 426 delete this; |
| 429 return; | 427 return; |
| 430 } | 428 } |
| 431 | 429 |
| 432 DCHECK(!decode_cb_.is_null()); | 430 DCHECK(!decode_cb_.is_null()); |
| 433 | 431 |
| 434 // If output was queued for rendering, then we have completed prerolling. | 432 // If output was queued for rendering, then we have completed prerolling. |
| 435 if (current_presentation_timestamp != kNoTimestamp()) | 433 if (presentation_timestamp != kNoTimestamp()) |
| 436 prerolling_ = false; | 434 prerolling_ = false; |
| 437 | 435 |
| 438 switch (status) { | 436 switch (status) { |
| 439 case MEDIA_CODEC_OK: | 437 case MEDIA_CODEC_OK: |
| 440 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER: | 438 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER: |
| 441 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: | 439 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: |
| 442 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: | 440 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: |
| 443 case MEDIA_CODEC_OUTPUT_END_OF_STREAM: | 441 case MEDIA_CODEC_OUTPUT_END_OF_STREAM: |
| 444 if (!input_eos_encountered_) | 442 if (!input_eos_encountered_) |
| 445 access_unit_index_[current_demuxer_data_index_]++; | 443 access_unit_index_[current_demuxer_data_index_]++; |
| 446 break; | 444 break; |
| 447 | 445 |
| 448 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: | 446 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: |
| 449 case MEDIA_CODEC_INPUT_END_OF_STREAM: | 447 case MEDIA_CODEC_INPUT_END_OF_STREAM: |
| 450 case MEDIA_CODEC_NO_KEY: | 448 case MEDIA_CODEC_NO_KEY: |
| 451 case MEDIA_CODEC_STOPPED: | 449 case MEDIA_CODEC_STOPPED: |
| 452 case MEDIA_CODEC_ERROR: | 450 case MEDIA_CODEC_ERROR: |
| 453 // Do nothing. | 451 // Do nothing. |
| 454 break; | 452 break; |
| 455 }; | 453 }; |
| 456 | 454 |
| 457 stop_decode_pending_ = false; | 455 stop_decode_pending_ = false; |
| 458 base::ResetAndReturn(&decode_cb_).Run( | 456 base::ResetAndReturn(&decode_cb_).Run(status, presentation_timestamp, |
| 459 status, current_presentation_timestamp, max_presentation_timestamp); | 457 audio_output_bytes); |
| 460 } | 458 } |
| 461 | 459 |
| 462 const AccessUnit& MediaDecoderJob::CurrentAccessUnit() const { | 460 const AccessUnit& MediaDecoderJob::CurrentAccessUnit() const { |
| 463 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 461 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
| 464 DCHECK(HasData()); | 462 DCHECK(HasData()); |
| 465 int index = NoAccessUnitsRemainingInChunk(true) ? | 463 int index = NoAccessUnitsRemainingInChunk(true) ? |
| 466 inactive_demuxer_data_index() : current_demuxer_data_index_; | 464 inactive_demuxer_data_index() : current_demuxer_data_index_; |
| 467 return received_data_[index].access_units[access_unit_index_[index]]; | 465 return received_data_[index].access_units[access_unit_index_[index]]; |
| 468 } | 466 } |
| 469 | 467 |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 503 } | 501 } |
| 504 | 502 |
| 505 void MediaDecoderJob::InitializeReceivedData() { | 503 void MediaDecoderJob::InitializeReceivedData() { |
| 506 for (size_t i = 0; i < 2; ++i) { | 504 for (size_t i = 0; i < 2; ++i) { |
| 507 received_data_[i] = DemuxerData(); | 505 received_data_[i] = DemuxerData(); |
| 508 access_unit_index_[i] = 0; | 506 access_unit_index_[i] = 0; |
| 509 } | 507 } |
| 510 } | 508 } |
| 511 | 509 |
| 512 } // namespace media | 510 } // namespace media |
| OLD | NEW |