Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 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.
| |
| 406 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.
| |
| 407 (bufferedVideoSize + bufferedAudioSize); | |
| 408 return estimatedVideoData; | |
| 409 } | |
| 410 | |
| 411 bool SourceState::EvictCodedFrames(DecodeTimestamp media_time, | |
| 412 size_t newDataSize) { | |
| 413 bool success = true; | |
| 414 | |
| 415 DVLOG(3) << __FUNCTION__ << " media_time=" << media_time.InSecondsF() | |
| 416 << " newDataSize=" << newDataSize | |
| 417 << " bufferedVideoSize=" << (video_ ? video_->GetBufferedSize() : 0) | |
| 418 << " bufferedAudioSize=" << (audio_ ? audio_->GetBufferedSize() : 0); | |
| 419 | |
| 420 size_t newAudioSize = 0; | |
| 421 size_t newVideoSize = 0; | |
| 422 if (audio_ && video_) { | |
| 423 newVideoSize = EstimateVideoDataSize(newDataSize); | |
| 424 newAudioSize = newDataSize - newVideoSize; | |
| 425 } else if (video_) { | |
| 426 newVideoSize = newDataSize; | |
| 427 } else if (audio_) { | |
| 428 newAudioSize = newDataSize; | |
| 429 } | |
| 430 | |
| 431 DVLOG(3) << __FUNCTION__ << " estimated audio/video sizes: " | |
| 432 << " newVideoSize=" << newVideoSize | |
| 433 << " newAudioSize=" << newAudioSize; | |
| 434 | |
| 435 if (audio_) | |
| 436 success = audio_->EvictCodedFrames(media_time, newAudioSize) && success; | |
| 437 | |
| 438 if (video_) | |
| 439 success = video_->EvictCodedFrames(media_time, newVideoSize) && success; | |
| 440 | |
| 441 for (TextStreamMap::iterator itr = text_stream_map_.begin(); | |
| 442 itr != text_stream_map_.end(); ++itr) { | |
| 443 success = itr->second->EvictCodedFrames(media_time, 0) && success; | |
| 444 } | |
| 445 | |
| 446 DVLOG(3) << __FUNCTION__ << " result=" << success | |
| 447 << " bufferedVideoSize=" << (video_ ? video_->GetBufferedSize() : 0) | |
| 448 << " bufferedAudioSize=" << (audio_ ? audio_->GetBufferedSize() : 0); | |
| 449 | |
| 450 return success; | |
| 451 } | |
| 452 | |
| 372 Ranges<TimeDelta> SourceState::GetBufferedRanges(TimeDelta duration, | 453 Ranges<TimeDelta> SourceState::GetBufferedRanges(TimeDelta duration, |
| 373 bool ended) const { | 454 bool ended) const { |
| 374 // TODO(acolwell): When we start allowing disabled tracks we'll need to update | 455 // TODO(acolwell): When we start allowing disabled tracks we'll need to update |
| 375 // this code to only add ranges from active tracks. | 456 // this code to only add ranges from active tracks. |
| 376 RangesList ranges_list; | 457 RangesList ranges_list; |
| 377 if (audio_) | 458 if (audio_) |
| 378 ranges_list.push_back(audio_->GetBufferedRanges(duration)); | 459 ranges_list.push_back(audio_->GetBufferedRanges(duration)); |
| 379 | 460 |
| 380 if (video_) | 461 if (video_) |
| 381 ranges_list.push_back(video_->GetBufferedRanges(duration)); | 462 ranges_list.push_back(video_->GetBufferedRanges(duration)); |
| (...skipping 490 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 872 | 953 |
| 873 return true; | 954 return true; |
| 874 } | 955 } |
| 875 | 956 |
| 876 void ChunkDemuxerStream::Remove(TimeDelta start, TimeDelta end, | 957 void ChunkDemuxerStream::Remove(TimeDelta start, TimeDelta end, |
| 877 TimeDelta duration) { | 958 TimeDelta duration) { |
| 878 base::AutoLock auto_lock(lock_); | 959 base::AutoLock auto_lock(lock_); |
| 879 stream_->Remove(start, end, duration); | 960 stream_->Remove(start, end, duration); |
| 880 } | 961 } |
| 881 | 962 |
| 963 bool ChunkDemuxerStream::EvictCodedFrames(DecodeTimestamp media_time, | |
| 964 size_t newDataSize) { | |
| 965 base::AutoLock auto_lock(lock_); | |
| 966 return stream_->GarbageCollectIfNeeded(media_time, newDataSize); | |
| 967 } | |
| 968 | |
| 882 void ChunkDemuxerStream::OnSetDuration(TimeDelta duration) { | 969 void ChunkDemuxerStream::OnSetDuration(TimeDelta duration) { |
| 883 base::AutoLock auto_lock(lock_); | 970 base::AutoLock auto_lock(lock_); |
| 884 stream_->OnSetDuration(duration); | 971 stream_->OnSetDuration(duration); |
| 885 } | 972 } |
| 886 | 973 |
| 887 Ranges<TimeDelta> ChunkDemuxerStream::GetBufferedRanges( | 974 Ranges<TimeDelta> ChunkDemuxerStream::GetBufferedRanges( |
| 888 TimeDelta duration) const { | 975 TimeDelta duration) const { |
| 889 base::AutoLock auto_lock(lock_); | 976 base::AutoLock auto_lock(lock_); |
| 890 | 977 |
| 891 if (type_ == TEXT) { | 978 if (type_ == TEXT) { |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 908 // range. | 995 // range. |
| 909 Ranges<TimeDelta> valid_time_range; | 996 Ranges<TimeDelta> valid_time_range; |
| 910 valid_time_range.Add(range.start(0), duration); | 997 valid_time_range.Add(range.start(0), duration); |
| 911 return range.IntersectionWith(valid_time_range); | 998 return range.IntersectionWith(valid_time_range); |
| 912 } | 999 } |
| 913 | 1000 |
| 914 TimeDelta ChunkDemuxerStream::GetBufferedDuration() const { | 1001 TimeDelta ChunkDemuxerStream::GetBufferedDuration() const { |
| 915 return stream_->GetBufferedDuration(); | 1002 return stream_->GetBufferedDuration(); |
| 916 } | 1003 } |
| 917 | 1004 |
| 1005 size_t ChunkDemuxerStream::GetBufferedSize() const { | |
| 1006 return stream_->GetBufferedSize(); | |
| 1007 } | |
| 1008 | |
| 918 void ChunkDemuxerStream::OnNewMediaSegment(DecodeTimestamp start_timestamp) { | 1009 void ChunkDemuxerStream::OnNewMediaSegment(DecodeTimestamp start_timestamp) { |
| 919 DVLOG(2) << "ChunkDemuxerStream::OnNewMediaSegment(" | 1010 DVLOG(2) << "ChunkDemuxerStream::OnNewMediaSegment(" |
| 920 << start_timestamp.InSecondsF() << ")"; | 1011 << start_timestamp.InSecondsF() << ")"; |
| 921 base::AutoLock auto_lock(lock_); | 1012 base::AutoLock auto_lock(lock_); |
| 922 stream_->OnNewMediaSegment(start_timestamp); | 1013 stream_->OnNewMediaSegment(start_timestamp); |
| 923 } | 1014 } |
| 924 | 1015 |
| 925 bool ChunkDemuxerStream::UpdateAudioConfig( | 1016 bool ChunkDemuxerStream::UpdateAudioConfig( |
| 926 const AudioDecoderConfig& config, | 1017 const AudioDecoderConfig& config, |
| 927 const scoped_refptr<MediaLog>& media_log) { | 1018 const scoped_refptr<MediaLog>& media_log) { |
| (...skipping 386 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1314 Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges(const std::string& id) const { | 1405 Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges(const std::string& id) const { |
| 1315 base::AutoLock auto_lock(lock_); | 1406 base::AutoLock auto_lock(lock_); |
| 1316 DCHECK(!id.empty()); | 1407 DCHECK(!id.empty()); |
| 1317 | 1408 |
| 1318 SourceStateMap::const_iterator itr = source_state_map_.find(id); | 1409 SourceStateMap::const_iterator itr = source_state_map_.find(id); |
| 1319 | 1410 |
| 1320 DCHECK(itr != source_state_map_.end()); | 1411 DCHECK(itr != source_state_map_.end()); |
| 1321 return itr->second->GetBufferedRanges(duration_, state_ == ENDED); | 1412 return itr->second->GetBufferedRanges(duration_, state_ == ENDED); |
| 1322 } | 1413 } |
| 1323 | 1414 |
| 1415 bool ChunkDemuxer::EvictCodedFrames(const std::string& id, | |
| 1416 base::TimeDelta currentMediaTime, | |
| 1417 size_t newDataSize) { | |
| 1418 DVLOG(1) << __FUNCTION__ << "(" << id << ")" | |
| 1419 << " media_time=" << currentMediaTime.InSecondsF() | |
| 1420 << " newDataSize=" << newDataSize; | |
| 1421 base::AutoLock auto_lock(lock_); | |
| 1422 | |
| 1423 // Note: The direct conversion from PTS to DTS is safe here, since we don't | |
| 1424 // need to know currentTime precisely for GC. GC only needs to know which GOP | |
| 1425 // currentTime points to. | |
| 1426 DecodeTimestamp media_time_dts = | |
| 1427 DecodeTimestamp::FromPresentationTime(currentMediaTime); | |
| 1428 | |
| 1429 DCHECK(!id.empty()); | |
| 1430 SourceStateMap::const_iterator itr = source_state_map_.find(id); | |
| 1431 if (itr == source_state_map_.end()) { | |
| 1432 LOG(WARNING) << __FUNCTION__ << " stream " << id << " not found"; | |
| 1433 return false; | |
| 1434 } | |
| 1435 return itr->second->EvictCodedFrames(media_time_dts, newDataSize); | |
| 1436 } | |
| 1437 | |
| 1324 void ChunkDemuxer::AppendData( | 1438 void ChunkDemuxer::AppendData( |
| 1325 const std::string& id, | 1439 const std::string& id, |
| 1326 const uint8* data, | 1440 const uint8* data, |
| 1327 size_t length, | 1441 size_t length, |
| 1328 TimeDelta append_window_start, | 1442 TimeDelta append_window_start, |
| 1329 TimeDelta append_window_end, | 1443 TimeDelta append_window_end, |
| 1330 TimeDelta* timestamp_offset, | 1444 TimeDelta* timestamp_offset, |
| 1331 const InitSegmentReceivedCB& init_segment_received_cb) { | 1445 const InitSegmentReceivedCB& init_segment_received_cb) { |
| 1332 DVLOG(1) << "AppendData(" << id << ", " << length << ")"; | 1446 DVLOG(1) << "AppendData(" << id << ", " << length << ")"; |
| 1333 | 1447 |
| (...skipping 500 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1834 } | 1948 } |
| 1835 | 1949 |
| 1836 void ChunkDemuxer::ShutdownAllStreams() { | 1950 void ChunkDemuxer::ShutdownAllStreams() { |
| 1837 for (SourceStateMap::iterator itr = source_state_map_.begin(); | 1951 for (SourceStateMap::iterator itr = source_state_map_.begin(); |
| 1838 itr != source_state_map_.end(); ++itr) { | 1952 itr != source_state_map_.end(); ++itr) { |
| 1839 itr->second->Shutdown(); | 1953 itr->second->Shutdown(); |
| 1840 } | 1954 } |
| 1841 } | 1955 } |
| 1842 | 1956 |
| 1843 } // namespace media | 1957 } // namespace media |
| OLD | NEW |