Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(99)

Side by Side Diff: media/filters/chunk_demuxer.cc

Issue 1008463002: Fix MSE GC, make it less aggressive, more spec-compliant (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Added overflow check to sanity checks and use CHECK instead of DCHECK Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « media/filters/chunk_demuxer.h ('k') | media/filters/chunk_demuxer_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/filters/chunk_demuxer.h" 5 #include "media/filters/chunk_demuxer.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <limits> 8 #include <limits>
9 #include <list> 9 #include <list>
10 10
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
124 124
125 // Aborts the current append sequence and resets the parser. 125 // Aborts the current append sequence and resets the parser.
126 void Abort(TimeDelta append_window_start, 126 void Abort(TimeDelta append_window_start,
127 TimeDelta append_window_end, 127 TimeDelta append_window_end,
128 TimeDelta* timestamp_offset); 128 TimeDelta* timestamp_offset);
129 129
130 // Calls Remove(|start|, |end|, |duration|) on all 130 // Calls Remove(|start|, |end|, |duration|) on all
131 // ChunkDemuxerStreams managed by this object. 131 // ChunkDemuxerStreams managed by this object.
132 void Remove(TimeDelta start, TimeDelta end, TimeDelta duration); 132 void Remove(TimeDelta start, TimeDelta end, TimeDelta duration);
133 133
134 // If the buffer is full, attempts to try to free up space, as specified in
135 // the "Coded Frame Eviction Algorithm" in the Media Source Extensions Spec.
136 // Returns false iff buffer is still full after running eviction.
137 // https://w3c.github.io/media-source/#sourcebuffer-coded-frame-eviction
138 bool EvictCodedFrames(DecodeTimestamp media_time, size_t newDataSize);
139
134 // Returns true if currently parsing a media segment, or false otherwise. 140 // Returns true if currently parsing a media segment, or false otherwise.
135 bool parsing_media_segment() const { return parsing_media_segment_; } 141 bool parsing_media_segment() const { return parsing_media_segment_; }
136 142
137 // Sets |frame_processor_|'s sequence mode to |sequence_mode|. 143 // Sets |frame_processor_|'s sequence mode to |sequence_mode|.
138 void SetSequenceMode(bool sequence_mode); 144 void SetSequenceMode(bool sequence_mode);
139 145
140 // Signals the coded frame processor to update its group start timestamp to be 146 // Signals the coded frame processor to update its group start timestamp to be
141 // |timestamp_offset| if it is in sequence append mode. 147 // |timestamp_offset| if it is in sequence append mode.
142 void SetGroupStartTimestampIfInSequenceMode(base::TimeDelta timestamp_offset); 148 void SetGroupStartTimestampIfInSequenceMode(base::TimeDelta timestamp_offset);
143 149
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
188 // appending the processed frames to associated demuxer streams for each 194 // appending the processed frames to associated demuxer streams for each
189 // frame's track. 195 // frame's track.
190 // Returns true on a successful call. Returns false if an error occurred while 196 // Returns true on a successful call. Returns false if an error occurred while
191 // processing the buffers. 197 // processing the buffers.
192 bool OnNewBuffers(const StreamParser::BufferQueue& audio_buffers, 198 bool OnNewBuffers(const StreamParser::BufferQueue& audio_buffers,
193 const StreamParser::BufferQueue& video_buffers, 199 const StreamParser::BufferQueue& video_buffers,
194 const StreamParser::TextBufferQueueMap& text_map); 200 const StreamParser::TextBufferQueueMap& text_map);
195 201
196 void OnSourceInitDone(const StreamParser::InitParameters& params); 202 void OnSourceInitDone(const StreamParser::InitParameters& params);
197 203
204 // EstimateVideoDataSize uses some heuristics to estimate the size of the
205 // video size in the chunk of muxed audio/video data without parsing it.
206 // This is used by EvictCodedFrames algorithm, which happens before Append
207 // (and therefore before parsing is performed) to prepare space for new data.
208 size_t EstimateVideoDataSize(size_t muxed_data_chunk_size) const;
209
198 CreateDemuxerStreamCB create_demuxer_stream_cb_; 210 CreateDemuxerStreamCB create_demuxer_stream_cb_;
199 NewTextTrackCB new_text_track_cb_; 211 NewTextTrackCB new_text_track_cb_;
200 212
201 // During Append(), if OnNewBuffers() coded frame processing updates the 213 // During Append(), if OnNewBuffers() coded frame processing updates the
202 // timestamp offset then |*timestamp_offset_during_append_| is also updated 214 // timestamp offset then |*timestamp_offset_during_append_| is also updated
203 // so Append()'s caller can know the new offset. This pointer is only non-NULL 215 // so Append()'s caller can know the new offset. This pointer is only non-NULL
204 // during the lifetime of an Append() call. 216 // during the lifetime of an Append() call.
205 TimeDelta* timestamp_offset_during_append_; 217 TimeDelta* timestamp_offset_during_append_;
206 218
207 // During Append(), coded frame processing triggered by OnNewBuffers() 219 // During Append(), coded frame processing triggered by OnNewBuffers()
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after
362 374
363 if (video_) 375 if (video_)
364 video_->Remove(start, end, duration); 376 video_->Remove(start, end, duration);
365 377
366 for (TextStreamMap::iterator itr = text_stream_map_.begin(); 378 for (TextStreamMap::iterator itr = text_stream_map_.begin();
367 itr != text_stream_map_.end(); ++itr) { 379 itr != text_stream_map_.end(); ++itr) {
368 itr->second->Remove(start, end, duration); 380 itr->second->Remove(start, end, duration);
369 } 381 }
370 } 382 }
371 383
384 size_t SourceState::EstimateVideoDataSize(size_t muxed_data_chunk_size) const {
385 DCHECK(audio_);
386 DCHECK(video_);
387
388 size_t bufferedVideoSize = video_->GetBufferedSize();
389 size_t bufferedAudioSize = audio_->GetBufferedSize();
390 if (bufferedVideoSize == 0 || bufferedAudioSize == 0) {
391 // At this point either audio or video buffer is empty, which means buffer
392 // levels are probably low anyway and we should have enough space in the
393 // buffers for appending new data, so just take a very rough guess.
394 return muxed_data_chunk_size / 2;
395 }
396
397 // We need to estimate how much audio and video data is going to be in the
398 // newly appended data chunk to make space for the new data. And we need to do
399 // that without parsing the data (which will happen later, in the Append
400 // phase). So for now we can only rely on some heuristic here. Let's assume
401 // that the proportion of the audio/video in the new data chunk is the same as
402 // the current ratio of buffered audio/video.
403 // Longer term this should go away once we further change the MSE GC algorithm
404 // to work across all streams of a SourceBuffer (see crbug.com/520704).
405 CHECK(bufferedVideoSize + bufferedAudioSize > 0);
406 // Overflow check
407 CHECK(bufferedAudioSize <
wolenetz 2015/08/21 20:25:02 tl;dr off by one, but still lgtm. (bufferedAudioSi
408 std::numeric_limits<size_t>::max() - bufferedVideoSize);
409 size_t estimatedVideoData = (muxed_data_chunk_size * bufferedVideoSize) /
410 (bufferedVideoSize + bufferedAudioSize);
411 return estimatedVideoData;
412 }
413
414 bool SourceState::EvictCodedFrames(DecodeTimestamp media_time,
415 size_t newDataSize) {
416 bool success = true;
417
418 DVLOG(3) << __FUNCTION__ << " media_time=" << media_time.InSecondsF()
419 << " newDataSize=" << newDataSize
420 << " bufferedVideoSize=" << (video_ ? video_->GetBufferedSize() : 0)
421 << " bufferedAudioSize=" << (audio_ ? audio_->GetBufferedSize() : 0);
422
423 size_t newAudioSize = 0;
424 size_t newVideoSize = 0;
425 if (audio_ && video_) {
426 newVideoSize = EstimateVideoDataSize(newDataSize);
427 newAudioSize = newDataSize - newVideoSize;
428 } else if (video_) {
429 newVideoSize = newDataSize;
430 } else if (audio_) {
431 newAudioSize = newDataSize;
432 }
433
434 DVLOG(3) << __FUNCTION__ << " estimated audio/video sizes: "
435 << " newVideoSize=" << newVideoSize
436 << " newAudioSize=" << newAudioSize;
437
438 if (audio_)
439 success = audio_->EvictCodedFrames(media_time, newAudioSize) && success;
440
441 if (video_)
442 success = video_->EvictCodedFrames(media_time, newVideoSize) && success;
443
444 for (TextStreamMap::iterator itr = text_stream_map_.begin();
445 itr != text_stream_map_.end(); ++itr) {
446 success = itr->second->EvictCodedFrames(media_time, 0) && success;
447 }
448
449 DVLOG(3) << __FUNCTION__ << " result=" << success
450 << " bufferedVideoSize=" << (video_ ? video_->GetBufferedSize() : 0)
451 << " bufferedAudioSize=" << (audio_ ? audio_->GetBufferedSize() : 0);
452
453 return success;
454 }
455
372 Ranges<TimeDelta> SourceState::GetBufferedRanges(TimeDelta duration, 456 Ranges<TimeDelta> SourceState::GetBufferedRanges(TimeDelta duration,
373 bool ended) const { 457 bool ended) const {
374 // TODO(acolwell): When we start allowing disabled tracks we'll need to update 458 // TODO(acolwell): When we start allowing disabled tracks we'll need to update
375 // this code to only add ranges from active tracks. 459 // this code to only add ranges from active tracks.
376 RangesList ranges_list; 460 RangesList ranges_list;
377 if (audio_) 461 if (audio_)
378 ranges_list.push_back(audio_->GetBufferedRanges(duration)); 462 ranges_list.push_back(audio_->GetBufferedRanges(duration));
379 463
380 if (video_) 464 if (video_)
381 ranges_list.push_back(video_->GetBufferedRanges(duration)); 465 ranges_list.push_back(video_->GetBufferedRanges(duration));
(...skipping 490 matching lines...) Expand 10 before | Expand all | Expand 10 after
872 956
873 return true; 957 return true;
874 } 958 }
875 959
876 void ChunkDemuxerStream::Remove(TimeDelta start, TimeDelta end, 960 void ChunkDemuxerStream::Remove(TimeDelta start, TimeDelta end,
877 TimeDelta duration) { 961 TimeDelta duration) {
878 base::AutoLock auto_lock(lock_); 962 base::AutoLock auto_lock(lock_);
879 stream_->Remove(start, end, duration); 963 stream_->Remove(start, end, duration);
880 } 964 }
881 965
966 bool ChunkDemuxerStream::EvictCodedFrames(DecodeTimestamp media_time,
967 size_t newDataSize) {
968 base::AutoLock auto_lock(lock_);
969 return stream_->GarbageCollectIfNeeded(media_time, newDataSize);
970 }
971
882 void ChunkDemuxerStream::OnSetDuration(TimeDelta duration) { 972 void ChunkDemuxerStream::OnSetDuration(TimeDelta duration) {
883 base::AutoLock auto_lock(lock_); 973 base::AutoLock auto_lock(lock_);
884 stream_->OnSetDuration(duration); 974 stream_->OnSetDuration(duration);
885 } 975 }
886 976
887 Ranges<TimeDelta> ChunkDemuxerStream::GetBufferedRanges( 977 Ranges<TimeDelta> ChunkDemuxerStream::GetBufferedRanges(
888 TimeDelta duration) const { 978 TimeDelta duration) const {
889 base::AutoLock auto_lock(lock_); 979 base::AutoLock auto_lock(lock_);
890 980
891 if (type_ == TEXT) { 981 if (type_ == TEXT) {
(...skipping 16 matching lines...) Expand all
908 // range. 998 // range.
909 Ranges<TimeDelta> valid_time_range; 999 Ranges<TimeDelta> valid_time_range;
910 valid_time_range.Add(range.start(0), duration); 1000 valid_time_range.Add(range.start(0), duration);
911 return range.IntersectionWith(valid_time_range); 1001 return range.IntersectionWith(valid_time_range);
912 } 1002 }
913 1003
914 TimeDelta ChunkDemuxerStream::GetBufferedDuration() const { 1004 TimeDelta ChunkDemuxerStream::GetBufferedDuration() const {
915 return stream_->GetBufferedDuration(); 1005 return stream_->GetBufferedDuration();
916 } 1006 }
917 1007
1008 size_t ChunkDemuxerStream::GetBufferedSize() const {
1009 return stream_->GetBufferedSize();
1010 }
1011
918 void ChunkDemuxerStream::OnNewMediaSegment(DecodeTimestamp start_timestamp) { 1012 void ChunkDemuxerStream::OnNewMediaSegment(DecodeTimestamp start_timestamp) {
919 DVLOG(2) << "ChunkDemuxerStream::OnNewMediaSegment(" 1013 DVLOG(2) << "ChunkDemuxerStream::OnNewMediaSegment("
920 << start_timestamp.InSecondsF() << ")"; 1014 << start_timestamp.InSecondsF() << ")";
921 base::AutoLock auto_lock(lock_); 1015 base::AutoLock auto_lock(lock_);
922 stream_->OnNewMediaSegment(start_timestamp); 1016 stream_->OnNewMediaSegment(start_timestamp);
923 } 1017 }
924 1018
925 bool ChunkDemuxerStream::UpdateAudioConfig( 1019 bool ChunkDemuxerStream::UpdateAudioConfig(
926 const AudioDecoderConfig& config, 1020 const AudioDecoderConfig& config,
927 const scoped_refptr<MediaLog>& media_log) { 1021 const scoped_refptr<MediaLog>& media_log) {
(...skipping 386 matching lines...) Expand 10 before | Expand all | Expand 10 after
1314 Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges(const std::string& id) const { 1408 Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges(const std::string& id) const {
1315 base::AutoLock auto_lock(lock_); 1409 base::AutoLock auto_lock(lock_);
1316 DCHECK(!id.empty()); 1410 DCHECK(!id.empty());
1317 1411
1318 SourceStateMap::const_iterator itr = source_state_map_.find(id); 1412 SourceStateMap::const_iterator itr = source_state_map_.find(id);
1319 1413
1320 DCHECK(itr != source_state_map_.end()); 1414 DCHECK(itr != source_state_map_.end());
1321 return itr->second->GetBufferedRanges(duration_, state_ == ENDED); 1415 return itr->second->GetBufferedRanges(duration_, state_ == ENDED);
1322 } 1416 }
1323 1417
1418 bool ChunkDemuxer::EvictCodedFrames(const std::string& id,
1419 base::TimeDelta currentMediaTime,
1420 size_t newDataSize) {
1421 DVLOG(1) << __FUNCTION__ << "(" << id << ")"
1422 << " media_time=" << currentMediaTime.InSecondsF()
1423 << " newDataSize=" << newDataSize;
1424 base::AutoLock auto_lock(lock_);
1425
1426 // Note: The direct conversion from PTS to DTS is safe here, since we don't
1427 // need to know currentTime precisely for GC. GC only needs to know which GOP
1428 // currentTime points to.
1429 DecodeTimestamp media_time_dts =
1430 DecodeTimestamp::FromPresentationTime(currentMediaTime);
1431
1432 DCHECK(!id.empty());
1433 SourceStateMap::const_iterator itr = source_state_map_.find(id);
1434 if (itr == source_state_map_.end()) {
1435 LOG(WARNING) << __FUNCTION__ << " stream " << id << " not found";
1436 return false;
1437 }
1438 return itr->second->EvictCodedFrames(media_time_dts, newDataSize);
1439 }
1440
1324 void ChunkDemuxer::AppendData( 1441 void ChunkDemuxer::AppendData(
1325 const std::string& id, 1442 const std::string& id,
1326 const uint8* data, 1443 const uint8* data,
1327 size_t length, 1444 size_t length,
1328 TimeDelta append_window_start, 1445 TimeDelta append_window_start,
1329 TimeDelta append_window_end, 1446 TimeDelta append_window_end,
1330 TimeDelta* timestamp_offset, 1447 TimeDelta* timestamp_offset,
1331 const InitSegmentReceivedCB& init_segment_received_cb) { 1448 const InitSegmentReceivedCB& init_segment_received_cb) {
1332 DVLOG(1) << "AppendData(" << id << ", " << length << ")"; 1449 DVLOG(1) << "AppendData(" << id << ", " << length << ")";
1333 1450
(...skipping 500 matching lines...) Expand 10 before | Expand all | Expand 10 after
1834 } 1951 }
1835 1952
1836 void ChunkDemuxer::ShutdownAllStreams() { 1953 void ChunkDemuxer::ShutdownAllStreams() {
1837 for (SourceStateMap::iterator itr = source_state_map_.begin(); 1954 for (SourceStateMap::iterator itr = source_state_map_.begin();
1838 itr != source_state_map_.end(); ++itr) { 1955 itr != source_state_map_.end(); ++itr) {
1839 itr->second->Shutdown(); 1956 itr->second->Shutdown();
1840 } 1957 }
1841 } 1958 }
1842 1959
1843 } // namespace media 1960 } // namespace media
OLDNEW
« no previous file with comments | « media/filters/chunk_demuxer.h ('k') | media/filters/chunk_demuxer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698