 Chromium Code Reviews
 Chromium Code Reviews Issue 1008463002:
  Fix MSE GC, make it less aggressive, more spec-compliant  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master
    
  
    Issue 1008463002:
  Fix MSE GC, make it less aggressive, more spec-compliant  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master| Index: media/filters/chunk_demuxer.cc | 
| diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc | 
| index 51511c8f69ad9dff37773d5d147c54b993211786..45db656e8e42aad9e2c9fb92c4c3f881fabe3c56 100644 | 
| --- a/media/filters/chunk_demuxer.cc | 
| +++ b/media/filters/chunk_demuxer.cc | 
| @@ -131,6 +131,12 @@ class SourceState { | 
| // ChunkDemuxerStreams managed by this object. | 
| void Remove(TimeDelta start, TimeDelta end, TimeDelta duration); | 
| + // If the buffer is full, attempts to try to free up space, as specified in | 
| + // the "Coded Frame Eviction Algorithm" in the Media Source Extensions Spec. | 
| + // Returns false iff buffer is still full after running eviction. | 
| + // https://w3c.github.io/media-source/#sourcebuffer-coded-frame-eviction | 
| + bool EvictCodedFrames(DecodeTimestamp media_time, size_t newDataSize); | 
| + | 
| // Returns true if currently parsing a media segment, or false otherwise. | 
| bool parsing_media_segment() const { return parsing_media_segment_; } | 
| @@ -195,6 +201,12 @@ class SourceState { | 
| void OnSourceInitDone(const StreamParser::InitParameters& params); | 
| + // EstimateVideoDataSize uses some heuristics to estimate the size of the | 
| + // video size in the chunk of muxed audio/video data without parsing it. | 
| + // This is used by EvictCodedFrames algorithm, which happens before Append | 
| + // (and therefore before parsing is performed) to prepare space for new data. | 
| + size_t EstimateVideoDataSize(size_t muxed_data_chunk_size) const; | 
| + | 
| CreateDemuxerStreamCB create_demuxer_stream_cb_; | 
| NewTextTrackCB new_text_track_cb_; | 
| @@ -369,6 +381,75 @@ void SourceState::Remove(TimeDelta start, TimeDelta end, TimeDelta duration) { | 
| } | 
| } | 
| +size_t SourceState::EstimateVideoDataSize(size_t muxed_data_chunk_size) const { | 
| + DCHECK(audio_); | 
| + DCHECK(video_); | 
| + | 
| + size_t bufferedVideoSize = video_->GetBufferedSize(); | 
| + size_t bufferedAudioSize = audio_->GetBufferedSize(); | 
| + if (bufferedVideoSize == 0 || bufferedAudioSize == 0) { | 
| + // At this point either audio or video buffer is empty, which means buffer | 
| + // levels are probably low anyway and we should have enough space in the | 
| + // buffers for appending new data, so just take a very rough guess. | 
| + return muxed_data_chunk_size / 2; | 
| + } | 
| + | 
| + // We need to estimate how much audio and video data is going to be in the | 
| + // newly appended data chunk to make space for the new data. And we need to do | 
| + // that without parsing the data (which will happen later, in the Append | 
| + // phase). So for now we can only rely on some heuristic here. Let's assume | 
| + // that the proportion of the audio/video in the new data chunk is the same as | 
| + // the current ratio of buffered audio/video. | 
| + // Longer term this should go away once we further change the MSE GC algorithm | 
| + // to work across all streams of a SourceBuffer (see crbug.com/520704). | 
| + DCHECK(bufferedVideoSize + bufferedAudioSize > 0); | 
| 
wolenetz
2015/08/21 18:15:57
Hmm. size_t can't be negative. So this is only che
 
servolk
2015/08/21 18:19:14
Right
 
servolk
2015/08/21 19:06:45
(this is just a sanity check, since we are going t
 
wolenetz
2015/08/21 19:39:37
Acknowledged.
Let's please strengthen sanity here
 
servolk
2015/08/21 20:16:54
Done.
 | 
| + size_t estimatedVideoData = muxed_data_chunk_size * bufferedVideoSize / | 
| 
wolenetz
2015/08/21 18:15:56
not lgtm:
Working with integers, this may not do w
 
servolk
2015/08/21 18:19:14
I believe it would be calculated as (10 * 51) / 10
 
servolk
2015/08/21 19:06:45
Since I had to upload a new patchset with the upda
 
wolenetz
2015/08/21 19:39:37
Acknowledged.
 | 
| + (bufferedVideoSize + bufferedAudioSize); | 
| + return estimatedVideoData; | 
| +} | 
| + | 
| +bool SourceState::EvictCodedFrames(DecodeTimestamp media_time, | 
| + size_t newDataSize) { | 
| + bool success = true; | 
| + | 
| + DVLOG(3) << __FUNCTION__ << " media_time=" << media_time.InSecondsF() | 
| + << " newDataSize=" << newDataSize | 
| + << " bufferedVideoSize=" << (video_ ? video_->GetBufferedSize() : 0) | 
| + << " bufferedAudioSize=" << (audio_ ? audio_->GetBufferedSize() : 0); | 
| + | 
| + size_t newAudioSize = 0; | 
| + size_t newVideoSize = 0; | 
| + if (audio_ && video_) { | 
| + newVideoSize = EstimateVideoDataSize(newDataSize); | 
| + newAudioSize = newDataSize - newVideoSize; | 
| + } else if (video_) { | 
| + newVideoSize = newDataSize; | 
| + } else if (audio_) { | 
| + newAudioSize = newDataSize; | 
| + } | 
| + | 
| + DVLOG(3) << __FUNCTION__ << " estimated audio/video sizes: " | 
| + << " newVideoSize=" << newVideoSize | 
| + << " newAudioSize=" << newAudioSize; | 
| + | 
| + if (audio_) | 
| + success = audio_->EvictCodedFrames(media_time, newAudioSize) && success; | 
| + | 
| + if (video_) | 
| + success = video_->EvictCodedFrames(media_time, newVideoSize) && success; | 
| + | 
| + for (TextStreamMap::iterator itr = text_stream_map_.begin(); | 
| + itr != text_stream_map_.end(); ++itr) { | 
| + success = itr->second->EvictCodedFrames(media_time, 0) && success; | 
| + } | 
| + | 
| + DVLOG(3) << __FUNCTION__ << " result=" << success | 
| + << " bufferedVideoSize=" << (video_ ? video_->GetBufferedSize() : 0) | 
| + << " bufferedAudioSize=" << (audio_ ? audio_->GetBufferedSize() : 0); | 
| + | 
| + return success; | 
| +} | 
| + | 
| Ranges<TimeDelta> SourceState::GetBufferedRanges(TimeDelta duration, | 
| bool ended) const { | 
| // TODO(acolwell): When we start allowing disabled tracks we'll need to update | 
| @@ -879,6 +960,12 @@ void ChunkDemuxerStream::Remove(TimeDelta start, TimeDelta end, | 
| stream_->Remove(start, end, duration); | 
| } | 
| +bool ChunkDemuxerStream::EvictCodedFrames(DecodeTimestamp media_time, | 
| + size_t newDataSize) { | 
| + base::AutoLock auto_lock(lock_); | 
| + return stream_->GarbageCollectIfNeeded(media_time, newDataSize); | 
| +} | 
| + | 
| void ChunkDemuxerStream::OnSetDuration(TimeDelta duration) { | 
| base::AutoLock auto_lock(lock_); | 
| stream_->OnSetDuration(duration); | 
| @@ -915,6 +1002,10 @@ TimeDelta ChunkDemuxerStream::GetBufferedDuration() const { | 
| return stream_->GetBufferedDuration(); | 
| } | 
| +size_t ChunkDemuxerStream::GetBufferedSize() const { | 
| + return stream_->GetBufferedSize(); | 
| +} | 
| + | 
| void ChunkDemuxerStream::OnNewMediaSegment(DecodeTimestamp start_timestamp) { | 
| DVLOG(2) << "ChunkDemuxerStream::OnNewMediaSegment(" | 
| << start_timestamp.InSecondsF() << ")"; | 
| @@ -1321,6 +1412,29 @@ Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges(const std::string& id) const { | 
| return itr->second->GetBufferedRanges(duration_, state_ == ENDED); | 
| } | 
| +bool ChunkDemuxer::EvictCodedFrames(const std::string& id, | 
| + base::TimeDelta currentMediaTime, | 
| + size_t newDataSize) { | 
| + DVLOG(1) << __FUNCTION__ << "(" << id << ")" | 
| + << " media_time=" << currentMediaTime.InSecondsF() | 
| + << " newDataSize=" << newDataSize; | 
| + base::AutoLock auto_lock(lock_); | 
| + | 
| + // Note: The direct conversion from PTS to DTS is safe here, since we don't | 
| + // need to know currentTime precisely for GC. GC only needs to know which GOP | 
| + // currentTime points to. | 
| + DecodeTimestamp media_time_dts = | 
| + DecodeTimestamp::FromPresentationTime(currentMediaTime); | 
| + | 
| + DCHECK(!id.empty()); | 
| + SourceStateMap::const_iterator itr = source_state_map_.find(id); | 
| + if (itr == source_state_map_.end()) { | 
| + LOG(WARNING) << __FUNCTION__ << " stream " << id << " not found"; | 
| + return false; | 
| + } | 
| + return itr->second->EvictCodedFrames(media_time_dts, newDataSize); | 
| +} | 
| + | 
| void ChunkDemuxer::AppendData( | 
| const std::string& id, | 
| const uint8* data, |