Fix buffered range updates in media::Pipeline
Currently the buffered ranges reported by
media::Pipeline::GetBufferedTimeRanges may be out of sync with the
actually buffered ranges in the demuxer, since there's no way for a
demuxer to indicate that some time range got evicted (e.g. in MSE case).
This CL replaces media::Pipeline::AddBufferedTimeRange with
OnBufferedTimeRangesChanged method that allows demuxer to report
arbitrary changes in buffered ranges.
BUG=570514
Review URL: https://codereview.chromium.org/1526303004
Cr-Commit-Position: refs/heads/master@{#369585}
diff --git a/chromecast/media/cma/test/frame_segmenter_for_test.cc b/chromecast/media/cma/test/frame_segmenter_for_test.cc
index e3c72d6d..7d4d06d 100644
--- a/chromecast/media/cma/test/frame_segmenter_for_test.cc
+++ b/chromecast/media/cma/test/frame_segmenter_for_test.cc
@@ -283,8 +283,8 @@
class FakeDemuxerHost : public ::media::DemuxerHost {
public:
// DemuxerHost implementation.
- void AddBufferedTimeRange(base::TimeDelta start,
- base::TimeDelta end) override {}
+ void OnBufferedTimeRangesChanged(
+ const ::media::Ranges<base::TimeDelta>& ranges) override {}
void SetDuration(base::TimeDelta duration) override {}
void OnDemuxerError(::media::PipelineStatus error) override {
LOG(FATAL) << "OnDemuxerError: " << error;
diff --git a/content/renderer/media/android/media_source_delegate.cc b/content/renderer/media/android/media_source_delegate.cc
index e29125d..b9ffb03d 100644
--- a/content/renderer/media/android/media_source_delegate.cc
+++ b/content/renderer/media/android/media_source_delegate.cc
@@ -307,9 +307,9 @@
media_weak_factory_.GetWeakPtr()));
}
-void MediaSourceDelegate::AddBufferedTimeRange(base::TimeDelta start,
- base::TimeDelta end) {
- buffered_time_ranges_.Add(start, end);
+void MediaSourceDelegate::OnBufferedTimeRangesChanged(
+ const media::Ranges<base::TimeDelta>& ranges) {
+ buffered_time_ranges_ = ranges;
}
void MediaSourceDelegate::SetDuration(base::TimeDelta duration) {
diff --git a/content/renderer/media/android/media_source_delegate.h b/content/renderer/media/android/media_source_delegate.h
index 9571d5a7..6d9719c 100644
--- a/content/renderer/media/android/media_source_delegate.h
+++ b/content/renderer/media/android/media_source_delegate.h
@@ -110,8 +110,8 @@
private:
// Methods inherited from DemuxerHost.
- void AddBufferedTimeRange(base::TimeDelta start,
- base::TimeDelta end) override;
+ void OnBufferedTimeRangesChanged(
+ const media::Ranges<base::TimeDelta>& ranges) override;
void SetDuration(base::TimeDelta duration) override;
void OnDemuxerError(media::PipelineStatus status) override;
void AddTextStream(media::DemuxerStream* text_stream,
diff --git a/media/base/demuxer.h b/media/base/demuxer.h
index cc9f665..5a2218a 100644
--- a/media/base/demuxer.h
+++ b/media/base/demuxer.h
@@ -17,6 +17,7 @@
#include "media/base/eme_constants.h"
#include "media/base/media_export.h"
#include "media/base/pipeline_status.h"
+#include "media/base/ranges.h"
namespace media {
@@ -24,9 +25,13 @@
class MEDIA_EXPORT DemuxerHost {
public:
- // Notify the host that time range [start,end] has been buffered.
- virtual void AddBufferedTimeRange(base::TimeDelta start,
- base::TimeDelta end) = 0;
+ // Notify the host that buffered time ranges have changed. Note that buffered
+ // time ranges can grow (when new media data is appended), but they can also
+ // shrink (when buffering reaches limit capacity and some buffered data
+ // becomes evicted, e.g. due to MSE GC algorithm, or by explicit removal of
+ // ranges directed by MSE web app).
+ virtual void OnBufferedTimeRangesChanged(
+ const Ranges<base::TimeDelta>& ranges) = 0;
// Sets the duration of the media in microseconds.
// Duration may be kInfiniteDuration() if the duration is not known.
diff --git a/media/base/demuxer_perftest.cc b/media/base/demuxer_perftest.cc
index 6743ac6c..7d8a10ba 100644
--- a/media/base/demuxer_perftest.cc
+++ b/media/base/demuxer_perftest.cc
@@ -31,8 +31,8 @@
~DemuxerHostImpl() override {}
// DemuxerHost implementation.
- void AddBufferedTimeRange(base::TimeDelta start,
- base::TimeDelta end) override {}
+ void OnBufferedTimeRangesChanged(
+ const Ranges<base::TimeDelta>& ranges) override {}
void SetDuration(base::TimeDelta duration) override {}
void OnDemuxerError(media::PipelineStatus error) override {}
void AddTextStream(media::DemuxerStream* text_stream,
diff --git a/media/base/mock_demuxer_host.h b/media/base/mock_demuxer_host.h
index 614f971d..6ab7814 100644
--- a/media/base/mock_demuxer_host.h
+++ b/media/base/mock_demuxer_host.h
@@ -17,8 +17,8 @@
MockDemuxerHost();
virtual ~MockDemuxerHost();
- MOCK_METHOD2(AddBufferedTimeRange, void(base::TimeDelta start,
- base::TimeDelta end));
+ MOCK_METHOD1(OnBufferedTimeRangesChanged,
+ void(const Ranges<base::TimeDelta>&));
MOCK_METHOD1(SetDuration, void(base::TimeDelta duration));
MOCK_METHOD1(OnDemuxerError, void(PipelineStatus error));
MOCK_METHOD2(AddTextStream, void(DemuxerStream*,
diff --git a/media/base/pipeline.cc b/media/base/pipeline.cc
index ce49c5d..867d630 100644
--- a/media/base/pipeline.cc
+++ b/media/base/pipeline.cc
@@ -507,10 +507,11 @@
}
}
-void Pipeline::AddBufferedTimeRange(TimeDelta start, TimeDelta end) {
+void Pipeline::OnBufferedTimeRangesChanged(
+ const Ranges<base::TimeDelta>& ranges) {
DCHECK(IsRunning());
base::AutoLock auto_lock(lock_);
- buffered_time_ranges_.Add(start, end);
+ buffered_time_ranges_ = ranges;
did_loading_progress_ = true;
}
diff --git a/media/base/pipeline.h b/media/base/pipeline.h
index ba99348..5eae2e09 100644
--- a/media/base/pipeline.h
+++ b/media/base/pipeline.h
@@ -246,8 +246,8 @@
void FinishSeek();
// DemuxerHost implementaion.
- void AddBufferedTimeRange(base::TimeDelta start,
- base::TimeDelta end) override;
+ void OnBufferedTimeRangesChanged(
+ const Ranges<base::TimeDelta>& ranges) override;
void SetDuration(base::TimeDelta duration) override;
void OnDemuxerError(PipelineStatus error) override;
void AddTextStream(DemuxerStream* text_stream,
@@ -350,7 +350,7 @@
// Amount of available buffered data as reported by |demuxer_|.
Ranges<base::TimeDelta> buffered_time_ranges_;
- // True when AddBufferedTimeRange() has been called more recently than
+ // True when OnBufferedTimeRangesChanged() has been called more recently than
// DidLoadingProgress().
bool did_loading_progress_;
diff --git a/media/base/pipeline_unittest.cc b/media/base/pipeline_unittest.cc
index 5406278..8f43d60 100644
--- a/media/base/pipeline_unittest.cc
+++ b/media/base/pipeline_unittest.cc
@@ -661,7 +661,9 @@
EXPECT_EQ(0u, pipeline_->GetBufferedTimeRanges().size());
EXPECT_FALSE(pipeline_->DidLoadingProgress());
- pipeline_->AddBufferedTimeRange(base::TimeDelta(), kDuration / 8);
+ Ranges<base::TimeDelta> ranges;
+ ranges.Add(base::TimeDelta(), kDuration / 8);
+ pipeline_->OnBufferedTimeRangesChanged(ranges);
EXPECT_TRUE(pipeline_->DidLoadingProgress());
EXPECT_FALSE(pipeline_->DidLoadingProgress());
EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index f8bffcd..374d618c 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -659,8 +659,7 @@
ranges = GetBufferedRanges_Locked();
}
- for (size_t i = 0; i < ranges.size(); ++i)
- host_->AddBufferedTimeRange(ranges.start(i), ranges.end(i));
+ host_->OnBufferedTimeRangesChanged(ranges);
}
void ChunkDemuxer::ResetParserState(const std::string& id,
@@ -702,6 +701,7 @@
return;
source_state_map_[id]->Remove(start, end, duration_);
+ host_->OnBufferedTimeRangesChanged(GetBufferedRanges_Locked());
}
double ChunkDemuxer::GetDuration() {
diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc
index 5e1f777..308f9eb 100644
--- a/media/filters/chunk_demuxer_unittest.cc
+++ b/media/filters/chunk_demuxer_unittest.cc
@@ -584,7 +584,7 @@
void AppendData(const std::string& source_id,
const uint8_t* data,
size_t length) {
- EXPECT_CALL(host_, AddBufferedTimeRange(_, _)).Times(AnyNumber());
+ EXPECT_CALL(host_, OnBufferedTimeRangesChanged(_)).Times(AnyNumber());
demuxer_->AppendData(source_id, data, length,
append_window_start_for_next_append_,
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index 7769c92..18863e6 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -1514,8 +1514,7 @@
} else if (video) {
buffered = video->GetBufferedRanges();
}
- for (size_t i = 0; i < buffered.size(); ++i)
- host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i));
+ host_->OnBufferedTimeRangesChanged(buffered);
}
void FFmpegDemuxer::OnDataSourceError() {
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc
index 1a7acab..e22e454 100644
--- a/media/filters/ffmpeg_demuxer_unittest.cc
+++ b/media/filters/ffmpeg_demuxer_unittest.cc
@@ -84,7 +84,7 @@
void CreateDemuxer(const std::string& name) {
CHECK(!demuxer_);
- EXPECT_CALL(host_, AddBufferedTimeRange(_, _)).Times(AnyNumber());
+ EXPECT_CALL(host_, OnBufferedTimeRangesChanged(_)).Times(AnyNumber());
CreateDataSource(name);
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc
index 47fc08d..43da5c1d 100644
--- a/media/test/pipeline_integration_test.cc
+++ b/media/test/pipeline_integration_test.cc
@@ -561,6 +561,19 @@
last_timestamp_offset_ = timestamp_offset;
}
+ void SetMemoryLimits(size_t limit_bytes) {
+ chunk_demuxer_->SetMemoryLimits(DemuxerStream::AUDIO, limit_bytes);
+ chunk_demuxer_->SetMemoryLimits(DemuxerStream::VIDEO, limit_bytes);
+ }
+
+ void EvictCodedFrames(base::TimeDelta currentMediaTime, size_t newDataSize) {
+ chunk_demuxer_->EvictCodedFrames(kSourceId, currentMediaTime, newDataSize);
+ }
+
+ void RemoveRange(base::TimeDelta start, base::TimeDelta end) {
+ chunk_demuxer_->Remove(kSourceId, start, end);
+ }
+
void EndOfStream() {
chunk_demuxer_->MarkEndOfStream(PIPELINE_OK);
}
@@ -1089,6 +1102,60 @@
Stop();
}
+TEST_F(PipelineIntegrationTest, MediaSource_Remove_Updates_BufferedRanges) {
+ const char* input_filename = "bear-320x240.webm";
+ MockMediaSource source(input_filename, kWebM, kAppendWholeFile);
+ StartPipelineWithMediaSource(&source);
+
+ auto buffered_ranges = pipeline_->GetBufferedTimeRanges();
+ EXPECT_EQ(1u, buffered_ranges.size());
+ EXPECT_EQ(0, buffered_ranges.start(0).InMilliseconds());
+ EXPECT_EQ(k320WebMFileDurationMs, buffered_ranges.end(0).InMilliseconds());
+
+ source.RemoveRange(base::TimeDelta::FromMilliseconds(1000),
+ base::TimeDelta::FromMilliseconds(k320WebMFileDurationMs));
+ buffered_ranges = pipeline_->GetBufferedTimeRanges();
+ EXPECT_EQ(1u, buffered_ranges.size());
+ EXPECT_EQ(0, buffered_ranges.start(0).InMilliseconds());
+ EXPECT_EQ(1001, buffered_ranges.end(0).InMilliseconds());
+
+ source.Shutdown();
+ Stop();
+}
+
+// This test case imitates media playback with advancing media_time and
+// continuously adding new data. At some point we should reach the buffering
+// limit, after that MediaSource should evict some buffered data and that
+// evicted data shold be reflected in the change of media::Pipeline buffered
+// ranges (returned by GetBufferedTimeRanges). At that point the buffered ranges
+// will no longer start at 0.
+TEST_F(PipelineIntegrationTest, MediaSource_FillUp_Buffer) {
+ const char* input_filename = "bear-320x240.webm";
+ MockMediaSource source(input_filename, kWebM, kAppendWholeFile);
+ StartPipelineWithMediaSource(&source);
+ source.SetMemoryLimits(1048576);
+
+ scoped_refptr<DecoderBuffer> file = ReadTestDataFile(input_filename);
+
+ auto buffered_ranges = pipeline_->GetBufferedTimeRanges();
+ EXPECT_EQ(1u, buffered_ranges.size());
+ do {
+ // Advance media_time to the end of the currently buffered data
+ base::TimeDelta media_time = buffered_ranges.end(0);
+ source.Seek(media_time);
+ // Ask MediaSource to evict buffered data if buffering limit has been
+ // reached (the data will be evicted from the front of the buffered range).
+ source.EvictCodedFrames(media_time, file->data_size());
+ source.AppendAtTime(media_time, file->data(), file->data_size());
+ buffered_ranges = pipeline_->GetBufferedTimeRanges();
+ } while (buffered_ranges.size() == 1 &&
+ buffered_ranges.start(0) == base::TimeDelta::FromSeconds(0));
+
+ EXPECT_EQ(1u, buffered_ranges.size());
+ source.Shutdown();
+ Stop();
+}
+
#if !defined(DISABLE_EME_TESTS)
TEST_F(PipelineIntegrationTest, MediaSource_ConfigChange_Encrypted_WebM) {
MockMediaSource source("bear-320x240-16x9-aspect-av_enc-av.webm", kWebM,