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

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

Issue 2226443002: Support multiple media tracks in MSE / ChunkDemuxer (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: mp4 format is not supported on some trybots, so use webm Created 4 years, 3 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
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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/media_source_state.h" 5 #include "media/filters/media_source_state.h"
6 6
7 #include <set>
8
7 #include "base/callback_helpers.h" 9 #include "base/callback_helpers.h"
8 #include "base/command_line.h" 10 #include "base/command_line.h"
9 #include "base/stl_util.h" 11 #include "base/stl_util.h"
10 #include "base/strings/string_number_conversions.h" 12 #include "base/strings/string_number_conversions.h"
11 #include "media/base/media_switches.h" 13 #include "media/base/media_switches.h"
12 #include "media/base/media_track.h" 14 #include "media/base/media_track.h"
13 #include "media/base/media_tracks.h" 15 #include "media/base/media_tracks.h"
16 #include "media/base/mime_util.h"
14 #include "media/filters/chunk_demuxer.h" 17 #include "media/filters/chunk_demuxer.h"
15 #include "media/filters/frame_processor.h" 18 #include "media/filters/frame_processor.h"
16 #include "media/filters/source_buffer_stream.h" 19 #include "media/filters/source_buffer_stream.h"
17 20
18 namespace media { 21 namespace media {
19 22
20 enum { 23 enum {
21 // Limits the number of MEDIA_LOG() calls warning the user that a muxed stream 24 // Limits the number of MEDIA_LOG() calls warning the user that a muxed stream
22 // media segment is missing a block from at least one of the audio or video 25 // media segment is missing a block from at least one of the audio or video
23 // tracks. 26 // tracks.
24 kMaxMissingTrackInSegmentLogs = 10, 27 kMaxMissingTrackInSegmentLogs = 10,
25 }; 28 };
26 29
27 static TimeDelta EndTimestamp(const StreamParser::BufferQueue& queue) { 30 namespace {
31
32 TimeDelta EndTimestamp(const StreamParser::BufferQueue& queue) {
28 return queue.back()->timestamp() + queue.back()->duration(); 33 return queue.back()->timestamp() + queue.back()->duration();
29 } 34 }
30 35
36 const char* ToStr(MediaTrack::Type type) {
wolenetz 2016/09/13 21:03:14 nit: useful elsewhere? If so, move to some method
servolk 2016/09/14 18:15:25 Done.
37 switch (type) {
38 case MediaTrack::Audio:
39 return "audio";
40 case MediaTrack::Text:
41 return "text";
42 case MediaTrack::Video:
43 return "video";
44 }
45 NOTREACHED();
46 return "INVALID";
47 }
48
49 } // namespace
50
31 // List of time ranges for each SourceBuffer. 51 // List of time ranges for each SourceBuffer.
32 // static 52 // static
33 Ranges<TimeDelta> MediaSourceState::ComputeRangesIntersection( 53 Ranges<TimeDelta> MediaSourceState::ComputeRangesIntersection(
34 const RangesList& active_ranges, 54 const RangesList& active_ranges,
35 bool ended) { 55 bool ended) {
36 // TODO(servolk): Perhaps this can be removed in favor of blink implementation 56 // TODO(servolk): Perhaps this can be removed in favor of blink implementation
37 // (MediaSource::buffered)? Currently this is only used on Android and for 57 // (MediaSource::buffered)? Currently this is only used on Android and for
38 // updating DemuxerHost's buffered ranges during AppendData() as well as 58 // updating DemuxerHost's buffered ranges during AppendData() as well as
39 // SourceBuffer.buffered property implemetation. 59 // SourceBuffer.buffered property implemetation.
40 // Implementation of HTMLMediaElement.buffered algorithm in MSE spec. 60 // Implementation of HTMLMediaElement.buffered algorithm in MSE spec.
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
87 } 107 }
88 108
89 MediaSourceState::MediaSourceState( 109 MediaSourceState::MediaSourceState(
90 std::unique_ptr<StreamParser> stream_parser, 110 std::unique_ptr<StreamParser> stream_parser,
91 std::unique_ptr<FrameProcessor> frame_processor, 111 std::unique_ptr<FrameProcessor> frame_processor,
92 const CreateDemuxerStreamCB& create_demuxer_stream_cb, 112 const CreateDemuxerStreamCB& create_demuxer_stream_cb,
93 const scoped_refptr<MediaLog>& media_log) 113 const scoped_refptr<MediaLog>& media_log)
94 : create_demuxer_stream_cb_(create_demuxer_stream_cb), 114 : create_demuxer_stream_cb_(create_demuxer_stream_cb),
95 timestamp_offset_during_append_(NULL), 115 timestamp_offset_during_append_(NULL),
96 parsing_media_segment_(false), 116 parsing_media_segment_(false),
97 media_segment_contained_audio_frame_(false),
98 media_segment_contained_video_frame_(false),
99 stream_parser_(stream_parser.release()), 117 stream_parser_(stream_parser.release()),
100 audio_(NULL),
101 video_(NULL),
102 frame_processor_(frame_processor.release()), 118 frame_processor_(frame_processor.release()),
103 media_log_(media_log), 119 media_log_(media_log),
104 state_(UNINITIALIZED), 120 state_(UNINITIALIZED),
105 auto_update_timestamp_offset_(false) { 121 auto_update_timestamp_offset_(false) {
106 DCHECK(!create_demuxer_stream_cb_.is_null()); 122 DCHECK(!create_demuxer_stream_cb_.is_null());
107 DCHECK(frame_processor_); 123 DCHECK(frame_processor_);
108 } 124 }
109 125
110 MediaSourceState::~MediaSourceState() { 126 MediaSourceState::~MediaSourceState() {
111 Shutdown(); 127 Shutdown();
112 128
113 base::STLDeleteValues(&text_stream_map_); 129 base::STLDeleteValues(&text_stream_map_);
114 } 130 }
115 131
116 void MediaSourceState::Init( 132 void MediaSourceState::Init(
117 const StreamParser::InitCB& init_cb, 133 const StreamParser::InitCB& init_cb,
118 bool allow_audio, 134 const std::string& expected_codecs,
119 bool allow_video,
120 const StreamParser::EncryptedMediaInitDataCB& encrypted_media_init_data_cb, 135 const StreamParser::EncryptedMediaInitDataCB& encrypted_media_init_data_cb,
121 const NewTextTrackCB& new_text_track_cb) { 136 const NewTextTrackCB& new_text_track_cb) {
122 DCHECK_EQ(state_, UNINITIALIZED); 137 DCHECK_EQ(state_, UNINITIALIZED);
123 new_text_track_cb_ = new_text_track_cb; 138 new_text_track_cb_ = new_text_track_cb;
124 init_cb_ = init_cb; 139 init_cb_ = init_cb;
125 140
126 state_ = PENDING_PARSER_CONFIG; 141 state_ = PENDING_PARSER_CONFIG;
127 stream_parser_->Init( 142 stream_parser_->Init(
128 base::Bind(&MediaSourceState::OnSourceInitDone, base::Unretained(this)), 143 base::Bind(&MediaSourceState::OnSourceInitDone, base::Unretained(this)),
129 base::Bind(&MediaSourceState::OnNewConfigs, base::Unretained(this), 144 base::Bind(&MediaSourceState::OnNewConfigs, base::Unretained(this),
130 allow_audio, allow_video), 145 expected_codecs),
131 base::Bind(&MediaSourceState::OnNewBuffers, base::Unretained(this)), 146 base::Bind(&MediaSourceState::OnNewBuffers, base::Unretained(this)),
132 new_text_track_cb_.is_null(), encrypted_media_init_data_cb, 147 new_text_track_cb_.is_null(), encrypted_media_init_data_cb,
133 base::Bind(&MediaSourceState::OnNewMediaSegment, base::Unretained(this)), 148 base::Bind(&MediaSourceState::OnNewMediaSegment, base::Unretained(this)),
134 base::Bind(&MediaSourceState::OnEndOfMediaSegment, 149 base::Bind(&MediaSourceState::OnEndOfMediaSegment,
135 base::Unretained(this)), 150 base::Unretained(this)),
136 media_log_); 151 media_log_);
137 } 152 }
138 153
139 void MediaSourceState::SetSequenceMode(bool sequence_mode) { 154 void MediaSourceState::SetSequenceMode(bool sequence_mode) {
140 DCHECK(!parsing_media_segment_); 155 DCHECK(!parsing_media_segment_);
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
189 DCHECK(!timestamp_offset_during_append_); 204 DCHECK(!timestamp_offset_during_append_);
190 timestamp_offset_during_append_ = timestamp_offset; 205 timestamp_offset_during_append_ = timestamp_offset;
191 append_window_start_during_append_ = append_window_start; 206 append_window_start_during_append_ = append_window_start;
192 append_window_end_during_append_ = append_window_end; 207 append_window_end_during_append_ = append_window_end;
193 208
194 stream_parser_->Flush(); 209 stream_parser_->Flush();
195 timestamp_offset_during_append_ = NULL; 210 timestamp_offset_during_append_ = NULL;
196 211
197 frame_processor_->Reset(); 212 frame_processor_->Reset();
198 parsing_media_segment_ = false; 213 parsing_media_segment_ = false;
199 media_segment_contained_audio_frame_ = false; 214 media_segment_has_data_for_track_.clear();
200 media_segment_contained_video_frame_ = false;
201 } 215 }
202 216
203 void MediaSourceState::Remove(TimeDelta start, 217 void MediaSourceState::Remove(TimeDelta start,
204 TimeDelta end, 218 TimeDelta end,
205 TimeDelta duration) { 219 TimeDelta duration) {
206 if (audio_) 220 for (const auto& it : audio_streams_) {
207 audio_->Remove(start, end, duration); 221 it.second->Remove(start, end, duration);
222 }
208 223
209 if (video_) 224 for (const auto& it : video_streams_) {
210 video_->Remove(start, end, duration); 225 it.second->Remove(start, end, duration);
226 }
211 227
212 for (TextStreamMap::iterator itr = text_stream_map_.begin(); 228 for (TextStreamMap::iterator itr = text_stream_map_.begin();
213 itr != text_stream_map_.end(); ++itr) { 229 itr != text_stream_map_.end(); ++itr) {
214 itr->second->Remove(start, end, duration); 230 itr->second->Remove(start, end, duration);
215 } 231 }
216 } 232 }
217 233
218 size_t MediaSourceState::EstimateVideoDataSize(
219 size_t muxed_data_chunk_size) const {
220 DCHECK(audio_);
221 DCHECK(video_);
222
223 size_t videoBufferedSize = video_->GetBufferedSize();
224 size_t audioBufferedSize = audio_->GetBufferedSize();
225 if (videoBufferedSize == 0 || audioBufferedSize == 0) {
226 // At this point either audio or video buffer is empty, which means buffer
227 // levels are probably low anyway and we should have enough space in the
228 // buffers for appending new data, so just take a very rough guess.
229 return muxed_data_chunk_size * 7 / 8;
230 }
231
232 // We need to estimate how much audio and video data is going to be in the
233 // newly appended data chunk to make space for the new data. And we need to do
234 // that without parsing the data (which will happen later, in the Append
235 // phase). So for now we can only rely on some heuristic here. Let's assume
236 // that the proportion of the audio/video in the new data chunk is the same as
237 // the current ratio of buffered audio/video.
238 // Longer term this should go away once we further change the MSE GC algorithm
239 // to work across all streams of a SourceBuffer (see crbug.com/520704).
240 double videoBufferedSizeF = static_cast<double>(videoBufferedSize);
241 double audioBufferedSizeF = static_cast<double>(audioBufferedSize);
242
243 double totalBufferedSizeF = videoBufferedSizeF + audioBufferedSizeF;
244 CHECK_GT(totalBufferedSizeF, 0.0);
245
246 double videoRatio = videoBufferedSizeF / totalBufferedSizeF;
247 CHECK_GE(videoRatio, 0.0);
248 CHECK_LE(videoRatio, 1.0);
249 double estimatedVideoSize = muxed_data_chunk_size * videoRatio;
250 return static_cast<size_t>(estimatedVideoSize);
251 }
252
253 bool MediaSourceState::EvictCodedFrames(DecodeTimestamp media_time, 234 bool MediaSourceState::EvictCodedFrames(DecodeTimestamp media_time,
254 size_t newDataSize) { 235 size_t newDataSize) {
255 bool success = true; 236 bool success = true;
256 237
257 DVLOG(3) << __func__ << " media_time=" << media_time.InSecondsF() 238 DVLOG(3) << __func__ << " media_time=" << media_time.InSecondsF()
258 << " newDataSize=" << newDataSize 239 << " newDataSize=" << newDataSize;
259 << " videoBufferedSize=" << (video_ ? video_->GetBufferedSize() : 0)
260 << " audioBufferedSize=" << (audio_ ? audio_->GetBufferedSize() : 0);
261 240
262 size_t newAudioSize = 0; 241 DVLOG(4) << "Before EvictCodedFrames:";
263 size_t newVideoSize = 0; 242 for (const auto& it : audio_streams_) {
264 if (audio_ && video_) { 243 DVLOG(4) << "Audio track_id=" << it.second->media_track_id()
265 newVideoSize = EstimateVideoDataSize(newDataSize); 244 << " buffered_size=" << it.second->GetBufferedSize();
266 newAudioSize = newDataSize - newVideoSize; 245 }
267 } else if (video_) { 246 for (const auto& it : video_streams_) {
268 newVideoSize = newDataSize; 247 DVLOG(4) << "Video track_id=" << it.second->media_track_id()
269 } else if (audio_) { 248 << " buffered_size=" << it.second->GetBufferedSize();
270 newAudioSize = newDataSize;
271 } 249 }
272 250
273 DVLOG(3) << __func__ 251 size_t estimatedAudioSize = newDataSize;
274 << " estimated audio/video sizes: newVideoSize=" << newVideoSize 252 size_t estimatedVideoSize = newDataSize;
275 << " newAudioSize=" << newAudioSize; 253 if (!audio_streams_.empty() && !video_streams_.empty()) {
254 estimatedAudioSize = newDataSize / 16;
wolenetz 2016/09/13 21:03:14 nit: Assumption is overall, across all A+V tracks
servolk 2016/09/14 18:15:26 Yeah, after pondering this a bit more, I think we
wolenetz 2016/09/14 23:31:21 Acknowledged.
255 estimatedVideoSize = newDataSize - estimatedAudioSize;
256 }
257 if (audio_streams_.size() > 0)
258 estimatedAudioSize /= audio_streams_.size();
259 if (video_streams_.size() > 0)
260 estimatedVideoSize /= video_streams_.size();
276 261
277 if (audio_) 262 for (const auto& it : audio_streams_) {
278 success = audio_->EvictCodedFrames(media_time, newAudioSize) && success; 263 success &= it.second->EvictCodedFrames(media_time, estimatedAudioSize);
279 264 }
280 if (video_) 265 for (const auto& it : video_streams_) {
281 success = video_->EvictCodedFrames(media_time, newVideoSize) && success; 266 success &= it.second->EvictCodedFrames(media_time, estimatedVideoSize);
267 }
282 268
283 for (TextStreamMap::iterator itr = text_stream_map_.begin(); 269 for (TextStreamMap::iterator itr = text_stream_map_.begin();
284 itr != text_stream_map_.end(); ++itr) { 270 itr != text_stream_map_.end(); ++itr) {
285 success = itr->second->EvictCodedFrames(media_time, 0) && success; 271 success &= itr->second->EvictCodedFrames(media_time, 0);
286 } 272 }
287 273
288 DVLOG(3) << __func__ << " result=" << success 274 DVLOG(4) << "After EvictCodedFrames (success=" << success << "):";
289 << " videoBufferedSize=" << (video_ ? video_->GetBufferedSize() : 0) 275 for (const auto& it : audio_streams_) {
290 << " audioBufferedSize=" << (audio_ ? audio_->GetBufferedSize() : 0); 276 DVLOG(4) << "Audio track_id=" << it.second->media_track_id()
277 << " buffered_size=" << it.second->GetBufferedSize();
278 }
279 for (const auto& it : video_streams_) {
280 DVLOG(4) << "Video track_id=" << it.second->media_track_id()
281 << " buffered_size=" << it.second->GetBufferedSize();
282 }
291 283
292 return success; 284 return success;
293 } 285 }
294 286
295 Ranges<TimeDelta> MediaSourceState::GetBufferedRanges(TimeDelta duration, 287 Ranges<TimeDelta> MediaSourceState::GetBufferedRanges(TimeDelta duration,
wolenetz 2016/09/13 21:03:14 hmm. I think that TODO was wrong. SourceBuffer.buf
servolk 2016/09/14 18:15:26 Ok, in that case we can just drop the check for en
296 bool ended) const { 288 bool ended) const {
297 // TODO(acolwell): When we start allowing disabled tracks we'll need to update
298 // this code to only add ranges from active tracks.
299 RangesList ranges_list; 289 RangesList ranges_list;
300 if (audio_) 290 for (const auto& it : audio_streams_) {
301 ranges_list.push_back(audio_->GetBufferedRanges(duration)); 291 if (it.second->enabled())
292 ranges_list.push_back(it.second->GetBufferedRanges(duration));
293 }
302 294
303 if (video_) 295 for (const auto& it : video_streams_) {
304 ranges_list.push_back(video_->GetBufferedRanges(duration)); 296 if (it.second->enabled())
297 ranges_list.push_back(it.second->GetBufferedRanges(duration));
298 }
305 299
306 for (TextStreamMap::const_iterator itr = text_stream_map_.begin(); 300 for (TextStreamMap::const_iterator itr = text_stream_map_.begin();
307 itr != text_stream_map_.end(); ++itr) { 301 itr != text_stream_map_.end(); ++itr) {
308 ranges_list.push_back(itr->second->GetBufferedRanges(duration)); 302 ranges_list.push_back(itr->second->GetBufferedRanges(duration));
309 } 303 }
310 304
311 return ComputeRangesIntersection(ranges_list, ended); 305 return ComputeRangesIntersection(ranges_list, ended);
312 } 306 }
313 307
314 TimeDelta MediaSourceState::GetHighestPresentationTimestamp() const { 308 TimeDelta MediaSourceState::GetHighestPresentationTimestamp() const {
315 TimeDelta max_pts; 309 TimeDelta max_pts;
316 310
317 if (audio_) 311 for (const auto& it : audio_streams_) {
318 max_pts = std::max(max_pts, audio_->GetHighestPresentationTimestamp()); 312 max_pts = std::max(max_pts, it.second->GetHighestPresentationTimestamp());
313 }
319 314
320 if (video_) 315 for (const auto& it : video_streams_) {
321 max_pts = std::max(max_pts, video_->GetHighestPresentationTimestamp()); 316 max_pts = std::max(max_pts, it.second->GetHighestPresentationTimestamp());
317 }
322 318
323 for (TextStreamMap::const_iterator itr = text_stream_map_.begin(); 319 for (TextStreamMap::const_iterator itr = text_stream_map_.begin();
324 itr != text_stream_map_.end(); ++itr) { 320 itr != text_stream_map_.end(); ++itr) {
325 max_pts = std::max(max_pts, itr->second->GetHighestPresentationTimestamp()); 321 max_pts = std::max(max_pts, itr->second->GetHighestPresentationTimestamp());
326 } 322 }
327 323
328 return max_pts; 324 return max_pts;
329 } 325 }
330 326
331 TimeDelta MediaSourceState::GetMaxBufferedDuration() const { 327 TimeDelta MediaSourceState::GetMaxBufferedDuration() const {
332 TimeDelta max_duration; 328 TimeDelta max_duration;
333 329
334 if (audio_) 330 for (const auto& it : audio_streams_) {
335 max_duration = std::max(max_duration, audio_->GetBufferedDuration()); 331 max_duration = std::max(max_duration, it.second->GetBufferedDuration());
332 }
336 333
337 if (video_) 334 for (const auto& it : video_streams_) {
338 max_duration = std::max(max_duration, video_->GetBufferedDuration()); 335 max_duration = std::max(max_duration, it.second->GetBufferedDuration());
336 }
339 337
340 for (TextStreamMap::const_iterator itr = text_stream_map_.begin(); 338 for (TextStreamMap::const_iterator itr = text_stream_map_.begin();
341 itr != text_stream_map_.end(); ++itr) { 339 itr != text_stream_map_.end(); ++itr) {
342 max_duration = std::max(max_duration, itr->second->GetBufferedDuration()); 340 max_duration = std::max(max_duration, itr->second->GetBufferedDuration());
343 } 341 }
344 342
345 return max_duration; 343 return max_duration;
346 } 344 }
347 345
348 void MediaSourceState::StartReturningData() { 346 void MediaSourceState::StartReturningData() {
349 if (audio_) 347 for (const auto& it : audio_streams_) {
350 audio_->StartReturningData(); 348 it.second->StartReturningData();
349 }
351 350
352 if (video_) 351 for (const auto& it : video_streams_) {
353 video_->StartReturningData(); 352 it.second->StartReturningData();
353 }
354 354
355 for (TextStreamMap::iterator itr = text_stream_map_.begin(); 355 for (TextStreamMap::iterator itr = text_stream_map_.begin();
356 itr != text_stream_map_.end(); ++itr) { 356 itr != text_stream_map_.end(); ++itr) {
357 itr->second->StartReturningData(); 357 itr->second->StartReturningData();
358 } 358 }
359 } 359 }
360 360
361 void MediaSourceState::AbortReads() { 361 void MediaSourceState::AbortReads() {
362 if (audio_) 362 for (const auto& it : audio_streams_) {
363 audio_->AbortReads(); 363 it.second->AbortReads();
364 }
364 365
365 if (video_) 366 for (const auto& it : video_streams_) {
366 video_->AbortReads(); 367 it.second->AbortReads();
368 }
367 369
368 for (TextStreamMap::iterator itr = text_stream_map_.begin(); 370 for (TextStreamMap::iterator itr = text_stream_map_.begin();
369 itr != text_stream_map_.end(); ++itr) { 371 itr != text_stream_map_.end(); ++itr) {
370 itr->second->AbortReads(); 372 itr->second->AbortReads();
371 } 373 }
372 } 374 }
373 375
374 void MediaSourceState::Seek(TimeDelta seek_time) { 376 void MediaSourceState::Seek(TimeDelta seek_time) {
375 if (audio_) 377 for (const auto& it : audio_streams_) {
376 audio_->Seek(seek_time); 378 it.second->Seek(seek_time);
379 }
377 380
378 if (video_) 381 for (const auto& it : video_streams_) {
379 video_->Seek(seek_time); 382 it.second->Seek(seek_time);
383 }
380 384
381 for (TextStreamMap::iterator itr = text_stream_map_.begin(); 385 for (TextStreamMap::iterator itr = text_stream_map_.begin();
382 itr != text_stream_map_.end(); ++itr) { 386 itr != text_stream_map_.end(); ++itr) {
383 itr->second->Seek(seek_time); 387 itr->second->Seek(seek_time);
384 } 388 }
385 } 389 }
386 390
387 void MediaSourceState::CompletePendingReadIfPossible() { 391 void MediaSourceState::CompletePendingReadIfPossible() {
388 if (audio_) 392 for (const auto& it : audio_streams_) {
389 audio_->CompletePendingReadIfPossible(); 393 it.second->CompletePendingReadIfPossible();
394 }
390 395
391 if (video_) 396 for (const auto& it : video_streams_) {
392 video_->CompletePendingReadIfPossible(); 397 it.second->CompletePendingReadIfPossible();
398 }
393 399
394 for (TextStreamMap::iterator itr = text_stream_map_.begin(); 400 for (TextStreamMap::iterator itr = text_stream_map_.begin();
395 itr != text_stream_map_.end(); ++itr) { 401 itr != text_stream_map_.end(); ++itr) {
396 itr->second->CompletePendingReadIfPossible(); 402 itr->second->CompletePendingReadIfPossible();
397 } 403 }
398 } 404 }
399 405
400 void MediaSourceState::OnSetDuration(TimeDelta duration) { 406 void MediaSourceState::OnSetDuration(TimeDelta duration) {
401 if (audio_) 407 for (const auto& it : audio_streams_) {
402 audio_->OnSetDuration(duration); 408 it.second->OnSetDuration(duration);
409 }
403 410
404 if (video_) 411 for (const auto& it : video_streams_) {
405 video_->OnSetDuration(duration); 412 it.second->OnSetDuration(duration);
413 }
406 414
407 for (TextStreamMap::iterator itr = text_stream_map_.begin(); 415 for (TextStreamMap::iterator itr = text_stream_map_.begin();
408 itr != text_stream_map_.end(); ++itr) { 416 itr != text_stream_map_.end(); ++itr) {
409 itr->second->OnSetDuration(duration); 417 itr->second->OnSetDuration(duration);
410 } 418 }
411 } 419 }
412 420
413 void MediaSourceState::MarkEndOfStream() { 421 void MediaSourceState::MarkEndOfStream() {
414 if (audio_) 422 for (const auto& it : audio_streams_) {
415 audio_->MarkEndOfStream(); 423 it.second->MarkEndOfStream();
424 }
416 425
417 if (video_) 426 for (const auto& it : video_streams_) {
418 video_->MarkEndOfStream(); 427 it.second->MarkEndOfStream();
428 }
419 429
420 for (TextStreamMap::iterator itr = text_stream_map_.begin(); 430 for (TextStreamMap::iterator itr = text_stream_map_.begin();
421 itr != text_stream_map_.end(); ++itr) { 431 itr != text_stream_map_.end(); ++itr) {
422 itr->second->MarkEndOfStream(); 432 itr->second->MarkEndOfStream();
423 } 433 }
424 } 434 }
425 435
426 void MediaSourceState::UnmarkEndOfStream() { 436 void MediaSourceState::UnmarkEndOfStream() {
427 if (audio_) 437 for (const auto& it : audio_streams_) {
428 audio_->UnmarkEndOfStream(); 438 it.second->UnmarkEndOfStream();
439 }
429 440
430 if (video_) 441 for (const auto& it : video_streams_) {
431 video_->UnmarkEndOfStream(); 442 it.second->UnmarkEndOfStream();
443 }
432 444
433 for (TextStreamMap::iterator itr = text_stream_map_.begin(); 445 for (TextStreamMap::iterator itr = text_stream_map_.begin();
434 itr != text_stream_map_.end(); ++itr) { 446 itr != text_stream_map_.end(); ++itr) {
435 itr->second->UnmarkEndOfStream(); 447 itr->second->UnmarkEndOfStream();
436 } 448 }
437 } 449 }
438 450
439 void MediaSourceState::Shutdown() { 451 void MediaSourceState::Shutdown() {
440 if (audio_) 452 for (const auto& it : audio_streams_) {
441 audio_->Shutdown(); 453 it.second->Shutdown();
454 }
442 455
443 if (video_) 456 for (const auto& it : video_streams_) {
444 video_->Shutdown(); 457 it.second->Shutdown();
458 }
445 459
446 for (TextStreamMap::iterator itr = text_stream_map_.begin(); 460 for (TextStreamMap::iterator itr = text_stream_map_.begin();
447 itr != text_stream_map_.end(); ++itr) { 461 itr != text_stream_map_.end(); ++itr) {
448 itr->second->Shutdown(); 462 itr->second->Shutdown();
449 } 463 }
450 } 464 }
451 465
452 void MediaSourceState::SetMemoryLimits(DemuxerStream::Type type, 466 void MediaSourceState::SetMemoryLimits(DemuxerStream::Type type,
wolenetz 2016/09/13 21:03:14 nit:ForTest here, in ChunkDemuxer, and in Pipeline
servolk 2016/09/14 18:15:26 Well, because of the cmdline flags I think we shou
wolenetz 2016/09/14 23:31:21 Unless I'm missing something new in this CL, or cs
servolk 2016/09/15 00:18:32 Ah, ok, you are right, I've got confused, thought
453 size_t memory_limit) { 467 size_t memory_limit) {
454 switch (type) { 468 switch (type) {
455 case DemuxerStream::AUDIO: 469 case DemuxerStream::AUDIO:
456 if (audio_) 470 for (const auto& it : audio_streams_) {
457 audio_->SetStreamMemoryLimit(memory_limit); 471 it.second->SetStreamMemoryLimit(memory_limit);
472 }
458 break; 473 break;
459 case DemuxerStream::VIDEO: 474 case DemuxerStream::VIDEO:
460 if (video_) 475 for (const auto& it : video_streams_) {
461 video_->SetStreamMemoryLimit(memory_limit); 476 it.second->SetStreamMemoryLimit(memory_limit);
477 }
462 break; 478 break;
463 case DemuxerStream::TEXT: 479 case DemuxerStream::TEXT:
464 for (TextStreamMap::iterator itr = text_stream_map_.begin(); 480 for (TextStreamMap::iterator itr = text_stream_map_.begin();
465 itr != text_stream_map_.end(); ++itr) { 481 itr != text_stream_map_.end(); ++itr) {
466 itr->second->SetStreamMemoryLimit(memory_limit); 482 itr->second->SetStreamMemoryLimit(memory_limit);
467 } 483 }
468 break; 484 break;
469 case DemuxerStream::UNKNOWN: 485 case DemuxerStream::UNKNOWN:
470 case DemuxerStream::NUM_TYPES: 486 case DemuxerStream::NUM_TYPES:
471 NOTREACHED(); 487 NOTREACHED();
472 break; 488 break;
473 } 489 }
474 } 490 }
475 491
476 bool MediaSourceState::IsSeekWaitingForData() const { 492 bool MediaSourceState::IsSeekWaitingForData() const {
477 if (audio_ && audio_->IsSeekWaitingForData()) 493 for (const auto& it : audio_streams_) {
478 return true; 494 if (it.second->IsSeekWaitingForData())
495 return true;
496 }
479 497
480 if (video_ && video_->IsSeekWaitingForData()) 498 for (const auto& it : video_streams_) {
481 return true; 499 if (it.second->IsSeekWaitingForData())
500 return true;
501 }
482 502
483 // NOTE: We are intentionally not checking the text tracks 503 // NOTE: We are intentionally not checking the text tracks
484 // because text tracks are discontinuous and may not have data 504 // because text tracks are discontinuous and may not have data
485 // for the seek position. This is ok and playback should not be 505 // for the seek position. This is ok and playback should not be
486 // stalled because we don't have cues. If cues, with timestamps after 506 // stalled because we don't have cues. If cues, with timestamps after
487 // the seek time, eventually arrive they will be delivered properly 507 // the seek time, eventually arrive they will be delivered properly
488 // in response to ChunkDemuxerStream::Read() calls. 508 // in response to ChunkDemuxerStream::Read() calls.
489 509
490 return false; 510 return false;
491 } 511 }
492 512
513 bool CheckBytestreamTrackIds(
wolenetz 2016/09/13 21:03:14 move to anon namespace at top of file
servolk 2016/09/14 18:15:25 Done.
514 const MediaTracks& tracks,
515 const StreamParser::TextTrackConfigMap& text_configs) {
516 std::set<StreamParser::TrackId> bytestream_ids;
517 for (const auto& track : tracks.tracks()) {
518 const StreamParser::TrackId& track_id = track->bytestream_track_id();
519 if (bytestream_ids.find(track_id) != bytestream_ids.end()) {
520 return false;
521 }
522 bytestream_ids.insert(track_id);
523 }
524 for (const auto& text_track : text_configs) {
525 const StreamParser::TrackId& track_id = text_track.first;
526 if (bytestream_ids.find(track_id) != bytestream_ids.end()) {
527 return false;
528 }
529 bytestream_ids.insert(track_id);
530 }
531 return true;
532 }
533
493 bool MediaSourceState::OnNewConfigs( 534 bool MediaSourceState::OnNewConfigs(
494 bool allow_audio, 535 std::string expected_codecs,
495 bool allow_video,
496 std::unique_ptr<MediaTracks> tracks, 536 std::unique_ptr<MediaTracks> tracks,
497 const StreamParser::TextTrackConfigMap& text_configs) { 537 const StreamParser::TextTrackConfigMap& text_configs) {
538 DCHECK(tracks.get());
539 DVLOG(1) << __func__ << " expected_codecs=" << expected_codecs
540 << " tracks=" << tracks->tracks().size();
498 DCHECK_GE(state_, PENDING_PARSER_CONFIG); 541 DCHECK_GE(state_, PENDING_PARSER_CONFIG);
499 DCHECK(tracks.get()); 542
500 543 // Check that there is no clashing bytestream track ids.
501 MediaTrack* audio_track = nullptr; 544 if (!CheckBytestreamTrackIds(*tracks, text_configs)) {
502 MediaTrack* video_track = nullptr; 545 MEDIA_LOG(ERROR, media_log_)
503 AudioDecoderConfig audio_config; 546 << "Error: duplicate bytestream track ids detected";
wolenetz 2016/09/13 21:03:13 nit: s/Error: duplicate/Duplicate/ (ERROR already
servolk 2016/09/14 18:15:26 Done.
504 VideoDecoderConfig video_config; 547 for (const auto& track : tracks->tracks()) {
548 const StreamParser::TrackId& track_id = track->bytestream_track_id();
549 MEDIA_LOG(ERROR, media_log_) << ToStr(track->type()) << " track "
wolenetz 2016/09/13 21:03:13 nit:s/ERROR/DEBUG/ so we can expose the last cache
servolk 2016/09/14 18:15:25 Done.
550 << " bytestream track id=" << track_id;
551 }
552 return false;
553 }
554
555 // MSE spec allows new configs to be emitted only during Append, but not
556 // during Flush or parser reset operations.
557 CHECK(append_in_progress_);
558
559 bool success = true;
560
561 std::vector<std::string> expected_codecs_parsed;
562 ParseCodecString(expected_codecs, &expected_codecs_parsed, false);
wolenetz 2016/09/13 21:03:14 nit: Every init segment, we parse these? Can we in
servolk 2016/09/14 18:15:25 Yeah, I don't think it's going to make a big diffe
wolenetz 2016/09/14 23:31:21 Acknowledged.
563
564 std::vector<AudioCodec> expected_acodecs;
565 std::vector<VideoCodec> expected_vcodecs;
566 for (const auto& codec_id : expected_codecs_parsed) {
567 AudioCodec acodec = StringToAudioCodec(codec_id);
568 if (acodec != kUnknownAudioCodec) {
569 expected_acodecs.push_back(acodec);
570 continue;
571 }
572 VideoCodec vcodec = StringToVideoCodec(codec_id);
573 if (vcodec != kUnknownVideoCodec) {
574 expected_vcodecs.push_back(vcodec);
575 continue;
576 }
577 MEDIA_LOG(INFO, media_log_) << "Unrecognized media codec: " << codec_id;
578 }
579
505 for (const auto& track : tracks->tracks()) { 580 for (const auto& track : tracks->tracks()) {
506 const auto& track_id = track->bytestream_track_id(); 581 const auto& track_id = track->bytestream_track_id();
507 582
508 if (track->type() == MediaTrack::Audio) { 583 if (track->type() == MediaTrack::Audio) {
509 if (audio_track) { 584 AudioDecoderConfig audio_config = tracks->getAudioConfig(track_id);
510 MEDIA_LOG(ERROR, media_log_) 585 DVLOG(1) << "Audio track_id=" << track_id
511 << "Error: more than one audio track is currently not supported."; 586 << " config: " << audio_config.AsHumanReadableString();
587 DCHECK(audio_config.IsValidConfig());
588
589 const auto& it = std::find(expected_acodecs.begin(),
590 expected_acodecs.end(), audio_config.codec());
591 if (it == expected_acodecs.end()) {
592 MEDIA_LOG(ERROR, media_log_) << "Audio stream codec "
593 << GetCodecName(audio_config.codec())
594 << " doesn't match SourceBuffer codecs.";
512 return false; 595 return false;
513 } 596 }
514 audio_track = track.get(); 597 expected_acodecs.erase(it);
wolenetz 2016/09/13 21:03:13 This is a problem for the following test I suggest
servolk 2016/09/14 18:15:26 Wait, shouldn't the mime type be 'audio/webm; code
wolenetz 2016/09/14 23:31:21 I've filed spec bug (MSE vNext) https://github.com
servolk 2016/09/15 00:18:32 Done.
515 audio_config = tracks->getAudioConfig(track_id); 598
516 DCHECK(audio_config.IsValidConfig()); 599 ChunkDemuxerStream* stream = nullptr;
600 if (!first_init_segment_received_) {
601 DCHECK(audio_streams_.find(track_id) == audio_streams_.end());
602 stream = create_demuxer_stream_cb_.Run(DemuxerStream::AUDIO);
603 if (!stream || !frame_processor_->AddTrack(track_id, stream)) {
604 MEDIA_LOG(ERROR, media_log_) << "Failed to create audio stream.";
605 return false;
606 }
607 audio_streams_[track_id] = stream;
608 media_log_->SetBooleanProperty("found_audio_stream", true);
609 media_log_->SetStringProperty("audio_codec_name",
610 GetCodecName(audio_config.codec()));
611 } else {
612 if (audio_streams_.size() > 1) {
613 stream = audio_streams_[track_id];
614 } else {
615 // If there is only one video track then bytestream id might change in
wolenetz 2016/09/13 21:03:14 nit:s/video/audio/
servolk 2016/09/14 18:15:26 Done.
616 // a new init segment. So update our state and nofity frame processor.
617 const auto& it = audio_streams_.begin();
618 if (it != audio_streams_.end()) {
619 stream = it->second;
620 if (it->first != track_id) {
621 frame_processor_->UpdateTrack(it->first, track_id);
622 audio_streams_[track_id] = stream;
623 audio_streams_.erase(it->first);
624 }
625 }
626 }
627 if (!stream) {
628 MEDIA_LOG(ERROR, media_log_) << "Got unexpected audio track"
629 << " track_id=" << track_id;
630 return false;
631 }
632 }
633
634 track->set_id(stream->media_track_id());
635 frame_processor_->OnPossibleAudioConfigUpdate(audio_config);
636 success &= stream->UpdateAudioConfig(audio_config, media_log_);
517 } else if (track->type() == MediaTrack::Video) { 637 } else if (track->type() == MediaTrack::Video) {
518 if (video_track) { 638 VideoDecoderConfig video_config = tracks->getVideoConfig(track_id);
519 MEDIA_LOG(ERROR, media_log_) 639 DVLOG(1) << "Video track_id=" << track_id
520 << "Error: more than one video track is currently not supported."; 640 << " config: " << video_config.AsHumanReadableString();
641 DCHECK(video_config.IsValidConfig());
642
643 const auto& it = std::find(expected_vcodecs.begin(),
644 expected_vcodecs.end(), video_config.codec());
645 if (it == expected_vcodecs.end()) {
646 MEDIA_LOG(ERROR, media_log_) << "Video stream codec "
647 << GetCodecName(video_config.codec())
648 << " doesn't match SourceBuffer codecs.";
521 return false; 649 return false;
522 } 650 }
523 video_track = track.get(); 651 expected_vcodecs.erase(it);
wolenetz 2016/09/13 21:03:13 ditto missing test and problem for multiple video
servolk 2016/09/14 18:15:26 Acknowledged.
524 video_config = tracks->getVideoConfig(track_id); 652
525 DCHECK(video_config.IsValidConfig()); 653 ChunkDemuxerStream* stream = nullptr;
654 if (!first_init_segment_received_) {
655 DCHECK(video_streams_.find(track_id) == video_streams_.end());
656 stream = create_demuxer_stream_cb_.Run(DemuxerStream::VIDEO);
657 if (!stream || !frame_processor_->AddTrack(track_id, stream)) {
658 MEDIA_LOG(ERROR, media_log_) << "Failed to create video stream.";
659 return false;
660 }
661 video_streams_[track_id] = stream;
662 media_log_->SetBooleanProperty("found_video_stream", true);
663 media_log_->SetStringProperty("video_codec_name",
664 GetCodecName(video_config.codec()));
665 } else {
666 if (video_streams_.size() > 1) {
667 stream = video_streams_[track_id];
668 } else {
669 // If there is only one video track then bytestream id might change in
670 // a new init segment. So update our state and nofity frame processor.
671 const auto& it = video_streams_.begin();
672 if (it != video_streams_.end()) {
673 stream = it->second;
674 if (it->first != track_id) {
675 frame_processor_->UpdateTrack(it->first, track_id);
676 video_streams_[track_id] = stream;
677 video_streams_.erase(it->first);
678 }
679 }
680 }
681 if (!stream) {
682 MEDIA_LOG(ERROR, media_log_) << "Got unexpected video track"
683 << " track_id=" << track_id;
684 return false;
685 }
686 }
687
688 track->set_id(stream->media_track_id());
689 success &= stream->UpdateVideoConfig(video_config, media_log_);
526 } else { 690 } else {
527 MEDIA_LOG(ERROR, media_log_) << "Error: unsupported media track type " 691 MEDIA_LOG(ERROR, media_log_) << "Error: unsupported media track type "
528 << track->type(); 692 << track->type();
529 return false; 693 return false;
530 } 694 }
531 } 695 }
532 696
533 DVLOG(1) << "OnNewConfigs(" << allow_audio << ", " << allow_video << ", " 697 if (!expected_acodecs.empty() || !expected_vcodecs.empty()) {
534 << audio_config.IsValidConfig() << ", " 698 for (const auto& acodec : expected_acodecs) {
535 << video_config.IsValidConfig() << ")"; 699 MEDIA_LOG(ERROR, media_log_) << "Initialization segment misses expected "
536 // MSE spec allows new configs to be emitted only during Append, but not 700 << GetCodecName(acodec) << " track.";
537 // during Flush or parser reset operations. 701 }
538 CHECK(append_in_progress_); 702 for (const auto& vcodec : expected_vcodecs) {
539 703 MEDIA_LOG(ERROR, media_log_) << "Initialization segment misses expected "
540 if (!audio_config.IsValidConfig() && !video_config.IsValidConfig()) { 704 << GetCodecName(vcodec) << " track.";
541 DVLOG(1) << "OnNewConfigs() : Audio & video config are not valid!"; 705 }
542 return false; 706 return false;
543 } 707 }
544 708
545 // Signal an error if we get configuration info for stream types that weren't
546 // specified in AddId() or more configs after a stream is initialized.
547 if (allow_audio != audio_config.IsValidConfig()) {
548 MEDIA_LOG(ERROR, media_log_)
549 << "Initialization segment"
550 << (audio_config.IsValidConfig() ? " has" : " does not have")
551 << " an audio track, but the mimetype"
552 << (allow_audio ? " specifies" : " does not specify")
553 << " an audio codec.";
554 return false;
555 }
556
557 if (allow_video != video_config.IsValidConfig()) {
558 MEDIA_LOG(ERROR, media_log_)
559 << "Initialization segment"
560 << (video_config.IsValidConfig() ? " has" : " does not have")
561 << " a video track, but the mimetype"
562 << (allow_video ? " specifies" : " does not specify")
563 << " a video codec.";
564 return false;
565 }
566
567 bool success = true;
568 if (audio_config.IsValidConfig()) {
569 if (!audio_) {
570 media_log_->SetBooleanProperty("found_audio_stream", true);
571 }
572 if (!audio_ ||
573 audio_->audio_decoder_config().codec() != audio_config.codec()) {
574 media_log_->SetStringProperty("audio_codec_name",
575 GetCodecName(audio_config.codec()));
576 }
577
578 bool audio_stream_just_created = false;
579 if (!audio_) {
580 audio_ = create_demuxer_stream_cb_.Run(DemuxerStream::AUDIO);
581
582 if (!audio_) {
583 DVLOG(1) << "Failed to create an audio stream.";
584 return false;
585 }
586 audio_stream_just_created = true;
587
588 if (!frame_processor_->AddTrack(FrameProcessor::kAudioTrackId, audio_)) {
589 DVLOG(1) << "Failed to add audio track to frame processor.";
590 return false;
591 }
592 }
593
594 frame_processor_->OnPossibleAudioConfigUpdate(audio_config);
595 success &= audio_->UpdateAudioConfig(audio_config, media_log_);
596
597 if (audio_stream_just_created) {
598 std::string audio_buf_limit_switch =
599 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
600 switches::kMSEAudioBufferSizeLimit);
601 unsigned audio_buf_size_limit = 0;
602 if (base::StringToUint(audio_buf_limit_switch, &audio_buf_size_limit) &&
603 audio_buf_size_limit > 0) {
604 MEDIA_LOG(INFO, media_log_) << "Custom audio SourceBuffer size limit="
605 << audio_buf_size_limit;
606 audio_->SetStreamMemoryLimit(audio_buf_size_limit);
607 }
608 }
609 }
610
611 if (video_config.IsValidConfig()) {
612 if (!video_) {
613 media_log_->SetBooleanProperty("found_video_stream", true);
614 }
615 if (!video_ ||
616 video_->video_decoder_config().codec() != video_config.codec()) {
617 media_log_->SetStringProperty("video_codec_name",
618 GetCodecName(video_config.codec()));
619 }
620
621 bool video_stream_just_created = false;
622 if (!video_) {
623 video_ = create_demuxer_stream_cb_.Run(DemuxerStream::VIDEO);
624
625 if (!video_) {
626 DVLOG(1) << "Failed to create a video stream.";
627 return false;
628 }
629 video_stream_just_created = true;
630
631 if (!frame_processor_->AddTrack(FrameProcessor::kVideoTrackId, video_)) {
632 DVLOG(1) << "Failed to add video track to frame processor.";
633 return false;
634 }
635 }
636
637 success &= video_->UpdateVideoConfig(video_config, media_log_);
638
639 if (video_stream_just_created) {
640 std::string video_buf_limit_switch =
641 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
642 switches::kMSEVideoBufferSizeLimit);
643 unsigned video_buf_size_limit = 0;
644 if (base::StringToUint(video_buf_limit_switch, &video_buf_size_limit) &&
645 video_buf_size_limit > 0) {
646 MEDIA_LOG(INFO, media_log_) << "Custom video SourceBuffer size limit="
647 << video_buf_size_limit;
648 video_->SetStreamMemoryLimit(video_buf_size_limit);
649 }
650 }
651 }
652
653 typedef StreamParser::TextTrackConfigMap::const_iterator TextConfigItr; 709 typedef StreamParser::TextTrackConfigMap::const_iterator TextConfigItr;
654 if (text_stream_map_.empty()) { 710 if (text_stream_map_.empty()) {
655 for (TextConfigItr itr = text_configs.begin(); itr != text_configs.end(); 711 for (TextConfigItr itr = text_configs.begin(); itr != text_configs.end();
656 ++itr) { 712 ++itr) {
657 ChunkDemuxerStream* const text_stream = 713 ChunkDemuxerStream* const text_stream =
658 create_demuxer_stream_cb_.Run(DemuxerStream::TEXT); 714 create_demuxer_stream_cb_.Run(DemuxerStream::TEXT);
659 if (!frame_processor_->AddTrack(itr->first, text_stream)) { 715 if (!frame_processor_->AddTrack(itr->first, text_stream)) {
660 success &= false; 716 success &= false;
661 MEDIA_LOG(ERROR, media_log_) << "Failed to add text track ID " 717 MEDIA_LOG(ERROR, media_log_) << "Failed to add text track ID "
662 << itr->first << " to frame processor."; 718 << itr->first << " to frame processor.";
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
718 success &= false; 774 success &= false;
719 MEDIA_LOG(ERROR, media_log_) << "New text track config for track ID " 775 MEDIA_LOG(ERROR, media_log_) << "New text track config for track ID "
720 << config_itr->first 776 << config_itr->first
721 << " does not match old one."; 777 << " does not match old one.";
722 break; 778 break;
723 } 779 }
724 } 780 }
725 } 781 }
726 } 782 }
727 783
784 if (audio_streams_.empty() && video_streams_.empty()) {
785 DVLOG(1) << __func__ << ": couldn't find a valid audio or video stream";
786 return false;
787 }
788
728 frame_processor_->SetAllTrackBuffersNeedRandomAccessPoint(); 789 frame_processor_->SetAllTrackBuffersNeedRandomAccessPoint();
729 790
730 if (audio_track) { 791 if (!first_init_segment_received_) {
731 DCHECK(audio_); 792 first_init_segment_received_ = true;
732 audio_track->set_id(audio_->media_track_id()); 793 SetStreamMemoryLimits();
733 }
734 if (video_track) {
735 DCHECK(video_);
736 video_track->set_id(video_->media_track_id());
737 } 794 }
738 795
739 DVLOG(1) << "OnNewConfigs() : " << (success ? "success" : "failed"); 796 DVLOG(1) << "OnNewConfigs() : " << (success ? "success" : "failed");
740 if (success) { 797 if (success) {
741 if (state_ == PENDING_PARSER_CONFIG) 798 if (state_ == PENDING_PARSER_CONFIG)
742 state_ = PENDING_PARSER_INIT; 799 state_ = PENDING_PARSER_INIT;
743 DCHECK(!init_segment_received_cb_.is_null()); 800 DCHECK(!init_segment_received_cb_.is_null());
744 init_segment_received_cb_.Run(std::move(tracks)); 801 init_segment_received_cb_.Run(std::move(tracks));
745 } 802 }
746 803
747 return success; 804 return success;
748 } 805 }
749 806
807 void MediaSourceState::SetStreamMemoryLimits() {
808 auto cmd_line = base::CommandLine::ForCurrentProcess();
809
810 std::string audio_buf_limit_switch =
811 cmd_line->GetSwitchValueASCII(switches::kMSEAudioBufferSizeLimit);
812 unsigned audio_buf_size_limit = 0;
813 if (base::StringToUint(audio_buf_limit_switch, &audio_buf_size_limit) &&
814 audio_buf_size_limit > 0) {
815 MEDIA_LOG(INFO, media_log_) << "Custom audio SourceBuffer size limit="
wolenetz 2016/09/13 21:03:14 nit: audio *per-track* SourceBuffer size limit=...
servolk 2016/09/14 18:15:26 Done.
816 << audio_buf_size_limit;
817 for (const auto& it : audio_streams_) {
818 it.second->SetStreamMemoryLimit(audio_buf_size_limit);
819 }
820 }
821
822 std::string video_buf_limit_switch =
823 cmd_line->GetSwitchValueASCII(switches::kMSEVideoBufferSizeLimit);
824 unsigned video_buf_size_limit = 0;
825 if (base::StringToUint(video_buf_limit_switch, &video_buf_size_limit) &&
826 video_buf_size_limit > 0) {
827 MEDIA_LOG(INFO, media_log_) << "Custom video SourceBuffer size limit="
wolenetz 2016/09/13 21:03:14 nit ditto
servolk 2016/09/14 18:15:26 Done.
828 << video_buf_size_limit;
829 for (const auto& it : video_streams_) {
830 it.second->SetStreamMemoryLimit(video_buf_size_limit);
831 }
832 }
833 }
834
750 void MediaSourceState::OnNewMediaSegment() { 835 void MediaSourceState::OnNewMediaSegment() {
751 DVLOG(2) << "OnNewMediaSegment()"; 836 DVLOG(2) << "OnNewMediaSegment()";
752 DCHECK_EQ(state_, PARSER_INITIALIZED); 837 DCHECK_EQ(state_, PARSER_INITIALIZED);
753 parsing_media_segment_ = true; 838 parsing_media_segment_ = true;
754 media_segment_contained_audio_frame_ = false; 839 media_segment_has_data_for_track_.clear();
755 media_segment_contained_video_frame_ = false;
756 } 840 }
757 841
758 void MediaSourceState::OnEndOfMediaSegment() { 842 void MediaSourceState::OnEndOfMediaSegment() {
759 DVLOG(2) << "OnEndOfMediaSegment()"; 843 DVLOG(2) << "OnEndOfMediaSegment()";
760 DCHECK_EQ(state_, PARSER_INITIALIZED); 844 DCHECK_EQ(state_, PARSER_INITIALIZED);
761 parsing_media_segment_ = false; 845 parsing_media_segment_ = false;
762 846
763 const bool missing_audio = audio_ && !media_segment_contained_audio_frame_; 847 for (const auto& it : audio_streams_) {
764 const bool missing_video = video_ && !media_segment_contained_video_frame_; 848 if (!media_segment_has_data_for_track_[it.first]) {
765 if (!missing_audio && !missing_video) 849 LIMITED_MEDIA_LOG(DEBUG, media_log_, num_missing_track_logs_,
766 return; 850 kMaxMissingTrackInSegmentLogs)
767 851 << "Media segment did not contain any coded frames for track "
768 LIMITED_MEDIA_LOG(DEBUG, media_log_, num_missing_track_logs_, 852 << it.first << ", mismatching initialization segment. Therefore, MSE"
769 kMaxMissingTrackInSegmentLogs) 853 " coded frame processing may not interoperably detect"
770 << "Media segment did not contain any " 854 " discontinuities in appended media.";
771 << (missing_audio && missing_video ? "audio or video" 855 }
772 : missing_audio ? "audio" : "video") 856 }
773 << " coded frames, mismatching initialization segment. Therefore, MSE " 857 for (const auto& it : video_streams_) {
774 "coded frame processing may not interoperably detect discontinuities " 858 if (!media_segment_has_data_for_track_[it.first]) {
775 "in appended media."; 859 LIMITED_MEDIA_LOG(DEBUG, media_log_, num_missing_track_logs_,
860 kMaxMissingTrackInSegmentLogs)
861 << "Media segment did not contain any coded frames for track "
862 << it.first << ", mismatching initialization segment. Therefore, MSE"
863 " coded frame processing may not interoperably detect"
864 " discontinuities in appended media.";
865 }
866 }
776 } 867 }
777 868
778 bool MediaSourceState::OnNewBuffers( 869 bool MediaSourceState::OnNewBuffers(
779 const StreamParser::BufferQueueMap& buffer_queue_map) { 870 const StreamParser::BufferQueueMap& buffer_queue_map) {
780 DVLOG(2) << "OnNewBuffers()"; 871 DVLOG(2) << __func__ << " buffer_queues=" << buffer_queue_map.size();
781 DCHECK_EQ(state_, PARSER_INITIALIZED); 872 DCHECK_EQ(state_, PARSER_INITIALIZED);
782 DCHECK(timestamp_offset_during_append_); 873 DCHECK(timestamp_offset_during_append_);
783 DCHECK(parsing_media_segment_); 874 DCHECK(parsing_media_segment_);
784 875
785 for (const auto& it : buffer_queue_map) { 876 for (const auto& it : buffer_queue_map) {
786 const StreamParser::BufferQueue& bufq = it.second; 877 const StreamParser::BufferQueue& bufq = it.second;
787 DCHECK(!bufq.empty()); 878 DCHECK(!bufq.empty());
788 if (bufq[0]->type() == DemuxerStream::AUDIO) { 879 media_segment_has_data_for_track_[it.first] = true;
789 media_segment_contained_audio_frame_ = true;
790 } else if (bufq[0]->type() == DemuxerStream::VIDEO) {
791 media_segment_contained_video_frame_ = true;
792 }
793 } 880 }
794 881
795 const TimeDelta timestamp_offset_before_processing = 882 const TimeDelta timestamp_offset_before_processing =
796 *timestamp_offset_during_append_; 883 *timestamp_offset_during_append_;
797 884
798 // Calculate the new timestamp offset for audio/video tracks if the stream 885 // Calculate the new timestamp offset for audio/video tracks if the stream
799 // parser has requested automatic updates. 886 // parser has requested automatic updates.
800 TimeDelta new_timestamp_offset = timestamp_offset_before_processing; 887 TimeDelta new_timestamp_offset = timestamp_offset_before_processing;
801 if (auto_update_timestamp_offset_) { 888 if (auto_update_timestamp_offset_) {
802 TimeDelta min_end_timestamp = kNoTimestamp; 889 TimeDelta min_end_timestamp = kNoTimestamp;
(...skipping 17 matching lines...) Expand all
820 } 907 }
821 908
822 // Only update the timestamp offset if the frame processor hasn't already. 909 // Only update the timestamp offset if the frame processor hasn't already.
823 if (auto_update_timestamp_offset_ && 910 if (auto_update_timestamp_offset_ &&
824 timestamp_offset_before_processing == *timestamp_offset_during_append_) { 911 timestamp_offset_before_processing == *timestamp_offset_during_append_) {
825 *timestamp_offset_during_append_ = new_timestamp_offset; 912 *timestamp_offset_during_append_ = new_timestamp_offset;
826 } 913 }
827 914
828 return true; 915 return true;
829 } 916 }
830
831 void MediaSourceState::OnSourceInitDone( 917 void MediaSourceState::OnSourceInitDone(
832 const StreamParser::InitParameters& params) { 918 const StreamParser::InitParameters& params) {
833 DCHECK_EQ(state_, PENDING_PARSER_INIT); 919 DCHECK_EQ(state_, PENDING_PARSER_INIT);
834 state_ = PARSER_INITIALIZED; 920 state_ = PARSER_INITIALIZED;
835 auto_update_timestamp_offset_ = params.auto_update_timestamp_offset; 921 auto_update_timestamp_offset_ = params.auto_update_timestamp_offset;
836 base::ResetAndReturn(&init_cb_).Run(params); 922 base::ResetAndReturn(&init_cb_).Run(params);
837 } 923 }
838 924
839 } // namespace media 925 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698