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, |