1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/filters/source_buffer_state.h"
9 #include "base/callback_helpers.h"
10 #include "base/command_line.h"
11 #include "base/ranges/algorithm.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "build/build_config.h"
14 #include "build/chromeos_buildflags.h"
15 #include "media/base/media_switches.h"
16 #include "media/base/media_track.h"
17 #include "media/base/media_tracks.h"
18 #include "media/base/mime_util.h"
19 #include "media/filters/chunk_demuxer.h"
20 #include "media/filters/frame_processor.h"
21 #include "media/filters/source_buffer_stream.h"
22 #include "media/media_buildflags.h"
27 // Limits the number of MEDIA_LOG() calls warning the user that a muxed stream
28 // media segment is missing a block from at least one of the audio or video
30 kMaxMissingTrackInSegmentLogs = 10,
35 base::TimeDelta EndTimestamp(const StreamParser::BufferQueue& queue) {
36 return queue.back()->timestamp() + queue.back()->duration();
39 // Check the input |text_configs| and |bytestream_ids| and return false if
40 // duplicate track ids are detected.
41 bool CheckBytestreamTrackIds(
42 const MediaTracks& tracks,
43 const StreamParser::TextTrackConfigMap& text_configs) {
44 std::set<StreamParser::TrackId> bytestream_ids;
45 for (const auto& track : tracks.tracks()) {
46 const StreamParser::TrackId& track_id = track->bytestream_track_id();
47 if (bytestream_ids.find(track_id) != bytestream_ids.end()) {
50 bytestream_ids.insert(track_id);
52 for (const auto& text_track : text_configs) {
53 const StreamParser::TrackId& track_id = text_track.first;
54 if (bytestream_ids.find(track_id) != bytestream_ids.end()) {
57 bytestream_ids.insert(track_id);
62 unsigned GetMSEBufferSizeLimitIfExists(base::StringPiece switch_string) {
63 auto* command_line = base::CommandLine::ForCurrentProcess();
64 unsigned memory_limit;
65 if (command_line->HasSwitch(switch_string) &&
66 base::StringToUint(command_line->GetSwitchValueASCII(switch_string),
68 return memory_limit * 1024 * 1024;
75 // List of time ranges for each SourceBuffer.
77 Ranges<base::TimeDelta> SourceBufferState::ComputeRangesIntersection(
78 const RangesList& active_ranges,
80 // TODO(servolk): Perhaps this can be removed in favor of blink implementation
81 // (MediaSource::buffered)? Currently this is only used on Android and for
82 // updating DemuxerHost's buffered ranges during AppendData() as well as
83 // SourceBuffer.buffered property implementation.
84 // Implementation of HTMLMediaElement.buffered algorithm in MSE spec.
85 // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#dom-htmlmediaelement.buffered
87 // Step 1: If activeSourceBuffers.length equals 0 then return an empty
88 // TimeRanges object and abort these steps.
89 if (active_ranges.empty())
90 return Ranges<base::TimeDelta>();
92 // Step 2: Let active ranges be the ranges returned by buffered for each
93 // SourceBuffer object in activeSourceBuffers.
94 // Step 3: Let highest end time be the largest range end time in the active
96 base::TimeDelta highest_end_time;
97 for (const auto& range : active_ranges) {
101 highest_end_time = std::max(highest_end_time, range.end(range.size() - 1));
104 // Step 4: Let intersection ranges equal a TimeRange object containing a
105 // single range from 0 to highest end time.
106 Ranges<base::TimeDelta> intersection_ranges;
107 intersection_ranges.Add(base::TimeDelta(), highest_end_time);
109 // Step 5: For each SourceBuffer object in activeSourceBuffers run the
111 for (const auto& range : active_ranges) {
112 // Step 5.1: Let source ranges equal the ranges returned by the buffered
113 // attribute on the current SourceBuffer.
114 Ranges<base::TimeDelta> source_ranges = range;
116 // Step 5.2: If readyState is "ended", then set the end time on the last
117 // range in source ranges to highest end time.
118 if (ended && source_ranges.size()) {
119 source_ranges.Add(source_ranges.start(source_ranges.size() - 1),
123 // Step 5.3: Let new intersection ranges equal the intersection between
124 // the intersection ranges and the source ranges.
125 // Step 5.4: Replace the ranges in intersection ranges with the new
126 // intersection ranges.
127 intersection_ranges = intersection_ranges.IntersectionWith(source_ranges);
130 return intersection_ranges;
133 SourceBufferState::SourceBufferState(
134 std::unique_ptr<StreamParser> stream_parser,
135 std::unique_ptr<FrameProcessor> frame_processor,
136 CreateDemuxerStreamCB create_demuxer_stream_cb,
138 : timestamp_offset_during_append_(nullptr),
139 parsing_media_segment_(false),
140 stream_parser_(stream_parser.release()),
141 frame_processor_(frame_processor.release()),
142 create_demuxer_stream_cb_(std::move(create_demuxer_stream_cb)),
143 media_log_(media_log),
144 state_(UNINITIALIZED) {
145 DCHECK(create_demuxer_stream_cb_);
146 DCHECK(frame_processor_);
149 SourceBufferState::~SourceBufferState() {
153 void SourceBufferState::Init(
154 StreamParser::InitCB init_cb,
155 const std::string& expected_codecs,
156 const StreamParser::EncryptedMediaInitDataCB& encrypted_media_init_data_cb,
157 NewTextTrackCB new_text_track_cb) {
158 DCHECK_EQ(state_, UNINITIALIZED);
159 init_cb_ = std::move(init_cb);
160 encrypted_media_init_data_cb_ = encrypted_media_init_data_cb;
161 new_text_track_cb_ = std::move(new_text_track_cb);
162 state_ = PENDING_PARSER_CONFIG;
163 InitializeParser(expected_codecs);
166 void SourceBufferState::ChangeType(
167 std::unique_ptr<StreamParser> new_stream_parser,
168 const std::string& new_expected_codecs) {
169 DCHECK_GE(state_, PENDING_PARSER_CONFIG);
170 DCHECK_NE(state_, PENDING_PARSER_INIT);
171 DCHECK(!parsing_media_segment_);
173 // If this source buffer has already handled an initialization segment, avoid
174 // running |init_cb_| again later.
175 if (state_ == PARSER_INITIALIZED)
176 state_ = PENDING_PARSER_RECONFIG;
178 stream_parser_ = std::move(new_stream_parser);
179 InitializeParser(new_expected_codecs);
182 void SourceBufferState::SetSequenceMode(bool sequence_mode) {
183 DCHECK(!parsing_media_segment_);
185 frame_processor_->SetSequenceMode(sequence_mode);
188 void SourceBufferState::SetGroupStartTimestampIfInSequenceMode(
189 base::TimeDelta timestamp_offset) {
190 DCHECK(!parsing_media_segment_);
192 frame_processor_->SetGroupStartTimestampIfInSequenceMode(timestamp_offset);
195 void SourceBufferState::SetTracksWatcher(
196 Demuxer::MediaTracksUpdatedCB tracks_updated_cb) {
197 DCHECK(!init_segment_received_cb_);
198 DCHECK(tracks_updated_cb);
199 init_segment_received_cb_ = std::move(tracks_updated_cb);
202 void SourceBufferState::SetParseWarningCallback(
203 SourceBufferParseWarningCB parse_warning_cb) {
204 // Give the callback to |frame_processor_|; none of these warnings are
205 // currently emitted elsewhere.
206 frame_processor_->SetParseWarningCallback(std::move(parse_warning_cb));
209 bool SourceBufferState::Append(const uint8_t* data,
211 base::TimeDelta append_window_start,
212 base::TimeDelta append_window_end,
213 base::TimeDelta* timestamp_offset) {
214 append_in_progress_ = true;
215 DCHECK(timestamp_offset);
216 DCHECK(!timestamp_offset_during_append_);
217 append_window_start_during_append_ = append_window_start;
218 append_window_end_during_append_ = append_window_end;
219 timestamp_offset_during_append_ = timestamp_offset;
221 // TODO(wolenetz): Curry and pass a NewBuffersCB here bound with append window
222 // and timestamp offset pointer. See http://crbug.com/351454.
223 bool result = stream_parser_->Parse(data, length);
225 MEDIA_LOG(ERROR, media_log_)
226 << __func__ << ": stream parsing failed. Data size=" << length
227 << " append_window_start=" << append_window_start.InSecondsF()
228 << " append_window_end=" << append_window_end.InSecondsF();
231 timestamp_offset_during_append_ = nullptr;
232 append_in_progress_ = false;
236 bool SourceBufferState::AppendChunks(
237 std::unique_ptr<StreamParser::BufferQueue> buffer_queue,
238 base::TimeDelta append_window_start,
239 base::TimeDelta append_window_end,
240 base::TimeDelta* timestamp_offset) {
241 append_in_progress_ = true;
242 DCHECK(timestamp_offset);
243 DCHECK(!timestamp_offset_during_append_);
244 append_window_start_during_append_ = append_window_start;
245 append_window_end_during_append_ = append_window_end;
246 timestamp_offset_during_append_ = timestamp_offset;
248 // TODO(wolenetz): Curry and pass a NewBuffersCB here bound with append window
249 // and timestamp offset pointer. See http://crbug.com/351454.
250 bool result = stream_parser_->ProcessChunks(std::move(buffer_queue));
252 MEDIA_LOG(ERROR, media_log_)
253 << __func__ << ": Processing encoded chunks for buffering failed.";
256 timestamp_offset_during_append_ = nullptr;
257 append_in_progress_ = false;
261 void SourceBufferState::ResetParserState(base::TimeDelta append_window_start,
262 base::TimeDelta append_window_end,
263 base::TimeDelta* timestamp_offset) {
264 DCHECK(timestamp_offset);
265 DCHECK(!timestamp_offset_during_append_);
266 timestamp_offset_during_append_ = timestamp_offset;
267 append_window_start_during_append_ = append_window_start;
268 append_window_end_during_append_ = append_window_end;
270 stream_parser_->Flush();
271 timestamp_offset_during_append_ = nullptr;
273 frame_processor_->Reset();
274 parsing_media_segment_ = false;
275 media_segment_has_data_for_track_.clear();
278 void SourceBufferState::Remove(base::TimeDelta start,
280 base::TimeDelta duration) {
281 for (const auto& it : audio_streams_) {
282 it.second->Remove(start, end, duration);
285 for (const auto& it : video_streams_) {
286 it.second->Remove(start, end, duration);
289 for (const auto& it : text_streams_) {
290 it.second->Remove(start, end, duration);
294 bool SourceBufferState::EvictCodedFrames(base::TimeDelta media_time,
295 size_t newDataSize) {
296 size_t total_buffered_size = 0;
297 for (const auto& it : audio_streams_)
298 total_buffered_size += it.second->GetBufferedSize();
299 for (const auto& it : video_streams_)
300 total_buffered_size += it.second->GetBufferedSize();
301 for (const auto& it : text_streams_)
302 total_buffered_size += it.second->GetBufferedSize();
304 DVLOG(3) << __func__ << " media_time=" << media_time.InSecondsF()
305 << " newDataSize=" << newDataSize
306 << " total_buffered_size=" << total_buffered_size;
308 if (total_buffered_size == 0)
312 for (const auto& it : audio_streams_) {
313 uint64_t curr_size = it.second->GetBufferedSize();
316 uint64_t estimated_new_size = newDataSize * curr_size / total_buffered_size;
317 DCHECK_LE(estimated_new_size, SIZE_MAX);
318 success &= it.second->EvictCodedFrames(
319 media_time, static_cast<size_t>(estimated_new_size));
321 for (const auto& it : video_streams_) {
322 uint64_t curr_size = it.second->GetBufferedSize();
325 uint64_t estimated_new_size = newDataSize * curr_size / total_buffered_size;
326 DCHECK_LE(estimated_new_size, SIZE_MAX);
327 success &= it.second->EvictCodedFrames(
328 media_time, static_cast<size_t>(estimated_new_size));
330 for (const auto& it : text_streams_) {
331 uint64_t curr_size = it.second->GetBufferedSize();
334 uint64_t estimated_new_size = newDataSize * curr_size / total_buffered_size;
335 DCHECK_LE(estimated_new_size, SIZE_MAX);
336 success &= it.second->EvictCodedFrames(
337 media_time, static_cast<size_t>(estimated_new_size));
340 DVLOG(3) << __func__ << " success=" << success;
344 void SourceBufferState::OnMemoryPressure(
345 base::TimeDelta media_time,
346 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level,
347 bool force_instant_gc) {
348 // TODO(sebmarchand): Check if MEMORY_PRESSURE_LEVEL_MODERATE should also be
350 if (memory_pressure_level ==
351 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) {
355 // Notify video streams about memory pressure first, since video typically
356 // takes up the most memory and that's where we can expect most savings.
357 for (const auto& it : video_streams_) {
358 it.second->OnMemoryPressure(media_time, memory_pressure_level,
361 for (const auto& it : audio_streams_) {
362 it.second->OnMemoryPressure(media_time, memory_pressure_level,
365 for (const auto& it : text_streams_) {
366 it.second->OnMemoryPressure(media_time, memory_pressure_level,
371 Ranges<base::TimeDelta> SourceBufferState::GetBufferedRanges(
372 base::TimeDelta duration,
374 RangesList ranges_list;
375 for (const auto& it : audio_streams_)
376 ranges_list.push_back(it.second->GetBufferedRanges(duration));
378 for (const auto& it : video_streams_)
379 ranges_list.push_back(it.second->GetBufferedRanges(duration));
381 for (const auto& it : text_streams_)
382 ranges_list.push_back(it.second->GetBufferedRanges(duration));
384 return ComputeRangesIntersection(ranges_list, ended);
387 base::TimeDelta SourceBufferState::GetHighestPresentationTimestamp() const {
388 base::TimeDelta max_pts;
390 for (const auto& it : audio_streams_) {
391 max_pts = std::max(max_pts, it.second->GetHighestPresentationTimestamp());
394 for (const auto& it : video_streams_) {
395 max_pts = std::max(max_pts, it.second->GetHighestPresentationTimestamp());
398 for (const auto& it : text_streams_) {
399 max_pts = std::max(max_pts, it.second->GetHighestPresentationTimestamp());
405 base::TimeDelta SourceBufferState::GetMaxBufferedDuration() const {
406 base::TimeDelta max_duration;
408 for (const auto& it : audio_streams_) {
409 max_duration = std::max(max_duration, it.second->GetBufferedDuration());
412 for (const auto& it : video_streams_) {
413 max_duration = std::max(max_duration, it.second->GetBufferedDuration());
416 for (const auto& it : text_streams_) {
417 max_duration = std::max(max_duration, it.second->GetBufferedDuration());
423 void SourceBufferState::StartReturningData() {
424 for (const auto& it : audio_streams_) {
425 it.second->StartReturningData();
428 for (const auto& it : video_streams_) {
429 it.second->StartReturningData();
432 for (const auto& it : text_streams_) {
433 it.second->StartReturningData();
437 void SourceBufferState::AbortReads() {
438 for (const auto& it : audio_streams_) {
439 it.second->AbortReads();
442 for (const auto& it : video_streams_) {
443 it.second->AbortReads();
446 for (const auto& it : text_streams_) {
447 it.second->AbortReads();
451 void SourceBufferState::Seek(base::TimeDelta seek_time) {
452 for (const auto& it : audio_streams_) {
453 it.second->Seek(seek_time);
456 for (const auto& it : video_streams_) {
457 it.second->Seek(seek_time);
460 for (const auto& it : text_streams_) {
461 it.second->Seek(seek_time);
465 void SourceBufferState::CompletePendingReadIfPossible() {
466 for (const auto& it : audio_streams_) {
467 it.second->CompletePendingReadIfPossible();
470 for (const auto& it : video_streams_) {
471 it.second->CompletePendingReadIfPossible();
474 for (const auto& it : text_streams_) {
475 it.second->CompletePendingReadIfPossible();
479 void SourceBufferState::OnSetDuration(base::TimeDelta duration) {
480 for (const auto& it : audio_streams_) {
481 it.second->OnSetDuration(duration);
484 for (const auto& it : video_streams_) {
485 it.second->OnSetDuration(duration);
488 for (const auto& it : text_streams_) {
489 it.second->OnSetDuration(duration);
493 void SourceBufferState::MarkEndOfStream() {
494 for (const auto& it : audio_streams_) {
495 it.second->MarkEndOfStream();
498 for (const auto& it : video_streams_) {
499 it.second->MarkEndOfStream();
502 for (const auto& it : text_streams_) {
503 it.second->MarkEndOfStream();
507 void SourceBufferState::UnmarkEndOfStream() {
508 for (const auto& it : audio_streams_) {
509 it.second->UnmarkEndOfStream();
512 for (const auto& it : video_streams_) {
513 it.second->UnmarkEndOfStream();
516 for (const auto& it : text_streams_) {
517 it.second->UnmarkEndOfStream();
521 void SourceBufferState::Shutdown() {
522 for (const auto& it : audio_streams_) {
523 it.second->Shutdown();
526 for (const auto& it : video_streams_) {
527 it.second->Shutdown();
530 for (const auto& it : text_streams_) {
531 it.second->Shutdown();
535 void SourceBufferState::SetMemoryLimits(DemuxerStream::Type type,
536 size_t memory_limit) {
538 case DemuxerStream::AUDIO:
539 for (const auto& it : audio_streams_) {
540 it.second->SetStreamMemoryLimit(memory_limit);
543 case DemuxerStream::VIDEO:
544 for (const auto& it : video_streams_) {
545 it.second->SetStreamMemoryLimit(memory_limit);
548 case DemuxerStream::TEXT:
549 for (const auto& it : text_streams_) {
550 it.second->SetStreamMemoryLimit(memory_limit);
553 case DemuxerStream::UNKNOWN:
559 bool SourceBufferState::IsSeekWaitingForData() const {
560 for (const auto& it : audio_streams_) {
561 if (it.second->IsSeekWaitingForData())
565 for (const auto& it : video_streams_) {
566 if (it.second->IsSeekWaitingForData())
570 // NOTE: We are intentionally not checking the text tracks
571 // because text tracks are discontinuous and may not have data
572 // for the seek position. This is ok and playback should not be
573 // stalled because we don't have cues. If cues, with timestamps after
574 // the seek time, eventually arrive they will be delivered properly
575 // in response to ChunkDemuxerStream::Read() calls.
580 void SourceBufferState::InitializeParser(const std::string& expected_codecs) {
581 expected_audio_codecs_.clear();
582 expected_video_codecs_.clear();
584 std::vector<std::string> expected_codecs_parsed;
585 SplitCodecs(expected_codecs, &expected_codecs_parsed);
587 std::vector<AudioCodec> expected_acodecs;
588 std::vector<VideoCodec> expected_vcodecs;
589 for (const auto& codec_id : expected_codecs_parsed) {
590 AudioCodec acodec = StringToAudioCodec(codec_id);
591 if (acodec != AudioCodec::kUnknown) {
592 expected_audio_codecs_.push_back(acodec);
595 VideoCodec vcodec = StringToVideoCodec(codec_id);
596 if (vcodec != VideoCodec::kUnknown) {
597 expected_video_codecs_.push_back(vcodec);
600 MEDIA_LOG(INFO, media_log_) << "Unrecognized media codec: " << codec_id;
603 stream_parser_->Init(
604 base::BindOnce(&SourceBufferState::OnSourceInitDone,
605 base::Unretained(this)),
606 base::BindRepeating(&SourceBufferState::OnNewConfigs,
607 base::Unretained(this), expected_codecs),
608 base::BindRepeating(&SourceBufferState::OnNewBuffers,
609 base::Unretained(this)),
611 base::BindRepeating(&SourceBufferState::OnEncryptedMediaInitData,
612 base::Unretained(this)),
613 base::BindRepeating(&SourceBufferState::OnNewMediaSegment,
614 base::Unretained(this)),
615 base::BindRepeating(&SourceBufferState::OnEndOfMediaSegment,
616 base::Unretained(this)),
620 bool SourceBufferState::OnNewConfigs(
621 std::string expected_codecs,
622 std::unique_ptr<MediaTracks> tracks,
623 const StreamParser::TextTrackConfigMap& text_configs) {
624 DCHECK(tracks.get());
625 DVLOG(1) << __func__ << " expected_codecs=" << expected_codecs
626 << " tracks=" << tracks->tracks().size();
627 DCHECK_GE(state_, PENDING_PARSER_CONFIG);
629 // Check that there is no clashing bytestream track ids.
630 if (!CheckBytestreamTrackIds(*tracks, text_configs)) {
631 MEDIA_LOG(ERROR, media_log_) << "Duplicate bytestream track ids detected";
632 for (const auto& track : tracks->tracks()) {
633 const StreamParser::TrackId& track_id = track->bytestream_track_id();
634 MEDIA_LOG(DEBUG, media_log_) << TrackTypeToStr(track->type()) << " track "
635 << " bytestream track id=" << track_id;
640 // MSE spec allows new configs to be emitted only during Append, but not
641 // during Flush or parser reset operations.
642 CHECK(append_in_progress_);
646 // TODO(wolenetz): Update codec string strictness, if necessary, once spec
647 // issue https://github.com/w3c/media-source/issues/161 is resolved.
648 std::vector<AudioCodec> expected_acodecs = expected_audio_codecs_;
649 std::vector<VideoCodec> expected_vcodecs = expected_video_codecs_;
651 // TODO(wolenetz): Once codec strictness is relaxed, we can change
652 // |allow_codec_changes| to always be true. Until then, we only allow codec
653 // changes on explicit ChangeType().
654 const bool allow_codec_changes = state_ == PENDING_PARSER_RECONFIG;
656 FrameProcessor::TrackIdChanges track_id_changes;
657 for (const auto& track : tracks->tracks()) {
658 const auto& track_id = track->bytestream_track_id();
660 if (track->type() == MediaTrack::Audio) {
661 AudioDecoderConfig audio_config = tracks->getAudioConfig(track_id);
662 DVLOG(1) << "Audio track_id=" << track_id
663 << " config: " << audio_config.AsHumanReadableString();
664 DCHECK(audio_config.IsValidConfig());
667 base::ranges::find(expected_acodecs, audio_config.codec());
668 if (it == expected_acodecs.end()) {
669 MEDIA_LOG(ERROR, media_log_) << "Audio stream codec "
670 << GetCodecName(audio_config.codec())
671 << " doesn't match SourceBuffer codecs.";
674 expected_acodecs.erase(it);
676 ChunkDemuxerStream* stream = nullptr;
677 if (!first_init_segment_received_) {
678 DCHECK(audio_streams_.find(track_id) == audio_streams_.end());
679 stream = create_demuxer_stream_cb_.Run(DemuxerStream::AUDIO);
680 if (!stream || !frame_processor_->AddTrack(track_id, stream)) {
681 MEDIA_LOG(ERROR, media_log_) << "Failed to create audio stream.";
684 audio_streams_[track_id] = stream;
685 media_log_->SetProperty<MediaLogProperty::kAudioTracks>(
686 std::vector<AudioDecoderConfig>{audio_config});
688 if (audio_streams_.size() > 1) {
689 auto stream_it = audio_streams_.find(track_id);
690 if (stream_it != audio_streams_.end())
691 stream = stream_it->second;
693 // If there is only one audio track then bytestream id might change in
694 // a new init segment. So update our state and notify frame processor.
695 const auto& stream_it = audio_streams_.begin();
696 if (stream_it != audio_streams_.end()) {
697 stream = stream_it->second;
698 if (stream_it->first != track_id) {
699 track_id_changes[stream_it->first] = track_id;
700 audio_streams_[track_id] = stream;
701 audio_streams_.erase(stream_it->first);
706 MEDIA_LOG(ERROR, media_log_) << "Got unexpected audio track"
707 << " track_id=" << track_id;
712 track->set_id(stream->media_track_id());
713 frame_processor_->OnPossibleAudioConfigUpdate(audio_config);
714 success &= stream->UpdateAudioConfig(audio_config, allow_codec_changes,
716 } else if (track->type() == MediaTrack::Video) {
717 VideoDecoderConfig video_config = tracks->getVideoConfig(track_id);
718 DVLOG(1) << "Video track_id=" << track_id
719 << " config: " << video_config.AsHumanReadableString();
720 DCHECK(video_config.IsValidConfig());
722 #if BUILDFLAG(ENABLE_PLATFORM_ENCRYPTED_DOLBY_VISION)
723 // Only encrypted Dolby Vision (DV) is supported, so require the config
724 // to be for an encrypted track.
725 if (video_config.codec() == VideoCodec::kDolbyVision &&
726 !video_config.is_encrypted()) {
727 MEDIA_LOG(ERROR, media_log_)
728 << "MSE playback of DolbyVision is only supported via platform "
729 "decryptor, but the provided DV track is not encrypted.";
732 #endif // BUILDFLAG(ENABLE_PLATFORM_ENCRYPTED_DOLBY_VISION)
735 base::ranges::find(expected_vcodecs, video_config.codec());
736 if (it == expected_vcodecs.end()) {
737 MEDIA_LOG(ERROR, media_log_) << "Video stream codec "
738 << GetCodecName(video_config.codec())
739 << " doesn't match SourceBuffer codecs.";
742 expected_vcodecs.erase(it);
744 ChunkDemuxerStream* stream = nullptr;
745 if (!first_init_segment_received_) {
746 DCHECK(video_streams_.find(track_id) == video_streams_.end());
747 stream = create_demuxer_stream_cb_.Run(DemuxerStream::VIDEO);
748 if (!stream || !frame_processor_->AddTrack(track_id, stream)) {
749 MEDIA_LOG(ERROR, media_log_) << "Failed to create video stream.";
752 video_streams_[track_id] = stream;
754 media_log_->SetProperty<MediaLogProperty::kVideoTracks>(
755 std::vector<VideoDecoderConfig>{video_config});
757 if (video_streams_.size() > 1) {
758 auto stream_it = video_streams_.find(track_id);
759 if (stream_it != video_streams_.end())
760 stream = stream_it->second;
762 // If there is only one video track then bytestream id might change in
763 // a new init segment. So update our state and notify frame processor.
764 const auto& stream_it = video_streams_.begin();
765 if (stream_it != video_streams_.end()) {
766 stream = stream_it->second;
767 if (stream_it->first != track_id) {
768 track_id_changes[stream_it->first] = track_id;
769 video_streams_[track_id] = stream;
770 video_streams_.erase(stream_it->first);
775 MEDIA_LOG(ERROR, media_log_) << "Got unexpected video track"
776 << " track_id=" << track_id;
781 track->set_id(stream->media_track_id());
782 success &= stream->UpdateVideoConfig(video_config, allow_codec_changes,
785 MEDIA_LOG(ERROR, media_log_) << "Error: unsupported media track type "
791 if (!expected_acodecs.empty() || !expected_vcodecs.empty()) {
792 for (const auto& acodec : expected_acodecs) {
793 MEDIA_LOG(ERROR, media_log_) << "Initialization segment misses expected "
794 << GetCodecName(acodec) << " track.";
796 for (const auto& vcodec : expected_vcodecs) {
797 MEDIA_LOG(ERROR, media_log_) << "Initialization segment misses expected "
798 << GetCodecName(vcodec) << " track.";
803 if (text_streams_.empty()) {
804 for (auto itr = text_configs.begin(); itr != text_configs.end(); ++itr) {
805 ChunkDemuxerStream* const text_stream =
806 create_demuxer_stream_cb_.Run(DemuxerStream::TEXT);
807 if (!frame_processor_->AddTrack(itr->first, text_stream)) {
809 MEDIA_LOG(ERROR, media_log_) << "Failed to add text track ID "
810 << itr->first << " to frame processor.";
813 text_stream->UpdateTextConfig(itr->second, media_log_);
814 text_streams_[itr->first] = text_stream;
815 new_text_track_cb_.Run(text_stream, itr->second);
818 const size_t text_count = text_streams_.size();
819 if (text_configs.size() != text_count) {
821 MEDIA_LOG(ERROR, media_log_)
822 << "The number of text track configs changed.";
823 } else if (text_count == 1) {
824 auto config_itr = text_configs.begin();
825 auto stream_itr = text_streams_.begin();
826 ChunkDemuxerStream* text_stream = stream_itr->second;
827 TextTrackConfig old_config = text_stream->text_track_config();
828 TextTrackConfig new_config(
829 config_itr->second.kind(), config_itr->second.label(),
830 config_itr->second.language(), old_config.id());
831 if (!new_config.Matches(old_config)) {
833 MEDIA_LOG(ERROR, media_log_)
834 << "New text track config does not match old one.";
836 StreamParser::TrackId old_id = stream_itr->first;
837 StreamParser::TrackId new_id = config_itr->first;
838 if (new_id != old_id) {
839 track_id_changes[old_id] = new_id;
840 text_streams_.erase(old_id);
841 text_streams_[new_id] = text_stream;
845 for (auto config_itr = text_configs.begin();
846 config_itr != text_configs.end(); ++config_itr) {
847 auto stream_itr = text_streams_.find(config_itr->first);
848 if (stream_itr == text_streams_.end()) {
850 MEDIA_LOG(ERROR, media_log_)
851 << "Unexpected text track configuration for track ID "
852 << config_itr->first;
856 const TextTrackConfig& new_config = config_itr->second;
857 ChunkDemuxerStream* stream = stream_itr->second;
858 TextTrackConfig old_config = stream->text_track_config();
859 if (!new_config.Matches(old_config)) {
861 MEDIA_LOG(ERROR, media_log_) << "New text track config for track ID "
863 << " does not match old one.";
870 if (audio_streams_.empty() && video_streams_.empty()) {
871 DVLOG(1) << __func__ << ": couldn't find a valid audio or video stream";
875 if (!frame_processor_->UpdateTrackIds(track_id_changes)) {
876 DVLOG(1) << __func__ << ": failed to remap track ids in frame processor";
880 frame_processor_->SetAllTrackBuffersNeedRandomAccessPoint();
882 if (!first_init_segment_received_) {
883 first_init_segment_received_ = true;
884 SetStreamMemoryLimits();
887 DVLOG(1) << "OnNewConfigs() : " << (success ? "success" : "failed");
889 if (state_ == PENDING_PARSER_CONFIG)
890 state_ = PENDING_PARSER_INIT;
891 if (state_ == PENDING_PARSER_RECONFIG)
892 state_ = PENDING_PARSER_REINIT;
893 DCHECK(init_segment_received_cb_);
894 init_segment_received_cb_.Run(std::move(tracks));
900 void SourceBufferState::SetStreamMemoryLimits() {
901 size_t audio_buf_size_limit =
902 GetMSEBufferSizeLimitIfExists(switches::kMSEAudioBufferSizeLimitMb);
903 if (audio_buf_size_limit) {
904 MEDIA_LOG(INFO, media_log_)
905 << "Custom audio per-track SourceBuffer size limit="
906 << audio_buf_size_limit;
907 for (const auto& it : audio_streams_)
908 it.second->SetStreamMemoryLimit(audio_buf_size_limit);
911 size_t video_buf_size_limit =
912 GetMSEBufferSizeLimitIfExists(switches::kMSEVideoBufferSizeLimitMb);
913 if (video_buf_size_limit) {
914 MEDIA_LOG(INFO, media_log_)
915 << "Custom video per-track SourceBuffer size limit="
916 << video_buf_size_limit;
917 for (const auto& it : video_streams_)
918 it.second->SetStreamMemoryLimit(video_buf_size_limit);
922 void SourceBufferState::OnNewMediaSegment() {
923 DVLOG(2) << "OnNewMediaSegment()";
924 DCHECK_EQ(state_, PARSER_INITIALIZED);
925 parsing_media_segment_ = true;
926 media_segment_has_data_for_track_.clear();
929 void SourceBufferState::OnEndOfMediaSegment() {
930 DVLOG(2) << "OnEndOfMediaSegment()";
931 DCHECK_EQ(state_, PARSER_INITIALIZED);
932 parsing_media_segment_ = false;
934 for (const auto& it : audio_streams_) {
935 if (!media_segment_has_data_for_track_[it.first]) {
936 LIMITED_MEDIA_LOG(DEBUG, media_log_, num_missing_track_logs_,
937 kMaxMissingTrackInSegmentLogs)
938 << "Media segment did not contain any coded frames for track "
939 << it.first << ", mismatching initialization segment. Therefore, MSE"
940 " coded frame processing may not interoperably detect"
941 " discontinuities in appended media.";
944 for (const auto& it : video_streams_) {
945 if (!media_segment_has_data_for_track_[it.first]) {
946 LIMITED_MEDIA_LOG(DEBUG, media_log_, num_missing_track_logs_,
947 kMaxMissingTrackInSegmentLogs)
948 << "Media segment did not contain any coded frames for track "
949 << it.first << ", mismatching initialization segment. Therefore, MSE"
950 " coded frame processing may not interoperably detect"
951 " discontinuities in appended media.";
956 bool SourceBufferState::OnNewBuffers(
957 const StreamParser::BufferQueueMap& buffer_queue_map) {
958 DVLOG(2) << __func__ << " buffer_queues=" << buffer_queue_map.size();
959 DCHECK_EQ(state_, PARSER_INITIALIZED);
960 DCHECK(timestamp_offset_during_append_);
961 DCHECK(parsing_media_segment_);
963 for (const auto& [track_id, buffer_queue] : buffer_queue_map) {
964 DCHECK(!buffer_queue.empty());
965 media_segment_has_data_for_track_[track_id] = true;
968 const base::TimeDelta timestamp_offset_before_processing =
969 *timestamp_offset_during_append_;
971 // Calculate the new timestamp offset for audio/video tracks if the stream
972 // parser corresponds to MSE MIME type with 'Generate Timestamps Flag' set
974 base::TimeDelta predicted_timestamp_offset =
975 timestamp_offset_before_processing;
976 if (generate_timestamps_flag()) {
977 base::TimeDelta min_end_timestamp = kNoTimestamp;
978 for (const auto& [track_id, buffer_queue] : buffer_queue_map) {
979 DCHECK(!buffer_queue.empty());
980 if (min_end_timestamp == kNoTimestamp ||
981 EndTimestamp(buffer_queue) < min_end_timestamp) {
982 min_end_timestamp = EndTimestamp(buffer_queue);
983 DCHECK_NE(kNoTimestamp, min_end_timestamp);
986 if (min_end_timestamp != kNoTimestamp)
987 predicted_timestamp_offset += min_end_timestamp;
990 if (!frame_processor_->ProcessFrames(
991 buffer_queue_map, append_window_start_during_append_,
992 append_window_end_during_append_, timestamp_offset_during_append_)) {
996 // Only update the timestamp offset if the frame processor hasn't already.
997 if (generate_timestamps_flag() &&
998 timestamp_offset_before_processing == *timestamp_offset_during_append_) {
999 // TODO(wolenetz): This prediction assumes the last frame in each track
1000 // isn't dropped by append window trimming. See https://crbug.com/850316.
1001 *timestamp_offset_during_append_ = predicted_timestamp_offset;
1007 void SourceBufferState::OnEncryptedMediaInitData(
1008 EmeInitDataType type,
1009 const std::vector<uint8_t>& init_data) {
1010 encrypted_media_init_data_reported_ = true;
1011 encrypted_media_init_data_cb_.Run(type, init_data);
1014 void SourceBufferState::OnSourceInitDone(
1015 const StreamParser::InitParameters& params) {
1016 // We've either yet-to-run |init_cb_| if pending init, or we've previously
1017 // run it if pending reinit.
1018 DCHECK((init_cb_ && state_ == PENDING_PARSER_INIT) ||
1019 (!init_cb_ && state_ == PENDING_PARSER_REINIT));
1020 State old_state = state_;
1021 state_ = PARSER_INITIALIZED;
1023 if (old_state == PENDING_PARSER_INIT)
1024 std::move(init_cb_).Run(params);
1027 } // namespace media