1 // Copyright 2012 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/chunk_demuxer.h"
12 #include "base/functional/bind.h"
13 #include "base/functional/callback_helpers.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/task/bind_post_task.h"
16 #include "base/trace_event/trace_event.h"
17 #include "media/base/audio_decoder_config.h"
18 #include "media/base/demuxer.h"
19 #include "media/base/media_tracks.h"
20 #include "media/base/mime_util.h"
21 #include "media/base/stream_parser.h"
22 #include "media/base/stream_parser_buffer.h"
23 #include "media/base/timestamp_constants.h"
24 #include "media/base/video_codecs.h"
25 #include "media/base/video_decoder_config.h"
26 #include "media/filters/frame_processor.h"
27 #include "media/filters/source_buffer_stream.h"
28 #include "media/filters/stream_parser_factory.h"
32 // Helper to attempt construction of a StreamParser specific to |content_type|
34 // TODO(wolenetz): Consider relocating this to StreamParserFactory in
35 // conjunction with updating StreamParserFactory's isTypeSupported() to also
36 // parse codecs, rather than require preparsed vector.
37 std::unique_ptr<media::StreamParser> CreateParserForTypeAndCodecs(
38 const std::string& content_type,
39 const std::string& codecs,
40 media::MediaLog* media_log,
43 std::vector<std::string> parsed_codec_ids;
44 media::SplitCodecs(codecs, &parsed_codec_ids);
45 return media::StreamParserFactory::Create(content_type, parsed_codec_ids,
46 media_log, has_audio, has_video);
49 // Helper to calculate the expected codecs parsed from initialization segments
50 // for a few mime types that have an implicit codec.
51 std::string ExpectedCodecs(const std::string& content_type,
52 const std::string& codecs) {
53 if (codecs == "" && content_type == "audio/aac")
56 (content_type == "audio/mpeg" || content_type == "audio/mp3"))
65 ChunkDemuxerStream::ChunkDemuxerStream(Type type, MediaTrack::Id media_track_id)
67 liveness_(StreamLiveness::kUnknown),
68 media_track_id_(media_track_id),
69 state_(UNINITIALIZED),
72 void ChunkDemuxerStream::StartReturningData() {
73 DVLOG(1) << "ChunkDemuxerStream::StartReturningData()";
74 base::AutoLock auto_lock(lock_);
76 ChangeState_Locked(RETURNING_DATA_FOR_READS);
79 #if BUILDFLAG(IS_TIZEN_TV)
80 void ChunkDemuxerStream::SetFramerate(
81 const StreamFramerate::Framerate& framerate) {
83 LOG(ERROR) << "stream is null,this:" << (void*)this;
86 stream_->updateFramerate(framerate);
90 void ChunkDemuxerStream::AbortReads() {
91 DVLOG(1) << "ChunkDemuxerStream::AbortReads()";
92 base::AutoLock auto_lock(lock_);
93 ChangeState_Locked(RETURNING_ABORT_FOR_READS);
95 std::move(read_cb_).Run(kAborted, {});
98 void ChunkDemuxerStream::CompletePendingReadIfPossible() {
99 base::AutoLock auto_lock(lock_);
103 CompletePendingReadIfPossible_Locked();
106 void ChunkDemuxerStream::Shutdown() {
107 DVLOG(1) << "ChunkDemuxerStream::Shutdown()";
108 base::AutoLock auto_lock(lock_);
109 ChangeState_Locked(SHUTDOWN);
111 // Pass an end of stream buffer to the pending callback to signal that no more
112 // data will be sent.
114 std::move(read_cb_).Run(DemuxerStream::kOk,
115 {StreamParserBuffer::CreateEOSBuffer()});
119 bool ChunkDemuxerStream::IsSeekWaitingForData() const {
120 base::AutoLock auto_lock(lock_);
121 return stream_->IsSeekPending();
124 void ChunkDemuxerStream::Seek(base::TimeDelta time) {
125 DVLOG(1) << "ChunkDemuxerStream::Seek(" << time.InSecondsF() << ")";
126 base::AutoLock auto_lock(lock_);
128 DCHECK(state_ == UNINITIALIZED || state_ == RETURNING_ABORT_FOR_READS)
134 bool ChunkDemuxerStream::Append(const StreamParser::BufferQueue& buffers) {
135 if (append_observer_cb_)
136 append_observer_cb_.Run(&buffers);
141 base::AutoLock auto_lock(lock_);
142 DCHECK_NE(state_, SHUTDOWN);
143 stream_->Append(buffers);
146 CompletePendingReadIfPossible_Locked();
151 void ChunkDemuxerStream::Remove(base::TimeDelta start,
153 base::TimeDelta duration) {
154 base::AutoLock auto_lock(lock_);
155 stream_->Remove(start, end, duration);
158 bool ChunkDemuxerStream::EvictCodedFrames(base::TimeDelta media_time,
159 size_t newDataSize) {
160 base::AutoLock auto_lock(lock_);
162 // If the stream is disabled, then the renderer is not reading from it and
163 // thus the read position might be stale. MSE GC algorithm uses the read
164 // position to determine when to stop removing data from the front of buffered
165 // ranges, so do a Seek in order to update the read position and allow the GC
166 // to collect unnecessary data that is earlier than the GOP containing
169 stream_->Seek(media_time);
171 // |media_time| is allowed to be a little imprecise here. GC only needs to
172 // know which GOP currentTime points to.
173 return stream_->GarbageCollectIfNeeded(media_time, newDataSize);
176 void ChunkDemuxerStream::OnMemoryPressure(
177 base::TimeDelta media_time,
178 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level,
179 bool force_instant_gc) {
180 // TODO(sebmarchand): Check if MEMORY_PRESSURE_LEVEL_MODERATE should also be
182 if (memory_pressure_level ==
183 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) {
186 base::AutoLock auto_lock(lock_);
187 return stream_->OnMemoryPressure(media_time, memory_pressure_level,
191 void ChunkDemuxerStream::OnSetDuration(base::TimeDelta duration) {
192 base::AutoLock auto_lock(lock_);
193 stream_->OnSetDuration(duration);
196 Ranges<base::TimeDelta> ChunkDemuxerStream::GetBufferedRanges(
197 base::TimeDelta duration) const {
198 base::AutoLock auto_lock(lock_);
200 Ranges<base::TimeDelta> range = stream_->GetBufferedTime();
202 if (range.size() == 0u)
205 // Clamp the end of the stream's buffered ranges to fit within the duration.
206 // This can be done by intersecting the stream's range with the valid time
208 Ranges<base::TimeDelta> valid_time_range;
209 valid_time_range.Add(range.start(0), range.start(0) + duration);
210 return range.IntersectionWith(valid_time_range);
213 base::TimeDelta ChunkDemuxerStream::GetLowestPresentationTimestamp() const {
214 base::AutoLock auto_lock(lock_);
215 return stream_->GetLowestPresentationTimestamp();
218 base::TimeDelta ChunkDemuxerStream::GetHighestPresentationTimestamp() const {
219 base::AutoLock auto_lock(lock_);
220 return stream_->GetHighestPresentationTimestamp();
223 base::TimeDelta ChunkDemuxerStream::GetBufferedDuration() const {
224 base::AutoLock auto_lock(lock_);
225 return stream_->GetBufferedDuration();
228 size_t ChunkDemuxerStream::GetBufferedSize() const {
229 base::AutoLock auto_lock(lock_);
230 return stream_->GetBufferedSize();
233 void ChunkDemuxerStream::OnStartOfCodedFrameGroup(DecodeTimestamp start_dts,
234 base::TimeDelta start_pts) {
235 DVLOG(2) << "ChunkDemuxerStream::OnStartOfCodedFrameGroup(dts "
236 << start_dts.InSecondsF() << ", pts " << start_pts.InSecondsF()
239 if (group_start_observer_cb_)
240 group_start_observer_cb_.Run(start_dts, start_pts);
242 base::AutoLock auto_lock(lock_);
243 stream_->OnStartOfCodedFrameGroup(start_pts);
246 bool ChunkDemuxerStream::UpdateAudioConfig(const AudioDecoderConfig& config,
247 bool allow_codec_change,
248 MediaLog* media_log) {
249 DCHECK(config.IsValidConfig());
250 DCHECK_EQ(type_, AUDIO);
251 base::AutoLock auto_lock(lock_);
253 DCHECK_EQ(state_, UNINITIALIZED);
254 stream_ = std::make_unique<SourceBufferStream>(config, media_log);
258 return stream_->UpdateAudioConfig(config, allow_codec_change);
261 bool ChunkDemuxerStream::UpdateVideoConfig(const VideoDecoderConfig& config,
262 bool allow_codec_change,
263 MediaLog* media_log) {
264 DCHECK(config.IsValidConfig());
265 DCHECK_EQ(type_, VIDEO);
266 base::AutoLock auto_lock(lock_);
269 DCHECK_EQ(state_, UNINITIALIZED);
270 stream_ = std::make_unique<SourceBufferStream>(config, media_log);
274 return stream_->UpdateVideoConfig(config, allow_codec_change);
277 void ChunkDemuxerStream::MarkEndOfStream() {
278 base::AutoLock auto_lock(lock_);
279 stream_->MarkEndOfStream();
282 void ChunkDemuxerStream::UnmarkEndOfStream() {
283 base::AutoLock auto_lock(lock_);
284 stream_->UnmarkEndOfStream();
287 // DemuxerStream methods.
288 void ChunkDemuxerStream::Read(uint32_t count, ReadCB read_cb) {
289 base::AutoLock auto_lock(lock_);
290 DCHECK_NE(state_, UNINITIALIZED);
293 read_cb_ = base::BindPostTaskToCurrentDefault(std::move(read_cb));
294 requested_buffer_count_ = count;
297 DVLOG(1) << "Read from disabled stream, returning EOS";
298 std::move(read_cb_).Run(DemuxerStream::kOk,
299 {StreamParserBuffer::CreateEOSBuffer()});
303 CompletePendingReadIfPossible_Locked();
306 DemuxerStream::Type ChunkDemuxerStream::type() const { return type_; }
308 StreamLiveness ChunkDemuxerStream::liveness() const {
309 base::AutoLock auto_lock(lock_);
313 AudioDecoderConfig ChunkDemuxerStream::audio_decoder_config() {
314 CHECK_EQ(type_, AUDIO);
315 base::AutoLock auto_lock(lock_);
316 // Trying to track down crash. http://crbug.com/715761
318 return stream_->GetCurrentAudioDecoderConfig();
321 VideoDecoderConfig ChunkDemuxerStream::video_decoder_config() {
322 CHECK_EQ(type_, VIDEO);
323 base::AutoLock auto_lock(lock_);
324 // Trying to track down crash. http://crbug.com/715761
326 return stream_->GetCurrentVideoDecoderConfig();
329 bool ChunkDemuxerStream::SupportsConfigChanges() { return true; }
331 bool ChunkDemuxerStream::IsEnabled() const {
332 base::AutoLock auto_lock(lock_);
336 void ChunkDemuxerStream::SetEnabled(bool enabled, base::TimeDelta timestamp) {
337 base::AutoLock auto_lock(lock_);
339 if (enabled == is_enabled_)
342 is_enabled_ = enabled;
345 stream_->Seek(timestamp);
346 } else if (read_cb_) {
347 DVLOG(1) << "Read from disabled stream, returning EOS";
348 std::move(read_cb_).Run(kOk, {StreamParserBuffer::CreateEOSBuffer()});
352 void ChunkDemuxerStream::SetStreamMemoryLimit(size_t memory_limit) {
353 base::AutoLock auto_lock(lock_);
354 stream_->set_memory_limit(memory_limit);
357 void ChunkDemuxerStream::SetLiveness(StreamLiveness liveness) {
358 base::AutoLock auto_lock(lock_);
359 liveness_ = liveness;
362 void ChunkDemuxerStream::ChangeState_Locked(State state) {
363 lock_.AssertAcquired();
364 DVLOG(1) << "ChunkDemuxerStream::ChangeState_Locked() : "
366 << " - " << state_ << " -> " << state;
370 ChunkDemuxerStream::~ChunkDemuxerStream() = default;
372 void ChunkDemuxerStream::CompletePendingReadIfPossible_Locked() {
373 lock_.AssertAcquired();
378 if (stream_->GetCurrentVideoDecoderConfig().is_rtc())
380 NOTREACHED_NORETURN();
381 case RETURNING_ABORT_FOR_READS:
382 // Null buffers should be returned in this state since we are waiting
383 // for a seek. Any buffers in the SourceBuffer should NOT be returned
384 // because they are associated with the seek.
385 requested_buffer_count_ = 0;
386 std::move(read_cb_).Run(kAborted, {});
387 DVLOG(2) << __func__ << ": returning kAborted, type " << type_;
390 requested_buffer_count_ = 0;
391 std::move(read_cb_).Run(kOk, {StreamParserBuffer::CreateEOSBuffer()});
392 DVLOG(2) << __func__ << ": returning kOk with EOS buffer, type " << type_;
394 case RETURNING_DATA_FOR_READS:
397 DCHECK(state_ == RETURNING_DATA_FOR_READS);
399 auto [status, buffers] = GetPendingBuffers_Locked();
401 // If the status from |stream_| is kNeedBuffer and there's no buffers,
402 // then after ChunkDemuxerStream::Append, try to read data again,
403 // 'requested_buffer_count_' does not need to be cleared to 0.
404 if (status == SourceBufferStreamStatus::kNeedBuffer && buffers.empty()) {
407 // If the status from |stream_| is kConfigChange, the vector muse be
408 // empty, then need to notify new config by running |read_cb_|.
409 if (status == SourceBufferStreamStatus::kConfigChange) {
410 DCHECK(buffers.empty());
411 requested_buffer_count_ = 0;
412 std::move(read_cb_).Run(kConfigChanged, std::move(buffers));
415 // Other cases are kOk and just return the buffers.
416 DCHECK(!buffers.empty());
417 requested_buffer_count_ = 0;
418 std::move(read_cb_).Run(kOk, std::move(buffers));
421 std::pair<SourceBufferStreamStatus, DemuxerStream::DecoderBufferVector>
422 ChunkDemuxerStream::GetPendingBuffers_Locked() {
423 lock_.AssertAcquired();
424 DemuxerStream::DecoderBufferVector output_buffers;
425 for (uint32_t i = 0; i < requested_buffer_count_; ++i) {
426 // This aims to avoid send out buffers with different config. To
427 // simply the config change handling on renderer(receiver) side, prefer to
428 // send out buffers before config change happens.
429 if (stream_->IsNextBufferConfigChanged() && !output_buffers.empty()) {
430 DVLOG(3) << __func__ << " status=0"
431 << ", type=" << type_ << ", req_size=" << requested_buffer_count_
432 << ", out_size=" << output_buffers.size();
433 return {SourceBufferStreamStatus::kSuccess, std::move(output_buffers)};
436 scoped_refptr<StreamParserBuffer> buffer;
437 SourceBufferStreamStatus status = stream_->GetNextBuffer(&buffer);
439 case SourceBufferStreamStatus::kSuccess:
440 output_buffers.emplace_back(buffer);
442 case SourceBufferStreamStatus::kNeedBuffer:
443 // Return early with calling |read_cb_| if output_buffers has buffers
444 // since there is no more readable data.
445 DVLOG(3) << __func__ << " status=" << (int)status << ", type=" << type_
446 << ", req_size=" << requested_buffer_count_
447 << ", out_size=" << output_buffers.size();
448 return {status, std::move(output_buffers)};
449 case SourceBufferStreamStatus::kEndOfStream:
450 output_buffers.emplace_back(StreamParserBuffer::CreateEOSBuffer());
451 DVLOG(3) << __func__ << " status=" << (int)status << ", type=" << type_
452 << ", req_size=" << requested_buffer_count_
453 << ", out_size=" << output_buffers.size();
454 return {status, std::move(output_buffers)};
455 case SourceBufferStreamStatus::kConfigChange:
456 // Since IsNextBufferConfigChanged has detected config change happen and
457 // send out buffers if |output_buffers| has buffer. When confige
458 // change actually happen it should be the first time run this |for
459 // loop|, i.e. output_buffers should be empty.
460 DCHECK(output_buffers.empty());
461 DVLOG(3) << __func__ << " status=" << (int)status << ", type=" << type_
462 << ", req_size=" << requested_buffer_count_
463 << ", out_size=" << output_buffers.size();
464 return {status, std::move(output_buffers)};
468 DCHECK_EQ(output_buffers.size(),
469 static_cast<size_t>(requested_buffer_count_));
470 DVLOG(3) << __func__ << " status are always kSuccess"
471 << ", type=" << type_ << ", req_size=" << requested_buffer_count_
472 << ", out_size=" << output_buffers.size();
473 return {SourceBufferStreamStatus::kSuccess, std::move(output_buffers)};
476 ChunkDemuxer::ChunkDemuxer(
477 base::OnceClosure open_cb,
478 base::RepeatingClosure progress_cb,
479 EncryptedMediaInitDataCB encrypted_media_init_data_cb,
480 AudioTracksCountChangedCB audio_tracks_count_changed_cb,
482 : open_cb_(std::move(open_cb)),
483 progress_cb_(std::move(progress_cb)),
484 encrypted_media_init_data_cb_(std::move(encrypted_media_init_data_cb)),
485 audio_tracks_count_changed_cb_(std::move(audio_tracks_count_changed_cb)),
486 media_log_(media_log) {
488 DCHECK(encrypted_media_init_data_cb_);
489 MEDIA_LOG(INFO, media_log_) << GetDisplayName();
492 std::string ChunkDemuxer::GetDisplayName() const {
493 return "ChunkDemuxer";
496 DemuxerType ChunkDemuxer::GetDemuxerType() const {
497 return DemuxerType::kChunkDemuxer;
500 void ChunkDemuxer::Initialize(DemuxerHost* host,
501 PipelineStatusCallback init_cb) {
502 DVLOG(1) << "Initialize()";
503 TRACE_EVENT_ASYNC_BEGIN0("media", "ChunkDemuxer::Initialize", this);
505 base::OnceClosure open_cb;
509 base::AutoLock auto_lock(lock_);
510 if (state_ == SHUTDOWN) {
511 // Init cb must only be run after this method returns, so post.
512 init_cb_ = base::BindPostTaskToCurrentDefault(std::move(init_cb));
513 RunInitCB_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
517 DCHECK_EQ(state_, WAITING_FOR_INIT);
519 // Do not post init_cb once this function returns because if there is an
520 // error after initialization, the error might be reported before init_cb
521 // has a chance to run. This is because ChunkDemuxer::ReportError_Locked
522 // directly calls DemuxerHost::OnDemuxerError: crbug.com/633016.
523 init_cb_ = std::move(init_cb);
525 ChangeState_Locked(INITIALIZING);
526 open_cb = std::move(open_cb_);
529 std::move(open_cb).Run();
531 #if BUILDFLAG(IS_TIZEN_TV)
532 framerate_set_cb_ = base::BindPostTaskToCurrentDefault(base::BindRepeating(
533 &ChunkDemuxer::OnFramerateSet, base::Unretained(this)));
537 void ChunkDemuxer::Stop() {
538 DVLOG(1) << "Stop()";
542 void ChunkDemuxer::Seek(base::TimeDelta time, PipelineStatusCallback cb) {
543 DVLOG(1) << "Seek(" << time.InSecondsF() << ")";
544 DCHECK(time >= base::TimeDelta());
545 TRACE_EVENT_ASYNC_BEGIN0("media", "ChunkDemuxer::Seek", this);
547 base::AutoLock auto_lock(lock_);
550 seek_cb_ = base::BindPostTaskToCurrentDefault(std::move(cb));
551 if (state_ != INITIALIZED && state_ != ENDED) {
552 RunSeekCB_Locked(PIPELINE_ERROR_INVALID_STATE);
556 if (cancel_next_seek_) {
557 cancel_next_seek_ = false;
558 RunSeekCB_Locked(PIPELINE_OK);
562 SeekAllSources(time);
563 StartReturningData();
565 if (IsSeekWaitingForData_Locked()) {
566 DVLOG(1) << "Seek() : waiting for more data to arrive.";
570 RunSeekCB_Locked(PIPELINE_OK);
573 bool ChunkDemuxer::IsSeekable() const {
577 // Demuxer implementation.
578 base::Time ChunkDemuxer::GetTimelineOffset() const {
579 return timeline_offset_;
582 std::vector<DemuxerStream*> ChunkDemuxer::GetAllStreams() {
583 base::AutoLock auto_lock(lock_);
584 std::vector<DemuxerStream*> result;
585 // Put enabled streams at the beginning of the list so that
586 // MediaResource::GetFirstStream returns the enabled stream if there is one.
587 // TODO(servolk): Revisit this after media track switching is supported.
588 for (const auto& stream : audio_streams_) {
589 if (stream->IsEnabled())
590 result.push_back(stream.get());
592 for (const auto& stream : video_streams_) {
593 if (stream->IsEnabled())
594 result.push_back(stream.get());
596 // Put disabled streams at the end of the vector.
597 for (const auto& stream : audio_streams_) {
598 if (!stream->IsEnabled())
599 result.push_back(stream.get());
601 for (const auto& stream : video_streams_) {
602 if (!stream->IsEnabled())
603 result.push_back(stream.get());
608 base::TimeDelta ChunkDemuxer::GetStartTime() const {
609 return base::TimeDelta();
612 int64_t ChunkDemuxer::GetMemoryUsage() const {
613 base::AutoLock auto_lock(lock_);
615 for (const auto& s : audio_streams_)
616 mem += s->GetBufferedSize();
617 for (const auto& s : video_streams_)
618 mem += s->GetBufferedSize();
622 absl::optional<container_names::MediaContainerName>
623 ChunkDemuxer::GetContainerForMetrics() const {
624 return absl::nullopt;
627 void ChunkDemuxer::AbortPendingReads() {
628 base::AutoLock auto_lock(lock_);
629 DCHECK(state_ == INITIALIZED || state_ == ENDED || state_ == SHUTDOWN ||
630 state_ == PARSE_ERROR)
633 if (state_ == SHUTDOWN || state_ == PARSE_ERROR)
636 AbortPendingReads_Locked();
639 void ChunkDemuxer::StartWaitingForSeek(base::TimeDelta seek_time) {
640 DVLOG(1) << "StartWaitingForSeek()";
641 base::AutoLock auto_lock(lock_);
642 DCHECK(state_ == INITIALIZED || state_ == ENDED || state_ == SHUTDOWN ||
643 state_ == PARSE_ERROR) << state_;
646 if (state_ == SHUTDOWN || state_ == PARSE_ERROR)
649 AbortPendingReads_Locked();
650 SeekAllSources(seek_time);
652 // Cancel state set in CancelPendingSeek() since we want to
653 // accept the next Seek().
654 cancel_next_seek_ = false;
657 void ChunkDemuxer::CancelPendingSeek(base::TimeDelta seek_time) {
658 base::AutoLock auto_lock(lock_);
659 DCHECK_NE(state_, INITIALIZING);
660 DCHECK(!seek_cb_ || IsSeekWaitingForData_Locked());
662 if (cancel_next_seek_)
665 AbortPendingReads_Locked();
666 SeekAllSources(seek_time);
669 cancel_next_seek_ = true;
673 RunSeekCB_Locked(PIPELINE_OK);
676 ChunkDemuxer::Status ChunkDemuxer::AddId(
677 const std::string& id,
678 std::unique_ptr<AudioDecoderConfig> audio_config) {
679 DCHECK(audio_config);
680 DVLOG(1) << __func__ << " id="
681 << " audio_config=" << audio_config->AsHumanReadableString();
682 base::AutoLock auto_lock(lock_);
684 // Any valid audio config provided by WC is bufferable here, though decode
685 // error may occur later.
686 if (!audio_config->IsValidConfig())
687 return ChunkDemuxer::kNotSupported;
689 if ((state_ != WAITING_FOR_INIT && state_ != INITIALIZING) ||
690 IsValidId_Locked(id)) {
691 return kReachedIdLimit;
696 std::string expected_codec = GetCodecName(audio_config->codec());
697 std::unique_ptr<media::StreamParser> stream_parser(
698 media::StreamParserFactory::Create(std::move(audio_config)));
699 DCHECK(stream_parser);
701 return AddIdInternal(id, std::move(stream_parser), expected_codec, false);
704 ChunkDemuxer::Status ChunkDemuxer::AddId(
705 const std::string& id,
706 std::unique_ptr<VideoDecoderConfig> video_config) {
707 DCHECK(video_config);
708 DVLOG(1) << __func__ << " id="
709 << " video_config=" << video_config->AsHumanReadableString();
710 base::AutoLock auto_lock(lock_);
712 // Any valid video config provided by WC is bufferable here, though decode
713 // error may occur later.
714 if (!video_config->IsValidConfig())
715 return ChunkDemuxer::kNotSupported;
717 if ((state_ != WAITING_FOR_INIT && state_ != INITIALIZING) ||
718 IsValidId_Locked(id)) {
719 return kReachedIdLimit;
724 std::string expected_codec = GetCodecName(video_config->codec());
725 std::unique_ptr<media::StreamParser> stream_parser(
726 media::StreamParserFactory::Create(std::move(video_config)));
727 DCHECK(stream_parser);
729 return AddIdInternal(id, std::move(stream_parser), expected_codec, true);
732 ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id,
733 const std::string& content_type,
734 const std::string& codecs) {
735 DVLOG(1) << __func__ << " id=" << id << " content_type=" << content_type
736 << " codecs=" << codecs;
737 base::AutoLock auto_lock(lock_);
739 if ((state_ != WAITING_FOR_INIT && state_ != INITIALIZING) ||
740 IsValidId_Locked(id)) {
741 return kReachedIdLimit;
744 // TODO(wolenetz): Change to DCHECK once less verification in release build is
745 // needed. See https://crbug.com/786975.
748 bool has_audio = false;
749 bool has_video = false;
751 std::unique_ptr<media::StreamParser> stream_parser(
752 CreateParserForTypeAndCodecs(content_type, codecs, media_log_, &has_audio,
754 if (!stream_parser) {
755 DVLOG(1) << __func__ << " failed: unsupported content_type=" << content_type
756 << " codecs=" << codecs;
757 return ChunkDemuxer::kNotSupported;
760 return AddIdInternal(id, std::move(stream_parser),
761 ExpectedCodecs(content_type, codecs), has_video);
764 ChunkDemuxer::Status ChunkDemuxer::AddIdInternal(
765 const std::string& id,
766 std::unique_ptr<media::StreamParser> stream_parser,
767 std::string expected_codecs,
769 DVLOG(2) << __func__ << " id=" << id
770 << " expected_codecs=" << expected_codecs;
771 lock_.AssertAcquired();
773 std::unique_ptr<FrameProcessor> frame_processor =
774 std::make_unique<FrameProcessor>(
775 base::BindRepeating(&ChunkDemuxer::IncreaseDurationIfNecessary,
776 base::Unretained(this)),
779 std::unique_ptr<SourceBufferState> source_state =
780 std::make_unique<SourceBufferState>(
781 std::move(stream_parser), std::move(frame_processor),
782 base::BindRepeating(&ChunkDemuxer::CreateDemuxerStream,
783 base::Unretained(this), id),
786 #if BUILDFLAG(IS_TIZEN_TV)
787 StreamParser::FramerateSetCB framerate_set_cb;
789 framerate_set_cb = framerate_set_cb_;
791 // Audio don't need this call back.
792 framerate_set_cb.Reset();
795 // TODO(wolenetz): Change these to DCHECKs or switch to returning
796 // kReachedIdLimit once less verification in release build is needed. See
797 // https://crbug.com/786975.
798 CHECK(pending_source_init_ids_.find(id) == pending_source_init_ids_.end());
799 auto insert_result = pending_source_init_ids_.insert(id);
800 CHECK(insert_result.first != pending_source_init_ids_.end());
801 CHECK(*insert_result.first == id);
802 CHECK(insert_result.second); // Only true if insertion succeeded.
804 source_state->Init(base::BindOnce(&ChunkDemuxer::OnSourceInitDone,
805 base::Unretained(this), id),
807 #if BUILDFLAG(IS_TIZEN_TV)
810 encrypted_media_init_data_cb_);
812 // TODO(wolenetz): Change to DCHECKs once less verification in release build
813 // is needed. See https://crbug.com/786975.
814 CHECK(!IsValidId_Locked(id));
815 source_state_map_[id] = std::move(source_state);
816 CHECK(IsValidId_Locked(id));
820 void ChunkDemuxer::SetTracksWatcher(const std::string& id,
821 MediaTracksUpdatedCB tracks_updated_cb) {
822 base::AutoLock auto_lock(lock_);
823 CHECK(IsValidId_Locked(id));
824 media_tracks_updated_cb_[id] = tracks_updated_cb;
825 source_state_map_[id]->SetTracksWatcher(base::BindRepeating(
826 &ChunkDemuxer::InitSegmentReceived, base::Unretained(this), id));
829 void ChunkDemuxer::InitSegmentReceived(const std::string& id,
830 std::unique_ptr<MediaTracks> tracks) {
831 bool audio_track_detected = false;
832 for (const auto& track : tracks->tracks()) {
833 if (track->type() == MediaTrack::Type::kAudio) {
834 audio_tracks_count_[id]++;
835 audio_tracks_count_total_++;
836 audio_track_detected = true;
839 if (audio_track_detected)
840 audio_tracks_count_changed_cb_.Run(audio_tracks_count_total_);
841 media_tracks_updated_cb_[id].Run(std::move(tracks));
844 void ChunkDemuxer::SetParseWarningCallback(
845 const std::string& id,
846 SourceBufferParseWarningCB parse_warning_cb) {
847 base::AutoLock auto_lock(lock_);
848 CHECK(IsValidId_Locked(id));
849 source_state_map_[id]->SetParseWarningCallback(std::move(parse_warning_cb));
852 void ChunkDemuxer::RemoveId(const std::string& id) {
853 DVLOG(1) << __func__ << " id=" << id;
854 base::AutoLock auto_lock(lock_);
855 CHECK(IsValidId_Locked(id));
857 source_state_map_.erase(id);
858 pending_source_init_ids_.erase(id);
859 // Remove demuxer streams created for this id.
860 for (const ChunkDemuxerStream* s : id_to_streams_map_[id]) {
861 bool stream_found = false;
862 for (size_t i = 0; i < audio_streams_.size(); ++i) {
863 if (audio_streams_[i].get() == s) {
865 removed_streams_.push_back(std::move(audio_streams_[i]));
866 audio_streams_.erase(audio_streams_.begin() + i);
872 for (size_t i = 0; i < video_streams_.size(); ++i) {
873 if (video_streams_[i].get() == s) {
875 removed_streams_.push_back(std::move(video_streams_[i]));
876 video_streams_.erase(video_streams_.begin() + i);
882 id_to_streams_map_.erase(id);
884 audio_tracks_count_total_ -= audio_tracks_count_[id];
885 audio_tracks_count_.erase(id);
886 audio_tracks_count_changed_cb_.Run(audio_tracks_count_total_);
887 media_tracks_updated_cb_.erase(id);
890 Ranges<base::TimeDelta> ChunkDemuxer::GetBufferedRanges(
891 const std::string& id) const {
892 base::AutoLock auto_lock(lock_);
895 auto itr = source_state_map_.find(id);
897 DCHECK(itr != source_state_map_.end());
898 return itr->second->GetBufferedRanges(duration_, state_ == ENDED);
901 base::TimeDelta ChunkDemuxer::GetLowestPresentationTimestamp(
902 const std::string& id) const {
903 base::AutoLock auto_lock(lock_);
906 auto itr = source_state_map_.find(id);
908 DCHECK(itr != source_state_map_.end());
909 return itr->second->GetLowestPresentationTimestamp();
912 base::TimeDelta ChunkDemuxer::GetHighestPresentationTimestamp(
913 const std::string& id) const {
914 base::AutoLock auto_lock(lock_);
917 auto itr = source_state_map_.find(id);
919 DCHECK(itr != source_state_map_.end());
920 return itr->second->GetHighestPresentationTimestamp();
923 void ChunkDemuxer::FindAndEnableProperTracks(
924 const std::vector<MediaTrack::Id>& track_ids,
925 base::TimeDelta curr_time,
926 DemuxerStream::Type track_type,
927 TrackChangeCB change_completed_cb) {
928 base::AutoLock auto_lock(lock_);
930 std::set<ChunkDemuxerStream*> enabled_streams;
931 for (const auto& id : track_ids) {
932 auto it = track_id_to_demux_stream_map_.find(id);
933 if (it == track_id_to_demux_stream_map_.end())
935 ChunkDemuxerStream* stream = it->second;
937 DCHECK_EQ(track_type, stream->type());
938 // TODO(servolk): Remove after multiple enabled audio tracks are supported
939 // by the media::RendererImpl.
940 if (!enabled_streams.empty()) {
941 MEDIA_LOG(INFO, media_log_)
942 << "Only one enabled track is supported, ignoring track " << id;
945 enabled_streams.insert(stream);
946 stream->SetEnabled(true, curr_time);
949 bool is_audio = track_type == DemuxerStream::AUDIO;
950 for (const auto& stream : is_audio ? audio_streams_ : video_streams_) {
951 if (stream && enabled_streams.find(stream.get()) == enabled_streams.end()) {
952 DVLOG(1) << __func__ << ": disabling stream " << stream.get();
953 stream->SetEnabled(false, curr_time);
957 std::vector<DemuxerStream*> streams(enabled_streams.begin(),
958 enabled_streams.end());
959 std::move(change_completed_cb).Run(track_type, streams);
962 void ChunkDemuxer::OnEnabledAudioTracksChanged(
963 const std::vector<MediaTrack::Id>& track_ids,
964 base::TimeDelta curr_time,
965 TrackChangeCB change_completed_cb) {
966 FindAndEnableProperTracks(track_ids, curr_time, DemuxerStream::AUDIO,
967 std::move(change_completed_cb));
970 void ChunkDemuxer::OnSelectedVideoTrackChanged(
971 const std::vector<MediaTrack::Id>& track_ids,
972 base::TimeDelta curr_time,
973 TrackChangeCB change_completed_cb) {
974 FindAndEnableProperTracks(track_ids, curr_time, DemuxerStream::VIDEO,
975 std::move(change_completed_cb));
978 void ChunkDemuxer::DisableCanChangeType() {
979 supports_change_type_ = false;
982 void ChunkDemuxer::OnMemoryPressure(
983 base::TimeDelta currentMediaTime,
984 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level,
985 bool force_instant_gc) {
986 // TODO(sebmarchand): Check if MEMORY_PRESSURE_LEVEL_MODERATE should also be
988 if (memory_pressure_level ==
989 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) {
992 base::AutoLock auto_lock(lock_);
993 for (const auto& [source, state] : source_state_map_) {
994 state->OnMemoryPressure(currentMediaTime, memory_pressure_level,
999 bool ChunkDemuxer::EvictCodedFrames(const std::string& id,
1000 base::TimeDelta currentMediaTime,
1001 size_t newDataSize) {
1002 DVLOG(1) << __func__ << "(" << id << ")"
1003 << " media_time=" << currentMediaTime.InSecondsF()
1004 << " newDataSize=" << newDataSize;
1005 base::AutoLock auto_lock(lock_);
1007 DCHECK(!id.empty());
1008 auto itr = source_state_map_.find(id);
1009 if (itr == source_state_map_.end()) {
1010 LOG(WARNING) << __func__ << " stream " << id << " not found";
1013 return itr->second->EvictCodedFrames(currentMediaTime, newDataSize);
1016 bool ChunkDemuxer::AppendToParseBuffer(const std::string& id,
1017 const uint8_t* data,
1019 DVLOG(1) << "AppendToParseBuffer(" << id << ", " << length << ")";
1021 DCHECK(!id.empty());
1024 // We don't DCHECK that |state_| != ENDED here, since |state_| is protected
1025 // by |lock_|. However, transition into ENDED can happen only on
1026 // MarkEndOfStream called by the MediaSource object on parse failure or on
1027 // app calling endOfStream(). In case that contract is violated for
1028 // nonzero-length appends, we still DCHECK within the lock, below.
1035 base::AutoLock auto_lock(lock_);
1036 DCHECK_NE(state_, ENDED);
1041 DCHECK(IsValidId_Locked(id));
1042 if (!source_state_map_[id]->AppendToParseBuffer(data, length)) {
1043 // Just indicate that the append failed. Let the caller give app an
1044 // error so that it may adapt. This is different from
1045 // RunSegmentParserLoop(), where fatal MediaSource failure should
1046 // occur if the underlying parse fails.
1052 case WAITING_FOR_INIT:
1055 DVLOG(1) << "AppendToParseBuffer(): called in unexpected state "
1057 // To preserve previous app-visible behavior in this hopefully
1058 // never-encountered path, report no failure to caller due to being in
1059 // invalid underlying state. If caller then proceeds with async parse
1060 // (via RunSegmentParserLoop, below), they will get the expected parse
1061 // failure for this set of states. If, instead, we returned false here,
1062 // then caller would instead tell app QuotaExceededErr synchronous with
1063 // the app's appendBuffer() call, instead of async decode error during
1065 // TODO(crbug.com/1379160): Instrument this path to see if it can be
1066 // changed to just NOTREACHED() << state_.
1074 StreamParser::ParseStatus ChunkDemuxer::RunSegmentParserLoop(
1075 const std::string& id,
1076 base::TimeDelta append_window_start,
1077 base::TimeDelta append_window_end,
1078 base::TimeDelta* timestamp_offset) {
1079 DVLOG(1) << "RunSegmentParserLoop(" << id << ")";
1081 DCHECK(!id.empty());
1082 DCHECK(timestamp_offset);
1084 Ranges<base::TimeDelta> ranges;
1086 StreamParser::ParseStatus result = StreamParser::ParseStatus::kFailed;
1089 base::AutoLock auto_lock(lock_);
1090 DCHECK_NE(state_, ENDED);
1092 // Capture if any of the SourceBuffers are waiting for data before we start
1094 bool old_waiting_for_data = IsSeekWaitingForData_Locked();
1099 DCHECK(IsValidId_Locked(id));
1100 result = source_state_map_[id]->RunSegmentParserLoop(
1101 append_window_start, append_window_end, timestamp_offset);
1102 if (result == StreamParser::ParseStatus::kFailed) {
1103 ReportError_Locked(CHUNK_DEMUXER_ERROR_APPEND_FAILED);
1109 case WAITING_FOR_INIT:
1112 DVLOG(1) << "RunSegmentParserLoop(): called in unexpected state "
1114 return StreamParser::ParseStatus::kFailed;
1117 // Check to see if newly parsed data was at the pending seek point. This
1118 // indicates we have parsed enough data to complete the seek. Work is still
1119 // in progress at this point, but it's okay since |seek_cb_| will post.
1120 if (old_waiting_for_data && !IsSeekWaitingForData_Locked() && seek_cb_) {
1121 RunSeekCB_Locked(PIPELINE_OK);
1124 ranges = GetBufferedRanges_Locked();
1127 DCHECK_NE(StreamParser::ParseStatus::kFailed, result);
1128 host_->OnBufferedTimeRangesChanged(ranges);
1133 bool ChunkDemuxer::AppendChunks(
1134 const std::string& id,
1135 std::unique_ptr<StreamParser::BufferQueue> buffer_queue,
1136 base::TimeDelta append_window_start,
1137 base::TimeDelta append_window_end,
1138 base::TimeDelta* timestamp_offset) {
1139 DCHECK(buffer_queue);
1140 DVLOG(1) << __func__ << ": " << id
1141 << ", buffer_queue size()=" << buffer_queue->size();
1143 DCHECK(!id.empty());
1144 DCHECK(timestamp_offset);
1146 Ranges<base::TimeDelta> ranges;
1149 base::AutoLock auto_lock(lock_);
1150 DCHECK_NE(state_, ENDED);
1152 // Capture if any of the SourceBuffers are waiting for data before we start
1153 // buffering new chunks.
1154 bool old_waiting_for_data = IsSeekWaitingForData_Locked();
1156 if (buffer_queue->size() == 0u)
1162 DCHECK(IsValidId_Locked(id));
1163 if (!source_state_map_[id]->AppendChunks(
1164 std::move(buffer_queue), append_window_start, append_window_end,
1165 timestamp_offset)) {
1166 ReportError_Locked(CHUNK_DEMUXER_ERROR_APPEND_FAILED);
1172 case WAITING_FOR_INIT:
1175 DVLOG(1) << "AppendChunks(): called in unexpected state " << state_;
1179 // Check to see if data was appended at the pending seek point. This
1180 // indicates we have parsed enough data to complete the seek. Work is still
1181 // in progress at this point, but it's okay since |seek_cb_| will post.
1182 if (old_waiting_for_data && !IsSeekWaitingForData_Locked() && seek_cb_)
1183 RunSeekCB_Locked(PIPELINE_OK);
1185 ranges = GetBufferedRanges_Locked();
1188 host_->OnBufferedTimeRangesChanged(ranges);
1193 void ChunkDemuxer::ResetParserState(const std::string& id,
1194 base::TimeDelta append_window_start,
1195 base::TimeDelta append_window_end,
1196 base::TimeDelta* timestamp_offset) {
1197 DVLOG(1) << "ResetParserState(" << id << ")";
1198 base::AutoLock auto_lock(lock_);
1199 DCHECK(!id.empty());
1200 CHECK(IsValidId_Locked(id));
1201 bool old_waiting_for_data = IsSeekWaitingForData_Locked();
1202 source_state_map_[id]->ResetParserState(append_window_start,
1205 // ResetParserState can possibly emit some buffers.
1206 // Need to check whether seeking can be completed.
1207 if (old_waiting_for_data && !IsSeekWaitingForData_Locked() && seek_cb_)
1208 RunSeekCB_Locked(PIPELINE_OK);
1211 void ChunkDemuxer::Remove(const std::string& id,
1212 base::TimeDelta start,
1213 base::TimeDelta end) {
1214 DVLOG(1) << "Remove(" << id << ", " << start.InSecondsF()
1215 << ", " << end.InSecondsF() << ")";
1216 base::AutoLock auto_lock(lock_);
1218 DCHECK(!id.empty());
1219 CHECK(IsValidId_Locked(id));
1220 DCHECK(start >= base::TimeDelta()) << start.InSecondsF();
1221 DCHECK(start < end) << "start " << start.InSecondsF()
1222 << " end " << end.InSecondsF();
1223 DCHECK(duration_ != kNoTimestamp);
1224 DCHECK(start <= duration_) << "start " << start.InSecondsF()
1225 << " duration " << duration_.InSecondsF();
1227 if (start == duration_)
1230 source_state_map_[id]->Remove(start, end, duration_);
1231 host_->OnBufferedTimeRangesChanged(GetBufferedRanges_Locked());
1234 bool ChunkDemuxer::CanChangeType(const std::string& id,
1235 const std::string& content_type,
1236 const std::string& codecs) {
1237 // Note, Chromium currently will not compare content_type and codecs, if any,
1238 // with previous content_type and codecs of the SourceBuffer.
1239 // TODO(wolenetz): Consider returning false if the codecs parameters are ever
1240 // made to be precise such that they signal that the number of tracks of
1241 // various media types differ from the first initialization segment (if
1242 // received already). Switching to an audio-only container, when the first
1243 // initialization segment only contained non-audio tracks, is one example we
1244 // could enforce earlier here.
1246 DVLOG(1) << __func__ << " id=" << id << " content_type=" << content_type
1247 << " codecs=" << codecs;
1248 base::AutoLock auto_lock(lock_);
1250 DCHECK(IsValidId_Locked(id));
1252 if (!supports_change_type_) {
1256 // CanChangeType() doesn't care if there has or hasn't been received a first
1257 // initialization segment for the source buffer corresponding to |id|.
1258 bool has_audio = false;
1259 bool has_video = false;
1261 std::unique_ptr<media::StreamParser> stream_parser(
1262 CreateParserForTypeAndCodecs(content_type, codecs, media_log_, &has_audio,
1264 return !!stream_parser;
1267 void ChunkDemuxer::ChangeType(const std::string& id,
1268 const std::string& content_type,
1269 const std::string& codecs) {
1270 DVLOG(1) << __func__ << " id=" << id << " content_type=" << content_type
1271 << " codecs=" << codecs;
1273 base::AutoLock auto_lock(lock_);
1275 DCHECK(state_ == INITIALIZING || state_ == INITIALIZED) << state_;
1276 DCHECK(IsValidId_Locked(id));
1278 bool has_audio = false;
1279 bool has_video = false;
1281 std::unique_ptr<media::StreamParser> stream_parser(
1282 CreateParserForTypeAndCodecs(content_type, codecs, media_log_, &has_audio,
1284 // Caller should query CanChangeType() first to protect from failing this.
1285 DCHECK(stream_parser);
1287 #if BUILDFLAG(IS_TIZEN_TV)
1288 StreamParser::FramerateSetCB framerate_set_cb;
1290 framerate_set_cb = framerate_set_cb_;
1292 // Audio don't need this call back.
1293 framerate_set_cb.Reset();
1295 source_state_map_[id]->ChangeType(std::move(stream_parser),
1296 #if BUILDFLAG(IS_TIZEN_TV)
1299 ExpectedCodecs(content_type, codecs));
1302 double ChunkDemuxer::GetDuration() {
1303 base::AutoLock auto_lock(lock_);
1304 return GetDuration_Locked();
1307 double ChunkDemuxer::GetDuration_Locked() {
1308 lock_.AssertAcquired();
1309 if (duration_ == kNoTimestamp)
1310 return std::numeric_limits<double>::quiet_NaN();
1312 // Return positive infinity if the resource is unbounded.
1313 // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#dom-media-duration
1314 if (duration_ == kInfiniteDuration)
1315 return std::numeric_limits<double>::infinity();
1317 if (user_specified_duration_ >= 0)
1318 return user_specified_duration_;
1320 return duration_.InSecondsF();
1323 void ChunkDemuxer::SetDuration(double duration) {
1324 base::AutoLock auto_lock(lock_);
1325 DVLOG(1) << "SetDuration(" << duration << ")";
1326 DCHECK_GE(duration, 0);
1328 if (duration == GetDuration_Locked())
1331 // Compute & bounds check the base::TimeDelta representation of duration.
1332 // This can be different if the value of |duration| doesn't fit the range or
1333 // precision of base::TimeDelta.
1334 base::TimeDelta min_duration = base::TimeDelta::FromInternalValue(1);
1335 // Don't use base::TimeDelta::Max() here, as we want the largest finite time
1337 base::TimeDelta max_duration = base::TimeDelta::FromInternalValue(
1338 std::numeric_limits<int64_t>::max() - 1);
1339 double min_duration_in_seconds = min_duration.InSecondsF();
1340 double max_duration_in_seconds = max_duration.InSecondsF();
1342 base::TimeDelta duration_td;
1343 if (duration == std::numeric_limits<double>::infinity()) {
1344 duration_td = media::kInfiniteDuration;
1345 } else if (duration < min_duration_in_seconds) {
1346 duration_td = min_duration;
1347 } else if (duration > max_duration_in_seconds) {
1348 duration_td = max_duration;
1351 base::Microseconds(duration * base::Time::kMicrosecondsPerSecond);
1354 DCHECK(duration_td.is_positive());
1356 user_specified_duration_ = duration;
1357 duration_ = duration_td;
1358 host_->SetDuration(duration_);
1360 for (auto itr = source_state_map_.begin(); itr != source_state_map_.end();
1362 itr->second->OnSetDuration(duration_);
1366 bool ChunkDemuxer::IsParsingMediaSegment(const std::string& id) {
1367 base::AutoLock auto_lock(lock_);
1368 DVLOG(1) << "IsParsingMediaSegment(" << id << ")";
1369 CHECK(IsValidId_Locked(id));
1371 return source_state_map_[id]->parsing_media_segment();
1374 bool ChunkDemuxer::GetGenerateTimestampsFlag(const std::string& id) {
1375 base::AutoLock auto_lock(lock_);
1376 DVLOG(1) << "GetGenerateTimestampsFlag(" << id << ")";
1377 CHECK(IsValidId_Locked(id));
1379 return source_state_map_[id]->generate_timestamps_flag();
1382 void ChunkDemuxer::SetSequenceMode(const std::string& id,
1383 bool sequence_mode) {
1384 base::AutoLock auto_lock(lock_);
1385 DVLOG(1) << "SetSequenceMode(" << id << ", " << sequence_mode << ")";
1386 CHECK(IsValidId_Locked(id));
1387 DCHECK_NE(state_, ENDED);
1389 source_state_map_[id]->SetSequenceMode(sequence_mode);
1392 void ChunkDemuxer::SetGroupStartTimestampIfInSequenceMode(
1393 const std::string& id,
1394 base::TimeDelta timestamp_offset) {
1395 base::AutoLock auto_lock(lock_);
1396 DVLOG(1) << "SetGroupStartTimestampIfInSequenceMode(" << id << ", "
1397 << timestamp_offset.InSecondsF() << ")";
1398 CHECK(IsValidId_Locked(id));
1399 DCHECK_NE(state_, ENDED);
1401 source_state_map_[id]->SetGroupStartTimestampIfInSequenceMode(
1406 void ChunkDemuxer::MarkEndOfStream(PipelineStatus status) {
1407 DVLOG(1) << "MarkEndOfStream(" << status << ")";
1408 base::AutoLock auto_lock(lock_);
1409 DCHECK_NE(state_, WAITING_FOR_INIT);
1410 DCHECK_NE(state_, ENDED);
1412 if (state_ == SHUTDOWN || state_ == PARSE_ERROR)
1415 if (state_ == INITIALIZING) {
1416 MEDIA_LOG(ERROR, media_log_)
1417 << "MediaSource endOfStream before demuxer initialization completes "
1418 "(before HAVE_METADATA) is treated as an error. This may also occur "
1419 "as consequence of other MediaSource errors before HAVE_METADATA.";
1420 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
1424 bool old_waiting_for_data = IsSeekWaitingForData_Locked();
1425 for (auto itr = source_state_map_.begin(); itr != source_state_map_.end();
1427 itr->second->MarkEndOfStream();
1430 CompletePendingReadsIfPossible();
1432 // Give a chance to resume the pending seek process.
1433 if (status != PIPELINE_OK) {
1434 DCHECK(status == CHUNK_DEMUXER_ERROR_EOS_STATUS_DECODE_ERROR ||
1435 status == CHUNK_DEMUXER_ERROR_EOS_STATUS_NETWORK_ERROR);
1436 ReportError_Locked(status);
1440 ChangeState_Locked(ENDED);
1441 DecreaseDurationIfNecessary();
1443 if (old_waiting_for_data && !IsSeekWaitingForData_Locked() && seek_cb_)
1444 RunSeekCB_Locked(PIPELINE_OK);
1447 void ChunkDemuxer::UnmarkEndOfStream() {
1448 DVLOG(1) << "UnmarkEndOfStream()";
1449 base::AutoLock auto_lock(lock_);
1450 DCHECK(state_ == ENDED || state_ == SHUTDOWN || state_ == PARSE_ERROR)
1453 // At least ReportError_Locked()'s error reporting to Blink hops threads, so
1454 // SourceBuffer may not be aware of media element error on another operation
1455 // that might race to this point.
1456 if (state_ == PARSE_ERROR || state_ == SHUTDOWN)
1459 ChangeState_Locked(INITIALIZED);
1461 for (auto itr = source_state_map_.begin(); itr != source_state_map_.end();
1463 itr->second->UnmarkEndOfStream();
1467 void ChunkDemuxer::Shutdown() {
1468 DVLOG(1) << "Shutdown()";
1469 base::AutoLock auto_lock(lock_);
1471 if (state_ == SHUTDOWN)
1474 ShutdownAllStreams();
1476 ChangeState_Locked(SHUTDOWN);
1479 RunSeekCB_Locked(PIPELINE_ERROR_ABORT);
1482 void ChunkDemuxer::SetMemoryLimitsForTest(DemuxerStream::Type type,
1483 size_t memory_limit) {
1484 for (auto itr = source_state_map_.begin(); itr != source_state_map_.end();
1486 itr->second->SetMemoryLimits(type, memory_limit);
1490 void ChunkDemuxer::ChangeState_Locked(State new_state) {
1491 lock_.AssertAcquired();
1492 DVLOG(1) << "ChunkDemuxer::ChangeState_Locked() : "
1493 << state_ << " -> " << new_state;
1495 // TODO(wolenetz): Change to DCHECK once less verification in release build is
1496 // needed. See https://crbug.com/786975.
1497 // Disallow changes from at or beyond PARSE_ERROR to below PARSE_ERROR.
1498 CHECK(!(state_ >= PARSE_ERROR && new_state < PARSE_ERROR));
1503 ChunkDemuxer::~ChunkDemuxer() {
1504 DCHECK_NE(state_, INITIALIZED);
1507 void ChunkDemuxer::ReportError_Locked(PipelineStatus error) {
1508 DVLOG(1) << "ReportError_Locked(" << error << ")";
1509 lock_.AssertAcquired();
1510 DCHECK(error != PIPELINE_OK);
1512 ChangeState_Locked(PARSE_ERROR);
1515 RunInitCB_Locked(error);
1519 ShutdownAllStreams();
1521 RunSeekCB_Locked(error);
1525 base::AutoUnlock auto_unlock(lock_);
1526 host_->OnDemuxerError(error);
1529 bool ChunkDemuxer::IsSeekWaitingForData_Locked() const {
1530 lock_.AssertAcquired();
1531 for (auto itr = source_state_map_.begin(); itr != source_state_map_.end();
1533 if (itr->second->IsSeekWaitingForData())
1540 #if BUILDFLAG(IS_TIZEN_TV)
1541 void ChunkDemuxer::OnFramerateSet(const StreamFramerate::Framerate& framerate) {
1542 if (framerate.num && framerate.den) {
1543 for (const auto& s : video_streams_)
1544 s->SetFramerate(framerate);
1549 void ChunkDemuxer::OnSourceInitDone(
1550 const std::string& source_id,
1551 const StreamParser::InitParameters& params) {
1552 DVLOG(1) << "OnSourceInitDone source_id=" << source_id
1553 << " duration=" << params.duration.InSecondsF();
1554 lock_.AssertAcquired();
1556 // TODO(wolenetz): Change these to DCHECKs once less verification in release
1557 // build is needed. See https://crbug.com/786975.
1558 CHECK(!pending_source_init_ids_.empty());
1559 CHECK(IsValidId_Locked(source_id));
1560 CHECK(pending_source_init_ids_.find(source_id) !=
1561 pending_source_init_ids_.end());
1563 CHECK_EQ(state_, INITIALIZING);
1564 if (audio_streams_.empty() && video_streams_.empty()) {
1565 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
1569 if (!params.duration.is_zero() && duration_ == kNoTimestamp)
1570 UpdateDuration(params.duration);
1572 if (!params.timeline_offset.is_null()) {
1573 if (!timeline_offset_.is_null() &&
1574 params.timeline_offset != timeline_offset_) {
1575 MEDIA_LOG(ERROR, media_log_)
1576 << "Timeline offset is not the same across all SourceBuffers.";
1577 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
1581 timeline_offset_ = params.timeline_offset;
1584 if (params.liveness != StreamLiveness::kUnknown) {
1585 for (const auto& s : audio_streams_)
1586 s->SetLiveness(params.liveness);
1587 for (const auto& s : video_streams_)
1588 s->SetLiveness(params.liveness);
1591 // Wait until all streams have initialized.
1592 pending_source_init_ids_.erase(source_id);
1593 if (!pending_source_init_ids_.empty())
1596 SeekAllSources(GetStartTime());
1597 StartReturningData();
1599 if (duration_ == kNoTimestamp)
1600 duration_ = kInfiniteDuration;
1602 // The demuxer is now initialized after the |start_timestamp_| was set.
1603 // TODO(wolenetz): Change these to DCHECKs once less verification in release
1604 // build is needed. See https://crbug.com/786975.
1605 CHECK_EQ(state_, INITIALIZING);
1606 ChangeState_Locked(INITIALIZED);
1607 RunInitCB_Locked(PIPELINE_OK);
1611 MediaTrack::Id ChunkDemuxer::GenerateMediaTrackId() {
1612 static unsigned g_track_count = 0;
1613 return MediaTrack::Id(base::NumberToString(++g_track_count));
1616 ChunkDemuxerStream* ChunkDemuxer::CreateDemuxerStream(
1617 const std::string& source_id,
1618 DemuxerStream::Type type) {
1619 // New ChunkDemuxerStreams can be created only during initialization segment
1620 // processing, which happens when a new chunk of data is appended and the
1621 // lock_ must be held by ChunkDemuxer::RunSegmentParserLoop/AppendChunks.
1622 lock_.AssertAcquired();
1624 MediaTrack::Id media_track_id = GenerateMediaTrackId();
1626 OwnedChunkDemuxerStreamVector* owning_vector = nullptr;
1628 case DemuxerStream::AUDIO:
1629 owning_vector = &audio_streams_;
1632 case DemuxerStream::VIDEO:
1633 owning_vector = &video_streams_;
1636 case DemuxerStream::UNKNOWN:
1637 NOTREACHED_NORETURN();
1640 std::unique_ptr<ChunkDemuxerStream> stream =
1641 std::make_unique<ChunkDemuxerStream>(type, media_track_id);
1642 DCHECK(track_id_to_demux_stream_map_.find(media_track_id) ==
1643 track_id_to_demux_stream_map_.end());
1644 track_id_to_demux_stream_map_[media_track_id] = stream.get();
1645 id_to_streams_map_[source_id].push_back(stream.get());
1646 stream->SetEnabled(owning_vector->empty(), base::TimeDelta());
1647 owning_vector->push_back(std::move(stream));
1648 return owning_vector->back().get();
1651 bool ChunkDemuxer::IsValidId_Locked(const std::string& source_id) const {
1652 lock_.AssertAcquired();
1653 return source_state_map_.count(source_id) > 0u;
1656 void ChunkDemuxer::UpdateDuration(base::TimeDelta new_duration) {
1657 DCHECK(duration_ != new_duration ||
1658 user_specified_duration_ != new_duration.InSecondsF());
1659 user_specified_duration_ = -1;
1660 duration_ = new_duration;
1661 host_->SetDuration(new_duration);
1664 void ChunkDemuxer::IncreaseDurationIfNecessary(base::TimeDelta new_duration) {
1665 DCHECK(new_duration != kNoTimestamp);
1666 DCHECK(new_duration != kInfiniteDuration);
1668 // Per April 1, 2014 MSE spec editor's draft:
1669 // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/
1670 // media-source.html#sourcebuffer-coded-frame-processing
1671 // 5. If the media segment contains data beyond the current duration, then run
1672 // the duration change algorithm with new duration set to the maximum of
1673 // the current duration and the group end timestamp.
1675 if (new_duration <= duration_)
1678 DVLOG(2) << __func__ << ": Increasing duration: " << duration_.InSecondsF()
1679 << " -> " << new_duration.InSecondsF();
1681 UpdateDuration(new_duration);
1684 void ChunkDemuxer::DecreaseDurationIfNecessary() {
1685 lock_.AssertAcquired();
1687 base::TimeDelta max_duration;
1689 for (auto itr = source_state_map_.begin(); itr != source_state_map_.end();
1691 max_duration = std::max(max_duration,
1692 itr->second->GetMaxBufferedDuration());
1695 if (max_duration.is_zero())
1698 // Note: be careful to also check |user_specified_duration_|, which may have
1699 // higher precision than |duration_|.
1700 if (max_duration < duration_ ||
1701 max_duration.InSecondsF() < user_specified_duration_) {
1702 UpdateDuration(max_duration);
1706 Ranges<base::TimeDelta> ChunkDemuxer::GetBufferedRanges() const {
1707 base::AutoLock auto_lock(lock_);
1708 return GetBufferedRanges_Locked();
1711 Ranges<base::TimeDelta> ChunkDemuxer::GetBufferedRanges_Locked() const {
1712 lock_.AssertAcquired();
1714 bool ended = state_ == ENDED;
1715 // TODO(acolwell): When we start allowing SourceBuffers that are not active,
1716 // we'll need to update this loop to only add ranges from active sources.
1717 SourceBufferState::RangesList ranges_list;
1718 for (auto itr = source_state_map_.begin(); itr != source_state_map_.end();
1720 ranges_list.push_back(itr->second->GetBufferedRanges(duration_, ended));
1723 return SourceBufferState::ComputeRangesIntersection(ranges_list, ended);
1726 void ChunkDemuxer::StartReturningData() {
1727 for (auto itr = source_state_map_.begin(); itr != source_state_map_.end();
1729 itr->second->StartReturningData();
1733 void ChunkDemuxer::AbortPendingReads_Locked() {
1734 for (auto itr = source_state_map_.begin(); itr != source_state_map_.end();
1736 itr->second->AbortReads();
1740 void ChunkDemuxer::SeekAllSources(base::TimeDelta seek_time) {
1741 for (auto itr = source_state_map_.begin(); itr != source_state_map_.end();
1743 itr->second->Seek(seek_time);
1747 void ChunkDemuxer::CompletePendingReadsIfPossible() {
1748 for (auto itr = source_state_map_.begin(); itr != source_state_map_.end();
1750 itr->second->CompletePendingReadIfPossible();
1754 void ChunkDemuxer::ShutdownAllStreams() {
1755 for (auto itr = source_state_map_.begin(); itr != source_state_map_.end();
1757 itr->second->Shutdown();
1761 void ChunkDemuxer::RunInitCB_Locked(PipelineStatus status) {
1762 lock_.AssertAcquired();
1764 TRACE_EVENT_ASYNC_END1("media", "ChunkDemuxer::Initialize", this, "status",
1765 PipelineStatusToString(status));
1766 std::move(init_cb_).Run(status);
1769 void ChunkDemuxer::RunSeekCB_Locked(PipelineStatus status) {
1770 lock_.AssertAcquired();
1772 TRACE_EVENT_ASYNC_END1("media", "ChunkDemuxer::Seek", this, "status",
1773 PipelineStatusToString(status));
1774 std::move(seek_cb_).Run(status);
1777 } // namespace media