OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/pipeline.h" | 5 #include "media/base/pipeline.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/callback.h" | 10 #include "base/callback.h" |
11 #include "base/callback_helpers.h" | 11 #include "base/callback_helpers.h" |
12 #include "base/compiler_specific.h" | 12 #include "base/compiler_specific.h" |
13 #include "base/location.h" | 13 #include "base/location.h" |
14 #include "base/metrics/histogram.h" | 14 #include "base/metrics/histogram.h" |
15 #include "base/single_thread_task_runner.h" | 15 #include "base/single_thread_task_runner.h" |
16 #include "base/stl_util.h" | 16 #include "base/stl_util.h" |
17 #include "base/strings/string_number_conversions.h" | 17 #include "base/strings/string_number_conversions.h" |
18 #include "base/strings/string_util.h" | 18 #include "base/strings/string_util.h" |
19 #include "base/synchronization/condition_variable.h" | 19 #include "base/synchronization/condition_variable.h" |
20 #include "media/base/audio_decoder.h" | 20 #include "media/base/audio_decoder.h" |
21 #include "media/base/audio_renderer.h" | 21 #include "media/base/audio_renderer.h" |
22 #include "media/base/bind_to_current_loop.h" | |
22 #include "media/base/clock.h" | 23 #include "media/base/clock.h" |
23 #include "media/base/filter_collection.h" | 24 #include "media/base/filter_collection.h" |
24 #include "media/base/media_log.h" | 25 #include "media/base/media_log.h" |
25 #include "media/base/text_renderer.h" | 26 #include "media/base/text_renderer.h" |
26 #include "media/base/text_track_config.h" | 27 #include "media/base/text_track_config.h" |
27 #include "media/base/video_decoder.h" | 28 #include "media/base/video_decoder.h" |
28 #include "media/base/video_decoder_config.h" | 29 #include "media/base/video_decoder_config.h" |
29 #include "media/base/video_renderer.h" | 30 #include "media/base/video_renderer.h" |
30 | 31 |
31 using base::TimeDelta; | 32 using base::TimeDelta; |
(...skipping 373 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
405 metadata.natural_size = stream->video_decoder_config().natural_size(); | 406 metadata.natural_size = stream->video_decoder_config().natural_size(); |
406 metadata_cb_.Run(metadata); | 407 metadata_cb_.Run(metadata); |
407 } | 408 } |
408 | 409 |
409 return DoInitialPreroll(done_cb); | 410 return DoInitialPreroll(done_cb); |
410 | 411 |
411 case kPlaying: | 412 case kPlaying: |
412 PlaybackRateChangedTask(GetPlaybackRate()); | 413 PlaybackRateChangedTask(GetPlaybackRate()); |
413 VolumeChangedTask(GetVolume()); | 414 VolumeChangedTask(GetVolume()); |
414 | 415 |
416 // Handle renderers that immediately signal they have enough data. | |
417 if (!WaitingForEnoughData()) | |
418 TransitionToNonWaiting(); | |
419 | |
415 // We enter this state from either kInitPrerolling or kSeeking. As of now | 420 // We enter this state from either kInitPrerolling or kSeeking. As of now |
416 // both those states call Preroll(), which means by time we enter this | 421 // both those states call Preroll(), which means by time we enter this |
417 // state we've already buffered enough data. Forcefully update the | 422 // state we've already buffered enough data. Forcefully update the |
418 // buffering state, which start the clock and renderers and transition | 423 // buffering state, which start the clock and renderers and transition |
419 // into kPlaying state. | 424 // into kPlaying state. |
420 // | 425 // |
421 // TODO(scherkus): Remove after renderers are taught to fire buffering | 426 // TODO(scherkus): Remove after renderers are taught to fire buffering |
422 // state callbacks http://crbug.com/144683 | 427 // state callbacks http://crbug.com/144683 |
423 DCHECK(WaitingForEnoughData()); | 428 if (video_renderer_) { |
424 if (audio_renderer_) | 429 DCHECK(WaitingForEnoughData()); |
425 BufferingStateChanged(&audio_buffering_state_, BUFFERING_HAVE_ENOUGH); | |
426 if (video_renderer_) | |
427 BufferingStateChanged(&video_buffering_state_, BUFFERING_HAVE_ENOUGH); | 430 BufferingStateChanged(&video_buffering_state_, BUFFERING_HAVE_ENOUGH); |
431 } | |
428 return; | 432 return; |
429 | 433 |
430 case kStopping: | 434 case kStopping: |
431 case kStopped: | 435 case kStopped: |
432 case kCreated: | 436 case kCreated: |
433 case kSeeking: | 437 case kSeeking: |
434 NOTREACHED() << "State has no transition: " << state_; | 438 NOTREACHED() << "State has no transition: " << state_; |
435 return; | 439 return; |
436 } | 440 } |
437 } | 441 } |
438 | 442 |
439 // Note that the usage of base::Unretained() with the audio/video renderers | 443 // Note that the usage of base::Unretained() with the audio/video renderers |
440 // in the following DoXXX() functions is considered safe as they are owned by | 444 // in the following DoXXX() functions is considered safe as they are owned by |
441 // |pending_callbacks_| and share the same lifetime. | 445 // |pending_callbacks_| and share the same lifetime. |
442 // | 446 // |
443 // That being said, deleting the renderers while keeping |pending_callbacks_| | 447 // That being said, deleting the renderers while keeping |pending_callbacks_| |
444 // running on the media thread would result in crashes. | 448 // running on the media thread would result in crashes. |
445 void Pipeline::DoInitialPreroll(const PipelineStatusCB& done_cb) { | 449 void Pipeline::DoInitialPreroll(const PipelineStatusCB& done_cb) { |
446 DCHECK(task_runner_->BelongsToCurrentThread()); | 450 DCHECK(task_runner_->BelongsToCurrentThread()); |
447 DCHECK(!pending_callbacks_.get()); | 451 DCHECK(!pending_callbacks_.get()); |
448 SerialRunner::Queue bound_fns; | 452 SerialRunner::Queue bound_fns; |
449 | 453 |
450 base::TimeDelta seek_timestamp = demuxer_->GetStartTime(); | 454 base::TimeDelta seek_timestamp = demuxer_->GetStartTime(); |
451 | 455 |
452 // Preroll renderers. | 456 // Preroll renderers. |
453 if (audio_renderer_) { | 457 if (audio_renderer_) { |
454 bound_fns.Push(base::Bind( | 458 bound_fns.Push(base::Bind(&AudioRenderer::StartPlayingFrom, |
455 &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()), | 459 base::Unretained(audio_renderer_.get()), |
456 seek_timestamp)); | 460 seek_timestamp)); |
457 } | 461 } |
458 | 462 |
459 if (video_renderer_) { | 463 if (video_renderer_) { |
460 bound_fns.Push(base::Bind( | 464 bound_fns.Push(base::Bind( |
461 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()), | 465 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()), |
462 seek_timestamp)); | 466 seek_timestamp)); |
463 | 467 |
464 // TODO(scherkus): Remove after VideoRenderer is taught to fire buffering | 468 // TODO(scherkus): Remove after VideoRenderer is taught to fire buffering |
465 // state callbacks http://crbug.com/144683 | 469 // state callbacks http://crbug.com/144683 |
466 bound_fns.Push(base::Bind(&VideoRenderer::Play, | 470 bound_fns.Push(base::Bind(&VideoRenderer::Play, |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
515 bound_fns.Push(base::Bind( | 519 bound_fns.Push(base::Bind( |
516 &TextRenderer::Flush, base::Unretained(text_renderer_.get()))); | 520 &TextRenderer::Flush, base::Unretained(text_renderer_.get()))); |
517 } | 521 } |
518 | 522 |
519 // Seek demuxer. | 523 // Seek demuxer. |
520 bound_fns.Push(base::Bind( | 524 bound_fns.Push(base::Bind( |
521 &Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); | 525 &Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); |
522 | 526 |
523 // Preroll renderers. | 527 // Preroll renderers. |
524 if (audio_renderer_) { | 528 if (audio_renderer_) { |
525 bound_fns.Push(base::Bind( | 529 bound_fns.Push(base::Bind(&AudioRenderer::StartPlayingFrom, |
526 &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()), | 530 base::Unretained(audio_renderer_.get()), |
527 seek_timestamp)); | 531 seek_timestamp)); |
528 } | 532 } |
529 | 533 |
530 if (video_renderer_) { | 534 if (video_renderer_) { |
531 bound_fns.Push(base::Bind( | 535 bound_fns.Push(base::Bind( |
532 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()), | 536 &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()), |
533 seek_timestamp)); | 537 seek_timestamp)); |
534 | 538 |
535 // TODO(scherkus): Remove after renderers are taught to fire buffering | 539 // TODO(scherkus): Remove after renderers are taught to fire buffering |
536 // state callbacks http://crbug.com/144683 | 540 // state callbacks http://crbug.com/144683 |
537 bound_fns.Push(base::Bind(&VideoRenderer::Play, | 541 bound_fns.Push(base::Bind(&VideoRenderer::Play, |
(...skipping 312 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
850 } | 854 } |
851 | 855 |
852 void Pipeline::InitializeAudioRenderer(const PipelineStatusCB& done_cb) { | 856 void Pipeline::InitializeAudioRenderer(const PipelineStatusCB& done_cb) { |
853 DCHECK(task_runner_->BelongsToCurrentThread()); | 857 DCHECK(task_runner_->BelongsToCurrentThread()); |
854 | 858 |
855 audio_renderer_ = filter_collection_->GetAudioRenderer(); | 859 audio_renderer_ = filter_collection_->GetAudioRenderer(); |
856 audio_renderer_->Initialize( | 860 audio_renderer_->Initialize( |
857 demuxer_->GetStream(DemuxerStream::AUDIO), | 861 demuxer_->GetStream(DemuxerStream::AUDIO), |
858 done_cb, | 862 done_cb, |
859 base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)), | 863 base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)), |
860 base::Bind(&Pipeline::OnAudioUnderflow, base::Unretained(this)), | |
861 base::Bind(&Pipeline::OnAudioTimeUpdate, base::Unretained(this)), | 864 base::Bind(&Pipeline::OnAudioTimeUpdate, base::Unretained(this)), |
865 // TODO(scherkus): Enforce AudioRendererImpl to call on right thread. | |
866 BindToCurrentLoop(base::Bind(&Pipeline::BufferingStateChanged, | |
acolwell GONE FROM CHROMIUM
2014/05/13 20:44:27
nit: Shouldn't we hide this wrapping in the AudioR
scherkus (not reviewing)
2014/05/15 23:28:43
Done.
| |
867 base::Unretained(this), | |
868 &audio_buffering_state_)), | |
862 base::Bind(&Pipeline::OnAudioRendererEnded, base::Unretained(this)), | 869 base::Bind(&Pipeline::OnAudioRendererEnded, base::Unretained(this)), |
863 base::Bind(&Pipeline::SetError, base::Unretained(this))); | 870 base::Bind(&Pipeline::SetError, base::Unretained(this))); |
864 } | 871 } |
865 | 872 |
866 void Pipeline::InitializeVideoRenderer(const PipelineStatusCB& done_cb) { | 873 void Pipeline::InitializeVideoRenderer(const PipelineStatusCB& done_cb) { |
867 DCHECK(task_runner_->BelongsToCurrentThread()); | 874 DCHECK(task_runner_->BelongsToCurrentThread()); |
868 | 875 |
869 video_renderer_ = filter_collection_->GetVideoRenderer(); | 876 video_renderer_ = filter_collection_->GetVideoRenderer(); |
870 video_renderer_->Initialize( | 877 video_renderer_->Initialize( |
871 demuxer_->GetStream(DemuxerStream::VIDEO), | 878 demuxer_->GetStream(DemuxerStream::VIDEO), |
872 demuxer_->GetLiveness() == Demuxer::LIVENESS_LIVE, | 879 demuxer_->GetLiveness() == Demuxer::LIVENESS_LIVE, |
873 done_cb, | 880 done_cb, |
874 base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)), | 881 base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)), |
875 base::Bind(&Pipeline::OnVideoTimeUpdate, base::Unretained(this)), | 882 base::Bind(&Pipeline::OnVideoTimeUpdate, base::Unretained(this)), |
876 base::Bind(&Pipeline::OnVideoRendererEnded, base::Unretained(this)), | 883 base::Bind(&Pipeline::OnVideoRendererEnded, base::Unretained(this)), |
877 base::Bind(&Pipeline::SetError, base::Unretained(this)), | 884 base::Bind(&Pipeline::SetError, base::Unretained(this)), |
878 base::Bind(&Pipeline::GetMediaTime, base::Unretained(this)), | 885 base::Bind(&Pipeline::GetMediaTime, base::Unretained(this)), |
879 base::Bind(&Pipeline::GetMediaDuration, base::Unretained(this))); | 886 base::Bind(&Pipeline::GetMediaDuration, base::Unretained(this))); |
880 } | 887 } |
881 | 888 |
882 void Pipeline::OnAudioUnderflow() { | |
883 if (!task_runner_->BelongsToCurrentThread()) { | |
acolwell GONE FROM CHROMIUM
2014/05/13 20:44:27
\o/ I'm happy to see this method die. It lived WAY
| |
884 task_runner_->PostTask(FROM_HERE, base::Bind( | |
885 &Pipeline::OnAudioUnderflow, base::Unretained(this))); | |
886 return; | |
887 } | |
888 | |
889 if (state_ != kPlaying) | |
890 return; | |
891 | |
892 if (audio_renderer_) | |
893 audio_renderer_->ResumeAfterUnderflow(); | |
894 } | |
895 | |
896 void Pipeline::BufferingStateChanged(BufferingState* buffering_state, | 889 void Pipeline::BufferingStateChanged(BufferingState* buffering_state, |
897 BufferingState new_buffering_state) { | 890 BufferingState new_buffering_state) { |
898 DVLOG(1) << __FUNCTION__ << "(" << *buffering_state << ", " | 891 DVLOG(1) << __FUNCTION__ << "(" << *buffering_state << ", " |
899 << " " << new_buffering_state << ") " | 892 << " " << new_buffering_state << ") " |
900 << (buffering_state == &audio_buffering_state_ ? "audio" : "video"); | 893 << (buffering_state == &audio_buffering_state_ ? "audio" : "video"); |
901 DCHECK(task_runner_->BelongsToCurrentThread()); | 894 DCHECK(task_runner_->BelongsToCurrentThread()); |
902 bool was_waiting_for_enough_data = WaitingForEnoughData(); | 895 bool was_waiting_for_enough_data = WaitingForEnoughData(); |
903 *buffering_state = new_buffering_state; | 896 *buffering_state = new_buffering_state; |
904 | 897 |
905 // Renderer underflowed. | 898 // Renderer underflowed. |
906 if (!was_waiting_for_enough_data && WaitingForEnoughData()) { | 899 if (!was_waiting_for_enough_data && WaitingForEnoughData()) { |
907 StartWaitingForEnoughData(); | 900 TransitionToWaiting(); |
908 return; | 901 return; |
909 } | 902 } |
910 | 903 |
911 // Renderer prerolled. | 904 // Renderer prerolled. |
912 if (was_waiting_for_enough_data && !WaitingForEnoughData()) { | 905 if (was_waiting_for_enough_data && !WaitingForEnoughData()) { |
913 StartPlayback(); | 906 TransitionToNonWaiting(); |
914 return; | 907 return; |
915 } | 908 } |
916 } | 909 } |
917 | 910 |
918 bool Pipeline::WaitingForEnoughData() const { | 911 bool Pipeline::WaitingForEnoughData() const { |
919 DCHECK(task_runner_->BelongsToCurrentThread()); | 912 DCHECK(task_runner_->BelongsToCurrentThread()); |
920 if (state_ != kPlaying) | 913 if (state_ != kPlaying) |
921 return false; | 914 return false; |
922 if (audio_renderer_ && audio_buffering_state_ != BUFFERING_HAVE_ENOUGH) | 915 if (audio_renderer_ && audio_buffering_state_ != BUFFERING_HAVE_ENOUGH) |
923 return true; | 916 return true; |
924 if (video_renderer_ && video_buffering_state_ != BUFFERING_HAVE_ENOUGH) | 917 if (video_renderer_ && video_buffering_state_ != BUFFERING_HAVE_ENOUGH) |
925 return true; | 918 return true; |
926 return false; | 919 return false; |
927 } | 920 } |
928 | 921 |
929 void Pipeline::StartWaitingForEnoughData() { | 922 void Pipeline::TransitionToWaiting() { |
930 DVLOG(1) << __FUNCTION__; | 923 DVLOG(1) << __FUNCTION__; |
931 DCHECK_EQ(state_, kPlaying); | 924 DCHECK_EQ(state_, kPlaying); |
932 DCHECK(WaitingForEnoughData()); | 925 DCHECK(WaitingForEnoughData()); |
926 task_runner_->PostTask( | |
927 FROM_HERE, base::Bind(&Pipeline::PausePlayback, base::Unretained(this))); | |
928 } | |
929 | |
930 void Pipeline::TransitionToNonWaiting() { | |
931 DVLOG(1) << __FUNCTION__; | |
932 DCHECK_EQ(state_, kPlaying); | |
933 DCHECK(!WaitingForEnoughData()); | |
934 task_runner_->PostTask( | |
acolwell GONE FROM CHROMIUM
2014/05/13 20:44:27
Why do you need to PostTask() here and above now?
scherkus (not reviewing)
2014/05/15 23:28:43
it's to avoid reentrancy into renderers ... but I
| |
935 FROM_HERE, base::Bind(&Pipeline::StartPlayback, base::Unretained(this))); | |
936 } | |
937 | |
938 void Pipeline::PausePlayback() { | |
939 DVLOG(1) << __FUNCTION__; | |
940 DCHECK(task_runner_->BelongsToCurrentThread()); | |
933 | 941 |
934 if (audio_renderer_) | 942 if (audio_renderer_) |
935 audio_renderer_->StopRendering(); | 943 audio_renderer_->StopRendering(); |
936 | 944 |
937 base::AutoLock auto_lock(lock_); | 945 base::AutoLock auto_lock(lock_); |
938 clock_->Pause(); | 946 clock_->Pause(); |
939 } | 947 } |
940 | 948 |
941 void Pipeline::StartPlayback() { | 949 void Pipeline::StartPlayback() { |
942 DVLOG(1) << __FUNCTION__; | 950 DVLOG(1) << __FUNCTION__; |
943 DCHECK_EQ(state_, kPlaying); | 951 DCHECK(task_runner_->BelongsToCurrentThread()); |
944 DCHECK(!WaitingForEnoughData()); | |
945 | 952 |
946 if (audio_renderer_) { | 953 if (audio_renderer_) { |
947 // We use audio stream to update the clock. So if there is such a | 954 // We use audio stream to update the clock. So if there is such a |
948 // stream, we pause the clock until we receive a valid timestamp. | 955 // stream, we pause the clock until we receive a valid timestamp. |
949 base::AutoLock auto_lock(lock_); | 956 base::AutoLock auto_lock(lock_); |
950 waiting_for_clock_update_ = true; | 957 waiting_for_clock_update_ = true; |
951 audio_renderer_->StartRendering(); | 958 audio_renderer_->StartRendering(); |
952 } else { | 959 } else { |
953 base::AutoLock auto_lock(lock_); | 960 base::AutoLock auto_lock(lock_); |
954 DCHECK(!waiting_for_clock_update_); | 961 DCHECK(!waiting_for_clock_update_); |
955 clock_->SetMaxTime(clock_->Duration()); | 962 clock_->SetMaxTime(clock_->Duration()); |
956 clock_->Play(); | 963 clock_->Play(); |
957 } | 964 } |
958 | 965 |
959 preroll_completed_cb_.Run(); | 966 preroll_completed_cb_.Run(); |
960 if (!seek_cb_.is_null()) | 967 if (!seek_cb_.is_null()) |
961 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); | 968 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); |
962 } | 969 } |
963 | 970 |
964 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() { | 971 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() { |
965 lock_.AssertAcquired(); | 972 lock_.AssertAcquired(); |
966 if (!waiting_for_clock_update_) | 973 if (!waiting_for_clock_update_) |
967 return; | 974 return; |
968 | 975 |
969 waiting_for_clock_update_ = false; | 976 waiting_for_clock_update_ = false; |
970 clock_->Play(); | 977 clock_->Play(); |
971 } | 978 } |
972 | 979 |
973 } // namespace media | 980 } // namespace media |
OLD | NEW |