OLD | NEW |
---|---|
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 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_source_player.h" | 5 #include "media/base/android/media_source_player.h" |
6 | 6 |
7 #include <limits> | 7 #include <limits> |
8 | 8 |
9 #include "base/android/jni_android.h" | 9 #include "base/android/jni_android.h" |
10 #include "base/android/jni_string.h" | 10 #include "base/android/jni_string.h" |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
54 is_video_encrypted_(false), | 54 is_video_encrypted_(false), |
55 volume_(-1.0), | 55 volume_(-1.0), |
56 clock_(&default_tick_clock_), | 56 clock_(&default_tick_clock_), |
57 next_video_data_is_iframe_(true), | 57 next_video_data_is_iframe_(true), |
58 doing_browser_seek_(false), | 58 doing_browser_seek_(false), |
59 pending_seek_(false), | 59 pending_seek_(false), |
60 reconfig_audio_decoder_(false), | 60 reconfig_audio_decoder_(false), |
61 reconfig_video_decoder_(false), | 61 reconfig_video_decoder_(false), |
62 weak_this_(this), | 62 weak_this_(this), |
63 drm_bridge_(NULL), | 63 drm_bridge_(NULL), |
64 is_waiting_for_key_(false) { | 64 is_waiting_for_key_(false), |
65 has_pending_audio_data_request_(false), | |
66 has_pending_video_data_request_(false) { | |
65 demuxer_->Initialize(this); | 67 demuxer_->Initialize(this); |
66 clock_.SetMaxTime(base::TimeDelta()); | 68 clock_.SetMaxTime(base::TimeDelta()); |
67 } | 69 } |
68 | 70 |
69 MediaSourcePlayer::~MediaSourcePlayer() { | 71 MediaSourcePlayer::~MediaSourcePlayer() { |
70 Release(); | 72 Release(); |
71 } | 73 } |
72 | 74 |
73 void MediaSourcePlayer::SetVideoSurface(gfx::ScopedJavaSurface surface) { | 75 void MediaSourcePlayer::SetVideoSurface(gfx::ScopedJavaSurface surface) { |
74 // For an empty surface, always pass it to the decoder job so that it | 76 // For an empty surface, always pass it to the decoder job so that it |
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
215 // or drop data received across Release()+Start(). See http://crbug.com/306314 | 217 // or drop data received across Release()+Start(). See http://crbug.com/306314 |
216 // and http://crbug.com/304234. | 218 // and http://crbug.com/304234. |
217 bool process_pending_events = false; | 219 bool process_pending_events = false; |
218 process_pending_events = IsEventPending(PREFETCH_DONE_EVENT_PENDING) || | 220 process_pending_events = IsEventPending(PREFETCH_DONE_EVENT_PENDING) || |
219 (audio_decoder_job_ && audio_decoder_job_->is_decoding()) || | 221 (audio_decoder_job_ && audio_decoder_job_->is_decoding()) || |
220 (video_decoder_job_ && video_decoder_job_->is_decoding()); | 222 (video_decoder_job_ && video_decoder_job_->is_decoding()); |
221 | 223 |
222 // Clear all the pending events except seeks and config changes. | 224 // Clear all the pending events except seeks and config changes. |
223 pending_event_ &= (SEEK_EVENT_PENDING | CONFIG_CHANGE_EVENT_PENDING); | 225 pending_event_ &= (SEEK_EVENT_PENDING | CONFIG_CHANGE_EVENT_PENDING); |
224 is_surface_in_use_ = false; | 226 is_surface_in_use_ = false; |
225 audio_decoder_job_.reset(); | 227 ResetAudioDecoderJob(); |
226 ResetVideoDecoderJob(); | 228 ResetVideoDecoderJob(); |
227 | 229 |
228 // Prevent job re-creation attempts in OnDemuxerConfigsAvailable() | 230 // Prevent job re-creation attempts in OnDemuxerConfigsAvailable() |
229 reconfig_audio_decoder_ = false; | 231 reconfig_audio_decoder_ = false; |
230 reconfig_video_decoder_ = false; | 232 reconfig_video_decoder_ = false; |
231 | 233 |
232 // Prevent player restart, including job re-creation attempts. | 234 // Prevent player restart, including job re-creation attempts. |
233 playing_ = false; | 235 playing_ = false; |
234 | 236 |
235 decoder_starvation_callback_.Cancel(); | 237 decoder_starvation_callback_.Cancel(); |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
338 | 340 |
339 // Resume decoding after the config change if we are still playing. | 341 // Resume decoding after the config change if we are still playing. |
340 if (playing_) | 342 if (playing_) |
341 StartInternal(); | 343 StartInternal(); |
342 } | 344 } |
343 } | 345 } |
344 | 346 |
345 void MediaSourcePlayer::OnDemuxerDataAvailable(const DemuxerData& data) { | 347 void MediaSourcePlayer::OnDemuxerDataAvailable(const DemuxerData& data) { |
346 DVLOG(1) << __FUNCTION__ << "(" << data.type << ")"; | 348 DVLOG(1) << __FUNCTION__ << "(" << data.type << ")"; |
347 DCHECK_LT(0u, data.access_units.size()); | 349 DCHECK_LT(0u, data.access_units.size()); |
350 | |
351 if (has_pending_audio_data_request_ && data.type == DemuxerStream::AUDIO) { | |
wolenetz
2014/03/17 19:51:00
Do I understand correctly that we drop on the floo
qinmin
2014/03/18 18:58:58
Yes, the purpose of this is to drop the request th
| |
352 has_pending_audio_data_request_ = false; | |
353 ProcessPendingEvents(); | |
354 return; | |
355 } | |
356 | |
357 if (has_pending_video_data_request_ && data.type == DemuxerStream::VIDEO) { | |
358 next_video_data_is_iframe_ = false; | |
359 has_pending_video_data_request_ = false; | |
360 ProcessPendingEvents(); | |
361 return; | |
362 } | |
363 | |
348 if (data.type == DemuxerStream::AUDIO && audio_decoder_job_) { | 364 if (data.type == DemuxerStream::AUDIO && audio_decoder_job_) { |
349 audio_decoder_job_->OnDataReceived(data); | 365 audio_decoder_job_->OnDataReceived(data); |
350 } else if (data.type == DemuxerStream::VIDEO) { | 366 } else if (data.type == DemuxerStream::VIDEO) { |
351 next_video_data_is_iframe_ = false; | 367 next_video_data_is_iframe_ = false; |
352 if (video_decoder_job_) | 368 if (video_decoder_job_) |
353 video_decoder_job_->OnDataReceived(data); | 369 video_decoder_job_->OnDataReceived(data); |
354 } | 370 } |
355 } | 371 } |
356 | 372 |
357 void MediaSourcePlayer::OnDemuxerDurationChanged(base::TimeDelta duration) { | 373 void MediaSourcePlayer::OnDemuxerDurationChanged(base::TimeDelta duration) { |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
411 DCHECK(doing_browser_seek_); | 427 DCHECK(doing_browser_seek_); |
412 pending_seek_ = false; | 428 pending_seek_ = false; |
413 SeekTo(pending_seek_time_); | 429 SeekTo(pending_seek_time_); |
414 return; | 430 return; |
415 } | 431 } |
416 | 432 |
417 // It is possible that a browser seek to I-frame had to seek to a buffered | 433 // It is possible that a browser seek to I-frame had to seek to a buffered |
418 // I-frame later than the requested one due to data removal or GC. Update | 434 // I-frame later than the requested one due to data removal or GC. Update |
419 // player clock to the actual seek target. | 435 // player clock to the actual seek target. |
420 if (doing_browser_seek_) { | 436 if (doing_browser_seek_) { |
421 DCHECK(actual_browser_seek_time != kNoTimestamp()); | 437 base::TimeDelta seek_time = (actual_browser_seek_time == kNoTimestamp()) ? |
438 base::TimeDelta() : actual_browser_seek_time; | |
422 // A browser seek must not jump into the past. Ideally, it seeks to the | 439 // A browser seek must not jump into the past. Ideally, it seeks to the |
423 // requested time, but it might jump into the future. | 440 // requested time, but it might jump into the future. |
424 DCHECK(actual_browser_seek_time >= GetCurrentTime()); | 441 DCHECK(seek_time >= GetCurrentTime()); |
425 DVLOG(1) << __FUNCTION__ << " : setting clock to actual browser seek time: " | 442 DVLOG(1) << __FUNCTION__ << " : setting clock to actual browser seek time: " |
426 << actual_browser_seek_time.InSecondsF(); | 443 << seek_time.InSecondsF(); |
427 clock_.SetTime(actual_browser_seek_time, actual_browser_seek_time); | 444 clock_.SetTime(seek_time, seek_time); |
428 if (audio_timestamp_helper_) | 445 if (audio_timestamp_helper_) |
429 audio_timestamp_helper_->SetBaseTimestamp(actual_browser_seek_time); | 446 audio_timestamp_helper_->SetBaseTimestamp(seek_time); |
430 } | 447 } |
431 | 448 |
432 reached_audio_eos_ = false; | 449 reached_audio_eos_ = false; |
433 reached_video_eos_ = false; | 450 reached_video_eos_ = false; |
434 | 451 |
435 base::TimeDelta current_time = GetCurrentTime(); | 452 base::TimeDelta current_time = GetCurrentTime(); |
436 // TODO(qinmin): Simplify the logic by using |start_presentation_timestamp_| | 453 // TODO(qinmin): Simplify the logic by using |start_presentation_timestamp_| |
437 // to preroll media decoder jobs. Currently |start_presentation_timestamp_| | 454 // to preroll media decoder jobs. Currently |start_presentation_timestamp_| |
438 // is calculated from decoder output, while preroll relies on the access | 455 // is calculated from decoder output, while preroll relies on the access |
439 // unit's timestamp. There are some differences between the two. | 456 // unit's timestamp. There are some differences between the two. |
(...skipping 29 matching lines...) Expand all Loading... | |
469 if (video_decoder_job_ && video_decoder_job_->is_decoding()) { | 486 if (video_decoder_job_ && video_decoder_job_->is_decoding()) { |
470 DVLOG(1) << __FUNCTION__ << " : A video job is still decoding."; | 487 DVLOG(1) << __FUNCTION__ << " : A video job is still decoding."; |
471 return; | 488 return; |
472 } | 489 } |
473 | 490 |
474 if (audio_decoder_job_ && audio_decoder_job_->is_decoding()) { | 491 if (audio_decoder_job_ && audio_decoder_job_->is_decoding()) { |
475 DVLOG(1) << __FUNCTION__ << " : An audio job is still decoding."; | 492 DVLOG(1) << __FUNCTION__ << " : An audio job is still decoding."; |
476 return; | 493 return; |
477 } | 494 } |
478 | 495 |
496 if (has_pending_audio_data_request_ || has_pending_video_data_request_) { | |
497 DVLOG(1) << __FUNCTION__ << " : has pending data request."; | |
498 return; | |
499 } | |
500 | |
479 if (IsEventPending(PREFETCH_DONE_EVENT_PENDING)) { | 501 if (IsEventPending(PREFETCH_DONE_EVENT_PENDING)) { |
480 DVLOG(1) << __FUNCTION__ << " : PREFETCH_DONE still pending."; | 502 DVLOG(1) << __FUNCTION__ << " : PREFETCH_DONE still pending."; |
481 return; | 503 return; |
482 } | 504 } |
483 | 505 |
484 if (IsEventPending(SEEK_EVENT_PENDING)) { | 506 if (IsEventPending(SEEK_EVENT_PENDING)) { |
485 DVLOG(1) << __FUNCTION__ << " : Handling SEEK_EVENT"; | 507 DVLOG(1) << __FUNCTION__ << " : Handling SEEK_EVENT"; |
486 ClearDecodingData(); | 508 ClearDecodingData(); |
487 demuxer_->RequestDemuxerSeek(GetCurrentTime(), doing_browser_seek_); | 509 demuxer_->RequestDemuxerSeek(GetCurrentTime(), doing_browser_seek_); |
488 return; | 510 return; |
(...skipping 14 matching lines...) Expand all Loading... | |
503 ConfigureVideoDecoderJob(); | 525 ConfigureVideoDecoderJob(); |
504 | 526 |
505 // Return early if we can't successfully configure a new video decoder job | 527 // Return early if we can't successfully configure a new video decoder job |
506 // yet. | 528 // yet. |
507 if (HasVideo() && !video_decoder_job_) | 529 if (HasVideo() && !video_decoder_job_) |
508 return; | 530 return; |
509 } | 531 } |
510 | 532 |
511 if (IsEventPending(PREFETCH_REQUEST_EVENT_PENDING)) { | 533 if (IsEventPending(PREFETCH_REQUEST_EVENT_PENDING)) { |
512 DVLOG(1) << __FUNCTION__ << " : Handling PREFETCH_REQUEST_EVENT."; | 534 DVLOG(1) << __FUNCTION__ << " : Handling PREFETCH_REQUEST_EVENT."; |
535 // If one of the decoder is not initialized, cancel this event as it will be | |
wolenetz
2014/03/17 19:51:00
Were we hitting the DCHECK's below? In other words
qinmin
2014/03/18 18:58:58
This is possible with the new implementation.
Cons
| |
536 // called later when Start() is called again. | |
537 if ((HasVideo() && !video_decoder_job_) || | |
538 (HasAudio() && !audio_decoder_job_)) { | |
539 ClearPendingEvent(PREFETCH_REQUEST_EVENT_PENDING); | |
540 return; | |
541 } | |
542 | |
513 DCHECK(audio_decoder_job_ || AudioFinished()); | 543 DCHECK(audio_decoder_job_ || AudioFinished()); |
514 DCHECK(video_decoder_job_ || VideoFinished()); | 544 DCHECK(video_decoder_job_ || VideoFinished()); |
515 | 545 |
516 int count = (AudioFinished() ? 0 : 1) + (VideoFinished() ? 0 : 1); | 546 int count = (AudioFinished() ? 0 : 1) + (VideoFinished() ? 0 : 1); |
517 | 547 |
518 // It is possible that all streams have finished decode, yet starvation | 548 // It is possible that all streams have finished decode, yet starvation |
519 // occurred during the last stream's EOS decode. In this case, prefetch is a | 549 // occurred during the last stream's EOS decode. In this case, prefetch is a |
520 // no-op. | 550 // no-op. |
521 ClearPendingEvent(PREFETCH_REQUEST_EVENT_PENDING); | 551 ClearPendingEvent(PREFETCH_REQUEST_EVENT_PENDING); |
522 if (count == 0) | 552 if (count == 0) |
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
740 bool MediaSourcePlayer::AudioFinished() { | 770 bool MediaSourcePlayer::AudioFinished() { |
741 return reached_audio_eos_ || !HasAudio(); | 771 return reached_audio_eos_ || !HasAudio(); |
742 } | 772 } |
743 | 773 |
744 bool MediaSourcePlayer::VideoFinished() { | 774 bool MediaSourcePlayer::VideoFinished() { |
745 return reached_video_eos_ || !HasVideo(); | 775 return reached_video_eos_ || !HasVideo(); |
746 } | 776 } |
747 | 777 |
748 void MediaSourcePlayer::ConfigureAudioDecoderJob() { | 778 void MediaSourcePlayer::ConfigureAudioDecoderJob() { |
749 if (!HasAudio()) { | 779 if (!HasAudio()) { |
750 audio_decoder_job_.reset(); | 780 ResetAudioDecoderJob(); |
751 return; | 781 return; |
752 } | 782 } |
753 | 783 |
754 // Create audio decoder job only if config changes. | 784 // Create audio decoder job only if config changes. |
755 if (audio_decoder_job_ && !reconfig_audio_decoder_) | 785 if (audio_decoder_job_ && !reconfig_audio_decoder_) |
756 return; | 786 return; |
757 | 787 |
758 base::android::ScopedJavaLocalRef<jobject> media_crypto = GetMediaCrypto(); | 788 base::android::ScopedJavaLocalRef<jobject> media_crypto = GetMediaCrypto(); |
759 if (is_audio_encrypted_ && media_crypto.is_null()) | 789 if (is_audio_encrypted_ && media_crypto.is_null()) |
760 return; | 790 return; |
761 | 791 |
762 DCHECK(!audio_decoder_job_ || !audio_decoder_job_->is_decoding()); | 792 DCHECK(!audio_decoder_job_ || !audio_decoder_job_->is_decoding()); |
763 | 793 |
794 ResetAudioDecoderJob(); | |
764 DVLOG(1) << __FUNCTION__ << " : creating new audio decoder job"; | 795 DVLOG(1) << __FUNCTION__ << " : creating new audio decoder job"; |
765 | |
766 audio_decoder_job_.reset(AudioDecoderJob::Create( | 796 audio_decoder_job_.reset(AudioDecoderJob::Create( |
767 audio_codec_, sampling_rate_, num_channels_, &audio_extra_data_[0], | 797 audio_codec_, sampling_rate_, num_channels_, &audio_extra_data_[0], |
768 audio_extra_data_.size(), media_crypto.obj(), | 798 audio_extra_data_.size(), media_crypto.obj(), |
769 base::Bind(&DemuxerAndroid::RequestDemuxerData, | 799 base::Bind(&DemuxerAndroid::RequestDemuxerData, |
770 base::Unretained(demuxer_.get()), DemuxerStream::AUDIO))); | 800 base::Unretained(demuxer_.get()), DemuxerStream::AUDIO))); |
771 | 801 |
772 if (audio_decoder_job_) { | 802 if (audio_decoder_job_) { |
773 SetVolumeInternal(); | 803 SetVolumeInternal(); |
774 audio_decoder_job_->BeginPrerolling(preroll_timestamp_); | 804 audio_decoder_job_->BeginPrerolling(preroll_timestamp_); |
775 reconfig_audio_decoder_ = false; | 805 reconfig_audio_decoder_ = false; |
776 } | 806 } |
777 } | 807 } |
778 | 808 |
779 void MediaSourcePlayer::ResetVideoDecoderJob() { | 809 void MediaSourcePlayer::ResetVideoDecoderJob() { |
810 if (video_decoder_job_) { | |
811 has_pending_video_data_request_ = | |
812 video_decoder_job_->is_requesting_demuxer_data(); | |
813 } | |
780 video_decoder_job_.reset(); | 814 video_decoder_job_.reset(); |
781 | 815 |
782 // Any eventual video decoder job re-creation will use the current |surface_|. | 816 // Any eventual video decoder job re-creation will use the current |surface_|. |
783 if (IsEventPending(SURFACE_CHANGE_EVENT_PENDING)) | 817 if (IsEventPending(SURFACE_CHANGE_EVENT_PENDING)) |
784 ClearPendingEvent(SURFACE_CHANGE_EVENT_PENDING); | 818 ClearPendingEvent(SURFACE_CHANGE_EVENT_PENDING); |
785 } | 819 } |
786 | 820 |
821 void MediaSourcePlayer::ResetAudioDecoderJob() { | |
822 if (audio_decoder_job_) { | |
823 has_pending_audio_data_request_ = | |
824 audio_decoder_job_->is_requesting_demuxer_data(); | |
825 } | |
826 audio_decoder_job_.reset(); | |
827 } | |
828 | |
787 void MediaSourcePlayer::ConfigureVideoDecoderJob() { | 829 void MediaSourcePlayer::ConfigureVideoDecoderJob() { |
788 if (!HasVideo() || surface_.IsEmpty()) { | 830 if (!HasVideo() || surface_.IsEmpty()) { |
789 ResetVideoDecoderJob(); | 831 ResetVideoDecoderJob(); |
790 return; | 832 return; |
791 } | 833 } |
792 | 834 |
793 // Create video decoder job only if config changes or we don't have a job. | 835 // Create video decoder job only if config changes or we don't have a job. |
794 if (video_decoder_job_ && !reconfig_video_decoder_) { | 836 if (video_decoder_job_ && !reconfig_video_decoder_) { |
795 DCHECK(!IsEventPending(SURFACE_CHANGE_EVENT_PENDING)); | 837 DCHECK(!IsEventPending(SURFACE_CHANGE_EVENT_PENDING)); |
796 return; | 838 return; |
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
968 | 1010 |
969 void MediaSourcePlayer::ClearPendingEvent(PendingEventFlags event) { | 1011 void MediaSourcePlayer::ClearPendingEvent(PendingEventFlags event) { |
970 DVLOG(1) << __FUNCTION__ << "(" << GetEventName(event) << ")"; | 1012 DVLOG(1) << __FUNCTION__ << "(" << GetEventName(event) << ")"; |
971 DCHECK_NE(event, NO_EVENT_PENDING); | 1013 DCHECK_NE(event, NO_EVENT_PENDING); |
972 DCHECK(IsEventPending(event)) << GetEventName(event); | 1014 DCHECK(IsEventPending(event)) << GetEventName(event); |
973 | 1015 |
974 pending_event_ &= ~event; | 1016 pending_event_ &= ~event; |
975 } | 1017 } |
976 | 1018 |
977 } // namespace media | 1019 } // namespace media |
OLD | NEW |