summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authoracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-25 00:07:26 +0000
committeracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-25 00:07:26 +0000
commit721d4385180621453687bc10bef9c2b20c2201c0 (patch)
tree3817bf01f85f4357d7add625873c5b1f1f99d9f9 /media
parent2b3c7b35d213aa98dfddf54e075e01a41646e794 (diff)
downloadchromium_src-721d4385180621453687bc10bef9c2b20c2201c0.zip
chromium_src-721d4385180621453687bc10bef9c2b20c2201c0.tar.gz
chromium_src-721d4385180621453687bc10bef9c2b20c2201c0.tar.bz2
Add Chromium-side functionality for SourceBuffer.remove().
BUG=166465 TEST=SourceBufferStreamTest.Remove* Review URL: https://chromiumcodereview.appspot.com/19826004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@213553 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/filters/chunk_demuxer.cc28
-rw-r--r--media/filters/chunk_demuxer.h7
-rw-r--r--media/filters/chunk_demuxer_unittest.cc11
-rw-r--r--media/filters/source_buffer_stream.cc58
-rw-r--r--media/filters/source_buffer_stream.h10
-rw-r--r--media/filters/source_buffer_stream_unittest.cc139
6 files changed, 252 insertions, 1 deletions
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index 670a8b0..29bfc6f 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -144,6 +144,15 @@ class ChunkDemuxerStream : public DemuxerStream {
// Returns true if buffers were successfully added.
bool Append(const StreamParser::BufferQueue& buffers);
+ // Removes buffers between |start| and |end| according to the steps
+ // in the "Coded Frame Removal Algorithm" in the Media Source
+ // Extensions Spec.
+ // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-coded-frame-removal
+ //
+ // |duration| is the current duration of the presentation. It is
+ // required by the computation outlined in the spec.
+ void Remove(TimeDelta start, TimeDelta end, TimeDelta duration);
+
// Signal to the stream that duration has changed to |duration|.
void OnSetDuration(TimeDelta duration);
@@ -477,6 +486,12 @@ bool ChunkDemuxerStream::Append(const StreamParser::BufferQueue& buffers) {
return true;
}
+void ChunkDemuxerStream::Remove(TimeDelta start, TimeDelta end,
+ TimeDelta duration) {
+ base::AutoLock auto_lock(lock_);
+ stream_->Remove(start, end, duration);
+}
+
void ChunkDemuxerStream::OnSetDuration(TimeDelta duration) {
base::AutoLock auto_lock(lock_);
stream_->OnSetDuration(duration);
@@ -943,6 +958,19 @@ void ChunkDemuxer::Abort(const std::string& id) {
source_state_map_[id]->Abort();
}
+void ChunkDemuxer::Remove(const std::string& id, base::TimeDelta start,
+ base::TimeDelta end) {
+ DVLOG(1) << "Remove(" << id << ", " << start.InSecondsF()
+ << ", " << end.InSecondsF() << ")";
+ base::AutoLock auto_lock(lock_);
+
+ if (id == source_id_audio_ && audio_)
+ audio_->Remove(start, end, duration_);
+
+ if (id == source_id_video_ && video_)
+ video_->Remove(start, end, duration_);
+}
+
double ChunkDemuxer::GetDuration() {
base::AutoLock auto_lock(lock_);
return GetDuration_Locked();
diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h
index 5983199..36177fb 100644
--- a/media/filters/chunk_demuxer.h
+++ b/media/filters/chunk_demuxer.h
@@ -110,6 +110,11 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
// it can accept a new segment.
void Abort(const std::string& id);
+ // Remove buffers between |start| and |end| for the source buffer
+ // associated with |id|.
+ void Remove(const std::string& id, base::TimeDelta start,
+ base::TimeDelta end);
+
// Returns the current presentation duration.
double GetDuration();
double GetDuration_Locked();
@@ -119,7 +124,7 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
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
+ // source buffer associated with |id|. Returns true if the offset is set
// properly, false if the offset cannot be applied because we're in the
// middle of parsing a media segment.
bool SetTimestampOffset(const std::string& id, base::TimeDelta offset);
diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc
index 9620eff..c83f4bb 100644
--- a/media/filters/chunk_demuxer_unittest.cc
+++ b/media/filters/chunk_demuxer_unittest.cc
@@ -2659,4 +2659,15 @@ TEST_F(ChunkDemuxerTest, TestGCDuringSeek) {
CheckExpectedRanges(kSourceId, "{ [500,592) [792,815) }");
}
+TEST_F(ChunkDemuxerTest, RemoveBeforeInitSegment) {
+ EXPECT_CALL(*this, DemuxerOpened());
+ demuxer_->Initialize(
+ &host_, CreateInitDoneCB(kNoTimestamp(), PIPELINE_OK));
+
+ EXPECT_EQ(ChunkDemuxer::kOk, AddId(kSourceId, true, true));
+
+ demuxer_->Remove(kSourceId, base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(1));
+}
+
} // namespace media
diff --git a/media/filters/source_buffer_stream.cc b/media/filters/source_buffer_stream.cc
index 948ea01..b0038dd 100644
--- a/media/filters/source_buffer_stream.cc
+++ b/media/filters/source_buffer_stream.cc
@@ -469,6 +469,64 @@ bool SourceBufferStream::Append(
return true;
}
+void SourceBufferStream::Remove(base::TimeDelta start, base::TimeDelta end,
+ base::TimeDelta duration) {
+ DCHECK(start >= base::TimeDelta()) << start.InSecondsF();
+ DCHECK(start < end) << "start " << start.InSecondsF()
+ << " end " << end.InSecondsF();
+ DCHECK(duration != kNoTimestamp());
+
+ base::TimeDelta remove_end_timestamp = duration;
+ base::TimeDelta keyframe_timestamp = FindKeyframeAfterTimestamp(end);
+ if (keyframe_timestamp != kNoTimestamp()) {
+ remove_end_timestamp = keyframe_timestamp;
+ } else if (end < remove_end_timestamp) {
+ remove_end_timestamp = end;
+ }
+
+ RangeList::iterator itr = ranges_.begin();
+
+ while (itr != ranges_.end()) {
+ SourceBufferRange* range = *itr;
+ if (range->GetStartTimestamp() >= remove_end_timestamp)
+ break;
+
+ // Split off any remaining end piece and add it to |ranges_|.
+ SourceBufferRange* new_range =
+ range->SplitRange(remove_end_timestamp, false);
+ if (new_range) {
+ itr = ranges_.insert(++itr, new_range);
+ --itr;
+ }
+
+ // If the current range now is completely covered by the removal
+ // range then delete it and move on.
+ if (start <= range->GetStartTimestamp()) {
+ if (selected_range_ == range)
+ SetSelectedRange(NULL);
+
+ delete range;
+ itr = ranges_.erase(itr);
+ continue;
+ }
+
+ // Truncate the current range so that it only contains data before
+ // the removal range.
+ BufferQueue saved_buffers;
+ range->TruncateAt(start, &saved_buffers, false);
+
+ // Check to see if the current playback position was removed and
+ // update the selected range appropriately.
+ if (!saved_buffers.empty()) {
+ SetSelectedRange(NULL);
+ SetSelectedRangeIfNeeded(saved_buffers.front()->GetDecodeTimestamp());
+ }
+
+ // Move on to the next range.
+ ++itr;
+ }
+}
+
void SourceBufferStream::ResetSeekState() {
SetSelectedRange(NULL);
track_buffer_.clear();
diff --git a/media/filters/source_buffer_stream.h b/media/filters/source_buffer_stream.h
index b372e13..6167e51 100644
--- a/media/filters/source_buffer_stream.h
+++ b/media/filters/source_buffer_stream.h
@@ -64,6 +64,16 @@ class MEDIA_EXPORT SourceBufferStream {
// TODO(vrk): Implement garbage collection. (crbug.com/125070)
bool Append(const BufferQueue& buffers);
+ // Removes buffers between |start| and |end| according to the steps
+ // in the "Coded Frame Removal Algorithm" in the Media Source
+ // Extensions Spec.
+ // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-coded-frame-removal
+ //
+ // |duration| is the current duration of the presentation. It is
+ // required by the computation outlined in the spec.
+ void Remove(base::TimeDelta start, base::TimeDelta end,
+ base::TimeDelta duration);
+
// Changes the SourceBufferStream's state so that it will start returning
// buffers starting from the closest keyframe before |timestamp|.
void Seek(base::TimeDelta timestamp);
diff --git a/media/filters/source_buffer_stream_unittest.cc b/media/filters/source_buffer_stream_unittest.cc
index f0d2e38..3c12074 100644
--- a/media/filters/source_buffer_stream_unittest.cc
+++ b/media/filters/source_buffer_stream_unittest.cc
@@ -108,6 +108,17 @@ class SourceBufferStreamTest : public testing::Test {
stream_->Seek(timestamp);
}
+ void RemoveInMs(int start, int end, int duration) {
+ Remove(base::TimeDelta::FromMilliseconds(start),
+ base::TimeDelta::FromMilliseconds(end),
+ base::TimeDelta::FromMilliseconds(duration));
+ }
+
+ void Remove(base::TimeDelta start, base::TimeDelta end,
+ base::TimeDelta duration) {
+ stream_->Remove(start, end, duration);
+ }
+
void CheckExpectedRanges(const std::string& expected) {
Ranges<base::TimeDelta> r = stream_->GetBufferedTime();
@@ -2825,6 +2836,134 @@ TEST_F(SourceBufferStreamTest, EndNotSelected_During_PendingSeek) {
EXPECT_TRUE(stream_->IsSeekPending());
}
+
+// Removing exact start & end of a range.
+TEST_F(SourceBufferStreamTest, Remove_WholeRange1) {
+ Seek(0);
+ NewSegmentAppend("10K 40 70K 100 130K");
+ CheckExpectedRangesByTimestamp("{ [10,160) }");
+ RemoveInMs(10, 160, 160);
+ CheckExpectedRangesByTimestamp("{ }");
+}
+
+// Removal range starts before range and ends exactly at end.
+TEST_F(SourceBufferStreamTest, Remove_WholeRange2) {
+ Seek(0);
+ NewSegmentAppend("10K 40 70K 100 130K");
+ CheckExpectedRangesByTimestamp("{ [10,160) }");
+ RemoveInMs(0, 160, 160);
+ CheckExpectedRangesByTimestamp("{ }");
+}
+
+// Removal range starts at the start of a range and ends beyond the
+// range end.
+TEST_F(SourceBufferStreamTest, Remove_WholeRange3) {
+ Seek(0);
+ NewSegmentAppend("10K 40 70K 100 130K");
+ CheckExpectedRangesByTimestamp("{ [10,160) }");
+ RemoveInMs(10, 200, 200);
+ CheckExpectedRangesByTimestamp("{ }");
+}
+
+// Removal range starts before range start and ends after the range end.
+TEST_F(SourceBufferStreamTest, Remove_WholeRange4) {
+ Seek(0);
+ NewSegmentAppend("10K 40 70K 100 130K");
+ CheckExpectedRangesByTimestamp("{ [10,160) }");
+ RemoveInMs(0, 200, 200);
+ CheckExpectedRangesByTimestamp("{ }");
+}
+
+// Removes multiple ranges.
+TEST_F(SourceBufferStreamTest, Remove_WholeRange5) {
+ Seek(0);
+ NewSegmentAppend("10K 40 70K 100 130K");
+ NewSegmentAppend("1000K 1030 1060K 1090 1120K");
+ NewSegmentAppend("2000K 2030 2060K 2090 2120K");
+ CheckExpectedRangesByTimestamp("{ [10,160) [1000,1150) [2000,2150) }");
+ RemoveInMs(10, 3000, 3000);
+ CheckExpectedRangesByTimestamp("{ }");
+}
+
+// Verifies a [0-infinity) range removes everything.
+TEST_F(SourceBufferStreamTest, Remove_ZeroToInfinity) {
+ Seek(0);
+ NewSegmentAppend("10K 40 70K 100 130K");
+ NewSegmentAppend("1000K 1030 1060K 1090 1120K");
+ NewSegmentAppend("2000K 2030 2060K 2090 2120K");
+ CheckExpectedRangesByTimestamp("{ [10,160) [1000,1150) [2000,2150) }");
+ Remove(base::TimeDelta(), kInfiniteDuration(), kInfiniteDuration());
+ CheckExpectedRangesByTimestamp("{ }");
+}
+
+// Removal range starts at the beginning of the range and ends in the
+// middle of the range. This test verifies that full GOPs are removed.
+TEST_F(SourceBufferStreamTest, Remove_Partial1) {
+ Seek(0);
+ NewSegmentAppend("10K 40 70K 100 130K");
+ NewSegmentAppend("1000K 1030 1060K 1090 1120K");
+ CheckExpectedRangesByTimestamp("{ [10,160) [1000,1150) }");
+ RemoveInMs(0, 80, 2200);
+ CheckExpectedRangesByTimestamp("{ [130,160) [1000,1150) }");
+}
+
+// Removal range starts in the middle of a range and ends at the exact
+// end of the range.
+TEST_F(SourceBufferStreamTest, Remove_Partial2) {
+ Seek(0);
+ NewSegmentAppend("10K 40 70K 100 130K");
+ NewSegmentAppend("1000K 1030 1060K 1090 1120K");
+ CheckExpectedRangesByTimestamp("{ [10,160) [1000,1150) }");
+ RemoveInMs(40, 160, 2200);
+ CheckExpectedRangesByTimestamp("{ [10,40) [1000,1150) }");
+}
+
+// Removal range starts and ends within a range.
+TEST_F(SourceBufferStreamTest, Remove_Partial3) {
+ Seek(0);
+ NewSegmentAppend("10K 40 70K 100 130K");
+ NewSegmentAppend("1000K 1030 1060K 1090 1120K");
+ CheckExpectedRangesByTimestamp("{ [10,160) [1000,1150) }");
+ RemoveInMs(40, 120, 2200);
+ CheckExpectedRangesByTimestamp("{ [10,40) [130,160) [1000,1150) }");
+}
+
+// Removal range starts in the middle of one range and ends in the
+// middle of another range.
+TEST_F(SourceBufferStreamTest, Remove_Partial4) {
+ Seek(0);
+ NewSegmentAppend("10K 40 70K 100 130K");
+ NewSegmentAppend("1000K 1030 1060K 1090 1120K");
+ NewSegmentAppend("2000K 2030 2060K 2090 2120K");
+ CheckExpectedRangesByTimestamp("{ [10,160) [1000,1150) [2000,2150) }");
+ RemoveInMs(40, 2030, 2200);
+ CheckExpectedRangesByTimestamp("{ [10,40) [2060,2150) }");
+}
+
+// Test behavior when the current positing is removed and new buffers
+// are appended over the removal range.
+TEST_F(SourceBufferStreamTest, Remove_CurrentPosition) {
+ Seek(0);
+ NewSegmentAppend("0K 30 60 90K 120 150 180K 210 240 270K 300 330");
+ CheckExpectedRangesByTimestamp("{ [0,360) }");
+ CheckExpectedBuffers("0K 30 60 90K 120");
+
+ // Remove a range that includes the next buffer (i.e., 150).
+ RemoveInMs(150, 210, 360);
+ CheckExpectedRangesByTimestamp("{ [0,150) [270,360) }");
+
+ // Verify that no next buffer is returned.
+ CheckNoNextBuffer();
+
+ // Append some buffers to fill the gap that was created.
+ NewSegmentAppend("120K 150 180 210K 240");
+ CheckExpectedRangesByTimestamp("{ [0,360) }");
+
+ // Verify that buffers resume at the next keyframe after the
+ // current position.
+ CheckExpectedBuffers("210K 240 270K 300 330");
+}
+
// TODO(vrk): Add unit tests where keyframes are unaligned between streams.
// (crbug.com/133557)