| Index: media/filters/source_buffer_stream.cc
|
| diff --git a/media/filters/source_buffer_stream.cc b/media/filters/source_buffer_stream.cc
|
| index dba3b4c020fc3bb9e0b868e5dcb3c156ec13c4ee..4b344a3aaf6fc6d5692d28e3452829316ed658ab 100644
|
| --- a/media/filters/source_buffer_stream.cc
|
| +++ b/media/filters/source_buffer_stream.cc
|
| @@ -362,8 +362,6 @@ bool SourceBufferStream::Append(const BufferQueue& buffers) {
|
|
|
| SetSelectedRangeIfNeeded(next_buffer_timestamp);
|
|
|
| - GarbageCollectIfNeeded();
|
| -
|
| DVLOG(1) << __FUNCTION__ << " " << GetStreamTypeName()
|
| << ": done. ranges_=" << RangesToString(ranges_);
|
| DCHECK(IsRangeListSorted(ranges_));
|
| @@ -612,47 +610,85 @@ void SourceBufferStream::SetConfigIds(const BufferQueue& buffers) {
|
| }
|
| }
|
|
|
| -void SourceBufferStream::GarbageCollectIfNeeded() {
|
| +bool SourceBufferStream::GarbageCollectIfNeeded(DecodeTimestamp media_time,
|
| + size_t newDataSize) {
|
| + DCHECK(media_time != kNoDecodeTimestamp());
|
| // Compute size of |ranges_|.
|
| - size_t ranges_size = 0;
|
| - for (RangeList::iterator itr = ranges_.begin(); itr != ranges_.end(); ++itr)
|
| - ranges_size += (*itr)->size_in_bytes();
|
| + size_t ranges_size = GetBufferedSize();
|
| +
|
| + // Sanity and overflow checks
|
| + if ((newDataSize > memory_limit_) ||
|
| + (ranges_size + newDataSize < ranges_size))
|
| + return false;
|
|
|
| // Return if we're under or at the memory limit.
|
| - if (ranges_size <= memory_limit_)
|
| - return;
|
| + if (ranges_size + newDataSize <= memory_limit_)
|
| + return true;
|
|
|
| - size_t bytes_to_free = ranges_size - memory_limit_;
|
| + size_t bytes_to_free = ranges_size + newDataSize - memory_limit_;
|
|
|
| DVLOG(2) << __FUNCTION__ << " " << GetStreamTypeName() << ": Before GC"
|
| - << " ranges_size=" << ranges_size
|
| + << " media_time=" << media_time.InSecondsF()
|
| << " ranges_=" << RangesToString(ranges_)
|
| - << " memory_limit_=" << memory_limit_;
|
| + << " ranges_size=" << ranges_size
|
| + << " newDataSize=" << newDataSize
|
| + << " memory_limit_=" << memory_limit_
|
| + << " last_appended_buffer_timestamp_="
|
| + << last_appended_buffer_timestamp_.InSecondsF();
|
|
|
| - // Begin deleting after the last appended buffer.
|
| - size_t bytes_freed = FreeBuffersAfterLastAppended(bytes_to_free);
|
| + size_t bytes_freed = 0;
|
|
|
| - // Begin deleting from the front.
|
| - if (bytes_freed < bytes_to_free)
|
| - bytes_freed += FreeBuffers(bytes_to_free - bytes_freed, false);
|
| + // If last appended buffer position was earlier than the current playback time
|
| + // then try deleting data between last append and current media_time.
|
| + if (last_appended_buffer_timestamp_ != kNoDecodeTimestamp() &&
|
| + last_appended_buffer_timestamp_ < media_time) {
|
| + size_t between = FreeBuffersAfterLastAppended(bytes_to_free, media_time);
|
| + DVLOG(3) << __FUNCTION__ << " FreeBuffersAfterLastAppended "
|
| + << " released " << between << " bytes"
|
| + << " ranges_=" << RangesToString(ranges_);
|
| + bytes_freed += between;
|
| +
|
| + // If the last append happened before the current playback position
|
| + // |media_time|, then JS player is probably preparing to seek back and we
|
| + // should try to preserve all most recently appended data (which is in
|
| + // range_for_next_append_) from being removed by GC (see crbug.com/440173)
|
| + if (range_for_next_append_ != ranges_.end()) {
|
| + DCHECK((*range_for_next_append_)->GetStartTimestamp() <= media_time);
|
| + media_time = (*range_for_next_append_)->GetStartTimestamp();
|
| + }
|
| + }
|
|
|
| - // Begin deleting from the back.
|
| - if (bytes_freed < bytes_to_free)
|
| - bytes_freed += FreeBuffers(bytes_to_free - bytes_freed, true);
|
| + // Try removing data from the front of the SourceBuffer up to |media_time|
|
| + // position.
|
| + if (bytes_freed < bytes_to_free) {
|
| + size_t front = FreeBuffers(bytes_to_free - bytes_freed, media_time, false);
|
| + DVLOG(3) << __FUNCTION__ << " Removed " << front << " bytes from the front"
|
| + << " ranges_=" << RangesToString(ranges_);
|
| + bytes_freed += front;
|
| + }
|
| +
|
| + // Try removing data from the back of the SourceBuffer, until we reach the
|
| + // most recent append position.
|
| + if (bytes_freed < bytes_to_free) {
|
| + size_t back = FreeBuffers(bytes_to_free - bytes_freed, media_time, true);
|
| + DVLOG(3) << __FUNCTION__ << " Removed " << back << " bytes from the back"
|
| + << " ranges_=" << RangesToString(ranges_);
|
| + bytes_freed += back;
|
| + }
|
|
|
| DVLOG(2) << __FUNCTION__ << " " << GetStreamTypeName() << ": After GC"
|
| + << " bytes_to_free=" << bytes_to_free
|
| << " bytes_freed=" << bytes_freed
|
| << " ranges_=" << RangesToString(ranges_);
|
| +
|
| + return bytes_freed >= bytes_to_free;
|
| }
|
|
|
| size_t SourceBufferStream::FreeBuffersAfterLastAppended(
|
| - size_t total_bytes_to_free) {
|
| - DecodeTimestamp next_buffer_timestamp = GetNextBufferTimestamp();
|
| - if (last_appended_buffer_timestamp_ == kNoDecodeTimestamp() ||
|
| - next_buffer_timestamp == kNoDecodeTimestamp() ||
|
| - last_appended_buffer_timestamp_ >= next_buffer_timestamp) {
|
| - return 0;
|
| - }
|
| + size_t total_bytes_to_free, DecodeTimestamp media_time) {
|
| + DVLOG(4) << __FUNCTION__ << " last_appended_buffer_timestamp_="
|
| + << last_appended_buffer_timestamp_.InSecondsF()
|
| + << " media_time=" << media_time.InSecondsF();
|
|
|
| DecodeTimestamp remove_range_start = last_appended_buffer_timestamp_;
|
| if (last_appended_buffer_is_keyframe_)
|
| @@ -662,18 +698,21 @@ size_t SourceBufferStream::FreeBuffersAfterLastAppended(
|
| remove_range_start);
|
| if (remove_range_start_keyframe != kNoDecodeTimestamp())
|
| remove_range_start = remove_range_start_keyframe;
|
| - if (remove_range_start >= next_buffer_timestamp)
|
| + if (remove_range_start >= media_time)
|
| return 0;
|
|
|
| DecodeTimestamp remove_range_end;
|
| size_t bytes_freed = GetRemovalRange(remove_range_start,
|
| - next_buffer_timestamp,
|
| + media_time,
|
| total_bytes_to_free,
|
| &remove_range_end);
|
| if (bytes_freed > 0) {
|
| + DVLOG(4) << __FUNCTION__ << " removing ["
|
| + << remove_range_start.ToPresentationTime().InSecondsF() << ";"
|
| + << remove_range_end.ToPresentationTime().InSecondsF() << "]";
|
| Remove(remove_range_start.ToPresentationTime(),
|
| remove_range_end.ToPresentationTime(),
|
| - next_buffer_timestamp.ToPresentationTime());
|
| + media_time.ToPresentationTime());
|
| }
|
|
|
| return bytes_freed;
|
| @@ -706,6 +745,7 @@ size_t SourceBufferStream::GetRemovalRange(
|
| }
|
|
|
| size_t SourceBufferStream::FreeBuffers(size_t total_bytes_to_free,
|
| + DecodeTimestamp media_time,
|
| bool reverse_direction) {
|
| TRACE_EVENT2("media", "SourceBufferStream::FreeBuffers",
|
| "total bytes to free", total_bytes_to_free,
|
| @@ -725,17 +765,24 @@ size_t SourceBufferStream::FreeBuffers(size_t total_bytes_to_free,
|
|
|
| if (reverse_direction) {
|
| current_range = ranges_.back();
|
| + DVLOG(5) << "current_range=" << RangeToString(*current_range);
|
| if (current_range->LastGOPContainsNextBufferPosition()) {
|
| DCHECK_EQ(current_range, selected_range_);
|
| + DVLOG(5) << "current_range contains next read position, stopping GC";
|
| break;
|
| }
|
| + DVLOG(5) << "Deleting GOP from back: " << RangeToString(*current_range);
|
| bytes_deleted = current_range->DeleteGOPFromBack(&buffers);
|
| } else {
|
| current_range = ranges_.front();
|
| - if (current_range->FirstGOPContainsNextBufferPosition()) {
|
| - DCHECK_EQ(current_range, selected_range_);
|
| + DVLOG(5) << "current_range=" << RangeToString(*current_range);
|
| + if (!current_range->FirstGOPEarlierThanMediaTime(media_time)) {
|
| + // We have removed all data up to the GOP that contains current playback
|
| + // position, we can't delete any further.
|
| + DVLOG(5) << "current_range contains playback position, stopping GC";
|
| break;
|
| }
|
| + DVLOG(4) << "Deleting GOP from front: " << RangeToString(*current_range);
|
| bytes_deleted = current_range->DeleteGOPFromFront(&buffers);
|
| }
|
|
|
| @@ -744,6 +791,7 @@ size_t SourceBufferStream::FreeBuffers(size_t total_bytes_to_free,
|
| if (end_timestamp == last_appended_buffer_timestamp_) {
|
| DCHECK(last_appended_buffer_timestamp_ != kNoDecodeTimestamp());
|
| DCHECK(!new_range_for_append);
|
| +
|
| // Create a new range containing these buffers.
|
| new_range_for_append = new SourceBufferRange(
|
| TypeToGapPolicy(GetType()),
|
| @@ -762,6 +810,11 @@ size_t SourceBufferStream::FreeBuffers(size_t total_bytes_to_free,
|
| delete current_range;
|
| reverse_direction ? ranges_.pop_back() : ranges_.pop_front();
|
| }
|
| +
|
| + if (reverse_direction && new_range_for_append) {
|
| + // We don't want to delete any further, or we'll be creating gaps
|
| + break;
|
| + }
|
| }
|
|
|
| // Insert |new_range_for_append| into |ranges_|, if applicable.
|
| @@ -1221,8 +1274,10 @@ void SourceBufferStream::SeekAndSetSelectedRange(
|
| }
|
|
|
| void SourceBufferStream::SetSelectedRange(SourceBufferRange* range) {
|
| - DVLOG(1) << __FUNCTION__ << " " << GetStreamTypeName()
|
| - << ": " << selected_range_ << " -> " << range;
|
| + DVLOG(1) << __FUNCTION__ << " " << GetStreamTypeName() << ": "
|
| + << selected_range_ << " "
|
| + << (selected_range_ ? RangeToString(*selected_range_) : "")
|
| + << " -> " << range << " " << (range ? RangeToString(*range) : "");
|
| if (selected_range_)
|
| selected_range_->ResetNextBufferPosition();
|
| DCHECK(!range || range->HasNextBufferPosition());
|
| @@ -1246,6 +1301,13 @@ base::TimeDelta SourceBufferStream::GetBufferedDuration() const {
|
| return ranges_.back()->GetBufferedEndTimestamp().ToPresentationTime();
|
| }
|
|
|
| +size_t SourceBufferStream::GetBufferedSize() const {
|
| + size_t ranges_size = 0;
|
| + for (const auto& range : ranges_)
|
| + ranges_size += range->size_in_bytes();
|
| + return ranges_size;
|
| +}
|
| +
|
| void SourceBufferStream::MarkEndOfStream() {
|
| DCHECK(!end_of_stream_);
|
| end_of_stream_ = true;
|
|
|