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 |
| 11 #include "base/bind.h" | 11 #include "base/bind.h" |
| 12 #include "base/callback_helpers.h" | 12 #include "base/callback_helpers.h" |
| 13 #include "base/location.h" | 13 #include "base/location.h" |
| 14 #include "base/message_loop/message_loop_proxy.h" | 14 #include "base/message_loop/message_loop_proxy.h" |
| 15 #include "base/stl_util.h" | 15 #include "base/stl_util.h" |
| 16 #include "media/base/audio_decoder_config.h" | 16 #include "media/base/audio_decoder_config.h" |
| 17 #include "media/base/bind_to_current_loop.h" | 17 #include "media/base/bind_to_current_loop.h" |
| 18 #include "media/base/stream_parser_buffer.h" | 18 #include "media/base/stream_parser_buffer.h" |
| 19 #include "media/base/video_decoder_config.h" | 19 #include "media/base/video_decoder_config.h" |
| 20 #include "media/filters/legacy_frame_processor.h" | 20 #include "media/filters/legacy_frame_processor.h" |
| 21 #include "media/filters/stream_parser_factory.h" | 21 #include "media/filters/stream_parser_factory.h" |
| 22 | 22 |
| 23 using base::TimeDelta; | 23 using base::TimeDelta; |
| 24 | 24 |
| 25 namespace media { | 25 namespace media { |
| 26 | 26 |
| 27 // Compute the end timestamp for buffers within an append window range. | |
| 28 // TODO(dalecurtis): This code should probably live in the frame processor, but | |
| 29 // requires some spec updates before we can do so. For now it's a logical clone | |
| 30 // of LegacyFrameProcessor::FilterWithAppendWindow(). | |
| 31 static TimeDelta EndTimestamp(const StreamParser::BufferQueue& buffers, | |
| 32 TimeDelta append_window_start, | |
| 33 TimeDelta append_window_end) { | |
| 34 TimeDelta end_timestamp = kNoTimestamp(); | |
| 35 | |
| 36 // Find the last buffer within the append window range. | |
| 37 for (StreamParser::BufferQueue::const_reverse_iterator itr = buffers.rbegin(); | |
| 38 itr != buffers.rend(); | |
| 39 ++itr) { | |
| 40 const scoped_refptr<StreamParserBuffer>& buffer = *itr; | |
| 41 if (buffer->timestamp() >= append_window_start && | |
| 42 buffer->timestamp() <= append_window_end) { | |
| 43 end_timestamp = buffer->timestamp() + buffer->duration(); | |
| 44 break; | |
| 45 } | |
| 46 } | |
| 47 | |
| 48 return end_timestamp; | |
| 49 } | |
| 50 | |
| 27 // List of time ranges for each SourceBuffer. | 51 // List of time ranges for each SourceBuffer. |
| 28 typedef std::list<Ranges<TimeDelta> > RangesList; | 52 typedef std::list<Ranges<TimeDelta> > RangesList; |
| 29 static Ranges<TimeDelta> ComputeIntersection(const RangesList& activeRanges, | 53 static Ranges<TimeDelta> ComputeIntersection(const RangesList& activeRanges, |
| 30 bool ended) { | 54 bool ended) { |
| 31 // Implementation of HTMLMediaElement.buffered algorithm in MSE spec. | 55 // Implementation of HTMLMediaElement.buffered algorithm in MSE spec. |
| 32 // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-sourc e.html#dom-htmlmediaelement.buffered | 56 // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-sourc e.html#dom-htmlmediaelement.buffered |
| 33 | 57 |
| 34 // Step 1: If activeSourceBuffers.length equals 0 then return an empty | 58 // Step 1: If activeSourceBuffers.length equals 0 then return an empty |
| 35 // TimeRanges object and abort these steps. | 59 // TimeRanges object and abort these steps. |
| 36 if (activeRanges.empty()) | 60 if (activeRanges.empty()) |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 82 // Contains state belonging to a source id. | 106 // Contains state belonging to a source id. |
| 83 class SourceState { | 107 class SourceState { |
| 84 public: | 108 public: |
| 85 // Callback signature used to create ChunkDemuxerStreams. | 109 // Callback signature used to create ChunkDemuxerStreams. |
| 86 typedef base::Callback<ChunkDemuxerStream*( | 110 typedef base::Callback<ChunkDemuxerStream*( |
| 87 DemuxerStream::Type)> CreateDemuxerStreamCB; | 111 DemuxerStream::Type)> CreateDemuxerStreamCB; |
| 88 | 112 |
| 89 typedef base::Callback<void( | 113 typedef base::Callback<void( |
| 90 ChunkDemuxerStream*, const TextTrackConfig&)> NewTextTrackCB; | 114 ChunkDemuxerStream*, const TextTrackConfig&)> NewTextTrackCB; |
| 91 | 115 |
| 116 // First parameter - Indicates initialization success. Set to true if | |
| 117 // initialization was successful. False if an error | |
| 118 // occurred. | |
| 119 // Second parameter - Indicates the stream duration. Only contains a valid | |
| 120 // value if the first parameter is true. | |
| 121 typedef base::Callback<void(bool, TimeDelta)> InitCB; | |
| 122 | |
| 92 SourceState( | 123 SourceState( |
| 93 scoped_ptr<StreamParser> stream_parser, | 124 scoped_ptr<StreamParser> stream_parser, |
| 94 scoped_ptr<FrameProcessorBase> frame_processor, const LogCB& log_cb, | 125 scoped_ptr<FrameProcessorBase> frame_processor, const LogCB& log_cb, |
| 95 const CreateDemuxerStreamCB& create_demuxer_stream_cb); | 126 const CreateDemuxerStreamCB& create_demuxer_stream_cb); |
| 96 | 127 |
| 97 ~SourceState(); | 128 ~SourceState(); |
| 98 | 129 |
| 99 void Init(const StreamParser::InitCB& init_cb, | 130 void Init(const InitCB& init_cb, |
| 100 bool allow_audio, | 131 bool allow_audio, |
| 101 bool allow_video, | 132 bool allow_video, |
| 102 const StreamParser::NeedKeyCB& need_key_cb, | 133 const StreamParser::NeedKeyCB& need_key_cb, |
| 103 const NewTextTrackCB& new_text_track_cb); | 134 const NewTextTrackCB& new_text_track_cb); |
| 104 | 135 |
| 105 // Appends new data to the StreamParser. | 136 // Appends new data to the StreamParser. |
| 106 // Returns true if the data was successfully appended. Returns false if an | 137 // Returns true if the data was successfully appended. Returns false if an |
| 107 // error occurred. |*timestamp_offset| is used and possibly updated by the | 138 // error occurred. |*timestamp_offset| is used and possibly updated by the |
| 108 // append. |append_window_start| and |append_window_end| correspond to the MSE | 139 // append. |append_window_start| and |append_window_end| correspond to the MSE |
| 109 // spec's similarly named source buffer attributes that are used in coded | 140 // spec's similarly named source buffer attributes that are used in coded |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 170 // Called by the |stream_parser_| when new buffers have been parsed. | 201 // Called by the |stream_parser_| when new buffers have been parsed. |
| 171 // It processes the new buffers using |frame_processor_|, which includes | 202 // It processes the new buffers using |frame_processor_|, which includes |
| 172 // appending the processed frames to associated demuxer streams for each | 203 // appending the processed frames to associated demuxer streams for each |
| 173 // frame's track. | 204 // frame's track. |
| 174 // Returns true on a successful call. Returns false if an error occurred while | 205 // Returns true on a successful call. Returns false if an error occurred while |
| 175 // processing the buffers. | 206 // processing the buffers. |
| 176 bool OnNewBuffers(const StreamParser::BufferQueue& audio_buffers, | 207 bool OnNewBuffers(const StreamParser::BufferQueue& audio_buffers, |
| 177 const StreamParser::BufferQueue& video_buffers, | 208 const StreamParser::BufferQueue& video_buffers, |
| 178 const StreamParser::TextBufferQueueMap& text_map); | 209 const StreamParser::TextBufferQueueMap& text_map); |
| 179 | 210 |
| 211 void OnSourceInitDone(bool success, | |
| 212 TimeDelta duration, | |
| 213 bool auto_update_timestamp_offset); | |
| 214 | |
| 180 CreateDemuxerStreamCB create_demuxer_stream_cb_; | 215 CreateDemuxerStreamCB create_demuxer_stream_cb_; |
| 181 NewTextTrackCB new_text_track_cb_; | 216 NewTextTrackCB new_text_track_cb_; |
| 182 | 217 |
| 183 // During Append(), if OnNewBuffers() coded frame processing updates the | 218 // During Append(), if OnNewBuffers() coded frame processing updates the |
| 184 // timestamp offset then |*timestamp_offset_during_append_| is also updated | 219 // timestamp offset then |*timestamp_offset_during_append_| is also updated |
| 185 // so Append()'s caller can know the new offset. This pointer is only non-NULL | 220 // so Append()'s caller can know the new offset. This pointer is only non-NULL |
| 186 // during the lifetime of an Append() call. | 221 // during the lifetime of an Append() call. |
| 187 TimeDelta* timestamp_offset_during_append_; | 222 TimeDelta* timestamp_offset_during_append_; |
| 188 | 223 |
| 189 // During Append(), coded frame processing triggered by OnNewBuffers() | 224 // During Append(), coded frame processing triggered by OnNewBuffers() |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 209 scoped_ptr<StreamParser> stream_parser_; | 244 scoped_ptr<StreamParser> stream_parser_; |
| 210 | 245 |
| 211 ChunkDemuxerStream* audio_; // Not owned by |this|. | 246 ChunkDemuxerStream* audio_; // Not owned by |this|. |
| 212 ChunkDemuxerStream* video_; // Not owned by |this|. | 247 ChunkDemuxerStream* video_; // Not owned by |this|. |
| 213 | 248 |
| 214 typedef std::map<StreamParser::TrackId, ChunkDemuxerStream*> TextStreamMap; | 249 typedef std::map<StreamParser::TrackId, ChunkDemuxerStream*> TextStreamMap; |
| 215 TextStreamMap text_stream_map_; // |this| owns the map's stream pointers. | 250 TextStreamMap text_stream_map_; // |this| owns the map's stream pointers. |
| 216 | 251 |
| 217 scoped_ptr<FrameProcessorBase> frame_processor_; | 252 scoped_ptr<FrameProcessorBase> frame_processor_; |
| 218 LogCB log_cb_; | 253 LogCB log_cb_; |
| 254 InitCB init_cb_; | |
| 255 | |
| 256 // Indicates that timestampOffset should be updated automatically during | |
| 257 // OnNewBuffers() based on the earliest end timestamp of the buffers provided. | |
| 258 bool auto_update_timestamp_offset_; | |
| 219 | 259 |
| 220 DISALLOW_COPY_AND_ASSIGN(SourceState); | 260 DISALLOW_COPY_AND_ASSIGN(SourceState); |
| 221 }; | 261 }; |
| 222 | 262 |
| 223 SourceState::SourceState( | 263 SourceState::SourceState( |
| 224 scoped_ptr<StreamParser> stream_parser, | 264 scoped_ptr<StreamParser> stream_parser, |
| 225 scoped_ptr<FrameProcessorBase> frame_processor, | 265 scoped_ptr<FrameProcessorBase> frame_processor, |
| 226 const LogCB& log_cb, | 266 const LogCB& log_cb, |
| 227 const CreateDemuxerStreamCB& create_demuxer_stream_cb) | 267 const CreateDemuxerStreamCB& create_demuxer_stream_cb) |
| 228 : create_demuxer_stream_cb_(create_demuxer_stream_cb), | 268 : create_demuxer_stream_cb_(create_demuxer_stream_cb), |
| 229 timestamp_offset_during_append_(NULL), | 269 timestamp_offset_during_append_(NULL), |
| 230 new_media_segment_(false), | 270 new_media_segment_(false), |
| 231 parsing_media_segment_(false), | 271 parsing_media_segment_(false), |
| 232 stream_parser_(stream_parser.release()), | 272 stream_parser_(stream_parser.release()), |
| 233 audio_(NULL), | 273 audio_(NULL), |
| 234 video_(NULL), | 274 video_(NULL), |
| 235 frame_processor_(frame_processor.release()), | 275 frame_processor_(frame_processor.release()), |
| 236 log_cb_(log_cb) { | 276 log_cb_(log_cb), |
| 277 auto_update_timestamp_offset_(false) { | |
| 237 DCHECK(!create_demuxer_stream_cb_.is_null()); | 278 DCHECK(!create_demuxer_stream_cb_.is_null()); |
| 238 DCHECK(frame_processor_); | 279 DCHECK(frame_processor_); |
| 239 } | 280 } |
| 240 | 281 |
| 241 SourceState::~SourceState() { | 282 SourceState::~SourceState() { |
| 242 Shutdown(); | 283 Shutdown(); |
| 243 | 284 |
| 244 STLDeleteValues(&text_stream_map_); | 285 STLDeleteValues(&text_stream_map_); |
| 245 } | 286 } |
| 246 | 287 |
| 247 void SourceState::Init(const StreamParser::InitCB& init_cb, | 288 void SourceState::Init(const InitCB& init_cb, |
| 248 bool allow_audio, | 289 bool allow_audio, |
| 249 bool allow_video, | 290 bool allow_video, |
| 250 const StreamParser::NeedKeyCB& need_key_cb, | 291 const StreamParser::NeedKeyCB& need_key_cb, |
| 251 const NewTextTrackCB& new_text_track_cb) { | 292 const NewTextTrackCB& new_text_track_cb) { |
| 252 new_text_track_cb_ = new_text_track_cb; | 293 new_text_track_cb_ = new_text_track_cb; |
| 294 init_cb_ = init_cb; | |
| 253 | 295 |
| 254 stream_parser_->Init(init_cb, | 296 stream_parser_->Init(base::Bind(&SourceState::OnSourceInitDone, |
| 297 base::Unretained(this)), | |
| 255 base::Bind(&SourceState::OnNewConfigs, | 298 base::Bind(&SourceState::OnNewConfigs, |
| 256 base::Unretained(this), | 299 base::Unretained(this), |
| 257 allow_audio, | 300 allow_audio, |
| 258 allow_video), | 301 allow_video), |
| 259 base::Bind(&SourceState::OnNewBuffers, | 302 base::Bind(&SourceState::OnNewBuffers, |
| 260 base::Unretained(this)), | 303 base::Unretained(this)), |
| 261 new_text_track_cb_.is_null(), | 304 new_text_track_cb_.is_null(), |
| 262 need_key_cb, | 305 need_key_cb, |
| 263 base::Bind(&SourceState::OnNewMediaSegment, | 306 base::Bind(&SourceState::OnNewMediaSegment, |
| 264 base::Unretained(this)), | 307 base::Unretained(this)), |
| (...skipping 366 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 631 new_media_segment_ = false; | 674 new_media_segment_ = false; |
| 632 } | 675 } |
| 633 | 676 |
| 634 bool SourceState::OnNewBuffers( | 677 bool SourceState::OnNewBuffers( |
| 635 const StreamParser::BufferQueue& audio_buffers, | 678 const StreamParser::BufferQueue& audio_buffers, |
| 636 const StreamParser::BufferQueue& video_buffers, | 679 const StreamParser::BufferQueue& video_buffers, |
| 637 const StreamParser::TextBufferQueueMap& text_map) { | 680 const StreamParser::TextBufferQueueMap& text_map) { |
| 638 DVLOG(2) << "OnNewBuffers()"; | 681 DVLOG(2) << "OnNewBuffers()"; |
| 639 DCHECK(timestamp_offset_during_append_); | 682 DCHECK(timestamp_offset_during_append_); |
| 640 | 683 |
| 641 return frame_processor_->ProcessFrames( | 684 const TimeDelta current_timestamp_offset = *timestamp_offset_during_append_; |
| 642 audio_buffers, video_buffers, text_map, | 685 const bool success = |
|
wolenetz
2014/03/18 02:09:30
nit: If |success| is false, this indicates a parse
DaleCurtis
2014/03/18 02:16:04
Done.
| |
| 643 append_window_start_during_append_, append_window_end_during_append_, | 686 frame_processor_->ProcessFrames(audio_buffers, |
| 644 &new_media_segment_, timestamp_offset_during_append_); | 687 video_buffers, |
| 688 text_map, | |
| 689 append_window_start_during_append_, | |
| 690 append_window_end_during_append_, | |
| 691 &new_media_segment_, | |
| 692 timestamp_offset_during_append_); | |
| 693 | |
| 694 // Update the timestamp offset for audio/video tracks if it hasn't already | |
| 695 // been updated by the frame processor. | |
| 696 if (auto_update_timestamp_offset_ && | |
| 697 current_timestamp_offset == *timestamp_offset_during_append_) { | |
| 698 const TimeDelta audio_end = EndTimestamp(audio_buffers, | |
| 699 append_window_start_during_append_, | |
| 700 append_window_end_during_append_); | |
| 701 const TimeDelta video_end = EndTimestamp(audio_buffers, | |
|
wolenetz
2014/03/18 02:09:30
s/audio_buf/video_buf/
DaleCurtis
2014/03/18 02:16:04
Done.
| |
| 702 append_window_start_during_append_, | |
| 703 append_window_end_during_append_); | |
| 704 const bool have_audio_buffers = audio_end != kNoTimestamp(); | |
| 705 const bool have_video_buffers = video_end != kNoTimestamp(); | |
| 706 if (have_audio_buffers && have_video_buffers) { | |
| 707 *timestamp_offset_during_append_ = std::min(audio_end, video_end); | |
| 708 } else if (have_audio_buffers) { | |
| 709 *timestamp_offset_during_append_ = audio_end; | |
| 710 } else if (have_video_buffers) { | |
| 711 *timestamp_offset_during_append_ = video_end; | |
| 712 } | |
| 713 } | |
| 714 | |
| 715 return success; | |
| 716 } | |
| 717 | |
| 718 void SourceState::OnSourceInitDone(bool success, | |
| 719 TimeDelta duration, | |
| 720 bool auto_update_timestamp_offset) { | |
| 721 auto_update_timestamp_offset_ = auto_update_timestamp_offset; | |
| 722 base::ResetAndReturn(&init_cb_).Run(success, duration); | |
| 645 } | 723 } |
| 646 | 724 |
| 647 ChunkDemuxerStream::ChunkDemuxerStream(Type type) | 725 ChunkDemuxerStream::ChunkDemuxerStream(Type type) |
| 648 : type_(type), | 726 : type_(type), |
| 649 state_(UNINITIALIZED) { | 727 state_(UNINITIALIZED) { |
| 650 } | 728 } |
| 651 | 729 |
| 652 void ChunkDemuxerStream::StartReturningData() { | 730 void ChunkDemuxerStream::StartReturningData() { |
| 653 DVLOG(1) << "ChunkDemuxerStream::StartReturningData()"; | 731 DVLOG(1) << "ChunkDemuxerStream::StartReturningData()"; |
| 654 base::AutoLock auto_lock(lock_); | 732 base::AutoLock auto_lock(lock_); |
| (...skipping 755 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1410 for (SourceStateMap::const_iterator itr = source_state_map_.begin(); | 1488 for (SourceStateMap::const_iterator itr = source_state_map_.begin(); |
| 1411 itr != source_state_map_.end(); ++itr) { | 1489 itr != source_state_map_.end(); ++itr) { |
| 1412 if (itr->second->IsSeekWaitingForData()) | 1490 if (itr->second->IsSeekWaitingForData()) |
| 1413 return true; | 1491 return true; |
| 1414 } | 1492 } |
| 1415 | 1493 |
| 1416 return false; | 1494 return false; |
| 1417 } | 1495 } |
| 1418 | 1496 |
| 1419 void ChunkDemuxer::OnSourceInitDone(bool success, TimeDelta duration) { | 1497 void ChunkDemuxer::OnSourceInitDone(bool success, TimeDelta duration) { |
| 1420 DVLOG(1) << "OnSourceInitDone(" << success << ", " | 1498 DVLOG(1) << "OnSourceInitDone(" << success |
| 1421 << duration.InSecondsF() << ")"; | 1499 << ", " << duration.InSecondsF() << ")"; |
| 1422 lock_.AssertAcquired(); | 1500 lock_.AssertAcquired(); |
| 1423 DCHECK_EQ(state_, INITIALIZING); | 1501 DCHECK_EQ(state_, INITIALIZING); |
| 1424 if (!success || (!audio_ && !video_)) { | 1502 if (!success || (!audio_ && !video_)) { |
| 1425 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); | 1503 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); |
| 1426 return; | 1504 return; |
| 1427 } | 1505 } |
| 1428 | 1506 |
| 1429 if (duration != TimeDelta() && duration_ == kNoTimestamp()) | 1507 if (duration != TimeDelta() && duration_ == kNoTimestamp()) |
| 1430 UpdateDuration(duration); | 1508 UpdateDuration(duration); |
| 1431 | 1509 |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1573 } | 1651 } |
| 1574 | 1652 |
| 1575 void ChunkDemuxer::ShutdownAllStreams() { | 1653 void ChunkDemuxer::ShutdownAllStreams() { |
| 1576 for (SourceStateMap::iterator itr = source_state_map_.begin(); | 1654 for (SourceStateMap::iterator itr = source_state_map_.begin(); |
| 1577 itr != source_state_map_.end(); ++itr) { | 1655 itr != source_state_map_.end(); ++itr) { |
| 1578 itr->second->Shutdown(); | 1656 itr->second->Shutdown(); |
| 1579 } | 1657 } |
| 1580 } | 1658 } |
| 1581 | 1659 |
| 1582 } // namespace media | 1660 } // namespace media |
| OLD | NEW |