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/frame_processor.h" | 20 #include "media/filters/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 using TextTrackConfigMap = media::StreamParser::TextTrackConfigMap; |
24 | 25 |
25 namespace media { | 26 namespace media { |
26 | 27 |
27 static TimeDelta EndTimestamp(const StreamParser::BufferQueue& queue) { | 28 static TimeDelta EndTimestamp(const StreamParser::BufferQueue& queue) { |
28 return queue.back()->timestamp() + queue.back()->duration(); | 29 return queue.back()->timestamp() + queue.back()->duration(); |
29 } | 30 } |
30 | 31 |
31 // List of time ranges for each SourceBuffer. | 32 // List of time ranges for each SourceBuffer. |
32 typedef std::list<Ranges<TimeDelta> > RangesList; | 33 typedef std::list<Ranges<TimeDelta> > RangesList; |
33 static Ranges<TimeDelta> ComputeIntersection(const RangesList& activeRanges, | 34 static Ranges<TimeDelta> ComputeIntersection(const RangesList& activeRanges, |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
76 // Step 5.3: Let new intersection ranges equal the intersection between | 77 // Step 5.3: Let new intersection ranges equal the intersection between |
77 // the intersection ranges and the source ranges. | 78 // the intersection ranges and the source ranges. |
78 // Step 5.4: Replace the ranges in intersection ranges with the new | 79 // Step 5.4: Replace the ranges in intersection ranges with the new |
79 // intersection ranges. | 80 // intersection ranges. |
80 intersection_ranges = intersection_ranges.IntersectionWith(source_ranges); | 81 intersection_ranges = intersection_ranges.IntersectionWith(source_ranges); |
81 } | 82 } |
82 | 83 |
83 return intersection_ranges; | 84 return intersection_ranges; |
84 } | 85 } |
85 | 86 |
| 87 InitSegment::InitSegment(const std::vector<AudioTrackInfo>& audio, |
| 88 const std::vector<VideoTrackInfo>& video, |
| 89 const std::vector<TextTrackInfo>& text) : |
| 90 audio_tracks(audio), |
| 91 video_tracks(video), |
| 92 text_tracks(text) { |
| 93 } |
| 94 |
| 95 InitSegment::~InitSegment() { |
| 96 } |
| 97 |
| 98 InitSegment InitSegment::Create( |
| 99 const AudioDecoderConfig& audio_config, |
| 100 const VideoDecoderConfig& video_config, |
| 101 const StreamParser::TextTrackConfigMap& text_configs) { |
| 102 std::vector<AudioTrackInfo> audio_tracks; |
| 103 std::vector<VideoTrackInfo> video_tracks; |
| 104 std::vector<TextTrackInfo> text_tracks; |
| 105 |
| 106 // BIG TODO: Reject/decode error on Invalid Configs?? |
| 107 // BIG TODO START HERE: Get Blink integration working... |
| 108 |
| 109 if (audio_config.IsValidConfig()) |
| 110 audio_tracks.push_back(AudioTrackInfo(FrameProcessor::kAudioTrackId, |
| 111 audio_config)); |
| 112 |
| 113 if (video_config.IsValidConfig()) |
| 114 video_tracks.push_back(VideoTrackInfo(FrameProcessor::kVideoTrackId, |
| 115 video_config)); |
| 116 |
| 117 for (TextTrackConfigMap::const_iterator itr = text_configs.begin(); |
| 118 itr != text_configs.end(); ++itr) { |
| 119 text_tracks.push_back(TextTrackInfo(itr->first, itr->second)); |
| 120 } |
| 121 |
| 122 return InitSegment(audio_tracks, video_tracks, text_tracks); |
| 123 } |
| 124 |
| 125 bool InitSegment::HasTracks() const { |
| 126 return !audio_tracks.empty() || !video_tracks.empty() || !text_tracks.empty(); |
| 127 } |
| 128 |
86 // Contains state belonging to a source id. | 129 // Contains state belonging to a source id. |
87 class SourceState { | 130 class SourceState { |
88 public: | 131 public: |
| 132 typedef ChunkDemuxer::NewInitSegmentCB NewInitSegmentCB; |
| 133 |
89 // Callback signature used to create ChunkDemuxerStreams. | 134 // Callback signature used to create ChunkDemuxerStreams. |
90 typedef base::Callback<ChunkDemuxerStream*( | 135 typedef base::Callback<ChunkDemuxerStream*( |
91 DemuxerStream::Type)> CreateDemuxerStreamCB; | 136 DemuxerStream::Type)> CreateDemuxerStreamCB; |
92 | 137 |
93 typedef base::Callback<void( | 138 typedef base::Callback<void( |
94 ChunkDemuxerStream*, const TextTrackConfig&)> NewTextTrackCB; | 139 ChunkDemuxerStream*, const TextTrackConfig&)> NewTextTrackCB; |
95 | 140 |
96 SourceState( | 141 SourceState( |
97 scoped_ptr<StreamParser> stream_parser, | 142 scoped_ptr<StreamParser> stream_parser, |
98 scoped_ptr<FrameProcessor> frame_processor, const LogCB& log_cb, | 143 scoped_ptr<FrameProcessor> frame_processor, |
| 144 const LogCB& log_cb, |
| 145 const NewInitSegmentCB& new_init_segment_cb, |
99 const CreateDemuxerStreamCB& create_demuxer_stream_cb); | 146 const CreateDemuxerStreamCB& create_demuxer_stream_cb); |
100 | 147 |
101 ~SourceState(); | 148 ~SourceState(); |
102 | 149 |
103 void Init(const StreamParser::InitCB& init_cb, | 150 void Init(const StreamParser::InitCB& init_cb, |
104 bool allow_audio, | 151 bool allow_audio, |
105 bool allow_video, | 152 bool allow_video, |
106 const StreamParser::NeedKeyCB& need_key_cb, | 153 const StreamParser::NeedKeyCB& need_key_cb, |
107 const NewTextTrackCB& new_text_track_cb); | 154 const NewTextTrackCB& new_text_track_cb); |
108 | 155 |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
155 void OnSetDuration(TimeDelta duration); | 202 void OnSetDuration(TimeDelta duration); |
156 void MarkEndOfStream(); | 203 void MarkEndOfStream(); |
157 void UnmarkEndOfStream(); | 204 void UnmarkEndOfStream(); |
158 void Shutdown(); | 205 void Shutdown(); |
159 // Sets the memory limit on each stream. |memory_limit| is the | 206 // Sets the memory limit on each stream. |memory_limit| is the |
160 // maximum number of bytes each stream is allowed to hold in its buffer. | 207 // maximum number of bytes each stream is allowed to hold in its buffer. |
161 void SetMemoryLimitsForTesting(int memory_limit); | 208 void SetMemoryLimitsForTesting(int memory_limit); |
162 bool IsSeekWaitingForData() const; | 209 bool IsSeekWaitingForData() const; |
163 | 210 |
164 private: | 211 private: |
165 // Called by the |stream_parser_| when a new initialization segment is | 212 // Called by the |stream_parse_| when a new initialization segment is |
166 // encountered. | 213 // encountered. This method implements the "Initialization segment received" |
| 214 // algorithm outlined in the MSE spec. |
| 215 // |
167 // Returns true on a successful call. Returns false if an error occurred while | 216 // Returns true on a successful call. Returns false if an error occurred while |
168 // processing decoder configurations. | 217 // processing decoder configurations. |
169 bool OnNewConfigs(bool allow_audio, bool allow_video, | 218 bool OnInitSegment(bool allow_audio, bool allow_video, |
170 const AudioDecoderConfig& audio_config, | 219 const AudioDecoderConfig& audio_config, |
171 const VideoDecoderConfig& video_config, | 220 const VideoDecoderConfig& video_config, |
172 const StreamParser::TextTrackConfigMap& text_configs); | 221 const StreamParser::TextTrackConfigMap& text_configs); |
| 222 |
| 223 // Implements step 3.1 - 3.3 of the "Initialization segment received" |
| 224 // algorithm. |
| 225 bool ValidateInitSegmentAndUpdateStreams(const InitSegment& init_segment); |
| 226 |
| 227 // Implements step 5.1 of the "Initialization segment received" algorithm. |
| 228 bool HasUnsupportedCodecs(bool allow_audio, bool allow_video, |
| 229 const InitSegment& init_segment) const; |
| 230 |
| 231 // Implements step 5.3 of the "Initialization segment received" algorithm. |
| 232 bool HandleNewAudioTracks(const std::vector<AudioTrackInfo>& audio_tracks); |
| 233 |
| 234 // Implements step 5.4 of the "Initialization segment received" algorithm. |
| 235 bool HandleNewVideoTracks(const std::vector<VideoTrackInfo>& video_tracks); |
| 236 |
| 237 // Implements step 5.4 of the "Initialization segment received" algorithm. |
| 238 bool HandleNewTextTracks(const std::vector<TextTrackInfo>& text_tracks); |
173 | 239 |
174 // Called by the |stream_parser_| at the beginning of a new media segment. | 240 // Called by the |stream_parser_| at the beginning of a new media segment. |
175 void OnNewMediaSegment(); | 241 void OnNewMediaSegment(); |
176 | 242 |
177 // Called by the |stream_parser_| at the end of a media segment. | 243 // Called by the |stream_parser_| at the end of a media segment. |
178 void OnEndOfMediaSegment(); | 244 void OnEndOfMediaSegment(); |
179 | 245 |
180 // Called by the |stream_parser_| when new buffers have been parsed. | 246 // Called by the |stream_parser_| when new buffers have been parsed. |
181 // It processes the new buffers using |frame_processor_|, which includes | 247 // It processes the new buffers using |frame_processor_|, which includes |
182 // appending the processed frames to associated demuxer streams for each | 248 // appending the processed frames to associated demuxer streams for each |
183 // frame's track. | 249 // frame's track. |
184 // Returns true on a successful call. Returns false if an error occurred while | 250 // Returns true on a successful call. Returns false if an error occurred while |
185 // processing the buffers. | 251 // processing the buffers. |
186 bool OnNewBuffers(const StreamParser::BufferQueue& audio_buffers, | 252 bool OnNewBuffers(const StreamParser::BufferQueue& audio_buffers, |
187 const StreamParser::BufferQueue& video_buffers, | 253 const StreamParser::BufferQueue& video_buffers, |
188 const StreamParser::TextBufferQueueMap& text_map); | 254 const StreamParser::TextBufferQueueMap& text_map); |
189 | 255 |
190 void OnSourceInitDone(bool success, | 256 void OnSourceInitDone(bool success, |
191 const StreamParser::InitParameters& params); | 257 const StreamParser::InitParameters& params); |
192 | 258 |
| 259 NewInitSegmentCB new_init_segment_cb_; |
193 CreateDemuxerStreamCB create_demuxer_stream_cb_; | 260 CreateDemuxerStreamCB create_demuxer_stream_cb_; |
194 NewTextTrackCB new_text_track_cb_; | 261 NewTextTrackCB new_text_track_cb_; |
195 | 262 |
196 // During Append(), if OnNewBuffers() coded frame processing updates the | 263 // During Append(), if OnNewBuffers() coded frame processing updates the |
197 // timestamp offset then |*timestamp_offset_during_append_| is also updated | 264 // timestamp offset then |*timestamp_offset_during_append_| is also updated |
198 // so Append()'s caller can know the new offset. This pointer is only non-NULL | 265 // so Append()'s caller can know the new offset. This pointer is only non-NULL |
199 // during the lifetime of an Append() call. | 266 // during the lifetime of an Append() call. |
200 TimeDelta* timestamp_offset_during_append_; | 267 TimeDelta* timestamp_offset_during_append_; |
201 | 268 |
202 // During Append(), coded frame processing triggered by OnNewBuffers() | 269 // During Append(), coded frame processing triggered by OnNewBuffers() |
203 // requires these two attributes. These are only valid during the lifetime of | 270 // requires these two attributes. These are only valid during the lifetime of |
204 // an Append() call. | 271 // an Append() call. |
205 TimeDelta append_window_start_during_append_; | 272 TimeDelta append_window_start_during_append_; |
206 TimeDelta append_window_end_during_append_; | 273 TimeDelta append_window_end_during_append_; |
207 | 274 |
208 // Set to true if the next buffers appended within the append window | 275 // Set to true if the next buffers appended within the append window |
209 // represent the start of a new media segment. This flag being set | 276 // represent the start of a new media segment. This flag being set |
210 // triggers a call to |new_segment_cb_| when the new buffers are | 277 // triggers a call to |new_segment_cb_| when the new buffers are |
211 // appended. The flag is set on actual media segment boundaries and | 278 // appended. The flag is set on actual media segment boundaries and |
212 // when the "append window" filtering causes discontinuities in the | 279 // when the "append window" filtering causes discontinuities in the |
213 // appended data. | 280 // appended data. |
214 // TODO(wolenetz/acolwell): Investigate if we need this, or if coded frame | 281 // TODO(wolenetz/acolwell): Investigate if we need this, or if coded frame |
215 // processing's discontinuity logic is enough. See http://crbug.com/351489. | 282 // processing's discontinuity logic is enough. See http://crbug.com/351489. |
216 bool new_media_segment_; | 283 bool new_media_segment_; |
217 | 284 |
218 // Keeps track of whether a media segment is being parsed. | 285 // Keeps track of whether a media segment is being parsed. |
219 bool parsing_media_segment_; | 286 bool parsing_media_segment_; |
220 | 287 |
| 288 // Keeps track of the "first initialization segment flag" state mentioned in |
| 289 // the MSE spec. |
| 290 bool received_first_init_segment_; |
| 291 |
221 // The object used to parse appended data. | 292 // The object used to parse appended data. |
222 scoped_ptr<StreamParser> stream_parser_; | 293 scoped_ptr<StreamParser> stream_parser_; |
223 | 294 |
224 ChunkDemuxerStream* audio_; // Not owned by |this|. | 295 ChunkDemuxerStream* audio_; // Not owned by |this|. |
225 ChunkDemuxerStream* video_; // Not owned by |this|. | 296 ChunkDemuxerStream* video_; // Not owned by |this|. |
226 | 297 |
227 typedef std::map<StreamParser::TrackId, ChunkDemuxerStream*> TextStreamMap; | 298 typedef std::map<StreamParser::TrackId, ChunkDemuxerStream*> TextStreamMap; |
228 TextStreamMap text_stream_map_; // |this| owns the map's stream pointers. | 299 TextStreamMap text_stream_map_; // |this| owns the map's stream pointers. |
229 | 300 |
230 scoped_ptr<FrameProcessor> frame_processor_; | 301 scoped_ptr<FrameProcessor> frame_processor_; |
231 LogCB log_cb_; | 302 LogCB log_cb_; |
232 StreamParser::InitCB init_cb_; | 303 StreamParser::InitCB init_cb_; |
233 | 304 |
234 // Indicates that timestampOffset should be updated automatically during | 305 // Indicates that timestampOffset should be updated automatically during |
235 // OnNewBuffers() based on the earliest end timestamp of the buffers provided. | 306 // OnNewBuffers() based on the earliest end timestamp of the buffers provided. |
236 // TODO(wolenetz): Refactor this function while integrating April 29, 2014 | 307 // TODO(wolenetz): Refactor this function while integrating April 29, 2014 |
237 // changes to MSE spec. See http://crbug.com/371499. | 308 // changes to MSE spec. See http://crbug.com/371499. |
238 bool auto_update_timestamp_offset_; | 309 bool auto_update_timestamp_offset_; |
239 | 310 |
240 DISALLOW_COPY_AND_ASSIGN(SourceState); | 311 DISALLOW_COPY_AND_ASSIGN(SourceState); |
241 }; | 312 }; |
242 | 313 |
243 SourceState::SourceState(scoped_ptr<StreamParser> stream_parser, | 314 SourceState::SourceState(scoped_ptr<StreamParser> stream_parser, |
244 scoped_ptr<FrameProcessor> frame_processor, | 315 scoped_ptr<FrameProcessor> frame_processor, |
245 const LogCB& log_cb, | 316 const LogCB& log_cb, |
| 317 const NewInitSegmentCB& new_init_segment_cb, |
246 const CreateDemuxerStreamCB& create_demuxer_stream_cb) | 318 const CreateDemuxerStreamCB& create_demuxer_stream_cb) |
247 : create_demuxer_stream_cb_(create_demuxer_stream_cb), | 319 : new_init_segment_cb_(new_init_segment_cb), |
| 320 create_demuxer_stream_cb_(create_demuxer_stream_cb), |
248 timestamp_offset_during_append_(NULL), | 321 timestamp_offset_during_append_(NULL), |
249 new_media_segment_(false), | 322 new_media_segment_(false), |
250 parsing_media_segment_(false), | 323 parsing_media_segment_(false), |
| 324 received_first_init_segment_(false), |
251 stream_parser_(stream_parser.release()), | 325 stream_parser_(stream_parser.release()), |
252 audio_(NULL), | 326 audio_(NULL), |
253 video_(NULL), | 327 video_(NULL), |
254 frame_processor_(frame_processor.release()), | 328 frame_processor_(frame_processor.release()), |
255 log_cb_(log_cb), | 329 log_cb_(log_cb), |
256 auto_update_timestamp_offset_(false) { | 330 auto_update_timestamp_offset_(false) { |
257 DCHECK(!create_demuxer_stream_cb_.is_null()); | 331 DCHECK(!create_demuxer_stream_cb_.is_null()); |
258 DCHECK(frame_processor_); | 332 DCHECK(frame_processor_); |
259 } | 333 } |
260 | 334 |
261 SourceState::~SourceState() { | 335 SourceState::~SourceState() { |
262 Shutdown(); | 336 Shutdown(); |
263 | 337 |
264 STLDeleteValues(&text_stream_map_); | 338 STLDeleteValues(&text_stream_map_); |
265 } | 339 } |
266 | 340 |
267 void SourceState::Init(const StreamParser::InitCB& init_cb, | 341 void SourceState::Init(const StreamParser::InitCB& init_cb, |
268 bool allow_audio, | 342 bool allow_audio, |
269 bool allow_video, | 343 bool allow_video, |
270 const StreamParser::NeedKeyCB& need_key_cb, | 344 const StreamParser::NeedKeyCB& need_key_cb, |
271 const NewTextTrackCB& new_text_track_cb) { | 345 const NewTextTrackCB& new_text_track_cb) { |
272 new_text_track_cb_ = new_text_track_cb; | 346 new_text_track_cb_ = new_text_track_cb; |
273 init_cb_ = init_cb; | 347 init_cb_ = init_cb; |
274 | 348 |
275 stream_parser_->Init( | 349 stream_parser_->Init( |
276 base::Bind(&SourceState::OnSourceInitDone, base::Unretained(this)), | 350 base::Bind(&SourceState::OnSourceInitDone, base::Unretained(this)), |
277 base::Bind(&SourceState::OnNewConfigs, | 351 base::Bind(&SourceState::OnInitSegment, |
278 base::Unretained(this), | 352 base::Unretained(this), |
279 allow_audio, | 353 allow_audio, |
280 allow_video), | 354 allow_video), |
281 base::Bind(&SourceState::OnNewBuffers, base::Unretained(this)), | 355 base::Bind(&SourceState::OnNewBuffers, base::Unretained(this)), |
282 new_text_track_cb_.is_null(), | 356 new_text_track_cb_.is_null(), |
283 need_key_cb, | 357 need_key_cb, |
284 base::Bind(&SourceState::OnNewMediaSegment, base::Unretained(this)), | 358 base::Bind(&SourceState::OnNewMediaSegment, base::Unretained(this)), |
285 base::Bind(&SourceState::OnEndOfMediaSegment, base::Unretained(this)), | 359 base::Bind(&SourceState::OnEndOfMediaSegment, base::Unretained(this)), |
286 log_cb_); | 360 log_cb_); |
287 } | 361 } |
(...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
508 // NOTE: We are intentionally not checking the text tracks | 582 // NOTE: We are intentionally not checking the text tracks |
509 // because text tracks are discontinuous and may not have data | 583 // because text tracks are discontinuous and may not have data |
510 // for the seek position. This is ok and playback should not be | 584 // for the seek position. This is ok and playback should not be |
511 // stalled because we don't have cues. If cues, with timestamps after | 585 // stalled because we don't have cues. If cues, with timestamps after |
512 // the seek time, eventually arrive they will be delivered properly | 586 // the seek time, eventually arrive they will be delivered properly |
513 // in response to ChunkDemuxerStream::Read() calls. | 587 // in response to ChunkDemuxerStream::Read() calls. |
514 | 588 |
515 return false; | 589 return false; |
516 } | 590 } |
517 | 591 |
518 bool SourceState::OnNewConfigs( | 592 bool SourceState::OnInitSegment( |
519 bool allow_audio, bool allow_video, | 593 bool allow_audio, bool allow_video, |
520 const AudioDecoderConfig& audio_config, | 594 const AudioDecoderConfig& audio_config, |
521 const VideoDecoderConfig& video_config, | 595 const VideoDecoderConfig& video_config, |
522 const StreamParser::TextTrackConfigMap& text_configs) { | 596 const StreamParser::TextTrackConfigMap& text_configs) { |
523 DVLOG(1) << "OnNewConfigs(" << allow_audio << ", " << allow_video | 597 DVLOG(2) << "OnInitSegment() " |
524 << ", " << audio_config.IsValidConfig() | 598 << ": allow_audio=" << allow_audio |
525 << ", " << video_config.IsValidConfig() << ")"; | 599 << ", allow_video=" << allow_video |
526 | 600 << ", audio_config is " |
527 if (!audio_config.IsValidConfig() && !video_config.IsValidConfig()) { | 601 << (audio_config.IsValidConfig() ? "valid" : "invalid") |
528 DVLOG(1) << "OnNewConfigs() : Audio & video config are not valid!"; | 602 << ", video_config is " |
529 return false; | 603 << (video_config.IsValidConfig() ? "valid" : "invalid") |
530 } | 604 << ", text_configs.size()=" << text_configs.size(); |
531 | 605 |
532 // Signal an error if we get configuration info for stream types that weren't | 606 // TODO(wolenetz/acolwell): Update StreamParser interface to emit InitSegments |
533 // specified in AddId() or more configs after a stream is initialized. | 607 // instead of individual configs. |
534 if (allow_audio != audio_config.IsValidConfig()) { | 608 InitSegment init_segment = |
| 609 InitSegment::Create(audio_config, video_config, text_configs); |
| 610 |
| 611 // Section 3.5.7 Initialization Segment Received |
| 612 // 1. Update the duration attribute if it currently equals NaN: |
| 613 // If the initialization segment contains a duration: |
| 614 // 1. Run the duration change algorithm with new duration set to the |
| 615 // duration in the initialization segment. |
| 616 // Otherwise: |
| 617 // 1. Run the duration change algorithm with new duration set to positive |
| 618 // Infinity. |
| 619 // TODO(wolenetz/acolwell): Refactor code so step 1 can actually be done here. |
| 620 |
| 621 // 2. If the initialization segment has no audio, video, or text tracks, then |
| 622 // run the end of stream algorithm with the error parameter set to "decode" |
| 623 // and abort these steps. |
| 624 if (!init_segment.HasTracks()) { |
| 625 MEDIA_LOG(log_cb_) << "No audio, video or text tracks " |
| 626 << "in initialization segment"; |
| 627 return false; |
| 628 } |
| 629 |
| 630 // 3. If the first initialization segment flag is true, then run the following |
| 631 // steps: |
| 632 if (received_first_init_segment_) { |
| 633 // 3.1 Verify the following properties. If any of the checks fail then run |
| 634 // the end of stream algorithm with the error parameter set to "decode" |
| 635 // and abort these steps. |
| 636 // * The number of audio, video, and text tracks match what was in the |
| 637 // first initialization segment. |
| 638 // * The codecs for each track match what was specified in the first |
| 639 // initialization segment. |
| 640 // * If more than one track for a single type are present (ie 2 audio |
| 641 // tracks), then the Track IDs match the ones in the first |
| 642 // initialization segment. |
| 643 // 3.2 Add the appropriate track descriptions from this initialization |
| 644 // segment to each of the track buffers. |
| 645 // 3.3 Set the need random access point flag on all track buffers to true. |
| 646 if (!ValidateInitSegmentAndUpdateStreams(init_segment)) |
| 647 return false; |
| 648 } |
| 649 |
| 650 // 4. Let active track flag equal false. |
| 651 // Note: This step is handled in Blink. |
| 652 // BIG TODO(wolenetz): confirm Blink does this... |
| 653 |
| 654 // 5. If the first initialization segment flag is false, then run the |
| 655 // following steps: |
| 656 // BIG TODO(wolenetz): confirm the spec steps match the comments here and the |
| 657 // SourceState declaration comments. |
| 658 if (!received_first_init_segment_) { |
| 659 // 5.1 If the initialization segment contains tracks with codecs the user |
| 660 // agent does not support, then run the end of stream algorithm with the |
| 661 // error parameter set to "decode" and abort these steps. |
| 662 if (HasUnsupportedCodecs(allow_audio, allow_video, init_segment)) |
| 663 return false; |
| 664 |
| 665 // 5.2 For each audio track in the initialization segment, run the following |
| 666 // steps: |
| 667 if (!init_segment.audio_tracks.empty() && |
| 668 !HandleNewAudioTracks(init_segment.audio_tracks)) { |
| 669 return false; |
| 670 } |
| 671 |
| 672 // 5.3 For each video track in the initialization segment, run the |
| 673 // following steps: |
| 674 if (!init_segment.video_tracks.empty() && |
| 675 !HandleNewVideoTracks(init_segment.video_tracks)) { |
| 676 return false; |
| 677 } |
| 678 |
| 679 // 5.4 For each text track in the initialization segment, run the |
| 680 // following steps: |
| 681 if (!init_segment.text_tracks.empty() && |
| 682 !HandleNewTextTracks(init_segment.text_tracks)) { |
| 683 return false; |
| 684 } |
| 685 |
| 686 // 5.5 If active track flag equals true, then run the following steps: |
| 687 // NOTE: Handled when |init_segment_state| is processed by Blink. |
| 688 // BIG TODO(wolenetz): confirm Blink does this... |
| 689 |
| 690 // 5.6 Set first initialization segment flag to true. |
| 691 received_first_init_segment_ = true; |
| 692 } |
| 693 |
| 694 // Steps 6 & 7 are handled when |init_segment_state| is processed by Blink. |
| 695 // BIG TODO(wolenetz): confirm Blink does this... |
| 696 |
| 697 return true; |
| 698 } |
| 699 |
| 700 bool SourceState::ValidateInitSegmentAndUpdateStreams( |
| 701 const InitSegment& init_segment) { |
| 702 if (!init_segment.audio_tracks.empty()) { |
| 703 const AudioDecoderConfig& audio_config = |
| 704 init_segment.audio_tracks[0].config(); |
| 705 if (!audio_ || !audio_->UpdateAudioConfig(audio_config, log_cb_)) |
| 706 return false; |
| 707 frame_processor_->OnPossibleAudioConfigUpdate(audio_config); |
| 708 } |
| 709 |
| 710 if (!init_segment.video_tracks.empty() && |
| 711 (!video_ || |
| 712 !video_->UpdateVideoConfig(init_segment.video_tracks[0].config(), |
| 713 log_cb_))) { |
| 714 return false; |
| 715 } |
| 716 |
| 717 const size_t text_count = init_segment.text_tracks.size(); |
| 718 if (text_stream_map_.size() != text_count) { |
| 719 MEDIA_LOG(log_cb_) << "The number of text track configs changed."; |
| 720 return false; |
| 721 } |
| 722 |
| 723 if (text_count == 1) { |
| 724 ChunkDemuxerStream* text_stream = text_stream_map_.begin()->second; |
| 725 const StreamParser::TrackId old_id = text_stream_map_.begin()->first; |
| 726 const TextTrackConfig& old_text_config = text_stream->text_track_config(); |
| 727 const StreamParser::TrackId new_id = init_segment.text_tracks[0].track_id(); |
| 728 const TextTrackConfig& new_text_config = |
| 729 init_segment.text_tracks[0].config(); |
| 730 |
| 731 if (!new_text_config.Matches(old_text_config)) { |
| 732 MEDIA_LOG(log_cb_) << "New text track config does not match old one."; |
| 733 return false; |
| 734 } |
| 735 |
| 736 if (new_id != old_id) { |
| 737 if (frame_processor_->UpdateTrack(old_id, new_id)) { |
| 738 text_stream_map_.clear(); |
| 739 text_stream_map_[new_id] = text_stream; |
| 740 } else { |
| 741 MEDIA_LOG(log_cb_) << "Error remapping single text track number"; |
| 742 return false; |
| 743 } |
| 744 } |
| 745 } else { |
| 746 for (size_t i = 0; i < text_count; ++i) { |
| 747 const TextTrackInfo& track_info = init_segment.text_tracks[i]; |
| 748 TextStreamMap::iterator stream_itr = |
| 749 text_stream_map_.find(track_info.track_id()); |
| 750 if (stream_itr == text_stream_map_.end()) { |
| 751 MEDIA_LOG(log_cb_) << "Unexpected text track configuration for track " |
| 752 << "ID " << track_info.track_id(); |
| 753 return false; |
| 754 } |
| 755 |
| 756 ChunkDemuxerStream* stream = stream_itr->second; |
| 757 if (!track_info.config().Matches(stream->text_track_config())) { |
| 758 MEDIA_LOG(log_cb_) << "New text track config for track ID " |
| 759 << track_info.track_id() |
| 760 << " does not match old one."; |
| 761 return false; |
| 762 } |
| 763 } |
| 764 } |
| 765 |
| 766 frame_processor_->SetAllTrackBuffersNeedRandomAccessPoint(); |
| 767 |
| 768 return true; |
| 769 } |
| 770 |
| 771 bool SourceState::HasUnsupportedCodecs(bool allow_audio, bool allow_video, |
| 772 const InitSegment& init_segment) const { |
| 773 bool has_audio = !init_segment.audio_tracks.empty(); |
| 774 bool has_video = !init_segment.video_tracks.empty(); |
| 775 |
| 776 if (allow_audio != has_audio) { |
535 MEDIA_LOG(log_cb_) | 777 MEDIA_LOG(log_cb_) |
536 << "Initialization segment" | 778 << "Initialization segment" |
537 << (audio_config.IsValidConfig() ? " has" : " does not have") | 779 << (has_audio ? " has" : " does not have") |
538 << " an audio track, but the mimetype" | 780 << " an audio track, but the mimetype" |
539 << (allow_audio ? " specifies" : " does not specify") | 781 << (allow_audio ? " specifies" : " does not specify") |
540 << " an audio codec."; | 782 << " an audio codec."; |
541 return false; | 783 return true; |
542 } | 784 } |
543 | 785 |
544 if (allow_video != video_config.IsValidConfig()) { | 786 if (allow_video != has_video) { |
545 MEDIA_LOG(log_cb_) | 787 MEDIA_LOG(log_cb_) |
546 << "Initialization segment" | 788 << "Initialization segment" |
547 << (video_config.IsValidConfig() ? " has" : " does not have") | 789 << (has_video ? " has" : " does not have") |
548 << " a video track, but the mimetype" | 790 << " a video track, but the mimetype" |
549 << (allow_video ? " specifies" : " does not specify") | 791 << (allow_video ? " specifies" : " does not specify") |
550 << " a video codec."; | 792 << " a video codec."; |
551 return false; | 793 return true; |
552 } | 794 } |
553 | 795 |
554 bool success = true; | 796 return false; |
555 if (audio_config.IsValidConfig()) { | 797 } |
556 if (!audio_) { | 798 |
557 audio_ = create_demuxer_stream_cb_.Run(DemuxerStream::AUDIO); | 799 bool SourceState::HandleNewAudioTracks( |
558 | 800 const std::vector<AudioTrackInfo>& audio_tracks) { |
559 if (!audio_) { | 801 DCHECK(!received_first_init_segment_); |
560 DVLOG(1) << "Failed to create an audio stream."; | 802 DCHECK(!audio_tracks.empty()); |
561 return false; | 803 DCHECK(!audio_); |
562 } | 804 audio_ = create_demuxer_stream_cb_.Run(DemuxerStream::AUDIO); |
563 | 805 if (!audio_) { |
564 if (!frame_processor_->AddTrack(FrameProcessor::kAudioTrackId, audio_)) { | 806 DVLOG(1) << "Failed to create an audio stream."; |
565 DVLOG(1) << "Failed to add audio track to frame processor."; | 807 return false; |
566 return false; | 808 } |
567 } | 809 |
568 } | 810 if (!frame_processor_->AddTrack(audio_tracks[0].track_id(), audio_)) { |
569 | 811 MEDIA_LOG(log_cb_) << "Failed to add audio track ID " |
570 frame_processor_->OnPossibleAudioConfigUpdate(audio_config); | 812 << audio_tracks[0].track_id() |
571 success &= audio_->UpdateAudioConfig(audio_config, log_cb_); | 813 << " to frame processor."; |
572 } | 814 return false; |
573 | 815 } |
574 if (video_config.IsValidConfig()) { | 816 |
575 if (!video_) { | 817 const AudioDecoderConfig& audio_config = audio_tracks[0].config(); |
576 video_ = create_demuxer_stream_cb_.Run(DemuxerStream::VIDEO); | 818 frame_processor_->OnPossibleAudioConfigUpdate(audio_config); |
577 | 819 if (!audio_->UpdateAudioConfig(audio_config, log_cb_)) { |
578 if (!video_) { | 820 MEDIA_LOG(log_cb_) << "Failed to set config for new audio track ID " |
579 DVLOG(1) << "Failed to create a video stream."; | 821 << audio_tracks[0].track_id(); |
580 return false; | 822 return false; |
581 } | 823 } |
582 | 824 |
583 if (!frame_processor_->AddTrack(FrameProcessor::kVideoTrackId, video_)) { | 825 return true; |
584 DVLOG(1) << "Failed to add video track to frame processor."; | 826 } |
585 return false; | 827 |
586 } | 828 bool SourceState::HandleNewVideoTracks( |
587 } | 829 const std::vector<VideoTrackInfo>& video_tracks) { |
588 | 830 DCHECK(!received_first_init_segment_); |
589 success &= video_->UpdateVideoConfig(video_config, log_cb_); | 831 DCHECK(!video_tracks.empty()); |
590 } | 832 DCHECK(!video_); |
591 | 833 video_ = create_demuxer_stream_cb_.Run(DemuxerStream::VIDEO); |
592 typedef StreamParser::TextTrackConfigMap::const_iterator TextConfigItr; | 834 if (!video_) { |
593 if (text_stream_map_.empty()) { | 835 DVLOG(1) << "Failed to create a video stream."; |
594 for (TextConfigItr itr = text_configs.begin(); | 836 return false; |
595 itr != text_configs.end(); ++itr) { | 837 } |
596 ChunkDemuxerStream* const text_stream = | 838 |
597 create_demuxer_stream_cb_.Run(DemuxerStream::TEXT); | 839 if (!frame_processor_->AddTrack(video_tracks[0].track_id(), video_)) { |
598 if (!frame_processor_->AddTrack(itr->first, text_stream)) { | 840 MEDIA_LOG(log_cb_) << "Failed to add video track ID " |
599 success &= false; | 841 << video_tracks[0].track_id() |
600 MEDIA_LOG(log_cb_) << "Failed to add text track ID " << itr->first | 842 << " to frame processor."; |
601 << " to frame processor."; | 843 return false; |
602 break; | 844 } |
603 } | 845 |
604 text_stream->UpdateTextConfig(itr->second, log_cb_); | 846 if (!video_->UpdateVideoConfig(video_tracks[0].config(), log_cb_)) { |
605 text_stream_map_[itr->first] = text_stream; | 847 MEDIA_LOG(log_cb_) << "Failed to set config for new video track ID " |
606 new_text_track_cb_.Run(text_stream, itr->second); | 848 << video_tracks[0].track_id(); |
607 } | 849 return false; |
608 } else { | 850 } |
609 const size_t text_count = text_stream_map_.size(); | 851 |
610 if (text_configs.size() != text_count) { | 852 return true; |
611 success &= false; | 853 } |
612 MEDIA_LOG(log_cb_) << "The number of text track configs changed."; | 854 |
613 } else if (text_count == 1) { | 855 bool SourceState::HandleNewTextTracks( |
614 TextConfigItr config_itr = text_configs.begin(); | 856 const std::vector<TextTrackInfo>& text_tracks) { |
615 const TextTrackConfig& new_config = config_itr->second; | 857 DCHECK(!received_first_init_segment_); |
616 TextStreamMap::iterator stream_itr = text_stream_map_.begin(); | 858 DCHECK(!text_tracks.empty()); |
617 ChunkDemuxerStream* text_stream = stream_itr->second; | 859 DCHECK(text_stream_map_.empty()); |
618 TextTrackConfig old_config = text_stream->text_track_config(); | 860 for (size_t i = 0; i < text_tracks.size(); ++i) { |
619 if (!new_config.Matches(old_config)) { | 861 const TextTrackInfo& track_info = text_tracks[i]; |
620 success &= false; | 862 ChunkDemuxerStream* const text_stream = |
621 MEDIA_LOG(log_cb_) << "New text track config does not match old one."; | 863 create_demuxer_stream_cb_.Run(DemuxerStream::TEXT); |
622 } else { | 864 if (!text_stream) { |
623 StreamParser::TrackId old_id = stream_itr->first; | 865 DVLOG(1) << "Failed to create a text stream."; |
624 StreamParser::TrackId new_id = config_itr->first; | 866 return false; |
625 if (new_id != old_id) { | 867 } |
626 if (frame_processor_->UpdateTrack(old_id, new_id)) { | 868 |
627 text_stream_map_.clear(); | 869 if (!frame_processor_->AddTrack(track_info.track_id(), text_stream)) { |
628 text_stream_map_[config_itr->first] = text_stream; | 870 MEDIA_LOG(log_cb_) << "Failed to add text track ID " |
629 } else { | 871 << track_info.track_id() |
630 success &= false; | 872 << " to frame processor."; |
631 MEDIA_LOG(log_cb_) << "Error remapping single text track number"; | 873 return false; |
632 } | 874 } |
633 } | 875 |
634 } | 876 text_stream->UpdateTextConfig(track_info.config(), log_cb_); |
635 } else { | 877 text_stream_map_[track_info.track_id()] = text_stream; |
636 for (TextConfigItr config_itr = text_configs.begin(); | 878 new_text_track_cb_.Run(text_stream, track_info.config()); |
637 config_itr != text_configs.end(); ++config_itr) { | 879 } |
638 TextStreamMap::iterator stream_itr = | 880 |
639 text_stream_map_.find(config_itr->first); | 881 return true; |
640 if (stream_itr == text_stream_map_.end()) { | |
641 success &= false; | |
642 MEDIA_LOG(log_cb_) << "Unexpected text track configuration " | |
643 "for track ID " | |
644 << config_itr->first; | |
645 break; | |
646 } | |
647 | |
648 const TextTrackConfig& new_config = config_itr->second; | |
649 ChunkDemuxerStream* stream = stream_itr->second; | |
650 TextTrackConfig old_config = stream->text_track_config(); | |
651 if (!new_config.Matches(old_config)) { | |
652 success &= false; | |
653 MEDIA_LOG(log_cb_) << "New text track config for track ID " | |
654 << config_itr->first | |
655 << " does not match old one."; | |
656 break; | |
657 } | |
658 } | |
659 } | |
660 } | |
661 | |
662 frame_processor_->SetAllTrackBuffersNeedRandomAccessPoint(); | |
663 | |
664 DVLOG(1) << "OnNewConfigs() : " << (success ? "success" : "failed"); | |
665 return success; | |
666 } | 882 } |
667 | 883 |
668 void SourceState::OnNewMediaSegment() { | 884 void SourceState::OnNewMediaSegment() { |
669 DVLOG(2) << "OnNewMediaSegment()"; | 885 DVLOG(2) << "OnNewMediaSegment()"; |
670 parsing_media_segment_ = true; | 886 parsing_media_segment_ = true; |
671 new_media_segment_ = true; | 887 new_media_segment_ = true; |
672 } | 888 } |
673 | 889 |
674 void SourceState::OnEndOfMediaSegment() { | 890 void SourceState::OnEndOfMediaSegment() { |
675 DVLOG(2) << "OnEndOfMediaSegment()"; | 891 DVLOG(2) << "OnEndOfMediaSegment()"; |
(...skipping 461 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1137 SeekAllSources(seek_time); | 1353 SeekAllSources(seek_time); |
1138 | 1354 |
1139 if (seek_cb_.is_null()) { | 1355 if (seek_cb_.is_null()) { |
1140 cancel_next_seek_ = true; | 1356 cancel_next_seek_ = true; |
1141 return; | 1357 return; |
1142 } | 1358 } |
1143 | 1359 |
1144 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); | 1360 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); |
1145 } | 1361 } |
1146 | 1362 |
1147 ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, | 1363 ChunkDemuxer::Status ChunkDemuxer::AddId( |
1148 const std::string& type, | 1364 const std::string& id, |
1149 std::vector<std::string>& codecs) { | 1365 const std::string& type, |
| 1366 std::vector<std::string>& codecs, |
| 1367 const NewInitSegmentCB& new_init_segment_cb) { |
1150 base::AutoLock auto_lock(lock_); | 1368 base::AutoLock auto_lock(lock_); |
1151 | 1369 |
1152 if ((state_ != WAITING_FOR_INIT && state_ != INITIALIZING) || IsValidId(id)) | 1370 if ((state_ != WAITING_FOR_INIT && state_ != INITIALIZING) || IsValidId(id)) |
1153 return kReachedIdLimit; | 1371 return kReachedIdLimit; |
1154 | 1372 |
1155 bool has_audio = false; | 1373 bool has_audio = false; |
1156 bool has_video = false; | 1374 bool has_video = false; |
1157 scoped_ptr<media::StreamParser> stream_parser( | 1375 scoped_ptr<media::StreamParser> stream_parser( |
1158 StreamParserFactory::Create(type, codecs, log_cb_, | 1376 StreamParserFactory::Create(type, codecs, log_cb_, |
1159 &has_audio, &has_video)); | 1377 &has_audio, &has_video)); |
(...skipping 11 matching lines...) Expand all Loading... |
1171 if (has_video) | 1389 if (has_video) |
1172 source_id_video_ = id; | 1390 source_id_video_ = id; |
1173 | 1391 |
1174 scoped_ptr<FrameProcessor> frame_processor( | 1392 scoped_ptr<FrameProcessor> frame_processor( |
1175 new FrameProcessor(base::Bind(&ChunkDemuxer::IncreaseDurationIfNecessary, | 1393 new FrameProcessor(base::Bind(&ChunkDemuxer::IncreaseDurationIfNecessary, |
1176 base::Unretained(this)))); | 1394 base::Unretained(this)))); |
1177 | 1395 |
1178 scoped_ptr<SourceState> source_state( | 1396 scoped_ptr<SourceState> source_state( |
1179 new SourceState(stream_parser.Pass(), | 1397 new SourceState(stream_parser.Pass(), |
1180 frame_processor.Pass(), log_cb_, | 1398 frame_processor.Pass(), log_cb_, |
| 1399 new_init_segment_cb, |
1181 base::Bind(&ChunkDemuxer::CreateDemuxerStream, | 1400 base::Bind(&ChunkDemuxer::CreateDemuxerStream, |
1182 base::Unretained(this)))); | 1401 base::Unretained(this)))); |
1183 | 1402 |
1184 SourceState::NewTextTrackCB new_text_track_cb; | 1403 SourceState::NewTextTrackCB new_text_track_cb; |
1185 | 1404 |
1186 if (enable_text_) { | 1405 if (enable_text_) { |
1187 new_text_track_cb = base::Bind(&ChunkDemuxer::OnNewTextTrack, | 1406 new_text_track_cb = base::Bind(&ChunkDemuxer::OnNewTextTrack, |
1188 base::Unretained(this)); | 1407 base::Unretained(this)); |
1189 } | 1408 } |
1190 | 1409 |
(...skipping 548 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1739 } | 1958 } |
1740 | 1959 |
1741 void ChunkDemuxer::ShutdownAllStreams() { | 1960 void ChunkDemuxer::ShutdownAllStreams() { |
1742 for (SourceStateMap::iterator itr = source_state_map_.begin(); | 1961 for (SourceStateMap::iterator itr = source_state_map_.begin(); |
1743 itr != source_state_map_.end(); ++itr) { | 1962 itr != source_state_map_.end(); ++itr) { |
1744 itr->second->Shutdown(); | 1963 itr->second->Shutdown(); |
1745 } | 1964 } |
1746 } | 1965 } |
1747 | 1966 |
1748 } // namespace media | 1967 } // namespace media |
OLD | NEW |