diff options
author | ycheo@chromium.org <ycheo@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-24 11:03:28 +0000 |
---|---|---|
committer | ycheo@chromium.org <ycheo@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-24 11:03:28 +0000 |
commit | 9df283e5d1828093d31e362675f4c90ae54aabbb (patch) | |
tree | 305b8162b154eb21f3658ec263f94ac0581cfc26 /media | |
parent | f213d34c82f16025708d110ceb72a8d12936d1c4 (diff) | |
download | chromium_src-9df283e5d1828093d31e362675f4c90ae54aabbb.zip chromium_src-9df283e5d1828093d31e362675f4c90ae54aabbb.tar.gz chromium_src-9df283e5d1828093d31e362675f4c90ae54aabbb.tar.bz2 |
Tweak GC algorithm in MSE to remove the buffers after last appended first.
BUG=293408
Review URL: https://chromiumcodereview.appspot.com/23919008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@224975 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/filters/source_buffer_stream.cc | 109 | ||||
-rw-r--r-- | media/filters/source_buffer_stream.h | 16 | ||||
-rw-r--r-- | media/filters/source_buffer_stream_unittest.cc | 181 |
3 files changed, 305 insertions, 1 deletions
diff --git a/media/filters/source_buffer_stream.cc b/media/filters/source_buffer_stream.cc index 15f894c..0f46f71 100644 --- a/media/filters/source_buffer_stream.cc +++ b/media/filters/source_buffer_stream.cc @@ -88,6 +88,15 @@ class SourceBufferRange { int DeleteGOPFromFront(BufferQueue* deleted_buffers); int DeleteGOPFromBack(BufferQueue* deleted_buffers); + // Gets the range of GOP to secure at least |bytes_to_free| from + // [|start_timestamp|, |end_timestamp|). + // Returns the size of the buffers to secure if the buffers of + // [|start_timestamp|, |end_removal_timestamp|) is removed. + // Will not update |end_removal_timestamp| if the returned size is 0. + int GetRemovalGOP( + base::TimeDelta start_timestamp, base::TimeDelta end_timestamp, + int bytes_to_free, base::TimeDelta* end_removal_timestamp); + // Indicates whether the GOP at the beginning or end of the range contains the // next buffer position. bool FirstGOPContainsNextBufferPosition() const; @@ -653,14 +662,73 @@ void SourceBufferStream::GarbageCollectIfNeeded() { int bytes_to_free = ranges_size - memory_limit_; + // Begin deleting after the last appended buffer. + int bytes_freed = FreeBuffersAfterLastAppended(bytes_to_free); + // Begin deleting from the front. - int bytes_freed = FreeBuffers(bytes_to_free, false); + if (bytes_to_free - bytes_freed > 0) + bytes_freed += FreeBuffers(bytes_to_free - bytes_freed, false); // Begin deleting from the back. if (bytes_to_free - bytes_freed > 0) FreeBuffers(bytes_to_free - bytes_freed, true); } +int SourceBufferStream::FreeBuffersAfterLastAppended(int total_bytes_to_free) { + base::TimeDelta next_buffer_timestamp = GetNextBufferTimestamp(); + if (last_appended_buffer_timestamp_ == kNoTimestamp() || + next_buffer_timestamp == kNoTimestamp() || + last_appended_buffer_timestamp_ >= next_buffer_timestamp) { + return 0; + } + + base::TimeDelta remove_range_start = last_appended_buffer_timestamp_; + if (last_appended_buffer_is_keyframe_) + remove_range_start += GetMaxInterbufferDistance(); + + base::TimeDelta remove_range_start_keyframe = FindKeyframeAfterTimestamp( + remove_range_start); + if (remove_range_start_keyframe != kNoTimestamp()) + remove_range_start = remove_range_start_keyframe; + if (remove_range_start >= next_buffer_timestamp) + return 0; + + base::TimeDelta remove_range_end; + int bytes_freed = GetRemovalRange( + remove_range_start, next_buffer_timestamp, total_bytes_to_free, + &remove_range_end); + if (bytes_freed > 0) + Remove(remove_range_start, remove_range_end, next_buffer_timestamp); + return bytes_freed; +} + +int SourceBufferStream::GetRemovalRange( + base::TimeDelta start_timestamp, base::TimeDelta end_timestamp, + int total_bytes_to_free, base::TimeDelta* removal_end_timestamp) { + DCHECK(start_timestamp >= base::TimeDelta()) << start_timestamp.InSecondsF(); + DCHECK(start_timestamp < end_timestamp) + << "start " << start_timestamp.InSecondsF() + << ", end " << end_timestamp.InSecondsF(); + + int bytes_to_free = total_bytes_to_free; + int bytes_freed = 0; + + for (RangeList::iterator itr = ranges_.begin(); + itr != ranges_.end() && bytes_to_free > 0; ++itr) { + SourceBufferRange* range = *itr; + if (range->GetStartTimestamp() >= end_timestamp) + break; + if (range->GetEndTimestamp() < start_timestamp) + continue; + + int bytes_removed = range->GetRemovalGOP( + start_timestamp, end_timestamp, bytes_to_free, removal_end_timestamp); + bytes_to_free -= bytes_removed; + bytes_freed += bytes_removed; + } + return bytes_freed; +} + int SourceBufferStream::FreeBuffers(int total_bytes_to_free, bool reverse_direction) { TRACE_EVENT2("mse", "SourceBufferStream::FreeBuffers", @@ -1627,6 +1695,45 @@ int SourceBufferRange::DeleteGOPFromBack(BufferQueue* deleted_buffers) { return total_bytes_deleted; } +int SourceBufferRange::GetRemovalGOP( + base::TimeDelta start_timestamp, base::TimeDelta end_timestamp, + int total_bytes_to_free, base::TimeDelta* removal_end_timestamp) { + int bytes_to_free = total_bytes_to_free; + int bytes_removed = 0; + + KeyframeMap::iterator gop_itr = GetFirstKeyframeAt(start_timestamp, false); + int keyframe_index = gop_itr->second - keyframe_map_index_base_; + BufferQueue::iterator buffer_itr = buffers_.begin() + keyframe_index; + KeyframeMap::iterator gop_end = keyframe_map_.end(); + if (end_timestamp < GetBufferedEndTimestamp()) + gop_end = GetFirstKeyframeBefore(end_timestamp); + + // Check if the removal range is within a GOP and skip the loop if so. + // [keyframe]...[start_timestamp]...[end_timestamp]...[keyframe] + KeyframeMap::iterator gop_itr_prev = gop_itr; + if (gop_itr_prev != keyframe_map_.begin() && --gop_itr_prev == gop_end) + gop_end = gop_itr; + + while (gop_itr != gop_end && bytes_to_free > 0) { + ++gop_itr; + + int gop_size = 0; + int next_gop_index = gop_itr == keyframe_map_.end() ? + buffers_.size() : gop_itr->second - keyframe_map_index_base_; + BufferQueue::iterator next_gop_start = buffers_.begin() + next_gop_index; + for (; buffer_itr != next_gop_start; ++buffer_itr) + gop_size += (*buffer_itr)->data_size(); + + bytes_removed += gop_size; + bytes_to_free -= gop_size; + } + if (bytes_removed > 0) { + *removal_end_timestamp = gop_itr == keyframe_map_.end() ? + GetBufferedEndTimestamp() : gop_itr->first; + } + return bytes_removed; +} + bool SourceBufferRange::FirstGOPContainsNextBufferPosition() const { if (!HasNextBufferPosition()) return false; diff --git a/media/filters/source_buffer_stream.h b/media/filters/source_buffer_stream.h index 6167e51..b218b31 100644 --- a/media/filters/source_buffer_stream.h +++ b/media/filters/source_buffer_stream.h @@ -126,6 +126,8 @@ class MEDIA_EXPORT SourceBufferStream { } private: + friend class SourceBufferStreamTest; + typedef std::list<SourceBufferRange*> RangeList; // Frees up space if the SourceBufferStream is taking up too much memory. @@ -137,6 +139,20 @@ class MEDIA_EXPORT SourceBufferStream { // is true. Returns the number of bytes freed. int FreeBuffers(int total_bytes_to_free, bool reverse_direction); + // Attempts to delete approximately |total_bytes_to_free| amount of data from + // |ranges_|, starting after the last appended buffer before the current + // playback position. + int FreeBuffersAfterLastAppended(int total_bytes_to_free); + + // Gets the removal range to secure |byte_to_free| from + // [|start_timestamp|, |end_timestamp|). + // Returns the size of buffers to secure if future + // Remove(|start_timestamp|, |removal_end_timestamp|, duration) is called. + // Will not update |removal_end_timestamp| if the returned size is 0. + int GetRemovalRange(base::TimeDelta start_timestamp, + base::TimeDelta end_timestamp, int byte_to_free, + base::TimeDelta* removal_end_timestamp); + // Appends |new_buffers| into |range_for_new_buffers_itr|, handling start and // end overlaps if necessary. // |deleted_buffers| is an output parameter containing candidates for diff --git a/media/filters/source_buffer_stream_unittest.cc b/media/filters/source_buffer_stream_unittest.cc index 0507175..d0d4df2 100644 --- a/media/filters/source_buffer_stream_unittest.cc +++ b/media/filters/source_buffer_stream_unittest.cc @@ -119,6 +119,18 @@ class SourceBufferStreamTest : public testing::Test { stream_->Remove(start, end, duration); } + int GetRemovalRangeInMs(int start, int end, int bytes_to_free, + int* removal_end) { + base::TimeDelta removal_end_timestamp = + base::TimeDelta::FromMilliseconds(*removal_end); + int bytes_removed = stream_->GetRemovalRange( + base::TimeDelta::FromMilliseconds(start), + base::TimeDelta::FromMilliseconds(end), bytes_to_free, + &removal_end_timestamp); + *removal_end = removal_end_timestamp.InMilliseconds(); + return bytes_removed; + } + void CheckExpectedRanges(const std::string& expected) { Ranges<base::TimeDelta> r = stream_->GetBufferedTime(); @@ -2136,6 +2148,47 @@ TEST_F(SourceBufferStreamTest, GarbageCollection_DeleteSeveralRanges) { CheckNoNextBuffer(); } +TEST_F(SourceBufferStreamTest, GarbageCollection_DeleteAfterLastAppend) { + // Set memory limit to 10 buffers. + SetMemoryLimit(10); + + // Append 1 GOP starting at 310ms, 30ms apart. + NewSegmentAppend("310K 340 370"); + + // Append 2 GOPs starting at 490ms, 30ms apart. + NewSegmentAppend("490K 520 550 580K 610 640"); + + CheckExpectedRangesByTimestamp("{ [310,400) [490,670) }"); + + // Seek to the GOP at 580ms. + SeekToTimestamp(base::TimeDelta::FromMilliseconds(580)); + + // Append 2 GOPs before the existing ranges. + // So the ranges before GC are "{ [100,280) [310,400) [490,670) }". + NewSegmentAppend("100K 130 160 190K 220 250K"); + + // Should save the newly appended GOPs. + CheckExpectedRangesByTimestamp("{ [100,280) [580,670) }"); +} + +TEST_F(SourceBufferStreamTest, GarbageCollection_DeleteAfterLastAppendMerged) { + // Set memory limit to 10 buffers. + SetMemoryLimit(10); + + // Append 3 GOPs starting at 400ms, 30ms apart. + NewSegmentAppend("400K 430 460 490K 520 550 580K 610 640"); + + // Seek to the GOP at 580ms. + SeekToTimestamp(base::TimeDelta::FromMilliseconds(580)); + + // Append 2 GOPs starting at 220ms, and they will be merged with the existing + // range. So the range before GC is "{ [220,670) }". + NewSegmentAppend("220K 250 280 310K 340 370"); + + // Should save the newly appended GOPs. + CheckExpectedRangesByTimestamp("{ [220,400) [580,670) }"); +} + TEST_F(SourceBufferStreamTest, GarbageCollection_NoSeek) { // Set memory limit to 20 buffers. SetMemoryLimit(20); @@ -2479,6 +2532,134 @@ TEST_F(SourceBufferStreamTest, GarbageCollection_Performance) { } } +TEST_F(SourceBufferStreamTest, GetRemovalRange_BytesToFree) { + // Append 2 GOPs starting at 300ms, 30ms apart. + NewSegmentAppend("300K 330 360 390K 420 450"); + + // Append 2 GOPs starting at 600ms, 30ms apart. + NewSegmentAppend("600K 630 660 690K 720 750"); + + // Append 2 GOPs starting at 900ms, 30ms apart. + NewSegmentAppend("900K 930 960 990K 1020 1050"); + + CheckExpectedRangesByTimestamp("{ [300,480) [600,780) [900,1080) }"); + + int remove_range_end = -1; + int bytes_removed = -1; + + // Size 0. + bytes_removed = GetRemovalRangeInMs(300, 1080, 0, &remove_range_end); + EXPECT_EQ(-1, remove_range_end); + EXPECT_EQ(0, bytes_removed); + + // Smaller than the size of GOP. + bytes_removed = GetRemovalRangeInMs(300, 1080, 1, &remove_range_end); + EXPECT_EQ(390, remove_range_end); + // Remove as the size of GOP. + EXPECT_EQ(3, bytes_removed); + + // The same size with a GOP. + bytes_removed = GetRemovalRangeInMs(300, 1080, 3, &remove_range_end); + EXPECT_EQ(390, remove_range_end); + EXPECT_EQ(3, bytes_removed); + + // The same size with a range. + bytes_removed = GetRemovalRangeInMs(300, 1080, 6, &remove_range_end); + EXPECT_EQ(480, remove_range_end); + EXPECT_EQ(6, bytes_removed); + + // A frame larger than a range. + bytes_removed = GetRemovalRangeInMs(300, 1080, 7, &remove_range_end); + EXPECT_EQ(690, remove_range_end); + EXPECT_EQ(9, bytes_removed); + + // The same size with two ranges. + bytes_removed = GetRemovalRangeInMs(300, 1080, 12, &remove_range_end); + EXPECT_EQ(780, remove_range_end); + EXPECT_EQ(12, bytes_removed); + + // Larger than two ranges. + bytes_removed = GetRemovalRangeInMs(300, 1080, 14, &remove_range_end); + EXPECT_EQ(990, remove_range_end); + EXPECT_EQ(15, bytes_removed); + + // The same size with the whole ranges. + bytes_removed = GetRemovalRangeInMs(300, 1080, 18, &remove_range_end); + EXPECT_EQ(1080, remove_range_end); + EXPECT_EQ(18, bytes_removed); + + // Larger than the whole ranges. + bytes_removed = GetRemovalRangeInMs(300, 1080, 20, &remove_range_end); + EXPECT_EQ(1080, remove_range_end); + EXPECT_EQ(18, bytes_removed); +} + +TEST_F(SourceBufferStreamTest, GetRemovalRange_Range) { + // Append 2 GOPs starting at 300ms, 30ms apart. + NewSegmentAppend("300K 330 360 390K 420 450"); + + // Append 2 GOPs starting at 600ms, 30ms apart. + NewSegmentAppend("600K 630 660 690K 720 750"); + + // Append 2 GOPs starting at 900ms, 30ms apart. + NewSegmentAppend("900K 930 960 990K 1020 1050"); + + CheckExpectedRangesByTimestamp("{ [300,480) [600,780) [900,1080) }"); + + int remove_range_end = -1; + int bytes_removed = -1; + + // Within a GOP and no keyframe. + bytes_removed = GetRemovalRangeInMs(630, 660, 20, &remove_range_end); + EXPECT_EQ(-1, remove_range_end); + EXPECT_EQ(0, bytes_removed); + + // Across a GOP and no keyframe. + bytes_removed = GetRemovalRangeInMs(630, 750, 20, &remove_range_end); + EXPECT_EQ(-1, remove_range_end); + EXPECT_EQ(0, bytes_removed); + + // The same size with a range. + bytes_removed = GetRemovalRangeInMs(600, 780, 20, &remove_range_end); + EXPECT_EQ(780, remove_range_end); + EXPECT_EQ(6, bytes_removed); + + // One frame larger than a range. + bytes_removed = GetRemovalRangeInMs(570, 810, 20, &remove_range_end); + EXPECT_EQ(780, remove_range_end); + EXPECT_EQ(6, bytes_removed); + + // Facing the other ranges. + bytes_removed = GetRemovalRangeInMs(480, 900, 20, &remove_range_end); + EXPECT_EQ(780, remove_range_end); + EXPECT_EQ(6, bytes_removed); + + // In the midle of the other ranges, but not including any GOP. + bytes_removed = GetRemovalRangeInMs(420, 960, 20, &remove_range_end); + EXPECT_EQ(780, remove_range_end); + EXPECT_EQ(6, bytes_removed); + + // In the middle of the other ranges. + bytes_removed = GetRemovalRangeInMs(390, 990, 20, &remove_range_end); + EXPECT_EQ(990, remove_range_end); + EXPECT_EQ(12, bytes_removed); + + // A frame smaller than the whole ranges. + bytes_removed = GetRemovalRangeInMs(330, 1050, 20, &remove_range_end); + EXPECT_EQ(990, remove_range_end); + EXPECT_EQ(12, bytes_removed); + + // The same with the whole ranges. + bytes_removed = GetRemovalRangeInMs(300, 1080, 20, &remove_range_end); + EXPECT_EQ(1080, remove_range_end); + EXPECT_EQ(18, bytes_removed); + + // Larger than the whole ranges. + bytes_removed = GetRemovalRangeInMs(270, 1110, 20, &remove_range_end); + EXPECT_EQ(1080, remove_range_end); + EXPECT_EQ(18, bytes_removed); +} + TEST_F(SourceBufferStreamTest, ConfigChange_Basic) { VideoDecoderConfig new_config = TestVideoConfig::Large(); ASSERT_FALSE(new_config.Matches(config_)); |