diff options
author | vrk@google.com <vrk@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-02 21:24:38 +0000 |
---|---|---|
committer | vrk@google.com <vrk@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-02 21:24:38 +0000 |
commit | 29dcc87a4d431025a5c8a6a4b5498172804cc9f9 (patch) | |
tree | 628c31d4be98e8a26e6530a74d0f6ccceeb45976 /media | |
parent | a9d31ef4508af244599ae096be8e33c6bfec029b (diff) | |
download | chromium_src-29dcc87a4d431025a5c8a6a4b5498172804cc9f9.zip chromium_src-29dcc87a4d431025a5c8a6a4b5498172804cc9f9.tar.gz chromium_src-29dcc87a4d431025a5c8a6a4b5498172804cc9f9.tar.bz2 |
Cap sourceBuffered() on duration and truncate duration on EoS
Adds logic + test to truncate the duration on Media Source streams when
EndOfStream is signalled. Also adds some logic to make sure buffered streams
never exceed the reported duration.
BUG=139899
Review URL: https://chromiumcodereview.appspot.com/10829108
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@149702 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/base/demuxer_stream.h | 3 | ||||
-rw-r--r-- | media/base/ranges.h | 4 | ||||
-rw-r--r-- | media/filters/chunk_demuxer.cc | 61 | ||||
-rw-r--r-- | media/filters/chunk_demuxer.h | 7 | ||||
-rw-r--r-- | media/filters/chunk_demuxer_unittest.cc | 51 | ||||
-rw-r--r-- | media/filters/dummy_demuxer.cc | 4 | ||||
-rw-r--r-- | media/filters/dummy_demuxer.h | 1 | ||||
-rw-r--r-- | media/filters/ffmpeg_demuxer.cc | 16 | ||||
-rw-r--r-- | media/filters/ffmpeg_demuxer.h | 9 |
9 files changed, 116 insertions, 40 deletions
diff --git a/media/base/demuxer_stream.h b/media/base/demuxer_stream.h index 65457c3..30ef1ec 100644 --- a/media/base/demuxer_stream.h +++ b/media/base/demuxer_stream.h @@ -64,9 +64,6 @@ class MEDIA_EXPORT DemuxerStream // if type() != VIDEO. virtual const VideoDecoderConfig& video_decoder_config() = 0; - // Returns time ranges known to have been seen by this stream. - virtual Ranges<base::TimeDelta> GetBufferedRanges() = 0; - // Returns the type of stream. virtual Type type() = 0; diff --git a/media/base/ranges.h b/media/base/ranges.h index 82bf7a0..f42b34a 100644 --- a/media/base/ranges.h +++ b/media/base/ranges.h @@ -39,7 +39,7 @@ class Ranges { void clear(); // Computes the intersection between this range and |other|. - Ranges<T> IntersectionWith(const Ranges<T>& other); + Ranges<T> IntersectionWith(const Ranges<T>& other) const; private: // Wrapper around DCHECK_LT allowing comparisons of operator<<'able T's. @@ -133,7 +133,7 @@ void Ranges<T>::clear() { } template<class T> -Ranges<T> Ranges<T>::IntersectionWith(const Ranges<T>& other) { +Ranges<T> Ranges<T>::IntersectionWith(const Ranges<T>& other) const { Ranges<T> result; size_t i = 0; diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc index b1c2389..e866da3d 100644 --- a/media/filters/chunk_demuxer.cc +++ b/media/filters/chunk_demuxer.cc @@ -176,6 +176,9 @@ class ChunkDemuxerStream : public DemuxerStream { // Returns true if buffers were successfully added. bool Append(const StreamParser::BufferQueue& buffers); + // Returns the range of buffered data in this stream, capped at |duration|. + Ranges<TimeDelta> GetBufferedRanges(base::TimeDelta duration) const; + // Signal to the stream that buffers handed in through subsequent calls to // Append() belong to a media segment that starts at |start_timestamp|. void OnNewMediaSegment(TimeDelta start_timestamp); @@ -199,7 +202,6 @@ class ChunkDemuxerStream : public DemuxerStream { virtual void EnableBitstreamConverter() OVERRIDE; virtual const AudioDecoderConfig& audio_decoder_config() OVERRIDE; virtual const VideoDecoderConfig& video_decoder_config() OVERRIDE; - virtual Ranges<TimeDelta> GetBufferedRanges() OVERRIDE; protected: virtual ~ChunkDemuxerStream(); @@ -318,9 +320,20 @@ bool ChunkDemuxerStream::Append(const StreamParser::BufferQueue& buffers) { return true; } -Ranges<TimeDelta> ChunkDemuxerStream::GetBufferedRanges() { +Ranges<TimeDelta> ChunkDemuxerStream::GetBufferedRanges( + base::TimeDelta duration) const { base::AutoLock auto_lock(lock_); - return stream_->GetBufferedTime(); + Ranges<TimeDelta> range = stream_->GetBufferedTime(); + + if (range.size() == 0u) + return range; + + // Clamp the end of the stream's buffered ranges to fit within the duration. + // This can be done by intersecting the stream's range with the valid time + // range. + Ranges<TimeDelta> valid_time_range; + valid_time_range.Add(range.start(0), duration); + return range.IntersectionWith(valid_time_range); } bool ChunkDemuxerStream::UpdateAudioConfig(const AudioDecoderConfig& config) { @@ -671,12 +684,12 @@ Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges(const std::string& id) const { if (id == source_id_audio_ && id != source_id_video_) { // Only include ranges that have been buffered in |audio_| - return audio_ ? audio_->GetBufferedRanges() : Ranges<TimeDelta>(); + return audio_ ? audio_->GetBufferedRanges(duration_) : Ranges<TimeDelta>(); } if (id != source_id_audio_ && id == source_id_video_) { // Only include ranges that have been buffered in |video_| - return video_ ? video_->GetBufferedRanges() : Ranges<TimeDelta>(); + return video_ ? video_->GetBufferedRanges(duration_) : Ranges<TimeDelta>(); } return ComputeIntersection(); @@ -689,8 +702,8 @@ Ranges<TimeDelta> ChunkDemuxer::ComputeIntersection() const { return Ranges<TimeDelta>(); // Include ranges that have been buffered in both |audio_| and |video_|. - Ranges<TimeDelta> audio_ranges = audio_->GetBufferedRanges(); - Ranges<TimeDelta> video_ranges = video_->GetBufferedRanges(); + Ranges<TimeDelta> audio_ranges = audio_->GetBufferedRanges(duration_); + Ranges<TimeDelta> video_ranges = video_->GetBufferedRanges(duration_); Ranges<TimeDelta> result = audio_ranges.IntersectionWith(video_ranges); if (state_ == ENDED && result.size() > 0) { @@ -763,13 +776,7 @@ bool ChunkDemuxer::AppendData(const std::string& id, std::swap(cb, seek_cb_); } - if (audio_ && !video_) { - ranges = audio_->GetBufferedRanges(); - } else if (!audio_ && video_) { - ranges = video_->GetBufferedRanges(); - } else { - ranges = ComputeIntersection(); - } + ranges = GetBufferedRanges(); } for (size_t i = 0; i < ranges.size(); ++i) @@ -825,10 +832,12 @@ bool ChunkDemuxer::EndOfStream(PipelineStatus status) { if (video_) video_->EndOfStream(); - if (status != PIPELINE_OK) + if (status != PIPELINE_OK) { ReportError_Locked(status); - else + } else { ChangeState_Locked(ENDED); + DecreaseDurationIfNecessary(); + } return true; } @@ -1121,7 +1130,7 @@ void ChunkDemuxer::IncreaseDurationIfNecessary( if (buffers.back()->GetTimestamp() <= duration_) return; - Ranges<TimeDelta> ranges = stream->GetBufferedRanges(); + Ranges<TimeDelta> ranges = stream->GetBufferedRanges(kInfiniteDuration()); DCHECK_GT(ranges.size(), 0u); base::TimeDelta last_timestamp_buffered = ranges.end(ranges.size() - 1); @@ -1129,4 +1138,22 @@ void ChunkDemuxer::IncreaseDurationIfNecessary( UpdateDuration(last_timestamp_buffered); } +void ChunkDemuxer::DecreaseDurationIfNecessary() { + Ranges<TimeDelta> ranges = GetBufferedRanges(); + if (ranges.size() == 0u) + return; + + base::TimeDelta last_timestamp_buffered = ranges.end(ranges.size() - 1); + if (last_timestamp_buffered < duration_) + UpdateDuration(last_timestamp_buffered); +} + +Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges() const { + if (audio_ && !video_) + return audio_->GetBufferedRanges(duration_); + else if (!audio_ && video_) + return video_->GetBufferedRanges(duration_); + return ComputeIntersection(); +} + } // namespace media diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h index fb53bca..fe8e671 100644 --- a/media/filters/chunk_demuxer.h +++ b/media/filters/chunk_demuxer.h @@ -143,9 +143,16 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { const StreamParser::BufferQueue& buffers, const scoped_refptr<ChunkDemuxerStream>& stream); + // Decreases |duration_| if the buffered region is less than |duration_| when + // EndOfStream() is called. + void DecreaseDurationIfNecessary(); + // Sets |duration_| to |new_duration| and notifies |host_|. void UpdateDuration(base::TimeDelta new_duration); + // Returns the ranges representing the buffered data in the demuxer. + Ranges<base::TimeDelta> GetBufferedRanges() const; + mutable base::Lock lock_; State state_; diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc index 1570bea..71ebe9a 100644 --- a/media/filters/chunk_demuxer_unittest.cc +++ b/media/filters/chunk_demuxer_unittest.cc @@ -47,6 +47,8 @@ static const int kVideoBlockDuration = 33; static const char* kSourceId = "SourceId"; static const char* kDefaultFirstClusterRange = "{ [0,46) }"; +static const int kDefaultFirstClusterEndTimestamp = 66; +static const int kDefaultSecondClusterEndTimestamp = 132; base::TimeDelta kDefaultDuration() { return base::TimeDelta::FromMilliseconds(201224); @@ -1100,6 +1102,8 @@ TEST_F(ChunkDemuxerTest, TestEndOfStreamWithPendingReads) { end_of_stream_helper_1.CheckIfReadDonesWereCalled(false); end_of_stream_helper_2.CheckIfReadDonesWereCalled(false); + EXPECT_CALL(host_, SetDuration( + base::TimeDelta::FromMilliseconds(kVideoBlockDuration))); demuxer_->EndOfStream(PIPELINE_OK); end_of_stream_helper_1.CheckIfReadDonesWereCalled(true); @@ -1139,6 +1143,8 @@ TEST_F(ChunkDemuxerTest, TestReadsAfterEndOfStream) { EXPECT_TRUE(video_read_done_1); end_of_stream_helper_1.CheckIfReadDonesWereCalled(false); + EXPECT_CALL(host_, SetDuration( + base::TimeDelta::FromMilliseconds(kVideoBlockDuration))); EXPECT_TRUE(demuxer_->EndOfStream(PIPELINE_OK)); end_of_stream_helper_1.CheckIfReadDonesWereCalled(true); @@ -1697,6 +1703,7 @@ TEST_F(ChunkDemuxerTest, GetBufferedRanges_EndOfStream) { CheckExpectedRanges("{ [0,90) }"); + EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(100))); demuxer_->EndOfStream(PIPELINE_OK); CheckExpectedRanges("{ [0,100) }"); @@ -1813,6 +1820,7 @@ TEST_F(ChunkDemuxerTest, TestEndOfStreamFailures) { // Make sure that end of stream fails because there is a gap between // the current position(0) and the end of the appended data. + EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(50))); ASSERT_FALSE(demuxer_->EndOfStream(PIPELINE_OK)); // Seek to an time that is inside the last ranges for both streams @@ -1863,6 +1871,8 @@ TEST_F(ChunkDemuxerTest, TestEndOfStreamDuringSeek) { demuxer_->StartWaitingForSeek(); ASSERT_TRUE(AppendData(cluster_b->data(), cluster_b->size())); + EXPECT_CALL(host_, SetDuration( + base::TimeDelta::FromMilliseconds(kDefaultSecondClusterEndTimestamp))); demuxer_->EndOfStream(PIPELINE_OK); demuxer_->Seek(base::TimeDelta::FromSeconds(0), @@ -2133,20 +2143,36 @@ TEST_F(ChunkDemuxerTest, TestTimestampOffsetMidParse) { TEST_F(ChunkDemuxerTest, TestDurationChange) { ASSERT_TRUE(InitDemuxer(true, true, false)); + static const int kStreamDuration = kDefaultDuration().InMilliseconds(); // Add data leading up to the currently set duration. scoped_ptr<Cluster> first_cluster = GenerateCluster( - kDefaultDuration().InMilliseconds() - kAudioBlockDuration, - kDefaultDuration().InMilliseconds() - kVideoBlockDuration, 2); + kStreamDuration - kAudioBlockDuration, + kStreamDuration - kVideoBlockDuration, 2); ASSERT_TRUE(AppendData(first_cluster->data(), first_cluster->size())); - // Now add data past the duration and expect a new duration to be signalled. + CheckExpectedRanges(kSourceId, "{ [201191,201224) }"); + + // Add data at the currently set duration. The duration should not increase. scoped_ptr<Cluster> second_cluster = GenerateCluster( - kDefaultDuration().InMilliseconds(), 4); - EXPECT_CALL(host_, SetDuration( - kDefaultDuration() + base::TimeDelta::FromMilliseconds( - kAudioBlockDuration * 2))); + kDefaultDuration().InMilliseconds(), 2); ASSERT_TRUE(AppendData(second_cluster->data(), second_cluster->size())); + + // Range should not be affected. + CheckExpectedRanges(kSourceId, "{ [201191,201224) }"); + + // Now add data past the duration and expect a new duration to be signalled. + static const int kNewStreamDuration = + kStreamDuration + kAudioBlockDuration * 2; + scoped_ptr<Cluster> third_cluster = GenerateCluster( + kStreamDuration + kAudioBlockDuration, + kStreamDuration + kVideoBlockDuration, 2); + EXPECT_CALL(host_, SetDuration( + base::TimeDelta::FromMilliseconds(kNewStreamDuration))); + ASSERT_TRUE(AppendData(third_cluster->data(), third_cluster->size())); + + // See that the range has increased appropriately. + CheckExpectedRanges(kSourceId, "{ [201191,201270) }"); } TEST_F(ChunkDemuxerTest, TestDurationChangeTimestampOffset) { @@ -2162,4 +2188,15 @@ TEST_F(ChunkDemuxerTest, TestDurationChangeTimestampOffset) { ASSERT_TRUE(AppendData(cluster->data(), cluster->size())); } +TEST_F(ChunkDemuxerTest, TestEndOfStreamTruncateDuration) { + ASSERT_TRUE(InitDemuxer(true, true, false)); + + scoped_ptr<Cluster> cluster_a(kDefaultFirstCluster()); + ASSERT_TRUE(AppendData(cluster_a->data(), cluster_a->size())); + + EXPECT_CALL(host_, SetDuration( + base::TimeDelta::FromMilliseconds(kDefaultFirstClusterEndTimestamp))); + demuxer_->EndOfStream(PIPELINE_OK); +} + } // namespace media diff --git a/media/filters/dummy_demuxer.cc b/media/filters/dummy_demuxer.cc index 317b543..bbfcff2 100644 --- a/media/filters/dummy_demuxer.cc +++ b/media/filters/dummy_demuxer.cc @@ -32,10 +32,6 @@ void DummyDemuxerStream::Read(const ReadCB& read_cb) {} void DummyDemuxerStream::EnableBitstreamConverter() {} -Ranges<base::TimeDelta> DummyDemuxerStream::GetBufferedRanges() { - return Ranges<base::TimeDelta>(); -} - DummyDemuxer::DummyDemuxer(bool has_video, bool has_audio) { streams_.resize(DemuxerStream::NUM_TYPES); if (has_audio) diff --git a/media/filters/dummy_demuxer.h b/media/filters/dummy_demuxer.h index c8c1b5e..700cd84 100644 --- a/media/filters/dummy_demuxer.h +++ b/media/filters/dummy_demuxer.h @@ -28,7 +28,6 @@ class DummyDemuxerStream : public DemuxerStream { virtual const AudioDecoderConfig& audio_decoder_config() OVERRIDE; virtual const VideoDecoderConfig& video_decoder_config() OVERRIDE; virtual void EnableBitstreamConverter() OVERRIDE; - virtual Ranges<base::TimeDelta> GetBufferedRanges() OVERRIDE; protected: virtual ~DummyDemuxerStream(); diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc index cb6494a..35c2d38 100644 --- a/media/filters/ffmpeg_demuxer.cc +++ b/media/filters/ffmpeg_demuxer.cc @@ -237,7 +237,7 @@ base::TimeDelta FFmpegDemuxerStream::GetElapsedTime() const { return ConvertStreamTimestamp(stream_->time_base, stream_->cur_dts); } -Ranges<base::TimeDelta> FFmpegDemuxerStream::GetBufferedRanges() { +Ranges<base::TimeDelta> FFmpegDemuxerStream::GetBufferedRanges() const { base::AutoLock auto_lock(lock_); return buffered_ranges_; } @@ -323,7 +323,12 @@ void FFmpegDemuxer::Initialize(DemuxerHost* host, scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream( DemuxerStream::Type type) { - StreamVector::iterator iter; + return GetFFmpegStream(type); +} + +scoped_refptr<FFmpegDemuxerStream> FFmpegDemuxer::GetFFmpegStream( + DemuxerStream::Type type) const { + StreamVector::const_iterator iter; for (iter = streams_.begin(); iter != streams_.end(); ++iter) { if (*iter && (*iter)->type() == type) { return *iter; @@ -717,9 +722,10 @@ void FFmpegDemuxer::SignalReadCompleted(int size) { void FFmpegDemuxer::NotifyBufferingChanged() { DCHECK_EQ(MessageLoop::current(), message_loop_); Ranges<base::TimeDelta> buffered; - scoped_refptr<DemuxerStream> audio = - audio_disabled_ ? NULL : GetStream(DemuxerStream::AUDIO); - scoped_refptr<DemuxerStream> video = GetStream(DemuxerStream::VIDEO); + scoped_refptr<FFmpegDemuxerStream> audio = + audio_disabled_ ? NULL : GetFFmpegStream(DemuxerStream::AUDIO); + scoped_refptr<FFmpegDemuxerStream> video = + GetFFmpegStream(DemuxerStream::VIDEO); if (audio && video) { buffered = audio->GetBufferedRanges().IntersectionWith( video->GetBufferedRanges()); diff --git a/media/filters/ffmpeg_demuxer.h b/media/filters/ffmpeg_demuxer.h index b90cbbb..f395a84 100644 --- a/media/filters/ffmpeg_demuxer.h +++ b/media/filters/ffmpeg_demuxer.h @@ -84,7 +84,9 @@ class FFmpegDemuxerStream : public DemuxerStream { virtual void EnableBitstreamConverter() OVERRIDE; virtual const AudioDecoderConfig& audio_decoder_config() OVERRIDE; virtual const VideoDecoderConfig& video_decoder_config() OVERRIDE; - virtual Ranges<base::TimeDelta> GetBufferedRanges() OVERRIDE; + + // Returns the range of buffered data in this stream. + Ranges<base::TimeDelta> GetBufferedRanges() const; // Returns elapsed time based on the already queued packets. // Used to determine stream duration when it's not known ahead of time. @@ -210,6 +212,11 @@ class MEDIA_EXPORT FFmpegDemuxer : public Demuxer, public FFmpegURLProtocol { // read or kReadError in case of error. virtual void SignalReadCompleted(int size); + // Returns the stream from |streams_| that matches |type| as an + // FFmpegDemuxerStream. + scoped_refptr<FFmpegDemuxerStream> GetFFmpegStream( + DemuxerStream::Type type) const; + DemuxerHost* host_; MessageLoop* message_loop_; |