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(), 0, 0, |
| 77 base::TimeTicks()); |
77 return; | 78 return; |
78 } | 79 } |
79 | 80 |
80 if (!done_cb.is_null()) | 81 if (!done_cb.is_null()) |
81 done_cb.Run(); | 82 done_cb.Run(); |
82 } | 83 } |
83 | 84 |
84 void MediaDecoderJob::Prefetch(const base::Closure& prefetch_cb) { | 85 void MediaDecoderJob::Prefetch(const base::Closure& prefetch_cb) { |
85 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 86 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
86 DCHECK(on_data_received_cb_.is_null()); | 87 DCHECK(on_data_received_cb_.is_null()); |
(...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
263 RequestCurrentChunkIfEmpty(); | 264 RequestCurrentChunkIfEmpty(); |
264 const AccessUnit& access_unit = CurrentAccessUnit(); | 265 const AccessUnit& access_unit = CurrentAccessUnit(); |
265 // If the first access unit is a config change, request the player to dequeue | 266 // 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. | 267 // the input buffer again so that it can request config data. |
267 if (access_unit.status == DemuxerStream::kConfigChanged) { | 268 if (access_unit.status == DemuxerStream::kConfigChanged) { |
268 ui_task_runner_->PostTask(FROM_HERE, | 269 ui_task_runner_->PostTask(FROM_HERE, |
269 base::Bind(&MediaDecoderJob::OnDecodeCompleted, | 270 base::Bind(&MediaDecoderJob::OnDecodeCompleted, |
270 base::Unretained(this), | 271 base::Unretained(this), |
271 MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER, | 272 MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER, |
272 kNoTimestamp(), | 273 kNoTimestamp(), |
273 0)); | 274 0, 0, base::TimeTicks())); |
274 return; | 275 return; |
275 } | 276 } |
276 | 277 |
277 decoder_task_runner_->PostTask(FROM_HERE, base::Bind( | 278 decoder_task_runner_->PostTask(FROM_HERE, base::Bind( |
278 &MediaDecoderJob::DecodeInternal, base::Unretained(this), | 279 &MediaDecoderJob::DecodeInternal, base::Unretained(this), |
279 access_unit, | 280 access_unit, |
280 start_time_ticks, start_presentation_timestamp, needs_flush_, | 281 start_time_ticks, start_presentation_timestamp, needs_flush_, |
281 media::BindToCurrentLoop(base::Bind( | 282 media::BindToCurrentLoop(base::Bind( |
282 &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this))))); | 283 &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this))))); |
283 needs_flush_ = false; | 284 needs_flush_ = false; |
284 } | 285 } |
285 | 286 |
286 void MediaDecoderJob::DecodeInternal( | 287 void MediaDecoderJob::DecodeInternal( |
287 const AccessUnit& unit, | 288 const AccessUnit& unit, |
288 base::TimeTicks start_time_ticks, | 289 base::TimeTicks start_time_ticks, |
289 base::TimeDelta start_presentation_timestamp, | 290 base::TimeDelta start_presentation_timestamp, |
290 bool needs_flush, | 291 bool needs_flush, |
291 const MediaDecoderJob::DecoderCallback& callback) { | 292 const MediaDecoderJob::DecoderCallback& callback) { |
292 DVLOG(1) << __FUNCTION__; | 293 DVLOG(1) << __FUNCTION__; |
293 DCHECK(decoder_task_runner_->BelongsToCurrentThread()); | 294 DCHECK(decoder_task_runner_->BelongsToCurrentThread()); |
294 TRACE_EVENT0("media", __FUNCTION__); | 295 TRACE_EVENT0("media", __FUNCTION__); |
295 | 296 |
296 if (needs_flush) { | 297 if (needs_flush) { |
297 DVLOG(1) << "DecodeInternal needs flush."; | 298 DVLOG(1) << "DecodeInternal needs flush."; |
298 input_eos_encountered_ = false; | 299 input_eos_encountered_ = false; |
299 output_eos_encountered_ = false; | 300 output_eos_encountered_ = false; |
300 MediaCodecStatus reset_status = media_codec_bridge_->Reset(); | 301 MediaCodecStatus reset_status = media_codec_bridge_->Reset(); |
301 if (MEDIA_CODEC_OK != reset_status) { | 302 if (MEDIA_CODEC_OK != reset_status) { |
302 callback.Run(reset_status, kNoTimestamp(), 0); | 303 callback.Run(reset_status, kNoTimestamp(), 0, 0, base::TimeTicks()); |
303 return; | 304 return; |
304 } | 305 } |
305 } | 306 } |
306 | 307 |
307 // Once output EOS has occurred, we should not be asked to decode again. | 308 // 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 | 309 // MediaCodec has undefined behavior if similarly asked to decode after output |
309 // EOS. | 310 // EOS. |
310 DCHECK(!output_eos_encountered_); | 311 DCHECK(!output_eos_encountered_); |
311 | 312 |
312 // For aborted access unit, just skip it and inform the player. | 313 // For aborted access unit, just skip it and inform the player. |
313 if (unit.status == DemuxerStream::kAborted) { | 314 if (unit.status == DemuxerStream::kAborted) { |
314 // TODO(qinmin): use a new enum instead of MEDIA_CODEC_STOPPED. | 315 // TODO(qinmin): use a new enum instead of MEDIA_CODEC_STOPPED. |
315 callback.Run(MEDIA_CODEC_STOPPED, kNoTimestamp(), 0); | 316 callback.Run(MEDIA_CODEC_STOPPED, kNoTimestamp(), 0, 0, base::TimeTicks()); |
316 return; | 317 return; |
317 } | 318 } |
318 | 319 |
319 if (skip_eos_enqueue_) { | 320 if (skip_eos_enqueue_) { |
320 if (unit.end_of_stream || unit.data.empty()) { | 321 if (unit.end_of_stream || unit.data.empty()) { |
321 input_eos_encountered_ = true; | 322 input_eos_encountered_ = true; |
322 output_eos_encountered_ = true; | 323 output_eos_encountered_ = true; |
323 callback.Run(MEDIA_CODEC_OUTPUT_END_OF_STREAM, kNoTimestamp(), 0); | 324 callback.Run(MEDIA_CODEC_OUTPUT_END_OF_STREAM, kNoTimestamp(), 0, 0, |
| 325 base::TimeTicks()); |
324 return; | 326 return; |
325 } | 327 } |
326 | 328 |
327 skip_eos_enqueue_ = false; | 329 skip_eos_enqueue_ = false; |
328 } | 330 } |
329 | 331 |
330 MediaCodecStatus input_status = MEDIA_CODEC_INPUT_END_OF_STREAM; | 332 MediaCodecStatus input_status = MEDIA_CODEC_INPUT_END_OF_STREAM; |
331 if (!input_eos_encountered_) { | 333 if (!input_eos_encountered_) { |
332 input_status = QueueInputBuffer(unit); | 334 input_status = QueueInputBuffer(unit); |
333 if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM) { | 335 if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM) { |
334 input_eos_encountered_ = true; | 336 input_eos_encountered_ = true; |
335 } else if (input_status != MEDIA_CODEC_OK) { | 337 } else if (input_status != MEDIA_CODEC_OK) { |
336 callback.Run(input_status, kNoTimestamp(), 0); | 338 callback.Run(input_status, kNoTimestamp(), 0, 0, base::TimeTicks()); |
337 return; | 339 return; |
338 } | 340 } |
339 } | 341 } |
340 | 342 |
341 int buffer_index = 0; | 343 int buffer_index = 0; |
342 size_t offset = 0; | 344 size_t offset = 0; |
343 size_t size = 0; | 345 size_t size = 0; |
344 base::TimeDelta presentation_timestamp; | 346 base::TimeDelta presentation_timestamp; |
345 | 347 |
346 base::TimeDelta timeout = base::TimeDelta::FromMilliseconds( | 348 base::TimeDelta timeout = base::TimeDelta::FromMilliseconds( |
347 kMediaCodecTimeoutInMilliseconds); | 349 kMediaCodecTimeoutInMilliseconds); |
348 | 350 |
349 MediaCodecStatus status = | 351 MediaCodecStatus status = |
350 media_codec_bridge_->DequeueOutputBuffer(timeout, | 352 media_codec_bridge_->DequeueOutputBuffer(timeout, |
351 &buffer_index, | 353 &buffer_index, |
352 &offset, | 354 &offset, |
353 &size, | 355 &size, |
354 &presentation_timestamp, | 356 &presentation_timestamp, |
355 &output_eos_encountered_, | 357 &output_eos_encountered_, |
356 NULL); | 358 NULL); |
357 | 359 |
358 if (status != MEDIA_CODEC_OK) { | 360 if (status != MEDIA_CODEC_OK) { |
359 if (status == MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED && | 361 if (status == MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED && |
360 !media_codec_bridge_->GetOutputBuffers()) { | 362 !media_codec_bridge_->GetOutputBuffers()) { |
361 status = MEDIA_CODEC_ERROR; | 363 status = MEDIA_CODEC_ERROR; |
362 } | 364 } |
363 callback.Run(status, kNoTimestamp(), 0); | 365 callback.Run(status, kNoTimestamp(), 0, 0, base::TimeTicks()); |
364 return; | 366 return; |
365 } | 367 } |
366 | 368 |
367 // TODO(xhwang/qinmin): This logic is correct but strange. Clean it up. | 369 // TODO(xhwang/qinmin): This logic is correct but strange. Clean it up. |
368 if (output_eos_encountered_) | 370 if (output_eos_encountered_) |
369 status = MEDIA_CODEC_OUTPUT_END_OF_STREAM; | 371 status = MEDIA_CODEC_OUTPUT_END_OF_STREAM; |
370 else if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM) | 372 else if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM) |
371 status = MEDIA_CODEC_INPUT_END_OF_STREAM; | 373 status = MEDIA_CODEC_INPUT_END_OF_STREAM; |
372 | 374 |
373 bool render_output = presentation_timestamp >= preroll_timestamp_ && | 375 bool render_output = presentation_timestamp >= preroll_timestamp_ && |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
405 } else { | 407 } else { |
406 presentation_timestamp = kNoTimestamp(); | 408 presentation_timestamp = kNoTimestamp(); |
407 } | 409 } |
408 ReleaseOutputCompletionCallback completion_callback = base::Bind( | 410 ReleaseOutputCompletionCallback completion_callback = base::Bind( |
409 callback, status, presentation_timestamp); | 411 callback, status, presentation_timestamp); |
410 ReleaseOutputBuffer(buffer_index, size, render_output, completion_callback); | 412 ReleaseOutputBuffer(buffer_index, size, render_output, 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 presentation_timestamp, |
415 size_t audio_output_bytes) { | 417 size_t audio_output_bytes, int64 audio_head_position, |
| 418 base::TimeTicks audio_render_time) { |
416 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 419 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
417 | 420 |
418 if (destroy_pending_) { | 421 if (destroy_pending_) { |
419 DVLOG(1) << __FUNCTION__ << " : completing pending deletion"; | 422 DVLOG(1) << __FUNCTION__ << " : completing pending deletion"; |
420 delete this; | 423 delete this; |
421 return; | 424 return; |
422 } | 425 } |
423 | 426 |
424 DCHECK(!decode_cb_.is_null()); | 427 DCHECK(!decode_cb_.is_null()); |
425 | 428 |
(...skipping 14 matching lines...) Expand all Loading... |
440 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: | 443 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: |
441 case MEDIA_CODEC_INPUT_END_OF_STREAM: | 444 case MEDIA_CODEC_INPUT_END_OF_STREAM: |
442 case MEDIA_CODEC_NO_KEY: | 445 case MEDIA_CODEC_NO_KEY: |
443 case MEDIA_CODEC_STOPPED: | 446 case MEDIA_CODEC_STOPPED: |
444 case MEDIA_CODEC_ERROR: | 447 case MEDIA_CODEC_ERROR: |
445 // Do nothing. | 448 // Do nothing. |
446 break; | 449 break; |
447 }; | 450 }; |
448 | 451 |
449 stop_decode_pending_ = false; | 452 stop_decode_pending_ = false; |
450 base::ResetAndReturn(&decode_cb_).Run(status, presentation_timestamp, | 453 base::ResetAndReturn(&decode_cb_).Run( |
451 audio_output_bytes); | 454 status, presentation_timestamp, audio_output_bytes, audio_head_position, |
| 455 audio_render_time); |
452 } | 456 } |
453 | 457 |
454 const AccessUnit& MediaDecoderJob::CurrentAccessUnit() const { | 458 const AccessUnit& MediaDecoderJob::CurrentAccessUnit() const { |
455 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 459 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
456 DCHECK(HasData()); | 460 DCHECK(HasData()); |
457 int index = NoAccessUnitsRemainingInChunk(true) ? | 461 int index = NoAccessUnitsRemainingInChunk(true) ? |
458 inactive_demuxer_data_index() : current_demuxer_data_index_; | 462 inactive_demuxer_data_index() : current_demuxer_data_index_; |
459 return received_data_[index].access_units[access_unit_index_[index]]; | 463 return received_data_[index].access_units[access_unit_index_[index]]; |
460 } | 464 } |
461 | 465 |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
495 } | 499 } |
496 | 500 |
497 void MediaDecoderJob::InitializeReceivedData() { | 501 void MediaDecoderJob::InitializeReceivedData() { |
498 for (size_t i = 0; i < 2; ++i) { | 502 for (size_t i = 0; i < 2; ++i) { |
499 received_data_[i] = DemuxerData(); | 503 received_data_[i] = DemuxerData(); |
500 access_unit_index_[i] = 0; | 504 access_unit_index_[i] = 0; |
501 } | 505 } |
502 } | 506 } |
503 | 507 |
504 } // namespace media | 508 } // namespace media |
OLD | NEW |