Chromium Code Reviews| 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 |