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 #ifndef MEDIA_FILTERS_CHUNK_DEMUXER_H_
6 #define MEDIA_FILTERS_CHUNK_DEMUXER_H_
16 #include "base/containers/circular_deque.h"
17 #include "base/memory/memory_pressure_listener.h"
18 #include "base/memory/raw_ptr.h"
19 #include "base/synchronization/lock.h"
20 #include "base/thread_annotations.h"
21 #include "base/time/time.h"
22 #include "media/base/demuxer.h"
23 #include "media/base/demuxer_stream.h"
24 #include "media/base/media_tracks.h"
25 #include "media/base/ranges.h"
26 #include "media/base/stream_parser.h"
27 #include "media/filters/source_buffer_parse_warnings.h"
28 #include "media/filters/source_buffer_state.h"
29 #include "media/filters/source_buffer_stream.h"
31 class MEDIA_EXPORT SourceBufferStream;
35 class AudioDecoderConfig;
36 class VideoDecoderConfig;
38 class MEDIA_EXPORT ChunkDemuxerStream : public DemuxerStream {
40 using BufferQueue = base::circular_deque<scoped_refptr<StreamParserBuffer>>;
42 ChunkDemuxerStream() = delete;
44 ChunkDemuxerStream(Type type, MediaTrack::Id media_track_id);
46 ChunkDemuxerStream(const ChunkDemuxerStream&) = delete;
47 ChunkDemuxerStream& operator=(const ChunkDemuxerStream&) = delete;
49 ~ChunkDemuxerStream() override;
51 // ChunkDemuxerStream control methods.
52 void StartReturningData();
54 void CompletePendingReadIfPossible();
57 #if BUILDFLAG(IS_TIZEN_TV)
58 void SetFramerate(const StreamFramerate::Framerate& framerate);
61 // SourceBufferStream manipulation methods.
62 void Seek(base::TimeDelta time);
63 bool IsSeekWaitingForData() const;
65 // Add buffers to this stream. Buffers are stored in SourceBufferStreams,
66 // which handle ordering and overlap resolution.
67 // Returns true if buffers were successfully added.
68 bool Append(const StreamParser::BufferQueue& buffers);
70 // Removes buffers between |start| and |end| according to the steps
71 // in the "Coded Frame Removal Algorithm" in the Media Source
73 // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-coded-frame-removal
75 // |duration| is the current duration of the presentation. It is
76 // required by the computation outlined in the spec.
77 void Remove(base::TimeDelta start, base::TimeDelta end,
78 base::TimeDelta duration);
80 // If the buffer is full, attempts to try to free up space, as specified in
81 // the "Coded Frame Eviction Algorithm" in the Media Source Extensions Spec.
82 // Returns false iff buffer is still full after running eviction.
83 // https://w3c.github.io/media-source/#sourcebuffer-coded-frame-eviction
84 bool EvictCodedFrames(base::TimeDelta media_time, size_t newDataSize);
86 void OnMemoryPressure(
87 base::TimeDelta media_time,
88 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level,
89 bool force_instant_gc);
91 // Signal to the stream that duration has changed to |duration|.
92 void OnSetDuration(base::TimeDelta duration);
94 // Returns the range of buffered data in this stream, capped at |duration|.
95 Ranges<base::TimeDelta> GetBufferedRanges(base::TimeDelta duration) const;
97 // Returns the lowest PTS of the buffered data.
98 // Returns base::TimeDelta() if the stream has no buffered data.
99 base::TimeDelta GetLowestPresentationTimestamp() const;
101 // Returns the highest PTS of the buffered data.
102 // Returns base::TimeDelta() if the stream has no buffered data.
103 base::TimeDelta GetHighestPresentationTimestamp() const;
105 // Returns the duration of the buffered data.
106 // Returns base::TimeDelta() if the stream has no buffered data.
107 base::TimeDelta GetBufferedDuration() const;
109 // Returns the size of the buffered data in bytes.
110 size_t GetBufferedSize() const;
112 // Signal to the stream that buffers handed in through subsequent calls to
113 // Append() belong to a coded frame group that starts at |start_pts|.
114 // |start_dts| is used only to help tests verify correctness of calls to this
115 // method. If |group_start_observer_cb_| is set, first invokes this test-only
116 // callback with |start_dts| and |start_pts| to assist test verification.
117 void OnStartOfCodedFrameGroup(DecodeTimestamp start_dts,
118 base::TimeDelta start_pts);
120 // Called when midstream config updates occur.
121 // For audio and video, if the codec is allowed to change, the caller should
122 // set |allow_codec_change| to true.
123 // Returns true if the new config is accepted.
124 // Returns false if the new config should trigger an error.
125 bool UpdateAudioConfig(const AudioDecoderConfig& config,
126 bool allow_codec_change,
127 MediaLog* media_log);
128 bool UpdateVideoConfig(const VideoDecoderConfig& config,
129 bool allow_codec_change,
130 MediaLog* media_log);
132 void MarkEndOfStream();
133 void UnmarkEndOfStream();
135 // DemuxerStream methods.
136 void Read(uint32_t count, ReadCB read_cb) override;
137 Type type() const override;
138 StreamLiveness liveness() const override;
139 AudioDecoderConfig audio_decoder_config() override;
140 VideoDecoderConfig video_decoder_config() override;
141 bool SupportsConfigChanges() override;
143 bool IsEnabled() const;
144 void SetEnabled(bool enabled, base::TimeDelta timestamp);
146 // Sets the memory limit, in bytes, on the SourceBufferStream.
147 void SetStreamMemoryLimit(size_t memory_limit);
149 void SetLiveness(StreamLiveness liveness);
151 MediaTrack::Id media_track_id() const { return media_track_id_; }
153 // Allows tests to verify invocations of Append().
154 using AppendObserverCB = base::RepeatingCallback<void(const BufferQueue*)>;
155 void set_append_observer_for_testing(AppendObserverCB append_observer_cb) {
156 append_observer_cb_ = std::move(append_observer_cb);
159 // Allows tests to verify invocations of OnStartOfCodedFrameGroup().
160 using GroupStartObserverCB =
161 base::RepeatingCallback<void(DecodeTimestamp, base::TimeDelta)>;
162 void set_group_start_observer_for_testing(
163 GroupStartObserverCB group_start_observer_cb) {
164 group_start_observer_cb_ = std::move(group_start_observer_cb);
170 RETURNING_DATA_FOR_READS,
171 RETURNING_ABORT_FOR_READS,
175 // Assigns |state_| to |state|
176 void ChangeState_Locked(State state) EXCLUSIVE_LOCKS_REQUIRED(lock_);
178 void CompletePendingReadIfPossible_Locked() EXCLUSIVE_LOCKS_REQUIRED(lock_);
180 std::pair<SourceBufferStreamStatus, DemuxerStream::DecoderBufferVector>
181 GetPendingBuffers_Locked() EXCLUSIVE_LOCKS_REQUIRED(lock_);
183 // Specifies the type of the stream.
186 StreamLiveness liveness_ GUARDED_BY(lock_);
188 std::unique_ptr<SourceBufferStream> stream_ GUARDED_BY(lock_);
190 const MediaTrack::Id media_track_id_;
192 // Test-only callbacks to assist verification of Append() and
193 // OnStartOfCodedFrameGroup() calls, respectively.
194 AppendObserverCB append_observer_cb_;
195 GroupStartObserverCB group_start_observer_cb_;
197 // Requested buffer count. The actual returned buffer count could be less
198 // according to DemuxerStream::Read() API.
199 uint32_t requested_buffer_count_ = 0;
201 mutable base::Lock lock_;
202 State state_ GUARDED_BY(lock_);
203 ReadCB read_cb_ GUARDED_BY(lock_);
204 bool is_enabled_ GUARDED_BY(lock_);
207 // Demuxer implementation that allows chunks of media data to be passed
208 // from JavaScript to the media stack.
209 class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
212 kOk, // ID added w/o error.
213 kNotSupported, // Type specified is not supported.
214 kReachedIdLimit, // Reached ID limit. We can't handle any more IDs.
217 // |open_cb| Run when Initialize() is called to signal that the demuxer
218 // is ready to receive media data via AppendToParseBuffer()/AppendChunks().
219 // |progress_cb| Run each time data is appended.
220 // |encrypted_media_init_data_cb| Run when the demuxer determines that an
221 // encryption key is needed to decrypt the content.
222 // |media_log| Used to report content and engine debug messages.
223 ChunkDemuxer(base::OnceClosure open_cb,
224 base::RepeatingClosure progress_cb,
225 EncryptedMediaInitDataCB encrypted_media_init_data_cb,
226 MediaLog* media_log);
228 ChunkDemuxer(const ChunkDemuxer&) = delete;
229 ChunkDemuxer& operator=(const ChunkDemuxer&) = delete;
231 ~ChunkDemuxer() override;
233 // Demuxer implementation.
234 std::string GetDisplayName() const override;
235 DemuxerType GetDemuxerType() const override;
237 void Initialize(DemuxerHost* host, PipelineStatusCallback init_cb) override;
238 void Stop() override;
239 void Seek(base::TimeDelta time, PipelineStatusCallback cb) override;
240 bool IsSeekable() const override;
241 base::Time GetTimelineOffset() const override;
242 std::vector<DemuxerStream*> GetAllStreams() override;
243 base::TimeDelta GetStartTime() const override;
244 int64_t GetMemoryUsage() const override;
245 absl::optional<container_names::MediaContainerName> GetContainerForMetrics()
247 void AbortPendingReads() override;
249 // ChunkDemuxer reads are abortable. StartWaitingForSeek() and
250 // CancelPendingSeek() always abort pending and future reads until the
251 // expected seek occurs, so that ChunkDemuxer can stay synchronized with the
252 // associated JS method calls.
253 void StartWaitingForSeek(base::TimeDelta seek_time) override;
254 void CancelPendingSeek(base::TimeDelta seek_time) override;
256 // Registers a new `id` to use for AppendToParseBuffer(),
257 // RunSegmentParserLoop(), AppendChunks(), etc calls. `content_type` indicates
258 // the MIME type's ContentType and `codecs` indicates the MIME type's "codecs"
259 // parameter string (if any) for the data that we intend to append for this
260 // ID. kOk is returned if the demuxer has enough resources to support another
261 // ID and supports the format indicated by `content_type` and `codecs`.
262 // kReachedIdLimit is returned if the demuxer cannot handle another ID right
263 // now. kNotSupported is returned if `content_type` and `codecs` is not a
265 // The `audio_config` and `video_config` overloads behave similarly, except
266 // the caller must provide valid, supported decoder configs; those overloads'
267 // usage indicates that we intend to append WebCodecs encoded audio or video
268 // chunks for this ID.
269 [[nodiscard]] Status AddId(const std::string& id,
270 const std::string& content_type,
271 const std::string& codecs);
272 [[nodiscard]] Status AddId(const std::string& id,
273 std::unique_ptr<AudioDecoderConfig> audio_config);
274 [[nodiscard]] Status AddId(const std::string& id,
275 std::unique_ptr<VideoDecoderConfig> video_config);
277 // Notifies a caller via `tracks_updated_cb` that the set of media tracks
278 // for a given `id` has changed. This callback must be set before any calls to
279 // AppendToParseBuffer() for this `id`.
280 void SetTracksWatcher(const std::string& id,
281 MediaTracksUpdatedCB tracks_updated_cb);
283 // Notifies a caller via `parse_warning_cb` of a parse warning. This callback
284 // must be set before any calls to AppendToParseBuffer() for this `id`.
285 void SetParseWarningCallback(const std::string& id,
286 SourceBufferParseWarningCB parse_warning_cb);
288 // Removed an ID & associated resources that were previously added with
290 void RemoveId(const std::string& id);
292 // Gets the currently buffered ranges for the specified ID.
293 Ranges<base::TimeDelta> GetBufferedRanges(const std::string& id) const;
295 // Gets the lowest buffered PTS for the specified |id|. If there is nothing
296 // buffered, returns base::TimeDelta().
297 base::TimeDelta GetLowestPresentationTimestamp(const std::string& id) const;
299 // Gets the highest buffered PTS for the specified |id|. If there is nothing
300 // buffered, returns base::TimeDelta().
301 base::TimeDelta GetHighestPresentationTimestamp(const std::string& id) const;
303 void OnEnabledAudioTracksChanged(const std::vector<MediaTrack::Id>& track_ids,
304 base::TimeDelta curr_time,
305 TrackChangeCB change_completed_cb) override;
307 void OnSelectedVideoTrackChanged(const std::vector<MediaTrack::Id>& track_ids,
308 base::TimeDelta curr_time,
309 TrackChangeCB change_completed_cb) override;
311 void SetPlaybackRate(double rate) override {}
313 void DisableCanChangeType() override;
315 // Appends media data to the source buffer's stream parser associated with
316 // `id`. No parsing is done, just buffering the media data for future parsing
317 // via RunSegmentParserLoop calls. Returns true on success. Returns false if
318 // the parser was unable to allocate resources; content in `data` is not
319 // copied as a result, and this failure is reported (through various layers)
320 // up to the SourceBuffer's implementation of appendBuffer(), which should
321 // then notify the app of append failure using a `QuotaExceededErr` exception
322 // per the MSE specification. App could use a back-off and retry strategy or
323 // otherwise alter their behavior to attempt to buffer media for further
325 [[nodiscard]] bool AppendToParseBuffer(const std::string& id,
329 // Tells the stream parser for the source buffer associated with `id` to parse
330 // more of the data previously sent to it from this object's
331 // AppendToParseBuffer(). This operation applies and possibly updates
332 // `*timestamp_offset` during coded frame processing. `append_window_start`
333 // and `append_window_end` correspond to the MSE spec's similarly named source
334 // buffer attributes that are used in coded frame processing.
335 // Returns kSuccess if the segment parser loop iteration succeeded and all
336 // previously provided data from AppendToParseBuffer() has been inspected.
337 // Returns kSuccessHasMoreData if the segment parser loop iteration succeeded,
338 // yet there remains uninspected data remaining from AppendToParseBuffer();
339 // more call(s) to this method are necessary for the parser to attempt
340 // inspection of that data.
341 // Returns kFailed if the segment parser loop iteration hit error and the
342 // caller needs to run the append error algorithm with decode error parameter
344 [[nodiscard]] StreamParser::ParseStatus RunSegmentParserLoop(
345 const std::string& id,
346 base::TimeDelta append_window_start,
347 base::TimeDelta append_window_end,
348 base::TimeDelta* timestamp_offset);
350 // Appends webcodecs encoded chunks (already converted by caller into a
351 // BufferQueue of StreamParserBuffers) to the source buffer associated with
352 // |id|, with same semantic for other parameters and return value as
353 // RunSegmentParserLoop().
354 [[nodiscard]] bool AppendChunks(
355 const std::string& id,
356 std::unique_ptr<StreamParser::BufferQueue> buffer_queue,
357 base::TimeDelta append_window_start,
358 base::TimeDelta append_window_end,
359 base::TimeDelta* timestamp_offset);
361 // Aborts parsing the current segment and reset the parser to a state where
362 // it can accept a new segment.
363 // Some pending frames can be emitted during that process. These frames are
364 // applied |timestamp_offset|.
365 void ResetParserState(const std::string& id,
366 base::TimeDelta append_window_start,
367 base::TimeDelta append_window_end,
368 base::TimeDelta* timestamp_offset);
370 // Remove buffers between |start| and |end| for the source buffer
371 // associated with |id|.
372 void Remove(const std::string& id, base::TimeDelta start,
373 base::TimeDelta end);
375 // Returns whether or not the source buffer associated with |id| can change
376 // its parser type to one which parses |content_type| and |codecs|.
377 // |content_type| indicates the ContentType of the MIME type for the data that
378 // we intend to append for this |id|; |codecs| similarly indicates the MIME
379 // type's "codecs" parameter, if any.
380 bool CanChangeType(const std::string& id,
381 const std::string& content_type,
382 const std::string& codecs);
384 // For the source buffer associated with |id|, changes its parser type to one
385 // which parses |content_type| and |codecs|. |content_type| indicates the
386 // ContentType of the MIME type for the data that we intend to append for this
387 // |id|; |codecs| similarly indicates the MIME type's "codecs" parameter, if
388 // any. Caller must first ensure CanChangeType() returns true for the same
389 // parameters. Caller must also ensure that ResetParserState() is done before
390 // calling this, to flush any pending frames.
391 void ChangeType(const std::string& id,
392 const std::string& content_type,
393 const std::string& codecs);
395 // If the buffer is full, attempts to try to free up space, as specified in
396 // the "Coded Frame Eviction Algorithm" in the Media Source Extensions Spec.
397 // Returns false iff buffer is still full after running eviction.
398 // https://w3c.github.io/media-source/#sourcebuffer-coded-frame-eviction
399 [[nodiscard]] bool EvictCodedFrames(const std::string& id,
400 base::TimeDelta currentMediaTime,
403 void OnMemoryPressure(
404 base::TimeDelta currentMediaTime,
405 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level,
406 bool force_instant_gc);
408 // Returns the current presentation duration.
409 double GetDuration();
410 double GetDuration_Locked();
412 // Notifies the demuxer that the duration of the media has changed to
414 void SetDuration(double duration);
416 // Returns true if the source buffer associated with |id| is currently parsing
417 // a media segment, or false otherwise.
418 bool IsParsingMediaSegment(const std::string& id);
420 // Returns the 'Generate Timestamps Flag', as described in the MSE Byte Stream
421 // Format Registry, for the source buffer associated with |id|.
422 bool GetGenerateTimestampsFlag(const std::string& id);
424 // Set the append mode to be applied to subsequent buffers appended to the
425 // source buffer associated with |id|. If |sequence_mode| is true, caller
426 // is requesting "sequence" mode. Otherwise, caller is requesting "segments"
428 void SetSequenceMode(const std::string& id, bool sequence_mode);
430 // Signals the coded frame processor for the source buffer associated with
431 // |id| to update its group start timestamp to be |timestamp_offset| if it is
432 // in sequence append mode.
433 void SetGroupStartTimestampIfInSequenceMode(const std::string& id,
434 base::TimeDelta timestamp_offset);
436 // Called to signal changes in the "end of stream"
437 // state. UnmarkEndOfStream() must not be called if a matching
438 // MarkEndOfStream() has not come before it.
439 void MarkEndOfStream(PipelineStatus status);
440 void UnmarkEndOfStream();
444 // Sets the memory limit on each stream of a specific type.
445 // |memory_limit| is the maximum number of bytes each stream of type |type|
446 // is allowed to hold in its buffer.
447 void SetMemoryLimitsForTest(DemuxerStream::Type type, size_t memory_limit);
449 // Returns the ranges representing the buffered data in the demuxer.
450 // TODO(wolenetz): Remove this method once MediaSourceDelegate no longer
451 // requires it for doing hack browser seeks to I-frame on Android. See
452 // http://crbug.com/304234.
453 Ranges<base::TimeDelta> GetBufferedRanges() const;
457 WAITING_FOR_INIT = 0,
462 // Any State at or beyond PARSE_ERROR cannot be changed to a state before
463 // this. See ChangeState_Locked.
468 // Helper for AddId's creation of FrameProcessor, and
469 // SourceBufferState creation, initialization and tracking in
470 // source_state_map_.
471 ChunkDemuxer::Status AddIdInternal(
472 const std::string& id,
473 std::unique_ptr<media::StreamParser> stream_parser,
474 std::string expected_codecs,
477 // Helper for vide and audio track changing.
478 void FindAndEnableProperTracks(const std::vector<MediaTrack::Id>& track_ids,
479 base::TimeDelta curr_time,
480 DemuxerStream::Type track_type,
481 TrackChangeCB change_completed_cb);
483 void ChangeState_Locked(State new_state);
485 // Reports an error and puts the demuxer in a state where it won't accept more
487 void ReportError_Locked(PipelineStatus error);
489 // Returns true if any stream has seeked to a time without buffered data.
490 bool IsSeekWaitingForData_Locked() const;
492 // Returns true if all streams can successfully call EndOfStream,
493 // false if any can not.
494 bool CanEndOfStream_Locked() const;
496 // SourceBufferState callbacks.
497 void OnSourceInitDone(const std::string& source_id,
498 const StreamParser::InitParameters& params);
500 #if BUILDFLAG(IS_TIZEN_TV)
501 void OnFramerateSet(const StreamFramerate::Framerate& framerate);
504 // Creates a DemuxerStream of the specified |type| for the SourceBufferState
505 // with the given |source_id|.
506 // Returns a pointer to a new ChunkDemuxerStream instance, which is owned by
508 ChunkDemuxerStream* CreateDemuxerStream(const std::string& source_id,
509 DemuxerStream::Type type);
511 // Returns true if |source_id| is valid, false otherwise.
512 bool IsValidId_Locked(const std::string& source_id) const;
514 // Increases |duration_| to |new_duration|, if |new_duration| is higher.
515 void IncreaseDurationIfNecessary(base::TimeDelta new_duration);
517 // Decreases |duration_| if the buffered region is less than |duration_| when
518 // EndOfStream() is called.
519 void DecreaseDurationIfNecessary();
521 // Sets |duration_| to |new_duration|, sets |user_specified_duration_| to -1
522 // and notifies |host_|.
523 void UpdateDuration(base::TimeDelta new_duration);
525 // Returns the ranges representing the buffered data in the demuxer.
526 Ranges<base::TimeDelta> GetBufferedRanges_Locked() const;
528 // Start returning data on all DemuxerStreams.
529 void StartReturningData();
531 void AbortPendingReads_Locked();
533 // Completes any pending reads if it is possible to do so.
534 void CompletePendingReadsIfPossible();
536 // Seeks all SourceBufferStreams to |seek_time|.
537 void SeekAllSources(base::TimeDelta seek_time);
539 // Generates and returns a unique media track id.
540 static MediaTrack::Id GenerateMediaTrackId();
542 // Shuts down all DemuxerStreams by calling Shutdown() on
543 // all objects in |source_state_map_|.
544 void ShutdownAllStreams();
546 // Executes |init_cb_| with |status| and closes out the async trace.
547 void RunInitCB_Locked(PipelineStatus status);
549 // Executes |seek_cb_| with |status| and closes out the async trace.
550 void RunSeekCB_Locked(PipelineStatus status);
552 mutable base::Lock lock_;
553 State state_ = WAITING_FOR_INIT;
554 bool cancel_next_seek_ = false;
556 // Found dangling on `linux-rel` in
557 // `benchmarks.system_health_smoke_test.SystemHealthBenchmarkSmokeTest.
558 // system_health.memory_desktop/browse:media:youtubetv:2019`.
559 raw_ptr<DemuxerHost, DanglingUntriaged> host_ = nullptr;
560 base::OnceClosure open_cb_;
561 const base::RepeatingClosure progress_cb_;
562 EncryptedMediaInitDataCB encrypted_media_init_data_cb_;
564 // MediaLog for reporting messages and properties to debug content and engine.
565 raw_ptr<MediaLog> media_log_;
567 PipelineStatusCallback init_cb_;
568 // Callback to execute upon seek completion.
569 // TODO(wolenetz/acolwell): Protect against possible double-locking by first
570 // releasing |lock_| before executing this callback. See
571 // http://crbug.com/308226
572 PipelineStatusCallback seek_cb_;
574 using OwnedChunkDemuxerStreamVector =
575 std::vector<std::unique_ptr<ChunkDemuxerStream>>;
576 OwnedChunkDemuxerStreamVector audio_streams_;
577 OwnedChunkDemuxerStreamVector video_streams_;
579 // Keep track of which ids still remain uninitialized so that we transition
580 // into the INITIALIZED only after all ids/SourceBuffers got init segment.
581 std::set<std::string> pending_source_init_ids_;
583 #if BUILDFLAG(IS_TIZEN_TV)
584 StreamParser::FramerateSetCB framerate_set_cb_;
587 base::TimeDelta duration_ = kNoTimestamp;
589 // The duration passed to the last SetDuration(). If SetDuration() is never
590 // called or a RunSegmentParserLoop()/AppendChunks() call or a EndOfStream()
591 // call changes `duration_`, then this variable is set to < 0 to indicate that
592 // the `duration_` represents the actual duration instead of a user specified
594 double user_specified_duration_ = -1;
596 base::Time timeline_offset_;
597 StreamLiveness liveness_ = StreamLiveness::kUnknown;
599 std::map<std::string, std::unique_ptr<SourceBufferState>> source_state_map_;
601 std::map<std::string, std::vector<ChunkDemuxerStream*>> id_to_streams_map_;
602 // Used to hold alive the demuxer streams that were created for removed /
603 // released SourceBufferState objects. Demuxer clients might still have
604 // references to these streams, so we need to keep them alive. But they'll be
605 // in a shut down state, so reading from them will return EOS.
606 std::vector<std::unique_ptr<ChunkDemuxerStream>> removed_streams_;
608 std::map<MediaTrack::Id, ChunkDemuxerStream*> track_id_to_demux_stream_map_;
610 bool supports_change_type_ = true;
615 #endif // MEDIA_FILTERS_CHUNK_DEMUXER_H_