OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "media/filters/media_source_state.h" | |
6 | |
7 #include <set> | |
8 | |
9 #include "base/callback_helpers.h" | |
10 #include "base/command_line.h" | |
11 #include "base/strings/string_number_conversions.h" | |
12 #include "media/base/media_switches.h" | |
13 #include "media/base/media_track.h" | |
14 #include "media/base/media_tracks.h" | |
15 #include "media/base/mime_util.h" | |
16 #include "media/filters/chunk_demuxer.h" | |
17 #include "media/filters/frame_processor.h" | |
18 #include "media/filters/source_buffer_stream.h" | |
19 | |
20 namespace media { | |
21 | |
22 enum { | |
23 // Limits the number of MEDIA_LOG() calls warning the user that a muxed stream | |
24 // media segment is missing a block from at least one of the audio or video | |
25 // tracks. | |
26 kMaxMissingTrackInSegmentLogs = 10, | |
27 }; | |
28 | |
29 namespace { | |
30 | |
31 TimeDelta EndTimestamp(const StreamParser::BufferQueue& queue) { | |
32 return queue.back()->timestamp() + queue.back()->duration(); | |
33 } | |
34 | |
35 // Check the input |text_configs| and |bytestream_ids| and return false if | |
36 // duplicate track ids are detected. | |
37 bool CheckBytestreamTrackIds( | |
38 const MediaTracks& tracks, | |
39 const StreamParser::TextTrackConfigMap& text_configs) { | |
40 std::set<StreamParser::TrackId> bytestream_ids; | |
41 for (const auto& track : tracks.tracks()) { | |
42 const StreamParser::TrackId& track_id = track->bytestream_track_id(); | |
43 if (bytestream_ids.find(track_id) != bytestream_ids.end()) { | |
44 return false; | |
45 } | |
46 bytestream_ids.insert(track_id); | |
47 } | |
48 for (const auto& text_track : text_configs) { | |
49 const StreamParser::TrackId& track_id = text_track.first; | |
50 if (bytestream_ids.find(track_id) != bytestream_ids.end()) { | |
51 return false; | |
52 } | |
53 bytestream_ids.insert(track_id); | |
54 } | |
55 return true; | |
56 } | |
57 | |
58 } // namespace | |
59 | |
60 // List of time ranges for each SourceBuffer. | |
61 // static | |
62 Ranges<TimeDelta> MediaSourceState::ComputeRangesIntersection( | |
63 const RangesList& active_ranges, | |
64 bool ended) { | |
65 // TODO(servolk): Perhaps this can be removed in favor of blink implementation | |
66 // (MediaSource::buffered)? Currently this is only used on Android and for | |
67 // updating DemuxerHost's buffered ranges during AppendData() as well as | |
68 // SourceBuffer.buffered property implementation. | |
69 // Implementation of HTMLMediaElement.buffered algorithm in MSE spec. | |
70 // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-sourc
e.html#dom-htmlmediaelement.buffered | |
71 | |
72 // Step 1: If activeSourceBuffers.length equals 0 then return an empty | |
73 // TimeRanges object and abort these steps. | |
74 if (active_ranges.empty()) | |
75 return Ranges<TimeDelta>(); | |
76 | |
77 // Step 2: Let active ranges be the ranges returned by buffered for each | |
78 // SourceBuffer object in activeSourceBuffers. | |
79 // Step 3: Let highest end time be the largest range end time in the active | |
80 // ranges. | |
81 TimeDelta highest_end_time; | |
82 for (const auto& range : active_ranges) { | |
83 if (!range.size()) | |
84 continue; | |
85 | |
86 highest_end_time = std::max(highest_end_time, range.end(range.size() - 1)); | |
87 } | |
88 | |
89 // Step 4: Let intersection ranges equal a TimeRange object containing a | |
90 // single range from 0 to highest end time. | |
91 Ranges<TimeDelta> intersection_ranges; | |
92 intersection_ranges.Add(TimeDelta(), highest_end_time); | |
93 | |
94 // Step 5: For each SourceBuffer object in activeSourceBuffers run the | |
95 // following steps: | |
96 for (const auto& range : active_ranges) { | |
97 // Step 5.1: Let source ranges equal the ranges returned by the buffered | |
98 // attribute on the current SourceBuffer. | |
99 Ranges<TimeDelta> source_ranges = range; | |
100 | |
101 // Step 5.2: If readyState is "ended", then set the end time on the last | |
102 // range in source ranges to highest end time. | |
103 if (ended && source_ranges.size()) { | |
104 source_ranges.Add(source_ranges.start(source_ranges.size() - 1), | |
105 highest_end_time); | |
106 } | |
107 | |
108 // Step 5.3: Let new intersection ranges equal the intersection between | |
109 // the intersection ranges and the source ranges. | |
110 // Step 5.4: Replace the ranges in intersection ranges with the new | |
111 // intersection ranges. | |
112 intersection_ranges = intersection_ranges.IntersectionWith(source_ranges); | |
113 } | |
114 | |
115 return intersection_ranges; | |
116 } | |
117 | |
118 MediaSourceState::MediaSourceState( | |
119 std::unique_ptr<StreamParser> stream_parser, | |
120 std::unique_ptr<FrameProcessor> frame_processor, | |
121 const CreateDemuxerStreamCB& create_demuxer_stream_cb, | |
122 const scoped_refptr<MediaLog>& media_log) | |
123 : create_demuxer_stream_cb_(create_demuxer_stream_cb), | |
124 timestamp_offset_during_append_(NULL), | |
125 parsing_media_segment_(false), | |
126 stream_parser_(stream_parser.release()), | |
127 frame_processor_(frame_processor.release()), | |
128 media_log_(media_log), | |
129 state_(UNINITIALIZED), | |
130 auto_update_timestamp_offset_(false) { | |
131 DCHECK(!create_demuxer_stream_cb_.is_null()); | |
132 DCHECK(frame_processor_); | |
133 } | |
134 | |
135 MediaSourceState::~MediaSourceState() { | |
136 Shutdown(); | |
137 } | |
138 | |
139 void MediaSourceState::Init( | |
140 const StreamParser::InitCB& init_cb, | |
141 const std::string& expected_codecs, | |
142 const StreamParser::EncryptedMediaInitDataCB& encrypted_media_init_data_cb, | |
143 const NewTextTrackCB& new_text_track_cb) { | |
144 DCHECK_EQ(state_, UNINITIALIZED); | |
145 new_text_track_cb_ = new_text_track_cb; | |
146 init_cb_ = init_cb; | |
147 | |
148 std::vector<std::string> expected_codecs_parsed; | |
149 ParseCodecString(expected_codecs, &expected_codecs_parsed, false); | |
150 | |
151 std::vector<AudioCodec> expected_acodecs; | |
152 std::vector<VideoCodec> expected_vcodecs; | |
153 for (const auto& codec_id : expected_codecs_parsed) { | |
154 AudioCodec acodec = StringToAudioCodec(codec_id); | |
155 if (acodec != kUnknownAudioCodec) { | |
156 expected_audio_codecs_.push_back(acodec); | |
157 continue; | |
158 } | |
159 VideoCodec vcodec = StringToVideoCodec(codec_id); | |
160 if (vcodec != kUnknownVideoCodec) { | |
161 expected_video_codecs_.push_back(vcodec); | |
162 continue; | |
163 } | |
164 MEDIA_LOG(INFO, media_log_) << "Unrecognized media codec: " << codec_id; | |
165 } | |
166 | |
167 state_ = PENDING_PARSER_CONFIG; | |
168 stream_parser_->Init( | |
169 base::Bind(&MediaSourceState::OnSourceInitDone, base::Unretained(this)), | |
170 base::Bind(&MediaSourceState::OnNewConfigs, base::Unretained(this), | |
171 expected_codecs), | |
172 base::Bind(&MediaSourceState::OnNewBuffers, base::Unretained(this)), | |
173 new_text_track_cb_.is_null(), encrypted_media_init_data_cb, | |
174 base::Bind(&MediaSourceState::OnNewMediaSegment, base::Unretained(this)), | |
175 base::Bind(&MediaSourceState::OnEndOfMediaSegment, | |
176 base::Unretained(this)), | |
177 media_log_); | |
178 } | |
179 | |
180 void MediaSourceState::SetSequenceMode(bool sequence_mode) { | |
181 DCHECK(!parsing_media_segment_); | |
182 | |
183 frame_processor_->SetSequenceMode(sequence_mode); | |
184 } | |
185 | |
186 void MediaSourceState::SetGroupStartTimestampIfInSequenceMode( | |
187 base::TimeDelta timestamp_offset) { | |
188 DCHECK(!parsing_media_segment_); | |
189 | |
190 frame_processor_->SetGroupStartTimestampIfInSequenceMode(timestamp_offset); | |
191 } | |
192 | |
193 void MediaSourceState::SetTracksWatcher( | |
194 const Demuxer::MediaTracksUpdatedCB& tracks_updated_cb) { | |
195 DCHECK(init_segment_received_cb_.is_null()); | |
196 DCHECK(!tracks_updated_cb.is_null()); | |
197 init_segment_received_cb_ = tracks_updated_cb; | |
198 } | |
199 | |
200 bool MediaSourceState::Append(const uint8_t* data, | |
201 size_t length, | |
202 TimeDelta append_window_start, | |
203 TimeDelta append_window_end, | |
204 TimeDelta* timestamp_offset) { | |
205 append_in_progress_ = true; | |
206 DCHECK(timestamp_offset); | |
207 DCHECK(!timestamp_offset_during_append_); | |
208 append_window_start_during_append_ = append_window_start; | |
209 append_window_end_during_append_ = append_window_end; | |
210 timestamp_offset_during_append_ = timestamp_offset; | |
211 | |
212 // TODO(wolenetz/acolwell): Curry and pass a NewBuffersCB here bound with | |
213 // append window and timestamp offset pointer. See http://crbug.com/351454. | |
214 bool result = stream_parser_->Parse(data, length); | |
215 if (!result) { | |
216 MEDIA_LOG(ERROR, media_log_) | |
217 << __func__ << ": stream parsing failed. Data size=" << length | |
218 << " append_window_start=" << append_window_start.InSecondsF() | |
219 << " append_window_end=" << append_window_end.InSecondsF(); | |
220 } | |
221 timestamp_offset_during_append_ = NULL; | |
222 append_in_progress_ = false; | |
223 return result; | |
224 } | |
225 | |
226 void MediaSourceState::ResetParserState(TimeDelta append_window_start, | |
227 TimeDelta append_window_end, | |
228 base::TimeDelta* timestamp_offset) { | |
229 DCHECK(timestamp_offset); | |
230 DCHECK(!timestamp_offset_during_append_); | |
231 timestamp_offset_during_append_ = timestamp_offset; | |
232 append_window_start_during_append_ = append_window_start; | |
233 append_window_end_during_append_ = append_window_end; | |
234 | |
235 stream_parser_->Flush(); | |
236 timestamp_offset_during_append_ = NULL; | |
237 | |
238 frame_processor_->Reset(); | |
239 parsing_media_segment_ = false; | |
240 media_segment_has_data_for_track_.clear(); | |
241 } | |
242 | |
243 void MediaSourceState::Remove(TimeDelta start, | |
244 TimeDelta end, | |
245 TimeDelta duration) { | |
246 for (const auto& it : audio_streams_) { | |
247 it.second->Remove(start, end, duration); | |
248 } | |
249 | |
250 for (const auto& it : video_streams_) { | |
251 it.second->Remove(start, end, duration); | |
252 } | |
253 | |
254 for (const auto& it : text_streams_) { | |
255 it.second->Remove(start, end, duration); | |
256 } | |
257 } | |
258 | |
259 bool MediaSourceState::EvictCodedFrames(DecodeTimestamp media_time, | |
260 size_t newDataSize) { | |
261 size_t total_buffered_size = 0; | |
262 for (const auto& it : audio_streams_) | |
263 total_buffered_size += it.second->GetBufferedSize(); | |
264 for (const auto& it : video_streams_) | |
265 total_buffered_size += it.second->GetBufferedSize(); | |
266 for (const auto& it : text_streams_) | |
267 total_buffered_size += it.second->GetBufferedSize(); | |
268 | |
269 DVLOG(3) << __func__ << " media_time=" << media_time.InSecondsF() | |
270 << " newDataSize=" << newDataSize | |
271 << " total_buffered_size=" << total_buffered_size; | |
272 | |
273 if (total_buffered_size == 0) | |
274 return true; | |
275 | |
276 bool success = true; | |
277 for (const auto& it : audio_streams_) { | |
278 uint64_t curr_size = it.second->GetBufferedSize(); | |
279 if (curr_size == 0) | |
280 continue; | |
281 uint64_t estimated_new_size = newDataSize * curr_size / total_buffered_size; | |
282 DCHECK_LE(estimated_new_size, SIZE_MAX); | |
283 success &= it.second->EvictCodedFrames( | |
284 media_time, static_cast<size_t>(estimated_new_size)); | |
285 } | |
286 for (const auto& it : video_streams_) { | |
287 uint64_t curr_size = it.second->GetBufferedSize(); | |
288 if (curr_size == 0) | |
289 continue; | |
290 uint64_t estimated_new_size = newDataSize * curr_size / total_buffered_size; | |
291 DCHECK_LE(estimated_new_size, SIZE_MAX); | |
292 success &= it.second->EvictCodedFrames( | |
293 media_time, static_cast<size_t>(estimated_new_size)); | |
294 } | |
295 for (const auto& it : text_streams_) { | |
296 uint64_t curr_size = it.second->GetBufferedSize(); | |
297 if (curr_size == 0) | |
298 continue; | |
299 uint64_t estimated_new_size = newDataSize * curr_size / total_buffered_size; | |
300 DCHECK_LE(estimated_new_size, SIZE_MAX); | |
301 success &= it.second->EvictCodedFrames( | |
302 media_time, static_cast<size_t>(estimated_new_size)); | |
303 } | |
304 | |
305 DVLOG(3) << __func__ << " success=" << success; | |
306 return success; | |
307 } | |
308 | |
309 Ranges<TimeDelta> MediaSourceState::GetBufferedRanges(TimeDelta duration, | |
310 bool ended) const { | |
311 RangesList ranges_list; | |
312 for (const auto& it : audio_streams_) | |
313 ranges_list.push_back(it.second->GetBufferedRanges(duration)); | |
314 | |
315 for (const auto& it : video_streams_) | |
316 ranges_list.push_back(it.second->GetBufferedRanges(duration)); | |
317 | |
318 for (const auto& it : text_streams_) | |
319 ranges_list.push_back(it.second->GetBufferedRanges(duration)); | |
320 | |
321 return ComputeRangesIntersection(ranges_list, ended); | |
322 } | |
323 | |
324 TimeDelta MediaSourceState::GetHighestPresentationTimestamp() const { | |
325 TimeDelta max_pts; | |
326 | |
327 for (const auto& it : audio_streams_) { | |
328 max_pts = std::max(max_pts, it.second->GetHighestPresentationTimestamp()); | |
329 } | |
330 | |
331 for (const auto& it : video_streams_) { | |
332 max_pts = std::max(max_pts, it.second->GetHighestPresentationTimestamp()); | |
333 } | |
334 | |
335 for (const auto& it : text_streams_) { | |
336 max_pts = std::max(max_pts, it.second->GetHighestPresentationTimestamp()); | |
337 } | |
338 | |
339 return max_pts; | |
340 } | |
341 | |
342 TimeDelta MediaSourceState::GetMaxBufferedDuration() const { | |
343 TimeDelta max_duration; | |
344 | |
345 for (const auto& it : audio_streams_) { | |
346 max_duration = std::max(max_duration, it.second->GetBufferedDuration()); | |
347 } | |
348 | |
349 for (const auto& it : video_streams_) { | |
350 max_duration = std::max(max_duration, it.second->GetBufferedDuration()); | |
351 } | |
352 | |
353 for (const auto& it : text_streams_) { | |
354 max_duration = std::max(max_duration, it.second->GetBufferedDuration()); | |
355 } | |
356 | |
357 return max_duration; | |
358 } | |
359 | |
360 void MediaSourceState::StartReturningData() { | |
361 for (const auto& it : audio_streams_) { | |
362 it.second->StartReturningData(); | |
363 } | |
364 | |
365 for (const auto& it : video_streams_) { | |
366 it.second->StartReturningData(); | |
367 } | |
368 | |
369 for (const auto& it : text_streams_) { | |
370 it.second->StartReturningData(); | |
371 } | |
372 } | |
373 | |
374 void MediaSourceState::AbortReads() { | |
375 for (const auto& it : audio_streams_) { | |
376 it.second->AbortReads(); | |
377 } | |
378 | |
379 for (const auto& it : video_streams_) { | |
380 it.second->AbortReads(); | |
381 } | |
382 | |
383 for (const auto& it : text_streams_) { | |
384 it.second->AbortReads(); | |
385 } | |
386 } | |
387 | |
388 void MediaSourceState::Seek(TimeDelta seek_time) { | |
389 for (const auto& it : audio_streams_) { | |
390 it.second->Seek(seek_time); | |
391 } | |
392 | |
393 for (const auto& it : video_streams_) { | |
394 it.second->Seek(seek_time); | |
395 } | |
396 | |
397 for (const auto& it : text_streams_) { | |
398 it.second->Seek(seek_time); | |
399 } | |
400 } | |
401 | |
402 void MediaSourceState::CompletePendingReadIfPossible() { | |
403 for (const auto& it : audio_streams_) { | |
404 it.second->CompletePendingReadIfPossible(); | |
405 } | |
406 | |
407 for (const auto& it : video_streams_) { | |
408 it.second->CompletePendingReadIfPossible(); | |
409 } | |
410 | |
411 for (const auto& it : text_streams_) { | |
412 it.second->CompletePendingReadIfPossible(); | |
413 } | |
414 } | |
415 | |
416 void MediaSourceState::OnSetDuration(TimeDelta duration) { | |
417 for (const auto& it : audio_streams_) { | |
418 it.second->OnSetDuration(duration); | |
419 } | |
420 | |
421 for (const auto& it : video_streams_) { | |
422 it.second->OnSetDuration(duration); | |
423 } | |
424 | |
425 for (const auto& it : text_streams_) { | |
426 it.second->OnSetDuration(duration); | |
427 } | |
428 } | |
429 | |
430 void MediaSourceState::MarkEndOfStream() { | |
431 for (const auto& it : audio_streams_) { | |
432 it.second->MarkEndOfStream(); | |
433 } | |
434 | |
435 for (const auto& it : video_streams_) { | |
436 it.second->MarkEndOfStream(); | |
437 } | |
438 | |
439 for (const auto& it : text_streams_) { | |
440 it.second->MarkEndOfStream(); | |
441 } | |
442 } | |
443 | |
444 void MediaSourceState::UnmarkEndOfStream() { | |
445 for (const auto& it : audio_streams_) { | |
446 it.second->UnmarkEndOfStream(); | |
447 } | |
448 | |
449 for (const auto& it : video_streams_) { | |
450 it.second->UnmarkEndOfStream(); | |
451 } | |
452 | |
453 for (const auto& it : text_streams_) { | |
454 it.second->UnmarkEndOfStream(); | |
455 } | |
456 } | |
457 | |
458 void MediaSourceState::Shutdown() { | |
459 for (const auto& it : audio_streams_) { | |
460 it.second->Shutdown(); | |
461 } | |
462 | |
463 for (const auto& it : video_streams_) { | |
464 it.second->Shutdown(); | |
465 } | |
466 | |
467 for (const auto& it : text_streams_) { | |
468 it.second->Shutdown(); | |
469 } | |
470 } | |
471 | |
472 void MediaSourceState::SetMemoryLimits(DemuxerStream::Type type, | |
473 size_t memory_limit) { | |
474 switch (type) { | |
475 case DemuxerStream::AUDIO: | |
476 for (const auto& it : audio_streams_) { | |
477 it.second->SetStreamMemoryLimit(memory_limit); | |
478 } | |
479 break; | |
480 case DemuxerStream::VIDEO: | |
481 for (const auto& it : video_streams_) { | |
482 it.second->SetStreamMemoryLimit(memory_limit); | |
483 } | |
484 break; | |
485 case DemuxerStream::TEXT: | |
486 for (const auto& it : text_streams_) { | |
487 it.second->SetStreamMemoryLimit(memory_limit); | |
488 } | |
489 break; | |
490 case DemuxerStream::UNKNOWN: | |
491 case DemuxerStream::NUM_TYPES: | |
492 NOTREACHED(); | |
493 break; | |
494 } | |
495 } | |
496 | |
497 bool MediaSourceState::IsSeekWaitingForData() const { | |
498 for (const auto& it : audio_streams_) { | |
499 if (it.second->IsSeekWaitingForData()) | |
500 return true; | |
501 } | |
502 | |
503 for (const auto& it : video_streams_) { | |
504 if (it.second->IsSeekWaitingForData()) | |
505 return true; | |
506 } | |
507 | |
508 // NOTE: We are intentionally not checking the text tracks | |
509 // because text tracks are discontinuous and may not have data | |
510 // 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 | |
512 // the seek time, eventually arrive they will be delivered properly | |
513 // in response to ChunkDemuxerStream::Read() calls. | |
514 | |
515 return false; | |
516 } | |
517 | |
518 bool MediaSourceState::OnNewConfigs( | |
519 std::string expected_codecs, | |
520 std::unique_ptr<MediaTracks> tracks, | |
521 const StreamParser::TextTrackConfigMap& text_configs) { | |
522 DCHECK(tracks.get()); | |
523 DVLOG(1) << __func__ << " expected_codecs=" << expected_codecs | |
524 << " tracks=" << tracks->tracks().size(); | |
525 DCHECK_GE(state_, PENDING_PARSER_CONFIG); | |
526 | |
527 // Check that there is no clashing bytestream track ids. | |
528 if (!CheckBytestreamTrackIds(*tracks, text_configs)) { | |
529 MEDIA_LOG(ERROR, media_log_) << "Duplicate bytestream track ids detected"; | |
530 for (const auto& track : tracks->tracks()) { | |
531 const StreamParser::TrackId& track_id = track->bytestream_track_id(); | |
532 MEDIA_LOG(DEBUG, media_log_) << TrackTypeToStr(track->type()) << " track " | |
533 << " bytestream track id=" << track_id; | |
534 } | |
535 return false; | |
536 } | |
537 | |
538 // MSE spec allows new configs to be emitted only during Append, but not | |
539 // during Flush or parser reset operations. | |
540 CHECK(append_in_progress_); | |
541 | |
542 bool success = true; | |
543 | |
544 // TODO(wolenetz): Update codec string strictness, if necessary, once spec | |
545 // issue https://github.com/w3c/media-source/issues/161 is resolved. | |
546 std::vector<AudioCodec> expected_acodecs = expected_audio_codecs_; | |
547 std::vector<VideoCodec> expected_vcodecs = expected_video_codecs_; | |
548 | |
549 for (const auto& track : tracks->tracks()) { | |
550 const auto& track_id = track->bytestream_track_id(); | |
551 | |
552 if (track->type() == MediaTrack::Audio) { | |
553 AudioDecoderConfig audio_config = tracks->getAudioConfig(track_id); | |
554 DVLOG(1) << "Audio track_id=" << track_id | |
555 << " config: " << audio_config.AsHumanReadableString(); | |
556 DCHECK(audio_config.IsValidConfig()); | |
557 | |
558 const auto& it = std::find(expected_acodecs.begin(), | |
559 expected_acodecs.end(), audio_config.codec()); | |
560 if (it == expected_acodecs.end()) { | |
561 MEDIA_LOG(ERROR, media_log_) << "Audio stream codec " | |
562 << GetCodecName(audio_config.codec()) | |
563 << " doesn't match SourceBuffer codecs."; | |
564 return false; | |
565 } | |
566 expected_acodecs.erase(it); | |
567 | |
568 ChunkDemuxerStream* stream = nullptr; | |
569 if (!first_init_segment_received_) { | |
570 DCHECK(audio_streams_.find(track_id) == audio_streams_.end()); | |
571 stream = create_demuxer_stream_cb_.Run(DemuxerStream::AUDIO); | |
572 if (!stream || !frame_processor_->AddTrack(track_id, stream)) { | |
573 MEDIA_LOG(ERROR, media_log_) << "Failed to create audio stream."; | |
574 return false; | |
575 } | |
576 audio_streams_[track_id] = stream; | |
577 media_log_->SetBooleanProperty("found_audio_stream", true); | |
578 media_log_->SetStringProperty("audio_codec_name", | |
579 GetCodecName(audio_config.codec())); | |
580 } else { | |
581 if (audio_streams_.size() > 1) { | |
582 auto it = audio_streams_.find(track_id); | |
583 if (it != audio_streams_.end()) | |
584 stream = it->second; | |
585 } else { | |
586 // If there is only one audio track then bytestream id might change in | |
587 // a new init segment. So update our state and notify frame processor. | |
588 const auto& it = audio_streams_.begin(); | |
589 if (it != audio_streams_.end()) { | |
590 stream = it->second; | |
591 if (it->first != track_id) { | |
592 frame_processor_->UpdateTrack(it->first, track_id); | |
593 audio_streams_[track_id] = stream; | |
594 audio_streams_.erase(it->first); | |
595 } | |
596 } | |
597 } | |
598 if (!stream) { | |
599 MEDIA_LOG(ERROR, media_log_) << "Got unexpected audio track" | |
600 << " track_id=" << track_id; | |
601 return false; | |
602 } | |
603 } | |
604 | |
605 track->set_id(stream->media_track_id()); | |
606 frame_processor_->OnPossibleAudioConfigUpdate(audio_config); | |
607 success &= stream->UpdateAudioConfig(audio_config, media_log_); | |
608 } else if (track->type() == MediaTrack::Video) { | |
609 VideoDecoderConfig video_config = tracks->getVideoConfig(track_id); | |
610 DVLOG(1) << "Video track_id=" << track_id | |
611 << " config: " << video_config.AsHumanReadableString(); | |
612 DCHECK(video_config.IsValidConfig()); | |
613 | |
614 const auto& it = std::find(expected_vcodecs.begin(), | |
615 expected_vcodecs.end(), video_config.codec()); | |
616 if (it == expected_vcodecs.end()) { | |
617 MEDIA_LOG(ERROR, media_log_) << "Video stream codec " | |
618 << GetCodecName(video_config.codec()) | |
619 << " doesn't match SourceBuffer codecs."; | |
620 return false; | |
621 } | |
622 expected_vcodecs.erase(it); | |
623 | |
624 ChunkDemuxerStream* stream = nullptr; | |
625 if (!first_init_segment_received_) { | |
626 DCHECK(video_streams_.find(track_id) == video_streams_.end()); | |
627 stream = create_demuxer_stream_cb_.Run(DemuxerStream::VIDEO); | |
628 if (!stream || !frame_processor_->AddTrack(track_id, stream)) { | |
629 MEDIA_LOG(ERROR, media_log_) << "Failed to create video stream."; | |
630 return false; | |
631 } | |
632 video_streams_[track_id] = stream; | |
633 media_log_->SetBooleanProperty("found_video_stream", true); | |
634 media_log_->SetStringProperty("video_codec_name", | |
635 GetCodecName(video_config.codec())); | |
636 } else { | |
637 if (video_streams_.size() > 1) { | |
638 auto it = video_streams_.find(track_id); | |
639 if (it != video_streams_.end()) | |
640 stream = it->second; | |
641 } else { | |
642 // If there is only one video track then bytestream id might change in | |
643 // a new init segment. So update our state and notify frame processor. | |
644 const auto& it = video_streams_.begin(); | |
645 if (it != video_streams_.end()) { | |
646 stream = it->second; | |
647 if (it->first != track_id) { | |
648 frame_processor_->UpdateTrack(it->first, track_id); | |
649 video_streams_[track_id] = stream; | |
650 video_streams_.erase(it->first); | |
651 } | |
652 } | |
653 } | |
654 if (!stream) { | |
655 MEDIA_LOG(ERROR, media_log_) << "Got unexpected video track" | |
656 << " track_id=" << track_id; | |
657 return false; | |
658 } | |
659 } | |
660 | |
661 track->set_id(stream->media_track_id()); | |
662 success &= stream->UpdateVideoConfig(video_config, media_log_); | |
663 } else { | |
664 MEDIA_LOG(ERROR, media_log_) << "Error: unsupported media track type " | |
665 << track->type(); | |
666 return false; | |
667 } | |
668 } | |
669 | |
670 if (!expected_acodecs.empty() || !expected_vcodecs.empty()) { | |
671 for (const auto& acodec : expected_acodecs) { | |
672 MEDIA_LOG(ERROR, media_log_) << "Initialization segment misses expected " | |
673 << GetCodecName(acodec) << " track."; | |
674 } | |
675 for (const auto& vcodec : expected_vcodecs) { | |
676 MEDIA_LOG(ERROR, media_log_) << "Initialization segment misses expected " | |
677 << GetCodecName(vcodec) << " track."; | |
678 } | |
679 return false; | |
680 } | |
681 | |
682 if (text_streams_.empty()) { | |
683 for (auto itr = text_configs.begin(); itr != text_configs.end(); ++itr) { | |
684 ChunkDemuxerStream* const text_stream = | |
685 create_demuxer_stream_cb_.Run(DemuxerStream::TEXT); | |
686 if (!frame_processor_->AddTrack(itr->first, text_stream)) { | |
687 success &= false; | |
688 MEDIA_LOG(ERROR, media_log_) << "Failed to add text track ID " | |
689 << itr->first << " to frame processor."; | |
690 break; | |
691 } | |
692 text_stream->UpdateTextConfig(itr->second, media_log_); | |
693 text_streams_[itr->first] = text_stream; | |
694 new_text_track_cb_.Run(text_stream, itr->second); | |
695 } | |
696 } else { | |
697 const size_t text_count = text_streams_.size(); | |
698 if (text_configs.size() != text_count) { | |
699 success &= false; | |
700 MEDIA_LOG(ERROR, media_log_) | |
701 << "The number of text track configs changed."; | |
702 } else if (text_count == 1) { | |
703 auto config_itr = text_configs.begin(); | |
704 auto stream_itr = text_streams_.begin(); | |
705 ChunkDemuxerStream* text_stream = stream_itr->second; | |
706 TextTrackConfig old_config = text_stream->text_track_config(); | |
707 TextTrackConfig new_config( | |
708 config_itr->second.kind(), config_itr->second.label(), | |
709 config_itr->second.language(), old_config.id()); | |
710 if (!new_config.Matches(old_config)) { | |
711 success &= false; | |
712 MEDIA_LOG(ERROR, media_log_) | |
713 << "New text track config does not match old one."; | |
714 } else { | |
715 StreamParser::TrackId old_id = stream_itr->first; | |
716 StreamParser::TrackId new_id = config_itr->first; | |
717 if (new_id != old_id) { | |
718 if (frame_processor_->UpdateTrack(old_id, new_id)) { | |
719 text_streams_.clear(); | |
720 text_streams_[config_itr->first] = text_stream; | |
721 } else { | |
722 success &= false; | |
723 MEDIA_LOG(ERROR, media_log_) | |
724 << "Error remapping single text track number"; | |
725 } | |
726 } | |
727 } | |
728 } else { | |
729 for (auto config_itr = text_configs.begin(); | |
730 config_itr != text_configs.end(); ++config_itr) { | |
731 auto stream_itr = text_streams_.find(config_itr->first); | |
732 if (stream_itr == text_streams_.end()) { | |
733 success &= false; | |
734 MEDIA_LOG(ERROR, media_log_) | |
735 << "Unexpected text track configuration for track ID " | |
736 << config_itr->first; | |
737 break; | |
738 } | |
739 | |
740 const TextTrackConfig& new_config = config_itr->second; | |
741 ChunkDemuxerStream* stream = stream_itr->second; | |
742 TextTrackConfig old_config = stream->text_track_config(); | |
743 if (!new_config.Matches(old_config)) { | |
744 success &= false; | |
745 MEDIA_LOG(ERROR, media_log_) << "New text track config for track ID " | |
746 << config_itr->first | |
747 << " does not match old one."; | |
748 break; | |
749 } | |
750 } | |
751 } | |
752 } | |
753 | |
754 if (audio_streams_.empty() && video_streams_.empty()) { | |
755 DVLOG(1) << __func__ << ": couldn't find a valid audio or video stream"; | |
756 return false; | |
757 } | |
758 | |
759 frame_processor_->SetAllTrackBuffersNeedRandomAccessPoint(); | |
760 | |
761 if (!first_init_segment_received_) { | |
762 first_init_segment_received_ = true; | |
763 SetStreamMemoryLimits(); | |
764 } | |
765 | |
766 DVLOG(1) << "OnNewConfigs() : " << (success ? "success" : "failed"); | |
767 if (success) { | |
768 if (state_ == PENDING_PARSER_CONFIG) | |
769 state_ = PENDING_PARSER_INIT; | |
770 DCHECK(!init_segment_received_cb_.is_null()); | |
771 init_segment_received_cb_.Run(std::move(tracks)); | |
772 } | |
773 | |
774 return success; | |
775 } | |
776 | |
777 void MediaSourceState::SetStreamMemoryLimits() { | |
778 auto cmd_line = base::CommandLine::ForCurrentProcess(); | |
779 | |
780 std::string audio_buf_limit_switch = | |
781 cmd_line->GetSwitchValueASCII(switches::kMSEAudioBufferSizeLimit); | |
782 unsigned audio_buf_size_limit = 0; | |
783 if (base::StringToUint(audio_buf_limit_switch, &audio_buf_size_limit) && | |
784 audio_buf_size_limit > 0) { | |
785 MEDIA_LOG(INFO, media_log_) | |
786 << "Custom audio per-track SourceBuffer size limit=" | |
787 << audio_buf_size_limit; | |
788 for (const auto& it : audio_streams_) { | |
789 it.second->SetStreamMemoryLimit(audio_buf_size_limit); | |
790 } | |
791 } | |
792 | |
793 std::string video_buf_limit_switch = | |
794 cmd_line->GetSwitchValueASCII(switches::kMSEVideoBufferSizeLimit); | |
795 unsigned video_buf_size_limit = 0; | |
796 if (base::StringToUint(video_buf_limit_switch, &video_buf_size_limit) && | |
797 video_buf_size_limit > 0) { | |
798 MEDIA_LOG(INFO, media_log_) | |
799 << "Custom video per-track SourceBuffer size limit=" | |
800 << video_buf_size_limit; | |
801 for (const auto& it : video_streams_) { | |
802 it.second->SetStreamMemoryLimit(video_buf_size_limit); | |
803 } | |
804 } | |
805 } | |
806 | |
807 void MediaSourceState::OnNewMediaSegment() { | |
808 DVLOG(2) << "OnNewMediaSegment()"; | |
809 DCHECK_EQ(state_, PARSER_INITIALIZED); | |
810 parsing_media_segment_ = true; | |
811 media_segment_has_data_for_track_.clear(); | |
812 } | |
813 | |
814 void MediaSourceState::OnEndOfMediaSegment() { | |
815 DVLOG(2) << "OnEndOfMediaSegment()"; | |
816 DCHECK_EQ(state_, PARSER_INITIALIZED); | |
817 parsing_media_segment_ = false; | |
818 | |
819 for (const auto& it : audio_streams_) { | |
820 if (!media_segment_has_data_for_track_[it.first]) { | |
821 LIMITED_MEDIA_LOG(DEBUG, media_log_, num_missing_track_logs_, | |
822 kMaxMissingTrackInSegmentLogs) | |
823 << "Media segment did not contain any coded frames for track " | |
824 << it.first << ", mismatching initialization segment. Therefore, MSE" | |
825 " coded frame processing may not interoperably detect" | |
826 " discontinuities in appended media."; | |
827 } | |
828 } | |
829 for (const auto& it : video_streams_) { | |
830 if (!media_segment_has_data_for_track_[it.first]) { | |
831 LIMITED_MEDIA_LOG(DEBUG, media_log_, num_missing_track_logs_, | |
832 kMaxMissingTrackInSegmentLogs) | |
833 << "Media segment did not contain any coded frames for track " | |
834 << it.first << ", mismatching initialization segment. Therefore, MSE" | |
835 " coded frame processing may not interoperably detect" | |
836 " discontinuities in appended media."; | |
837 } | |
838 } | |
839 } | |
840 | |
841 bool MediaSourceState::OnNewBuffers( | |
842 const StreamParser::BufferQueueMap& buffer_queue_map) { | |
843 DVLOG(2) << __func__ << " buffer_queues=" << buffer_queue_map.size(); | |
844 DCHECK_EQ(state_, PARSER_INITIALIZED); | |
845 DCHECK(timestamp_offset_during_append_); | |
846 DCHECK(parsing_media_segment_); | |
847 | |
848 for (const auto& it : buffer_queue_map) { | |
849 const StreamParser::BufferQueue& bufq = it.second; | |
850 DCHECK(!bufq.empty()); | |
851 media_segment_has_data_for_track_[it.first] = true; | |
852 } | |
853 | |
854 const TimeDelta timestamp_offset_before_processing = | |
855 *timestamp_offset_during_append_; | |
856 | |
857 // Calculate the new timestamp offset for audio/video tracks if the stream | |
858 // parser has requested automatic updates. | |
859 TimeDelta new_timestamp_offset = timestamp_offset_before_processing; | |
860 if (auto_update_timestamp_offset_) { | |
861 TimeDelta min_end_timestamp = kNoTimestamp; | |
862 for (const auto& it : buffer_queue_map) { | |
863 const StreamParser::BufferQueue& bufq = it.second; | |
864 DCHECK(!bufq.empty()); | |
865 if (min_end_timestamp == kNoTimestamp || | |
866 EndTimestamp(bufq) < min_end_timestamp) { | |
867 min_end_timestamp = EndTimestamp(bufq); | |
868 DCHECK_NE(kNoTimestamp, min_end_timestamp); | |
869 } | |
870 } | |
871 if (min_end_timestamp != kNoTimestamp) | |
872 new_timestamp_offset += min_end_timestamp; | |
873 } | |
874 | |
875 if (!frame_processor_->ProcessFrames( | |
876 buffer_queue_map, append_window_start_during_append_, | |
877 append_window_end_during_append_, timestamp_offset_during_append_)) { | |
878 return false; | |
879 } | |
880 | |
881 // Only update the timestamp offset if the frame processor hasn't already. | |
882 if (auto_update_timestamp_offset_ && | |
883 timestamp_offset_before_processing == *timestamp_offset_during_append_) { | |
884 *timestamp_offset_during_append_ = new_timestamp_offset; | |
885 } | |
886 | |
887 return true; | |
888 } | |
889 void MediaSourceState::OnSourceInitDone( | |
890 const StreamParser::InitParameters& params) { | |
891 DCHECK_EQ(state_, PENDING_PARSER_INIT); | |
892 state_ = PARSER_INITIALIZED; | |
893 auto_update_timestamp_offset_ = params.auto_update_timestamp_offset; | |
894 base::ResetAndReturn(&init_cb_).Run(params); | |
895 } | |
896 | |
897 } // namespace media | |
OLD | NEW |