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 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
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(), kNoTimestamp()); |
77 return; | 77 return; |
78 } | 78 } |
79 | 79 |
80 if (!done_cb.is_null()) | 80 if (!done_cb.is_null()) |
81 done_cb.Run(); | 81 done_cb.Run(); |
82 } | 82 } |
83 | 83 |
84 void MediaDecoderJob::Prefetch(const base::Closure& prefetch_cb) { | 84 void MediaDecoderJob::Prefetch(const base::Closure& prefetch_cb) { |
85 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 85 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
86 DCHECK(on_data_received_cb_.is_null()); | 86 DCHECK(on_data_received_cb_.is_null()); |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
262 | 262 |
263 RequestCurrentChunkIfEmpty(); | 263 RequestCurrentChunkIfEmpty(); |
264 const AccessUnit& access_unit = CurrentAccessUnit(); | 264 const AccessUnit& access_unit = CurrentAccessUnit(); |
265 // If the first access unit is a config change, request the player to dequeue | 265 // 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. | 266 // the input buffer again so that it can request config data. |
267 if (access_unit.status == DemuxerStream::kConfigChanged) { | 267 if (access_unit.status == DemuxerStream::kConfigChanged) { |
268 ui_task_runner_->PostTask(FROM_HERE, | 268 ui_task_runner_->PostTask(FROM_HERE, |
269 base::Bind(&MediaDecoderJob::OnDecodeCompleted, | 269 base::Bind(&MediaDecoderJob::OnDecodeCompleted, |
270 base::Unretained(this), | 270 base::Unretained(this), |
271 MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER, | 271 MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER, |
272 kNoTimestamp(), | 272 kNoTimestamp(), kNoTimestamp())); |
273 0)); | |
274 return; | 273 return; |
275 } | 274 } |
276 | 275 |
277 decoder_task_runner_->PostTask(FROM_HERE, base::Bind( | 276 decoder_task_runner_->PostTask(FROM_HERE, base::Bind( |
278 &MediaDecoderJob::DecodeInternal, base::Unretained(this), | 277 &MediaDecoderJob::DecodeInternal, base::Unretained(this), |
279 access_unit, | 278 access_unit, |
280 start_time_ticks, start_presentation_timestamp, needs_flush_, | 279 start_time_ticks, start_presentation_timestamp, needs_flush_, |
281 media::BindToCurrentLoop(base::Bind( | 280 media::BindToCurrentLoop(base::Bind( |
282 &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this))))); | 281 &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this))))); |
283 needs_flush_ = false; | 282 needs_flush_ = false; |
284 } | 283 } |
285 | 284 |
286 void MediaDecoderJob::DecodeInternal( | 285 void MediaDecoderJob::DecodeInternal( |
287 const AccessUnit& unit, | 286 const AccessUnit& unit, |
288 base::TimeTicks start_time_ticks, | 287 base::TimeTicks start_time_ticks, |
289 base::TimeDelta start_presentation_timestamp, | 288 base::TimeDelta start_presentation_timestamp, |
290 bool needs_flush, | 289 bool needs_flush, |
291 const MediaDecoderJob::DecoderCallback& callback) { | 290 const MediaDecoderJob::DecoderCallback& callback) { |
292 DVLOG(1) << __FUNCTION__; | 291 DVLOG(1) << __FUNCTION__; |
293 DCHECK(decoder_task_runner_->BelongsToCurrentThread()); | 292 DCHECK(decoder_task_runner_->BelongsToCurrentThread()); |
294 TRACE_EVENT0("media", __FUNCTION__); | 293 TRACE_EVENT0("media", __FUNCTION__); |
295 | 294 |
296 if (needs_flush) { | 295 if (needs_flush) { |
297 DVLOG(1) << "DecodeInternal needs flush."; | 296 DVLOG(1) << "DecodeInternal needs flush."; |
298 input_eos_encountered_ = false; | 297 input_eos_encountered_ = false; |
299 output_eos_encountered_ = false; | 298 output_eos_encountered_ = false; |
300 MediaCodecStatus reset_status = media_codec_bridge_->Reset(); | 299 MediaCodecStatus reset_status = media_codec_bridge_->Reset(); |
301 if (MEDIA_CODEC_OK != reset_status) { | 300 if (MEDIA_CODEC_OK != reset_status) { |
302 callback.Run(reset_status, kNoTimestamp(), 0); | 301 callback.Run(reset_status, kNoTimestamp(), kNoTimestamp()); |
303 return; | 302 return; |
304 } | 303 } |
305 } | 304 } |
306 | 305 |
307 // Once output EOS has occurred, we should not be asked to decode again. | 306 // 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 | 307 // MediaCodec has undefined behavior if similarly asked to decode after output |
309 // EOS. | 308 // EOS. |
310 DCHECK(!output_eos_encountered_); | 309 DCHECK(!output_eos_encountered_); |
311 | 310 |
312 // For aborted access unit, just skip it and inform the player. | 311 // For aborted access unit, just skip it and inform the player. |
313 if (unit.status == DemuxerStream::kAborted) { | 312 if (unit.status == DemuxerStream::kAborted) { |
314 // TODO(qinmin): use a new enum instead of MEDIA_CODEC_STOPPED. | 313 // TODO(qinmin): use a new enum instead of MEDIA_CODEC_STOPPED. |
315 callback.Run(MEDIA_CODEC_STOPPED, kNoTimestamp(), 0); | 314 callback.Run(MEDIA_CODEC_STOPPED, kNoTimestamp(), kNoTimestamp()); |
316 return; | 315 return; |
317 } | 316 } |
318 | 317 |
319 if (skip_eos_enqueue_) { | 318 if (skip_eos_enqueue_) { |
320 if (unit.end_of_stream || unit.data.empty()) { | 319 if (unit.end_of_stream || unit.data.empty()) { |
321 input_eos_encountered_ = true; | 320 input_eos_encountered_ = true; |
322 output_eos_encountered_ = true; | 321 output_eos_encountered_ = true; |
323 callback.Run(MEDIA_CODEC_OUTPUT_END_OF_STREAM, kNoTimestamp(), 0); | 322 callback.Run(MEDIA_CODEC_OUTPUT_END_OF_STREAM, kNoTimestamp(), |
| 323 kNoTimestamp()); |
324 return; | 324 return; |
325 } | 325 } |
326 | 326 |
327 skip_eos_enqueue_ = false; | 327 skip_eos_enqueue_ = false; |
328 } | 328 } |
329 | 329 |
330 MediaCodecStatus input_status = MEDIA_CODEC_INPUT_END_OF_STREAM; | 330 MediaCodecStatus input_status = MEDIA_CODEC_INPUT_END_OF_STREAM; |
331 if (!input_eos_encountered_) { | 331 if (!input_eos_encountered_) { |
332 input_status = QueueInputBuffer(unit); | 332 input_status = QueueInputBuffer(unit); |
333 if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM) { | 333 if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM) { |
334 input_eos_encountered_ = true; | 334 input_eos_encountered_ = true; |
335 } else if (input_status != MEDIA_CODEC_OK) { | 335 } else if (input_status != MEDIA_CODEC_OK) { |
336 callback.Run(input_status, kNoTimestamp(), 0); | 336 callback.Run(input_status, kNoTimestamp(), kNoTimestamp()); |
337 return; | 337 return; |
338 } | 338 } |
339 } | 339 } |
340 | 340 |
341 int buffer_index = 0; | 341 int buffer_index = 0; |
342 size_t offset = 0; | 342 size_t offset = 0; |
343 size_t size = 0; | 343 size_t size = 0; |
344 base::TimeDelta presentation_timestamp; | 344 base::TimeDelta presentation_timestamp; |
345 | 345 |
346 base::TimeDelta timeout = base::TimeDelta::FromMilliseconds( | 346 base::TimeDelta timeout = base::TimeDelta::FromMilliseconds( |
347 kMediaCodecTimeoutInMilliseconds); | 347 kMediaCodecTimeoutInMilliseconds); |
348 | 348 |
349 MediaCodecStatus status = | 349 MediaCodecStatus status = |
350 media_codec_bridge_->DequeueOutputBuffer(timeout, | 350 media_codec_bridge_->DequeueOutputBuffer(timeout, |
351 &buffer_index, | 351 &buffer_index, |
352 &offset, | 352 &offset, |
353 &size, | 353 &size, |
354 &presentation_timestamp, | 354 &presentation_timestamp, |
355 &output_eos_encountered_, | 355 &output_eos_encountered_, |
356 NULL); | 356 NULL); |
357 | 357 |
358 if (status != MEDIA_CODEC_OK) { | 358 if (status != MEDIA_CODEC_OK) { |
359 if (status == MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED && | 359 if (status == MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED && |
360 !media_codec_bridge_->GetOutputBuffers()) { | 360 !media_codec_bridge_->GetOutputBuffers()) { |
361 status = MEDIA_CODEC_ERROR; | 361 status = MEDIA_CODEC_ERROR; |
362 } | 362 } |
363 callback.Run(status, kNoTimestamp(), 0); | 363 callback.Run(status, kNoTimestamp(), kNoTimestamp()); |
364 return; | 364 return; |
365 } | 365 } |
366 | 366 |
367 // TODO(xhwang/qinmin): This logic is correct but strange. Clean it up. | 367 // TODO(xhwang/qinmin): This logic is correct but strange. Clean it up. |
368 if (output_eos_encountered_) | 368 if (output_eos_encountered_) |
369 status = MEDIA_CODEC_OUTPUT_END_OF_STREAM; | 369 status = MEDIA_CODEC_OUTPUT_END_OF_STREAM; |
370 else if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM) | 370 else if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM) |
371 status = MEDIA_CODEC_INPUT_END_OF_STREAM; | 371 status = MEDIA_CODEC_INPUT_END_OF_STREAM; |
372 | 372 |
373 bool render_output = presentation_timestamp >= preroll_timestamp_ && | 373 bool render_output = presentation_timestamp >= preroll_timestamp_ && |
374 (status != MEDIA_CODEC_OUTPUT_END_OF_STREAM || size != 0u); | 374 (status != MEDIA_CODEC_OUTPUT_END_OF_STREAM || size != 0u); |
375 base::TimeDelta time_to_render; | 375 base::TimeDelta time_to_render; |
376 DCHECK(!start_time_ticks.is_null()); | 376 DCHECK(!start_time_ticks.is_null()); |
377 if (render_output && ComputeTimeToRender()) { | 377 if (render_output && ComputeTimeToRender()) { |
378 time_to_render = presentation_timestamp - (base::TimeTicks::Now() - | 378 time_to_render = presentation_timestamp - (base::TimeTicks::Now() - |
379 start_time_ticks + start_presentation_timestamp); | 379 start_time_ticks + start_presentation_timestamp); |
380 } | 380 } |
381 | 381 |
382 if (time_to_render > base::TimeDelta()) { | 382 if (time_to_render > base::TimeDelta()) { |
383 decoder_task_runner_->PostDelayedTask( | 383 decoder_task_runner_->PostDelayedTask( |
384 FROM_HERE, | 384 FROM_HERE, |
385 base::Bind(&MediaDecoderJob::ReleaseOutputBuffer, | 385 base::Bind(&MediaDecoderJob::ReleaseOutputBuffer, |
386 weak_factory_.GetWeakPtr(), | 386 weak_factory_.GetWeakPtr(), |
387 buffer_index, | 387 buffer_index, |
388 size, | 388 size, |
389 render_output, | 389 render_output, |
390 base::Bind(callback, status, presentation_timestamp)), | 390 presentation_timestamp, |
| 391 base::Bind(callback, status)), |
391 time_to_render); | 392 time_to_render); |
392 return; | 393 return; |
393 } | 394 } |
394 | 395 |
395 // TODO(qinmin): The codec is lagging behind, need to recalculate the | 396 // TODO(qinmin): The codec is lagging behind, need to recalculate the |
396 // |start_presentation_timestamp_| and |start_time_ticks_| in | 397 // |start_presentation_timestamp_| and |start_time_ticks_| in |
397 // media_source_player.cc. | 398 // media_source_player.cc. |
398 DVLOG(1) << "codec is lagging behind :" << time_to_render.InMicroseconds(); | 399 DVLOG(1) << "codec is lagging behind :" << time_to_render.InMicroseconds(); |
399 if (render_output) { | 400 if (render_output) { |
400 // The player won't expect a timestamp smaller than the | 401 // The player won't expect a timestamp smaller than the |
401 // |start_presentation_timestamp|. However, this could happen due to decoder | 402 // |start_presentation_timestamp|. However, this could happen due to decoder |
402 // errors. | 403 // errors. |
403 presentation_timestamp = std::max( | 404 presentation_timestamp = std::max( |
404 presentation_timestamp, start_presentation_timestamp); | 405 presentation_timestamp, start_presentation_timestamp); |
405 } else { | 406 } else { |
406 presentation_timestamp = kNoTimestamp(); | 407 presentation_timestamp = kNoTimestamp(); |
407 } | 408 } |
408 ReleaseOutputCompletionCallback completion_callback = base::Bind( | 409 ReleaseOutputCompletionCallback completion_callback = base::Bind( |
409 callback, status, presentation_timestamp); | 410 callback, status); |
410 ReleaseOutputBuffer(buffer_index, size, render_output, completion_callback); | 411 ReleaseOutputBuffer(buffer_index, size, render_output, presentation_timestamp, |
| 412 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 current_presentation_timestamp, |
415 size_t audio_output_bytes) { | 417 base::TimeDelta max_presentation_timestamp) { |
416 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 418 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
417 | 419 |
418 if (destroy_pending_) { | 420 if (destroy_pending_) { |
419 DVLOG(1) << __FUNCTION__ << " : completing pending deletion"; | 421 DVLOG(1) << __FUNCTION__ << " : completing pending deletion"; |
420 delete this; | 422 delete this; |
421 return; | 423 return; |
422 } | 424 } |
423 | 425 |
424 DCHECK(!decode_cb_.is_null()); | 426 DCHECK(!decode_cb_.is_null()); |
425 | 427 |
426 // If output was queued for rendering, then we have completed prerolling. | 428 // If output was queued for rendering, then we have completed prerolling. |
427 if (presentation_timestamp != kNoTimestamp()) | 429 if (current_presentation_timestamp != kNoTimestamp()) |
428 prerolling_ = false; | 430 prerolling_ = false; |
429 | 431 |
430 switch (status) { | 432 switch (status) { |
431 case MEDIA_CODEC_OK: | 433 case MEDIA_CODEC_OK: |
432 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER: | 434 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER: |
433 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: | 435 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: |
434 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: | 436 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: |
435 case MEDIA_CODEC_OUTPUT_END_OF_STREAM: | 437 case MEDIA_CODEC_OUTPUT_END_OF_STREAM: |
436 if (!input_eos_encountered_) | 438 if (!input_eos_encountered_) |
437 access_unit_index_[current_demuxer_data_index_]++; | 439 access_unit_index_[current_demuxer_data_index_]++; |
438 break; | 440 break; |
439 | 441 |
440 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: | 442 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: |
441 case MEDIA_CODEC_INPUT_END_OF_STREAM: | 443 case MEDIA_CODEC_INPUT_END_OF_STREAM: |
442 case MEDIA_CODEC_NO_KEY: | 444 case MEDIA_CODEC_NO_KEY: |
443 case MEDIA_CODEC_STOPPED: | 445 case MEDIA_CODEC_STOPPED: |
444 case MEDIA_CODEC_ERROR: | 446 case MEDIA_CODEC_ERROR: |
445 // Do nothing. | 447 // Do nothing. |
446 break; | 448 break; |
447 }; | 449 }; |
448 | 450 |
449 stop_decode_pending_ = false; | 451 stop_decode_pending_ = false; |
450 base::ResetAndReturn(&decode_cb_).Run(status, presentation_timestamp, | 452 base::ResetAndReturn(&decode_cb_).Run( |
451 audio_output_bytes); | 453 status, current_presentation_timestamp, max_presentation_timestamp); |
452 } | 454 } |
453 | 455 |
454 const AccessUnit& MediaDecoderJob::CurrentAccessUnit() const { | 456 const AccessUnit& MediaDecoderJob::CurrentAccessUnit() const { |
455 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 457 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
456 DCHECK(HasData()); | 458 DCHECK(HasData()); |
457 int index = NoAccessUnitsRemainingInChunk(true) ? | 459 int index = NoAccessUnitsRemainingInChunk(true) ? |
458 inactive_demuxer_data_index() : current_demuxer_data_index_; | 460 inactive_demuxer_data_index() : current_demuxer_data_index_; |
459 return received_data_[index].access_units[access_unit_index_[index]]; | 461 return received_data_[index].access_units[access_unit_index_[index]]; |
460 } | 462 } |
461 | 463 |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
495 } | 497 } |
496 | 498 |
497 void MediaDecoderJob::InitializeReceivedData() { | 499 void MediaDecoderJob::InitializeReceivedData() { |
498 for (size_t i = 0; i < 2; ++i) { | 500 for (size_t i = 0; i < 2; ++i) { |
499 received_data_[i] = DemuxerData(); | 501 received_data_[i] = DemuxerData(); |
500 access_unit_index_[i] = 0; | 502 access_unit_index_[i] = 0; |
501 } | 503 } |
502 } | 504 } |
503 | 505 |
504 } // namespace media | 506 } // namespace media |
OLD | NEW |