summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-15 20:24:58 +0000
committeracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-15 20:24:58 +0000
commitc2a45e243be60e579477651cf8f69649b9bd9827 (patch)
tree09044ef8952fa55d771227e45050e6681011a3ef
parent1afaecc5ac52da8d37a7d3cf18159b0b7bbdbba1 (diff)
downloadchromium_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.cc11
-rw-r--r--media/base/pipeline.h9
-rw-r--r--media/base/pipeline_unittest.cc8
-rw-r--r--media/filters/chunk_demuxer.cc68
-rw-r--r--media/filters/chunk_demuxer.h16
-rw-r--r--media/filters/pipeline_integration_test.cc6
-rw-r--r--media/filters/pipeline_integration_test_base.cc6
-rw-r--r--media/tools/player_x11/player_x11.cc2
-rw-r--r--webkit/media/webmediaplayer_impl.cc79
-rw-r--r--webkit/media/webmediaplayer_impl.h7
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_;