diff options
author | acolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-15 20:24:58 +0000 |
---|---|---|
committer | acolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-15 20:24:58 +0000 |
commit | c2a45e243be60e579477651cf8f69649b9bd9827 (patch) | |
tree | 09044ef8952fa55d771227e45050e6681011a3ef | |
parent | 1afaecc5ac52da8d37a7d3cf18159b0b7bbdbba1 (diff) | |
download | chromium_src-c2a45e243be60e579477651cf8f69649b9bd9827.zip chromium_src-c2a45e243be60e579477651cf8f69649b9bd9827.tar.gz chromium_src-c2a45e243be60e579477651cf8f69649b9bd9827.tar.bz2 |
Move duration change logic from WebMediaPlayerImpl to ChunkDemuxer.
BUG=176085
TEST=All existing tests still pass.
Review URL: https://chromiumcodereview.appspot.com/12252017
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@182810 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | media/base/pipeline.cc | 11 | ||||
-rw-r--r-- | media/base/pipeline.h | 9 | ||||
-rw-r--r-- | media/base/pipeline_unittest.cc | 8 | ||||
-rw-r--r-- | media/filters/chunk_demuxer.cc | 68 | ||||
-rw-r--r-- | media/filters/chunk_demuxer.h | 16 | ||||
-rw-r--r-- | media/filters/pipeline_integration_test.cc | 6 | ||||
-rw-r--r-- | media/filters/pipeline_integration_test_base.cc | 6 | ||||
-rw-r--r-- | media/tools/player_x11/player_x11.cc | 2 | ||||
-rw-r--r-- | webkit/media/webmediaplayer_impl.cc | 79 | ||||
-rw-r--r-- | webkit/media/webmediaplayer_impl.h | 7 |
10 files changed, 126 insertions, 86 deletions
diff --git a/media/base/pipeline.cc b/media/base/pipeline.cc index 8cda84b..6af4935 100644 --- a/media/base/pipeline.cc +++ b/media/base/pipeline.cc @@ -106,7 +106,8 @@ void Pipeline::Start(scoped_ptr<FilterCollection> collection, const base::Closure& ended_cb, const PipelineStatusCB& error_cb, const PipelineStatusCB& seek_cb, - const BufferingStateCB& buffering_state_cb) { + const BufferingStateCB& buffering_state_cb, + const base::Closure& duration_change_cb) { base::AutoLock auto_lock(lock_); CHECK(!running_) << "Media pipeline is already running"; DCHECK(!buffering_state_cb.is_null()); @@ -114,7 +115,7 @@ void Pipeline::Start(scoped_ptr<FilterCollection> collection, running_ = true; message_loop_->PostTask(FROM_HERE, base::Bind( &Pipeline::StartTask, this, base::Passed(&collection), - ended_cb, error_cb, seek_cb, buffering_state_cb)); + ended_cb, error_cb, seek_cb, buffering_state_cb, duration_change_cb)); } void Pipeline::Stop(const base::Closure& stop_cb) { @@ -390,6 +391,8 @@ void Pipeline::SetDuration(TimeDelta duration) { base::AutoLock auto_lock(lock_); clock_->SetDuration(duration); + if (!duration_change_cb_.is_null()) + duration_change_cb_.Run(); } void Pipeline::SetTotalBytes(int64 total_bytes) { @@ -723,7 +726,8 @@ void Pipeline::StartTask(scoped_ptr<FilterCollection> filter_collection, const base::Closure& ended_cb, const PipelineStatusCB& error_cb, const PipelineStatusCB& seek_cb, - const BufferingStateCB& buffering_state_cb) { + const BufferingStateCB& buffering_state_cb, + const base::Closure& duration_change_cb) { DCHECK(message_loop_->BelongsToCurrentThread()); CHECK_EQ(kCreated, state_) << "Media pipeline cannot be started more than once"; @@ -733,6 +737,7 @@ void Pipeline::StartTask(scoped_ptr<FilterCollection> filter_collection, error_cb_ = error_cb; seek_cb_ = seek_cb; buffering_state_cb_ = buffering_state_cb; + duration_change_cb_ = duration_change_cb; StateTransitionTask(PIPELINE_OK); } diff --git a/media/base/pipeline.h b/media/base/pipeline.h index bb72e36..8f976c9 100644 --- a/media/base/pipeline.h +++ b/media/base/pipeline.h @@ -128,12 +128,15 @@ class MEDIA_EXPORT Pipeline // been reported already through another callback. // |buffering_state_cb| Optional callback that will be executed whenever the // pipeline's buffering state changes. + // |duration_change_cb| Optional callback that will be executed whenever the + // presentation duration changes. // It is an error to call this method after the pipeline has already started. void Start(scoped_ptr<FilterCollection> filter_collection, const base::Closure& ended_cb, const PipelineStatusCB& error_cb, const PipelineStatusCB& seek_cb, - const BufferingStateCB& buffering_state_cb); + const BufferingStateCB& buffering_state_cb, + const base::Closure& duration_change_cb); // Asynchronously stops the pipeline, executing |stop_cb| when the pipeline // teardown has completed. @@ -292,7 +295,8 @@ class MEDIA_EXPORT Pipeline const base::Closure& ended_cb, const PipelineStatusCB& error_cb, const PipelineStatusCB& seek_cb, - const BufferingStateCB& buffering_state_cb); + const BufferingStateCB& buffering_state_cb, + const base::Closure& duration_change_cb); // Stops and destroys all filters, placing the pipeline in the kStopped state. void StopTask(const base::Closure& stop_cb); @@ -448,6 +452,7 @@ class MEDIA_EXPORT Pipeline base::Closure ended_cb_; PipelineStatusCB error_cb_; BufferingStateCB buffering_state_cb_; + base::Closure duration_change_cb_; // Renderer references used for setting the volume, playback rate, and // determining when playback has finished. diff --git a/media/base/pipeline_unittest.cc b/media/base/pipeline_unittest.cc index fe3219d..320c83a 100644 --- a/media/base/pipeline_unittest.cc +++ b/media/base/pipeline_unittest.cc @@ -64,6 +64,7 @@ class CallbackHelper { MOCK_METHOD0(OnEnded, void()); MOCK_METHOD1(OnError, void(PipelineStatus)); MOCK_METHOD1(OnBufferingState, void(Pipeline::BufferingState)); + MOCK_METHOD0(OnDurationChange, void()); private: DISALLOW_COPY_AND_ASSIGN(CallbackHelper); @@ -138,6 +139,7 @@ class PipelineTest : public ::testing::Test { typedef std::vector<MockDemuxerStream*> MockDemuxerStreamVector; void InitializeDemuxer(MockDemuxerStreamVector* streams, const base::TimeDelta& duration) { + EXPECT_CALL(callbacks_, OnDurationChange()); EXPECT_CALL(*demuxer_, Initialize(_, _)) .WillOnce(DoAll(SetDemuxerProperties(duration), RunCallback<1>(PIPELINE_OK))); @@ -222,6 +224,8 @@ class PipelineTest : public ::testing::Test { base::Bind(&CallbackHelper::OnError, base::Unretained(&callbacks_)), base::Bind(&CallbackHelper::OnStart, base::Unretained(&callbacks_)), base::Bind(&CallbackHelper::OnBufferingState, + base::Unretained(&callbacks_)), + base::Bind(&CallbackHelper::OnDurationChange, base::Unretained(&callbacks_))); message_loop_.RunUntilIdle(); } @@ -361,6 +365,8 @@ TEST_F(PipelineTest, NeverInitializes) { base::Bind(&CallbackHelper::OnError, base::Unretained(&callbacks_)), base::Bind(&CallbackHelper::OnStart, base::Unretained(&callbacks_)), base::Bind(&CallbackHelper::OnBufferingState, + base::Unretained(&callbacks_)), + base::Bind(&CallbackHelper::OnDurationChange, base::Unretained(&callbacks_))); message_loop_.RunUntilIdle(); @@ -964,6 +970,8 @@ class PipelineTeardownTest : public PipelineTest { base::Bind(&CallbackHelper::OnError, base::Unretained(&callbacks_)), base::Bind(&CallbackHelper::OnStart, base::Unretained(&callbacks_)), base::Bind(&CallbackHelper::OnBufferingState, + base::Unretained(&callbacks_)), + base::Bind(&CallbackHelper::OnDurationChange, base::Unretained(&callbacks_))); message_loop_.RunUntilIdle(); } diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc index 8162bbf..ff58e7d 100644 --- a/media/filters/chunk_demuxer.cc +++ b/media/filters/chunk_demuxer.cc @@ -575,7 +575,9 @@ ChunkDemuxer::ChunkDemuxer(const base::Closure& open_cb, host_(NULL), open_cb_(open_cb), need_key_cb_(need_key_cb), - log_cb_(log_cb) { + log_cb_(log_cb), + duration_(kNoTimestamp()), + user_specified_duration_(-1) { DCHECK(!open_cb_.is_null()); DCHECK(!need_key_cb_.is_null()); } @@ -903,20 +905,67 @@ void ChunkDemuxer::Abort(const std::string& id) { source_info_map_[id].can_update_offset = true; } -void ChunkDemuxer::SetDuration(base::TimeDelta duration) { +double ChunkDemuxer::GetDuration() { base::AutoLock auto_lock(lock_); - DVLOG(1) << "SetDuration(" << duration.InSecondsF() << ")"; + return GetDuration_Locked(); +} + +double ChunkDemuxer::GetDuration_Locked() { + lock_.AssertAcquired(); + if (duration_ == kNoTimestamp()) + return std::numeric_limits<double>::quiet_NaN(); + + // Return positive infinity if the resource is unbounded. + // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#dom-media-duration + if (duration_ == kInfiniteDuration()) + return std::numeric_limits<double>::infinity(); + + if (user_specified_duration_ >= 0) + return user_specified_duration_; + + return duration_.InSecondsF(); +} - if (duration == duration_) +void ChunkDemuxer::SetDuration(double duration) { + base::AutoLock auto_lock(lock_); + DVLOG(1) << "SetDuration(" << duration << ")"; + DCHECK_GE(duration, 0); + + if (duration == GetDuration_Locked()) return; - UpdateDuration(duration); + // Compute & bounds check the TimeDelta representation of duration. + // This can be different if the value of |duration| doesn't fit the range or + // precision of base::TimeDelta. + base::TimeDelta min_duration = base::TimeDelta::FromInternalValue(1); + base::TimeDelta max_duration = + base::TimeDelta::FromInternalValue(kint64max - 1); + double min_duration_in_seconds = min_duration.InSecondsF(); + double max_duration_in_seconds = max_duration.InSecondsF(); + + base::TimeDelta duration_td; + if (duration == std::numeric_limits<double>::infinity()) { + duration_td = media::kInfiniteDuration(); + } else if (duration < min_duration_in_seconds) { + duration_td = min_duration; + } else if (duration > max_duration_in_seconds) { + duration_td = max_duration; + } else { + duration_td = base::TimeDelta::FromMicroseconds( + duration * base::Time::kMicrosecondsPerSecond); + } + + DCHECK(duration_td > base::TimeDelta()); + + user_specified_duration_ = duration; + duration_ = duration_td; + host_->SetDuration(duration_); if (audio_) - audio_->OnSetDuration(duration); + audio_->OnSetDuration(duration_); if (video_) - video_->OnSetDuration(duration); + video_->OnSetDuration(duration_); } bool ChunkDemuxer::SetTimestampOffset(const std::string& id, TimeDelta offset) { @@ -1065,7 +1114,7 @@ void ChunkDemuxer::OnStreamParserInitDone(bool success, TimeDelta duration) { return; } - if (duration != base::TimeDelta() && duration_ == base::TimeDelta()) + if (duration != base::TimeDelta() && duration_ == kNoTimestamp()) UpdateDuration(duration); // Wait until all streams have initialized. @@ -1079,7 +1128,7 @@ void ChunkDemuxer::OnStreamParserInitDone(bool success, TimeDelta duration) { if (video_) video_->Seek(TimeDelta()); - if (duration_ == TimeDelta()) + if (duration_ == kNoTimestamp()) duration_ = kInfiniteDuration(); // The demuxer is now initialized after the |start_timestamp_| was set. @@ -1238,6 +1287,7 @@ bool ChunkDemuxer::IsValidId(const std::string& source_id) const { void ChunkDemuxer::UpdateDuration(base::TimeDelta new_duration) { DCHECK(duration_ != new_duration); + user_specified_duration_ = -1; duration_ = new_duration; host_->SetDuration(new_duration); } diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h index 08517c1..2159148 100644 --- a/media/filters/chunk_demuxer.h +++ b/media/filters/chunk_demuxer.h @@ -84,9 +84,13 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { // it can accept a new segment. void Abort(const std::string& id); + // Returns the current presentation duration. + double GetDuration(); + double GetDuration_Locked(); + // Notifies the demuxer that the duration of the media has changed to // |duration|. - void SetDuration(base::TimeDelta duration); + void SetDuration(double duration); // Sets a time |offset| to be applied to subsequent buffers appended to the // source buffer assicated with |id|. Returns true if the offset is set @@ -162,7 +166,8 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { // EndOfStream() is called. void DecreaseDurationIfNecessary(); - // Sets |duration_| to |new_duration| and notifies |host_|. + // Sets |duration_| to |new_duration|, sets |user_specified_duration_| to -1 + // and notifies |host_|. void UpdateDuration(base::TimeDelta new_duration); // Returns the ranges representing the buffered data in the demuxer. @@ -186,6 +191,13 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { base::TimeDelta duration_; + // The duration passed to the last SetDuration(). If + // SetDuration() is never called or an AppendData() call or + // a EndOfStream() call changes |duration_|, then this + // variable is set to < 0 to indicate that the |duration_| represents + // the actual duration instead of a user specified value. + double user_specified_duration_; + typedef std::map<std::string, StreamParser*> StreamParserMap; StreamParserMap stream_parser_map_; diff --git a/media/filters/pipeline_integration_test.cc b/media/filters/pipeline_integration_test.cc index e96da57..da8b26f 100644 --- a/media/filters/pipeline_integration_test.cc +++ b/media/filters/pipeline_integration_test.cc @@ -341,7 +341,8 @@ class PipelineIntegrationTest base::Bind(&PipelineIntegrationTest::OnError, base::Unretained(this)), QuitOnStatusCB(PIPELINE_OK), base::Bind(&PipelineIntegrationTest::OnBufferingState, - base::Unretained(this))); + base::Unretained(this)), + base::Closure()); message_loop_.Run(); } @@ -359,7 +360,8 @@ class PipelineIntegrationTest base::Bind(&PipelineIntegrationTest::OnError, base::Unretained(this)), QuitOnStatusCB(PIPELINE_OK), base::Bind(&PipelineIntegrationTest::OnBufferingState, - base::Unretained(this))); + base::Unretained(this)), + base::Closure()); source->set_need_key_cb(base::Bind(&FakeEncryptedMedia::NeedKey, base::Unretained(encrypted_media))); diff --git a/media/filters/pipeline_integration_test_base.cc b/media/filters/pipeline_integration_test_base.cc index 630e297..c43cc92 100644 --- a/media/filters/pipeline_integration_test_base.cc +++ b/media/filters/pipeline_integration_test_base.cc @@ -99,7 +99,8 @@ bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path, base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)), QuitOnStatusCB(expected_status), base::Bind(&PipelineIntegrationTestBase::OnBufferingState, - base::Unretained(this))); + base::Unretained(this)), + base::Closure()); message_loop_.Run(); return (pipeline_status_ == PIPELINE_OK); } @@ -123,7 +124,8 @@ bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path) { base::Bind(&PipelineIntegrationTestBase::OnStatusCallback, base::Unretained(this)), base::Bind(&PipelineIntegrationTestBase::OnBufferingState, - base::Unretained(this))); + base::Unretained(this)), + base::Closure()); message_loop_.Run(); return (pipeline_status_ == PIPELINE_OK); } diff --git a/media/tools/player_x11/player_x11.cc b/media/tools/player_x11/player_x11.cc index 5b112cf..f7aa03a 100644 --- a/media/tools/player_x11/player_x11.cc +++ b/media/tools/player_x11/player_x11.cc @@ -130,7 +130,7 @@ bool InitPipeline(const scoped_refptr<base::MessageLoopProxy>& message_loop, media::PipelineStatusNotification note; (*pipeline)->Start( collection.Pass(), base::Closure(), media::PipelineStatusCB(), - note.Callback(), base::Bind(&OnBufferingState)); + note.Callback(), base::Bind(&OnBufferingState), base::Closure()); // Wait until the pipeline is fully initialized. note.Wait(); diff --git a/webkit/media/webmediaplayer_impl.cc b/webkit/media/webmediaplayer_impl.cc index 5948c1d..f904e25 100644 --- a/webkit/media/webmediaplayer_impl.cc +++ b/webkit/media/webmediaplayer_impl.cc @@ -4,6 +4,7 @@ #include "webkit/media/webmediaplayer_impl.h" +#include <algorithm> #include <limits> #include <string> #include <vector> @@ -131,7 +132,6 @@ WebMediaPlayerImpl::WebMediaPlayerImpl( paused_(true), seeking_(false), playback_rate_(0.0f), - user_specified_duration_(-1), pending_seek_(false), pending_seek_seconds_(0.0f), client_(client), @@ -491,14 +491,25 @@ float WebMediaPlayerImpl::duration() const { if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing) return std::numeric_limits<float>::quiet_NaN(); - double duration = user_specified_duration_; - if (duration < 0) + double duration; + if (chunk_demuxer_) { + duration = chunk_demuxer_->GetDuration(); + } else { duration = GetPipelineDuration(); + } - // Make sure super small durations don't get truncated to 0 by - // the double -> float conversion. - if (duration > 0.0 && duration < std::numeric_limits<float>::min()) - return std::numeric_limits<float>::min(); + // Make sure super small durations don't get truncated to 0 and + // large durations don't get converted to infinity by the double -> float + // conversion. + // + // TODO(acolwell): Remove when WebKit is changed to report duration as a + // double. + if (duration > 0.0 && duration < std::numeric_limits<double>::infinity()) { + duration = std::max(duration, + static_cast<double>(std::numeric_limits<float>::min())); + duration = std::min(duration, + static_cast<double>(std::numeric_limits<float>::max())); + } return static_cast<float>(duration); } @@ -693,17 +704,9 @@ bool WebMediaPlayerImpl::sourceAppend(const WebKit::WebString& id, unsigned length) { DCHECK_EQ(main_loop_, MessageLoop::current()); - double old_duration = GetPipelineDuration(); if (!chunk_demuxer_->AppendData(id.utf8().data(), data, length)) return false; - double new_duration = GetPipelineDuration(); - if (old_duration != new_duration) { - // Clear user specified duration since the AppendData() call caused - // the presentation duration to change. - user_specified_duration_ = -1; - OnDurationChange(); - } return true; } @@ -714,39 +717,7 @@ bool WebMediaPlayerImpl::sourceAbort(const WebKit::WebString& id) { void WebMediaPlayerImpl::sourceSetDuration(double new_duration) { DCHECK_GE(new_duration, 0); - - if (user_specified_duration_ == new_duration) - return; - - // Update |user_specified_duration_| so it reports exactly what the - // application specified. - user_specified_duration_ = new_duration; - - // Compute & bounds check the duration actually sent to the ChunkDemuxer. - // This can be different than the value of |user_specified_duration_| if the - // value of |new_duration| doesn't fit the range or precision of - // base::TimeDelta. - base::TimeDelta min_duration = base::TimeDelta::FromInternalValue(1); - base::TimeDelta max_duration = - base::TimeDelta::FromInternalValue(kint64max - 1); - double min_duration_in_seconds = min_duration.InSecondsF(); - double max_duration_in_seconds = max_duration.InSecondsF(); - - base::TimeDelta duration; - if (new_duration == std::numeric_limits<double>::infinity()) { - duration = media::kInfiniteDuration(); - } else if (new_duration < min_duration_in_seconds) { - duration = min_duration; - } else if (new_duration > max_duration_in_seconds) { - duration = max_duration; - } else { - duration = base::TimeDelta::FromMicroseconds( - new_duration * base::Time::kMicrosecondsPerSecond); - } - - DCHECK(duration > base::TimeDelta()); - chunk_demuxer_->SetDuration(duration); - OnDurationChange(); + chunk_demuxer_->SetDuration(new_duration); } void WebMediaPlayerImpl::sourceEndOfStream( @@ -767,17 +738,8 @@ void WebMediaPlayerImpl::sourceEndOfStream( NOTIMPLEMENTED(); } - double old_duration = GetPipelineDuration(); if (!chunk_demuxer_->EndOfStream(pipeline_status)) DVLOG(1) << "EndOfStream call failed."; - - double new_duration = GetPipelineDuration(); - if (old_duration != new_duration) { - // Clear user specified duration since the EndOfStream() call caused - // the presentation duration to change. - user_specified_duration_ = -1; - OnDurationChange(); - } } bool WebMediaPlayerImpl::sourceSetTimestampOffset(const WebKit::WebString& id, @@ -1170,7 +1132,8 @@ void WebMediaPlayerImpl::StartPipeline() { BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineEnded), BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineError), BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineSeek), - BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineBufferingState)); + BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineBufferingState), + BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDurationChange)); } void WebMediaPlayerImpl::SetNetworkState(WebMediaPlayer::NetworkState state) { diff --git a/webkit/media/webmediaplayer_impl.h b/webkit/media/webmediaplayer_impl.h index 81566fd..9518f24 100644 --- a/webkit/media/webmediaplayer_impl.h +++ b/webkit/media/webmediaplayer_impl.h @@ -311,13 +311,6 @@ class WebMediaPlayerImpl float playback_rate_; base::TimeDelta paused_time_; - // The duration passed to the last sourceSetDuration(). If - // sourceSetDuration() is never called or a sourceAppend() call or - // a sourceEndOfStream() call changes the pipeline duration, then this - // variable is set to < 0 to indicate that the pipeline duration represents - // the actual duration instead of a user specified value. - double user_specified_duration_; - // Seek gets pending if another seek is in progress. Only last pending seek // will have effect. bool pending_seek_; |