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 |