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 |