Report errors from ChromiumEnv::GetChildren in Posix.
[chromium-blink-merge.git] / media / filters / chunk_demuxer.cc
blobdbf1b031bec9d75e5d5cb2b82521d7c81b707366
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/filters/chunk_demuxer.h"
7 #include <algorithm>
8 #include <deque>
9 #include <limits>
11 #include "base/bind.h"
12 #include "base/callback_helpers.h"
13 #include "base/location.h"
14 #include "base/message_loop/message_loop_proxy.h"
15 #include "media/base/audio_decoder_config.h"
16 #include "media/base/bind_to_loop.h"
17 #include "media/base/stream_parser_buffer.h"
18 #include "media/base/video_decoder_config.h"
19 #include "media/filters/stream_parser_factory.h"
20 #include "media/webm/webm_webvtt_parser.h"
22 using base::TimeDelta;
24 namespace media {
26 // Contains state belonging to a source id.
27 class SourceState {
28 public:
29 // Callback signature used to create ChunkDemuxerStreams.
30 typedef base::Callback<ChunkDemuxerStream*(
31 DemuxerStream::Type)> CreateDemuxerStreamCB;
33 // Callback signature used to notify ChunkDemuxer of timestamps
34 // that may cause the duration to be updated.
35 typedef base::Callback<void(
36 TimeDelta, ChunkDemuxerStream*)> IncreaseDurationCB;
38 SourceState(scoped_ptr<StreamParser> stream_parser, const LogCB& log_cb,
39 const CreateDemuxerStreamCB& create_demuxer_stream_cb,
40 const IncreaseDurationCB& increase_duration_cb);
42 void Init(const StreamParser::InitCB& init_cb,
43 bool allow_audio,
44 bool allow_video,
45 const StreamParser::NewTextBuffersCB& text_cb,
46 const StreamParser::NeedKeyCB& need_key_cb,
47 const AddTextTrackCB& add_text_track_cb);
49 // Appends new data to the StreamParser.
50 // Returns true if the data was successfully appended. Returns false if an
51 // error occurred.
52 bool Append(const uint8* data, size_t length);
54 // Aborts the current append sequence and resets the parser.
55 void Abort();
57 // Sets |timestamp_offset_| if possible.
58 // Returns if the offset was set. Returns false if the offset could not be
59 // updated at this time.
60 bool SetTimestampOffset(TimeDelta timestamp_offset);
62 TimeDelta timestamp_offset() const { return timestamp_offset_; }
64 void set_append_window_start(TimeDelta start) {
65 append_window_start_ = start;
67 void set_append_window_end(TimeDelta end) { append_window_end_ = end; }
69 private:
70 // Called by the |stream_parser_| when a new initialization segment is
71 // encountered.
72 // Returns true on a successful call. Returns false if an error occured while
73 // processing decoder configurations.
74 bool OnNewConfigs(bool allow_audio, bool allow_video,
75 const AudioDecoderConfig& audio_config,
76 const VideoDecoderConfig& video_config);
78 // Called by the |stream_parser_| at the beginning of a new media segment.
79 void OnNewMediaSegment();
81 // Called by the |stream_parser_| at the end of a media segment.
82 void OnEndOfMediaSegment();
84 // Called by the |stream_parser_| when new buffers have been parsed. It
85 // applies |timestamp_offset_| to all buffers in |audio_buffers| and
86 // |video_buffers| and then calls Append() on |audio_| and/or
87 // |video_| with the modified buffers.
88 // Returns true on a successful call. Returns false if an error occured while
89 // processing the buffers.
90 bool OnNewBuffers(const StreamParser::BufferQueue& audio_buffers,
91 const StreamParser::BufferQueue& video_buffers);
93 // Called by the |stream_parser_| when new text buffers have been parsed. It
94 // applies |timestamp_offset_| to all buffers in |buffers| and then calls
95 // |new_buffers_cb| with the modified buffers.
96 // Returns true on a successful call. Returns false if an error occured while
97 // processing the buffers.
98 bool OnTextBuffers(const StreamParser::NewTextBuffersCB& new_buffers_cb,
99 TextTrack* text_track,
100 const StreamParser::BufferQueue& buffers);
102 // Helper function that adds |timestamp_offset_| to each buffer in |buffers|.
103 void AdjustBufferTimestamps(const StreamParser::BufferQueue& buffers);
105 // Filters out buffers that are outside of the append window
106 // [|append_window_start_|, |append_window_end_|).
107 // |needs_keyframe| is a pointer to the |xxx_need_keyframe_| flag
108 // associated with the |buffers|. Its state is read an updated as
109 // this method filters |buffers|.
110 // Buffers that are inside the append window are appended to the end
111 // of |filtered_buffers|.
112 void FilterWithAppendWindow(const StreamParser::BufferQueue& buffers,
113 bool* needs_keyframe,
114 StreamParser::BufferQueue* filtered_buffers);
116 CreateDemuxerStreamCB create_demuxer_stream_cb_;
117 IncreaseDurationCB increase_duration_cb_;
119 // The offset to apply to media segment timestamps.
120 TimeDelta timestamp_offset_;
122 TimeDelta append_window_start_;
123 TimeDelta append_window_end_;
125 // Set to true if the next buffers appended within the append window
126 // represent the start of a new media segment. This flag being set
127 // triggers a call to |new_segment_cb_| when the new buffers are
128 // appended. The flag is set on actual media segment boundaries and
129 // when the "append window" filtering causes discontinuities in the
130 // appended data.
131 bool new_media_segment_;
133 // Keeps track of whether |timestamp_offset_| can be modified.
134 bool can_update_offset_;
136 // The object used to parse appended data.
137 scoped_ptr<StreamParser> stream_parser_;
139 ChunkDemuxerStream* audio_;
140 bool audio_needs_keyframe_;
142 ChunkDemuxerStream* video_;
143 bool video_needs_keyframe_;
145 LogCB log_cb_;
147 DISALLOW_COPY_AND_ASSIGN(SourceState);
150 class ChunkDemuxerStream : public DemuxerStream {
151 public:
152 typedef std::deque<scoped_refptr<StreamParserBuffer> > BufferQueue;
154 explicit ChunkDemuxerStream(Type type);
155 virtual ~ChunkDemuxerStream();
157 // ChunkDemuxerStream control methods.
158 void StartReturningData();
159 void AbortReads();
160 void CompletePendingReadIfPossible();
161 void Shutdown();
163 // SourceBufferStream manipulation methods.
164 void Seek(TimeDelta time);
165 bool IsSeekWaitingForData() const;
167 // Add buffers to this stream. Buffers are stored in SourceBufferStreams,
168 // which handle ordering and overlap resolution.
169 // Returns true if buffers were successfully added.
170 bool Append(const StreamParser::BufferQueue& buffers);
172 // Removes buffers between |start| and |end| according to the steps
173 // in the "Coded Frame Removal Algorithm" in the Media Source
174 // Extensions Spec.
175 // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-coded-frame-removal
177 // |duration| is the current duration of the presentation. It is
178 // required by the computation outlined in the spec.
179 void Remove(TimeDelta start, TimeDelta end, TimeDelta duration);
181 // Signal to the stream that duration has changed to |duration|.
182 void OnSetDuration(TimeDelta duration);
184 // Returns the range of buffered data in this stream, capped at |duration|.
185 Ranges<TimeDelta> GetBufferedRanges(TimeDelta duration) const;
187 // Signal to the stream that buffers handed in through subsequent calls to
188 // Append() belong to a media segment that starts at |start_timestamp|.
189 void OnNewMediaSegment(TimeDelta start_timestamp);
191 // Called when midstream config updates occur.
192 // Returns true if the new config is accepted.
193 // Returns false if the new config should trigger an error.
194 bool UpdateAudioConfig(const AudioDecoderConfig& config, const LogCB& log_cb);
195 bool UpdateVideoConfig(const VideoDecoderConfig& config, const LogCB& log_cb);
197 void MarkEndOfStream();
198 void UnmarkEndOfStream();
200 // DemuxerStream methods.
201 virtual void Read(const ReadCB& read_cb) OVERRIDE;
202 virtual Type type() OVERRIDE;
203 virtual void EnableBitstreamConverter() OVERRIDE;
204 virtual AudioDecoderConfig audio_decoder_config() OVERRIDE;
205 virtual VideoDecoderConfig video_decoder_config() OVERRIDE;
207 void set_memory_limit_for_testing(int memory_limit) {
208 stream_->set_memory_limit_for_testing(memory_limit);
211 private:
212 enum State {
213 UNINITIALIZED,
214 RETURNING_DATA_FOR_READS,
215 RETURNING_ABORT_FOR_READS,
216 SHUTDOWN,
219 // Assigns |state_| to |state|
220 void ChangeState_Locked(State state);
222 void CompletePendingReadIfPossible_Locked();
224 // Gets the value to pass to the next Read() callback. Returns true if
225 // |status| and |buffer| should be passed to the callback. False indicates
226 // that |status| and |buffer| were not set and more data is needed.
227 bool GetNextBuffer_Locked(DemuxerStream::Status* status,
228 scoped_refptr<StreamParserBuffer>* buffer);
230 // Specifies the type of the stream (must be AUDIO or VIDEO for now).
231 Type type_;
233 scoped_ptr<SourceBufferStream> stream_;
235 mutable base::Lock lock_;
236 State state_;
237 ReadCB read_cb_;
239 DISALLOW_IMPLICIT_CONSTRUCTORS(ChunkDemuxerStream);
242 SourceState::SourceState(scoped_ptr<StreamParser> stream_parser,
243 const LogCB& log_cb,
244 const CreateDemuxerStreamCB& create_demuxer_stream_cb,
245 const IncreaseDurationCB& increase_duration_cb)
246 : create_demuxer_stream_cb_(create_demuxer_stream_cb),
247 increase_duration_cb_(increase_duration_cb),
248 append_window_end_(kInfiniteDuration()),
249 new_media_segment_(false),
250 can_update_offset_(true),
251 stream_parser_(stream_parser.release()),
252 audio_(NULL),
253 audio_needs_keyframe_(true),
254 video_(NULL),
255 video_needs_keyframe_(true),
256 log_cb_(log_cb) {
257 DCHECK(!create_demuxer_stream_cb_.is_null());
258 DCHECK(!increase_duration_cb_.is_null());
261 void SourceState::Init(const StreamParser::InitCB& init_cb,
262 bool allow_audio,
263 bool allow_video,
264 const StreamParser::NewTextBuffersCB& text_cb,
265 const StreamParser::NeedKeyCB& need_key_cb,
266 const AddTextTrackCB& add_text_track_cb) {
267 StreamParser::NewBuffersCB audio_cb;
269 stream_parser_->Init(init_cb,
270 base::Bind(&SourceState::OnNewConfigs,
271 base::Unretained(this),
272 allow_audio,
273 allow_video),
274 base::Bind(&SourceState::OnNewBuffers,
275 base::Unretained(this)),
276 base::Bind(&SourceState::OnTextBuffers,
277 base::Unretained(this), text_cb),
278 need_key_cb,
279 add_text_track_cb,
280 base::Bind(&SourceState::OnNewMediaSegment,
281 base::Unretained(this)),
282 base::Bind(&SourceState::OnEndOfMediaSegment,
283 base::Unretained(this)),
284 log_cb_);
287 bool SourceState::SetTimestampOffset(TimeDelta timestamp_offset) {
288 if (!can_update_offset_)
289 return false;
291 timestamp_offset_ = timestamp_offset;
292 return true;
295 bool SourceState::Append(const uint8* data, size_t length) {
296 return stream_parser_->Parse(data, length);
299 void SourceState::Abort() {
300 stream_parser_->Flush();
301 audio_needs_keyframe_ = true;
302 video_needs_keyframe_ = true;
303 can_update_offset_ = true;
306 void SourceState::AdjustBufferTimestamps(
307 const StreamParser::BufferQueue& buffers) {
308 if (timestamp_offset_ == TimeDelta())
309 return;
311 for (StreamParser::BufferQueue::const_iterator itr = buffers.begin();
312 itr != buffers.end(); ++itr) {
313 (*itr)->SetDecodeTimestamp(
314 (*itr)->GetDecodeTimestamp() + timestamp_offset_);
315 (*itr)->set_timestamp((*itr)->timestamp() + timestamp_offset_);
319 bool SourceState::OnNewConfigs(bool allow_audio, bool allow_video,
320 const AudioDecoderConfig& audio_config,
321 const VideoDecoderConfig& video_config) {
322 DVLOG(1) << "OnNewConfigs(" << allow_audio << ", " << allow_video
323 << ", " << audio_config.IsValidConfig()
324 << ", " << video_config.IsValidConfig() << ")";
326 if (!audio_config.IsValidConfig() && !video_config.IsValidConfig()) {
327 DVLOG(1) << "OnNewConfigs() : Audio & video config are not valid!";
328 return false;
331 // Signal an error if we get configuration info for stream types that weren't
332 // specified in AddId() or more configs after a stream is initialized.
333 if (allow_audio != audio_config.IsValidConfig()) {
334 MEDIA_LOG(log_cb_)
335 << "Initialization segment"
336 << (audio_config.IsValidConfig() ? " has" : " does not have")
337 << " an audio track, but the mimetype"
338 << (allow_audio ? " specifies" : " does not specify")
339 << " an audio codec.";
340 return false;
343 if (allow_video != video_config.IsValidConfig()) {
344 MEDIA_LOG(log_cb_)
345 << "Initialization segment"
346 << (video_config.IsValidConfig() ? " has" : " does not have")
347 << " a video track, but the mimetype"
348 << (allow_video ? " specifies" : " does not specify")
349 << " a video codec.";
350 return false;
353 bool success = true;
354 if (audio_config.IsValidConfig()) {
355 if (!audio_) {
356 audio_ = create_demuxer_stream_cb_.Run(DemuxerStream::AUDIO);
358 if (!audio_) {
359 DVLOG(1) << "Failed to create an audio stream.";
360 return false;
364 success &= audio_->UpdateAudioConfig(audio_config, log_cb_);
367 if (video_config.IsValidConfig()) {
368 if (!video_) {
369 video_ = create_demuxer_stream_cb_.Run(DemuxerStream::VIDEO);
371 if (!video_) {
372 DVLOG(1) << "Failed to create a video stream.";
373 return false;
377 success &= video_->UpdateVideoConfig(video_config, log_cb_);
380 DVLOG(1) << "OnNewConfigs() : " << (success ? "success" : "failed");
381 return success;
384 void SourceState::OnNewMediaSegment() {
385 DVLOG(2) << "OnNewMediaSegment()";
386 can_update_offset_ = false;
387 new_media_segment_ = true;
390 void SourceState::OnEndOfMediaSegment() {
391 DVLOG(2) << "OnEndOfMediaSegment()";
392 can_update_offset_ = true;
393 new_media_segment_ = false;
396 bool SourceState::OnNewBuffers(const StreamParser::BufferQueue& audio_buffers,
397 const StreamParser::BufferQueue& video_buffers) {
398 DCHECK(!audio_buffers.empty() || !video_buffers.empty());
399 AdjustBufferTimestamps(audio_buffers);
400 AdjustBufferTimestamps(video_buffers);
402 StreamParser::BufferQueue filtered_audio;
403 StreamParser::BufferQueue filtered_video;
405 FilterWithAppendWindow(audio_buffers, &audio_needs_keyframe_,
406 &filtered_audio);
408 FilterWithAppendWindow(video_buffers, &video_needs_keyframe_,
409 &filtered_video);
411 if (filtered_audio.empty() && filtered_video.empty())
412 return true;
414 if (new_media_segment_) {
415 // Find the earliest timestamp in the filtered buffers and use that for the
416 // segment start timestamp.
417 TimeDelta segment_timestamp = kNoTimestamp();
419 if (!filtered_audio.empty())
420 segment_timestamp = filtered_audio.front()->GetDecodeTimestamp();
422 if (!filtered_video.empty() &&
423 (segment_timestamp == kNoTimestamp() ||
424 filtered_video.front()->GetDecodeTimestamp() < segment_timestamp)) {
425 segment_timestamp = filtered_video.front()->GetDecodeTimestamp();
428 new_media_segment_ = false;
430 if (audio_)
431 audio_->OnNewMediaSegment(segment_timestamp);
433 if (video_)
434 video_->OnNewMediaSegment(segment_timestamp);
437 if (!filtered_audio.empty()) {
438 if (!audio_ || !audio_->Append(filtered_audio))
439 return false;
440 increase_duration_cb_.Run(filtered_audio.back()->timestamp(), audio_);
443 if (!filtered_video.empty()) {
444 if (!video_ || !video_->Append(filtered_video))
445 return false;
446 increase_duration_cb_.Run(filtered_video.back()->timestamp(), video_);
449 return true;
452 bool SourceState::OnTextBuffers(
453 const StreamParser::NewTextBuffersCB& new_buffers_cb,
454 TextTrack* text_track,
455 const StreamParser::BufferQueue& buffers) {
456 if (new_buffers_cb.is_null())
457 return false;
459 AdjustBufferTimestamps(buffers);
461 return new_buffers_cb.Run(text_track, buffers);
464 void SourceState::FilterWithAppendWindow(
465 const StreamParser::BufferQueue& buffers, bool* needs_keyframe,
466 StreamParser::BufferQueue* filtered_buffers) {
467 DCHECK(needs_keyframe);
468 DCHECK(filtered_buffers);
470 // This loop implements steps 1.9, 1.10, & 1.11 of the "Coded frame
471 // processing loop" in the Media Source Extensions spec.
472 // These steps filter out buffers that are not within the "append
473 // window" and handles resyncing on the next random access point
474 // (i.e., next keyframe) if a buffer gets dropped.
475 for (StreamParser::BufferQueue::const_iterator itr = buffers.begin();
476 itr != buffers.end(); ++itr) {
477 // Filter out buffers that are outside the append window. Anytime
478 // a buffer gets dropped we need to set |*needs_keyframe| to true
479 // because we can only resume decoding at keyframes.
480 TimeDelta presentation_timestamp = (*itr)->timestamp();
482 // TODO(acolwell): Change |frame_end_timestamp| value to
483 // |presentation_timestamp + (*itr)->duration()|, like the spec
484 // requires, once frame durations are actually present in all buffers.
485 TimeDelta frame_end_timestamp = presentation_timestamp;
486 if (presentation_timestamp < append_window_start_ ||
487 frame_end_timestamp > append_window_end_) {
488 DVLOG(1) << "Dropping buffer outside append window."
489 << " presentation_timestamp "
490 << presentation_timestamp.InSecondsF();
491 *needs_keyframe = true;
493 // This triggers a discontinuity so we need to treat the next frames
494 // appended within the append window as if they were the beginning of a
495 // new segment.
496 new_media_segment_ = true;
497 continue;
500 // If |*needs_keyframe| is true then filter out buffers until we
501 // encounter the next keyframe.
502 if (*needs_keyframe) {
503 if (!(*itr)->IsKeyframe()) {
504 DVLOG(1) << "Dropping non-keyframe. presentation_timestamp "
505 << presentation_timestamp.InSecondsF();
506 continue;
509 *needs_keyframe = false;
512 filtered_buffers->push_back(*itr);
516 ChunkDemuxerStream::ChunkDemuxerStream(Type type)
517 : type_(type),
518 state_(UNINITIALIZED) {
521 void ChunkDemuxerStream::StartReturningData() {
522 DVLOG(1) << "ChunkDemuxerStream::StartReturningData()";
523 base::AutoLock auto_lock(lock_);
524 DCHECK(read_cb_.is_null());
525 ChangeState_Locked(RETURNING_DATA_FOR_READS);
528 void ChunkDemuxerStream::AbortReads() {
529 DVLOG(1) << "ChunkDemuxerStream::AbortReads()";
530 base::AutoLock auto_lock(lock_);
531 ChangeState_Locked(RETURNING_ABORT_FOR_READS);
532 if (!read_cb_.is_null())
533 base::ResetAndReturn(&read_cb_).Run(kAborted, NULL);
536 void ChunkDemuxerStream::CompletePendingReadIfPossible() {
537 base::AutoLock auto_lock(lock_);
538 if (read_cb_.is_null())
539 return;
541 CompletePendingReadIfPossible_Locked();
544 void ChunkDemuxerStream::Shutdown() {
545 DVLOG(1) << "ChunkDemuxerStream::Shutdown()";
546 base::AutoLock auto_lock(lock_);
547 ChangeState_Locked(SHUTDOWN);
549 // Pass an end of stream buffer to the pending callback to signal that no more
550 // data will be sent.
551 if (!read_cb_.is_null()) {
552 base::ResetAndReturn(&read_cb_).Run(DemuxerStream::kOk,
553 StreamParserBuffer::CreateEOSBuffer());
557 bool ChunkDemuxerStream::IsSeekWaitingForData() const {
558 base::AutoLock auto_lock(lock_);
559 return stream_->IsSeekPending();
562 void ChunkDemuxerStream::Seek(TimeDelta time) {
563 DVLOG(1) << "ChunkDemuxerStream::Seek(" << time.InSecondsF() << ")";
564 base::AutoLock auto_lock(lock_);
565 DCHECK(read_cb_.is_null());
566 DCHECK(state_ == UNINITIALIZED || state_ == RETURNING_ABORT_FOR_READS)
567 << state_;
569 stream_->Seek(time);
572 bool ChunkDemuxerStream::Append(const StreamParser::BufferQueue& buffers) {
573 if (buffers.empty())
574 return false;
576 base::AutoLock auto_lock(lock_);
577 DCHECK_NE(state_, SHUTDOWN);
578 if (!stream_->Append(buffers)) {
579 DVLOG(1) << "ChunkDemuxerStream::Append() : stream append failed";
580 return false;
583 if (!read_cb_.is_null())
584 CompletePendingReadIfPossible_Locked();
586 return true;
589 void ChunkDemuxerStream::Remove(TimeDelta start, TimeDelta end,
590 TimeDelta duration) {
591 base::AutoLock auto_lock(lock_);
592 stream_->Remove(start, end, duration);
595 void ChunkDemuxerStream::OnSetDuration(TimeDelta duration) {
596 base::AutoLock auto_lock(lock_);
597 stream_->OnSetDuration(duration);
600 Ranges<TimeDelta> ChunkDemuxerStream::GetBufferedRanges(
601 TimeDelta duration) const {
602 base::AutoLock auto_lock(lock_);
603 Ranges<TimeDelta> range = stream_->GetBufferedTime();
605 if (range.size() == 0u)
606 return range;
608 // Clamp the end of the stream's buffered ranges to fit within the duration.
609 // This can be done by intersecting the stream's range with the valid time
610 // range.
611 Ranges<TimeDelta> valid_time_range;
612 valid_time_range.Add(range.start(0), duration);
613 return range.IntersectionWith(valid_time_range);
616 void ChunkDemuxerStream::OnNewMediaSegment(TimeDelta start_timestamp) {
617 DVLOG(2) << "ChunkDemuxerStream::OnNewMediaSegment("
618 << start_timestamp.InSecondsF() << ")";
619 base::AutoLock auto_lock(lock_);
620 stream_->OnNewMediaSegment(start_timestamp);
623 bool ChunkDemuxerStream::UpdateAudioConfig(const AudioDecoderConfig& config,
624 const LogCB& log_cb) {
625 DCHECK(config.IsValidConfig());
626 DCHECK_EQ(type_, AUDIO);
627 base::AutoLock auto_lock(lock_);
628 if (!stream_) {
629 DCHECK_EQ(state_, UNINITIALIZED);
630 stream_.reset(new SourceBufferStream(config, log_cb));
631 return true;
634 return stream_->UpdateAudioConfig(config);
637 bool ChunkDemuxerStream::UpdateVideoConfig(const VideoDecoderConfig& config,
638 const LogCB& log_cb) {
639 DCHECK(config.IsValidConfig());
640 DCHECK_EQ(type_, VIDEO);
641 base::AutoLock auto_lock(lock_);
643 if (!stream_) {
644 DCHECK_EQ(state_, UNINITIALIZED);
645 stream_.reset(new SourceBufferStream(config, log_cb));
646 return true;
649 return stream_->UpdateVideoConfig(config);
652 void ChunkDemuxerStream::MarkEndOfStream() {
653 base::AutoLock auto_lock(lock_);
654 stream_->MarkEndOfStream();
657 void ChunkDemuxerStream::UnmarkEndOfStream() {
658 base::AutoLock auto_lock(lock_);
659 stream_->UnmarkEndOfStream();
662 // DemuxerStream methods.
663 void ChunkDemuxerStream::Read(const ReadCB& read_cb) {
664 base::AutoLock auto_lock(lock_);
665 DCHECK_NE(state_, UNINITIALIZED);
666 DCHECK(read_cb_.is_null());
668 read_cb_ = BindToCurrentLoop(read_cb);
669 CompletePendingReadIfPossible_Locked();
672 DemuxerStream::Type ChunkDemuxerStream::type() { return type_; }
674 void ChunkDemuxerStream::EnableBitstreamConverter() {}
676 AudioDecoderConfig ChunkDemuxerStream::audio_decoder_config() {
677 CHECK_EQ(type_, AUDIO);
678 base::AutoLock auto_lock(lock_);
679 return stream_->GetCurrentAudioDecoderConfig();
682 VideoDecoderConfig ChunkDemuxerStream::video_decoder_config() {
683 CHECK_EQ(type_, VIDEO);
684 base::AutoLock auto_lock(lock_);
685 return stream_->GetCurrentVideoDecoderConfig();
688 void ChunkDemuxerStream::ChangeState_Locked(State state) {
689 lock_.AssertAcquired();
690 DVLOG(1) << "ChunkDemuxerStream::ChangeState_Locked() : "
691 << "type " << type_
692 << " - " << state_ << " -> " << state;
693 state_ = state;
696 ChunkDemuxerStream::~ChunkDemuxerStream() {}
698 void ChunkDemuxerStream::CompletePendingReadIfPossible_Locked() {
699 lock_.AssertAcquired();
700 DCHECK(!read_cb_.is_null());
702 DemuxerStream::Status status;
703 scoped_refptr<StreamParserBuffer> buffer;
705 switch (state_) {
706 case UNINITIALIZED:
707 NOTREACHED();
708 return;
709 case RETURNING_DATA_FOR_READS:
710 switch (stream_->GetNextBuffer(&buffer)) {
711 case SourceBufferStream::kSuccess:
712 status = DemuxerStream::kOk;
713 break;
714 case SourceBufferStream::kNeedBuffer:
715 // Return early without calling |read_cb_| since we don't have
716 // any data to return yet.
717 return;
718 case SourceBufferStream::kEndOfStream:
719 status = DemuxerStream::kOk;
720 buffer = StreamParserBuffer::CreateEOSBuffer();
721 break;
722 case SourceBufferStream::kConfigChange:
723 DVLOG(2) << "Config change reported to ChunkDemuxerStream.";
724 status = kConfigChanged;
725 buffer = NULL;
726 break;
728 break;
729 case RETURNING_ABORT_FOR_READS:
730 // Null buffers should be returned in this state since we are waiting
731 // for a seek. Any buffers in the SourceBuffer should NOT be returned
732 // because they are associated with the seek.
733 status = DemuxerStream::kAborted;
734 buffer = NULL;
735 break;
736 case SHUTDOWN:
737 status = DemuxerStream::kOk;
738 buffer = StreamParserBuffer::CreateEOSBuffer();
739 break;
742 base::ResetAndReturn(&read_cb_).Run(status, buffer);
745 ChunkDemuxer::ChunkDemuxer(const base::Closure& open_cb,
746 const NeedKeyCB& need_key_cb,
747 const AddTextTrackCB& add_text_track_cb,
748 const LogCB& log_cb)
749 : state_(WAITING_FOR_INIT),
750 cancel_next_seek_(false),
751 host_(NULL),
752 open_cb_(open_cb),
753 need_key_cb_(need_key_cb),
754 add_text_track_cb_(add_text_track_cb),
755 log_cb_(log_cb),
756 duration_(kNoTimestamp()),
757 user_specified_duration_(-1) {
758 DCHECK(!open_cb_.is_null());
759 DCHECK(!need_key_cb_.is_null());
762 void ChunkDemuxer::Initialize(DemuxerHost* host, const PipelineStatusCB& cb) {
763 DVLOG(1) << "Init()";
765 base::AutoLock auto_lock(lock_);
767 init_cb_ = BindToCurrentLoop(cb);
768 if (state_ == SHUTDOWN) {
769 base::ResetAndReturn(&init_cb_).Run(DEMUXER_ERROR_COULD_NOT_OPEN);
770 return;
772 DCHECK_EQ(state_, WAITING_FOR_INIT);
773 host_ = host;
775 ChangeState_Locked(INITIALIZING);
777 base::ResetAndReturn(&open_cb_).Run();
780 void ChunkDemuxer::Stop(const base::Closure& callback) {
781 DVLOG(1) << "Stop()";
782 Shutdown();
783 callback.Run();
786 void ChunkDemuxer::Seek(TimeDelta time, const PipelineStatusCB& cb) {
787 DVLOG(1) << "Seek(" << time.InSecondsF() << ")";
788 DCHECK(time >= TimeDelta());
790 base::AutoLock auto_lock(lock_);
791 DCHECK(seek_cb_.is_null());
793 seek_cb_ = BindToCurrentLoop(cb);
794 if (state_ != INITIALIZED && state_ != ENDED) {
795 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_ERROR_INVALID_STATE);
796 return;
799 if (cancel_next_seek_) {
800 cancel_next_seek_ = false;
801 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
802 return;
805 SeekAllSources(time);
806 StartReturningData();
808 if (IsSeekWaitingForData_Locked()) {
809 DVLOG(1) << "Seek() : waiting for more data to arrive.";
810 return;
813 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
816 void ChunkDemuxer::OnAudioRendererDisabled() {
817 base::AutoLock auto_lock(lock_);
818 audio_->Shutdown();
819 disabled_audio_ = audio_.Pass();
822 // Demuxer implementation.
823 DemuxerStream* ChunkDemuxer::GetStream(DemuxerStream::Type type) {
824 base::AutoLock auto_lock(lock_);
825 if (type == DemuxerStream::VIDEO)
826 return video_.get();
828 if (type == DemuxerStream::AUDIO)
829 return audio_.get();
831 return NULL;
834 TimeDelta ChunkDemuxer::GetStartTime() const {
835 return TimeDelta();
838 void ChunkDemuxer::StartWaitingForSeek(TimeDelta seek_time) {
839 DVLOG(1) << "StartWaitingForSeek()";
840 base::AutoLock auto_lock(lock_);
841 DCHECK(state_ == INITIALIZED || state_ == ENDED || state_ == SHUTDOWN ||
842 state_ == PARSE_ERROR) << state_;
843 DCHECK(seek_cb_.is_null());
845 if (state_ == SHUTDOWN || state_ == PARSE_ERROR)
846 return;
848 AbortPendingReads();
849 SeekAllSources(seek_time);
851 // Cancel state set in CancelPendingSeek() since we want to
852 // accept the next Seek().
853 cancel_next_seek_ = false;
856 void ChunkDemuxer::CancelPendingSeek(TimeDelta seek_time) {
857 base::AutoLock auto_lock(lock_);
858 DCHECK_NE(state_, INITIALIZING);
859 DCHECK(seek_cb_.is_null() || IsSeekWaitingForData_Locked());
861 if (cancel_next_seek_)
862 return;
864 AbortPendingReads();
865 SeekAllSources(seek_time);
867 if (seek_cb_.is_null()) {
868 cancel_next_seek_ = true;
869 return;
872 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
875 ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id,
876 const std::string& type,
877 std::vector<std::string>& codecs) {
878 base::AutoLock auto_lock(lock_);
880 if ((state_ != WAITING_FOR_INIT && state_ != INITIALIZING) || IsValidId(id))
881 return kReachedIdLimit;
883 bool has_audio = false;
884 bool has_video = false;
885 scoped_ptr<media::StreamParser> stream_parser(
886 StreamParserFactory::Create(type, codecs, log_cb_,
887 &has_audio, &has_video));
889 if (!stream_parser)
890 return ChunkDemuxer::kNotSupported;
892 if ((has_audio && !source_id_audio_.empty()) ||
893 (has_video && !source_id_video_.empty()))
894 return kReachedIdLimit;
896 if (has_audio)
897 source_id_audio_ = id;
899 if (has_video)
900 source_id_video_ = id;
902 scoped_ptr<SourceState> source_state(
903 new SourceState(stream_parser.Pass(), log_cb_,
904 base::Bind(&ChunkDemuxer::CreateDemuxerStream,
905 base::Unretained(this)),
906 base::Bind(&ChunkDemuxer::IncreaseDurationIfNecessary,
907 base::Unretained(this))));
909 source_state->Init(
910 base::Bind(&ChunkDemuxer::OnSourceInitDone, base::Unretained(this)),
911 has_audio,
912 has_video,
913 base::Bind(&ChunkDemuxer::OnTextBuffers, base::Unretained(this)),
914 need_key_cb_,
915 add_text_track_cb_);
917 source_state_map_[id] = source_state.release();
918 return kOk;
921 void ChunkDemuxer::RemoveId(const std::string& id) {
922 base::AutoLock auto_lock(lock_);
923 CHECK(IsValidId(id));
925 delete source_state_map_[id];
926 source_state_map_.erase(id);
928 if (source_id_audio_ == id) {
929 if (audio_)
930 audio_->Shutdown();
931 source_id_audio_.clear();
934 if (source_id_video_ == id) {
935 if (video_)
936 video_->Shutdown();
937 source_id_video_.clear();
941 Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges(const std::string& id) const {
942 base::AutoLock auto_lock(lock_);
943 DCHECK(!id.empty());
944 DCHECK(IsValidId(id));
945 DCHECK(id == source_id_audio_ || id == source_id_video_);
947 if (id == source_id_audio_ && id != source_id_video_) {
948 // Only include ranges that have been buffered in |audio_|
949 return audio_ ? audio_->GetBufferedRanges(duration_) : Ranges<TimeDelta>();
952 if (id != source_id_audio_ && id == source_id_video_) {
953 // Only include ranges that have been buffered in |video_|
954 return video_ ? video_->GetBufferedRanges(duration_) : Ranges<TimeDelta>();
957 return ComputeIntersection();
960 Ranges<TimeDelta> ChunkDemuxer::ComputeIntersection() const {
961 lock_.AssertAcquired();
963 if (!audio_ || !video_)
964 return Ranges<TimeDelta>();
966 // Include ranges that have been buffered in both |audio_| and |video_|.
967 Ranges<TimeDelta> audio_ranges = audio_->GetBufferedRanges(duration_);
968 Ranges<TimeDelta> video_ranges = video_->GetBufferedRanges(duration_);
969 Ranges<TimeDelta> result = audio_ranges.IntersectionWith(video_ranges);
971 if (state_ == ENDED && result.size() > 0) {
972 // If appending has ended, extend the last intersection range to include the
973 // max end time of the last audio/video range. This allows the buffered
974 // information to match the actual time range that will get played out if
975 // the streams have slightly different lengths.
976 TimeDelta audio_start = audio_ranges.start(audio_ranges.size() - 1);
977 TimeDelta audio_end = audio_ranges.end(audio_ranges.size() - 1);
978 TimeDelta video_start = video_ranges.start(video_ranges.size() - 1);
979 TimeDelta video_end = video_ranges.end(video_ranges.size() - 1);
981 // Verify the last audio range overlaps with the last video range.
982 // This is enforced by the logic that controls the transition to ENDED.
983 DCHECK((audio_start <= video_start && video_start <= audio_end) ||
984 (video_start <= audio_start && audio_start <= video_end));
985 result.Add(result.end(result.size()-1), std::max(audio_end, video_end));
988 return result;
991 void ChunkDemuxer::AppendData(const std::string& id,
992 const uint8* data,
993 size_t length) {
994 DVLOG(1) << "AppendData(" << id << ", " << length << ")";
996 DCHECK(!id.empty());
998 Ranges<TimeDelta> ranges;
1001 base::AutoLock auto_lock(lock_);
1002 DCHECK_NE(state_, ENDED);
1004 // Capture if any of the SourceBuffers are waiting for data before we start
1005 // parsing.
1006 bool old_waiting_for_data = IsSeekWaitingForData_Locked();
1008 if (length == 0u)
1009 return;
1011 DCHECK(data);
1013 switch (state_) {
1014 case INITIALIZING:
1015 DCHECK(IsValidId(id));
1016 if (!source_state_map_[id]->Append(data, length)) {
1017 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
1018 return;
1020 break;
1022 case INITIALIZED: {
1023 DCHECK(IsValidId(id));
1024 if (!source_state_map_[id]->Append(data, length)) {
1025 ReportError_Locked(PIPELINE_ERROR_DECODE);
1026 return;
1028 } break;
1030 case PARSE_ERROR:
1031 DVLOG(1) << "AppendData(): Ignoring data after a parse error.";
1032 return;
1034 case WAITING_FOR_INIT:
1035 case ENDED:
1036 case SHUTDOWN:
1037 DVLOG(1) << "AppendData(): called in unexpected state " << state_;
1038 return;
1041 // Check to see if data was appended at the pending seek point. This
1042 // indicates we have parsed enough data to complete the seek.
1043 if (old_waiting_for_data && !IsSeekWaitingForData_Locked() &&
1044 !seek_cb_.is_null()) {
1045 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
1048 ranges = GetBufferedRanges();
1051 for (size_t i = 0; i < ranges.size(); ++i)
1052 host_->AddBufferedTimeRange(ranges.start(i), ranges.end(i));
1055 void ChunkDemuxer::Abort(const std::string& id) {
1056 DVLOG(1) << "Abort(" << id << ")";
1057 base::AutoLock auto_lock(lock_);
1058 DCHECK(!id.empty());
1059 CHECK(IsValidId(id));
1060 source_state_map_[id]->Abort();
1063 void ChunkDemuxer::Remove(const std::string& id, base::TimeDelta start,
1064 base::TimeDelta end) {
1065 DVLOG(1) << "Remove(" << id << ", " << start.InSecondsF()
1066 << ", " << end.InSecondsF() << ")";
1067 base::AutoLock auto_lock(lock_);
1069 if (id == source_id_audio_ && audio_)
1070 audio_->Remove(start, end, duration_);
1072 if (id == source_id_video_ && video_)
1073 video_->Remove(start, end, duration_);
1076 double ChunkDemuxer::GetDuration() {
1077 base::AutoLock auto_lock(lock_);
1078 return GetDuration_Locked();
1081 double ChunkDemuxer::GetDuration_Locked() {
1082 lock_.AssertAcquired();
1083 if (duration_ == kNoTimestamp())
1084 return std::numeric_limits<double>::quiet_NaN();
1086 // Return positive infinity if the resource is unbounded.
1087 // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#dom-media-duration
1088 if (duration_ == kInfiniteDuration())
1089 return std::numeric_limits<double>::infinity();
1091 if (user_specified_duration_ >= 0)
1092 return user_specified_duration_;
1094 return duration_.InSecondsF();
1097 void ChunkDemuxer::SetDuration(double duration) {
1098 base::AutoLock auto_lock(lock_);
1099 DVLOG(1) << "SetDuration(" << duration << ")";
1100 DCHECK_GE(duration, 0);
1102 if (duration == GetDuration_Locked())
1103 return;
1105 // Compute & bounds check the TimeDelta representation of duration.
1106 // This can be different if the value of |duration| doesn't fit the range or
1107 // precision of TimeDelta.
1108 TimeDelta min_duration = TimeDelta::FromInternalValue(1);
1109 TimeDelta max_duration = TimeDelta::FromInternalValue(kint64max - 1);
1110 double min_duration_in_seconds = min_duration.InSecondsF();
1111 double max_duration_in_seconds = max_duration.InSecondsF();
1113 TimeDelta duration_td;
1114 if (duration == std::numeric_limits<double>::infinity()) {
1115 duration_td = media::kInfiniteDuration();
1116 } else if (duration < min_duration_in_seconds) {
1117 duration_td = min_duration;
1118 } else if (duration > max_duration_in_seconds) {
1119 duration_td = max_duration;
1120 } else {
1121 duration_td = TimeDelta::FromMicroseconds(
1122 duration * base::Time::kMicrosecondsPerSecond);
1125 DCHECK(duration_td > TimeDelta());
1127 user_specified_duration_ = duration;
1128 duration_ = duration_td;
1129 host_->SetDuration(duration_);
1131 if (audio_)
1132 audio_->OnSetDuration(duration_);
1134 if (video_)
1135 video_->OnSetDuration(duration_);
1138 bool ChunkDemuxer::SetTimestampOffset(const std::string& id, TimeDelta offset) {
1139 base::AutoLock auto_lock(lock_);
1140 DVLOG(1) << "SetTimestampOffset(" << id << ", " << offset.InSecondsF() << ")";
1141 CHECK(IsValidId(id));
1143 return source_state_map_[id]->SetTimestampOffset(offset);
1146 void ChunkDemuxer::MarkEndOfStream(PipelineStatus status) {
1147 DVLOG(1) << "MarkEndOfStream(" << status << ")";
1148 base::AutoLock auto_lock(lock_);
1149 DCHECK_NE(state_, WAITING_FOR_INIT);
1150 DCHECK_NE(state_, ENDED);
1152 if (state_ == SHUTDOWN || state_ == PARSE_ERROR)
1153 return;
1155 if (state_ == INITIALIZING) {
1156 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
1157 return;
1160 bool old_waiting_for_data = IsSeekWaitingForData_Locked();
1161 if (audio_)
1162 audio_->MarkEndOfStream();
1164 if (video_)
1165 video_->MarkEndOfStream();
1167 CompletePendingReadsIfPossible();
1169 // Give a chance to resume the pending seek process.
1170 if (status != PIPELINE_OK) {
1171 ReportError_Locked(status);
1172 return;
1175 ChangeState_Locked(ENDED);
1176 DecreaseDurationIfNecessary();
1178 if (old_waiting_for_data && !IsSeekWaitingForData_Locked() &&
1179 !seek_cb_.is_null()) {
1180 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
1184 void ChunkDemuxer::UnmarkEndOfStream() {
1185 DVLOG(1) << "UnmarkEndOfStream()";
1186 base::AutoLock auto_lock(lock_);
1187 DCHECK_EQ(state_, ENDED);
1189 ChangeState_Locked(INITIALIZED);
1191 if (audio_)
1192 audio_->UnmarkEndOfStream();
1194 if (video_)
1195 video_->UnmarkEndOfStream();
1198 void ChunkDemuxer::SetAppendWindowStart(const std::string& id,
1199 TimeDelta start) {
1200 base::AutoLock auto_lock(lock_);
1201 DVLOG(1) << "SetAppendWindowStart(" << id << ", "
1202 << start.InSecondsF() << ")";
1203 CHECK(IsValidId(id));
1204 source_state_map_[id]->set_append_window_start(start);
1207 void ChunkDemuxer::SetAppendWindowEnd(const std::string& id, TimeDelta end) {
1208 base::AutoLock auto_lock(lock_);
1209 DVLOG(1) << "SetAppendWindowEnd(" << id << ", " << end.InSecondsF() << ")";
1210 CHECK(IsValidId(id));
1211 source_state_map_[id]->set_append_window_end(end);
1214 void ChunkDemuxer::Shutdown() {
1215 DVLOG(1) << "Shutdown()";
1216 base::AutoLock auto_lock(lock_);
1218 if (state_ == SHUTDOWN)
1219 return;
1221 if (audio_)
1222 audio_->Shutdown();
1224 if (video_)
1225 video_->Shutdown();
1227 ChangeState_Locked(SHUTDOWN);
1229 if(!seek_cb_.is_null())
1230 base::ResetAndReturn(&seek_cb_).Run(PIPELINE_ERROR_ABORT);
1233 void ChunkDemuxer::SetMemoryLimitsForTesting(int memory_limit) {
1234 if (audio_)
1235 audio_->set_memory_limit_for_testing(memory_limit);
1237 if (video_)
1238 video_->set_memory_limit_for_testing(memory_limit);
1241 void ChunkDemuxer::ChangeState_Locked(State new_state) {
1242 lock_.AssertAcquired();
1243 DVLOG(1) << "ChunkDemuxer::ChangeState_Locked() : "
1244 << state_ << " -> " << new_state;
1245 state_ = new_state;
1248 ChunkDemuxer::~ChunkDemuxer() {
1249 DCHECK_NE(state_, INITIALIZED);
1250 for (SourceStateMap::iterator it = source_state_map_.begin();
1251 it != source_state_map_.end(); ++it) {
1252 delete it->second;
1254 source_state_map_.clear();
1257 void ChunkDemuxer::ReportError_Locked(PipelineStatus error) {
1258 DVLOG(1) << "ReportError_Locked(" << error << ")";
1259 lock_.AssertAcquired();
1260 DCHECK_NE(error, PIPELINE_OK);
1262 ChangeState_Locked(PARSE_ERROR);
1264 PipelineStatusCB cb;
1266 if (!init_cb_.is_null()) {
1267 std::swap(cb, init_cb_);
1268 } else {
1269 if (!seek_cb_.is_null())
1270 std::swap(cb, seek_cb_);
1272 if (audio_)
1273 audio_->Shutdown();
1275 if (video_)
1276 video_->Shutdown();
1279 if (!cb.is_null()) {
1280 cb.Run(error);
1281 return;
1284 base::AutoUnlock auto_unlock(lock_);
1285 host_->OnDemuxerError(error);
1288 bool ChunkDemuxer::IsSeekWaitingForData_Locked() const {
1289 lock_.AssertAcquired();
1290 bool waiting_for_data = false;
1292 if (audio_)
1293 waiting_for_data = audio_->IsSeekWaitingForData();
1295 if (!waiting_for_data && video_)
1296 waiting_for_data = video_->IsSeekWaitingForData();
1298 return waiting_for_data;
1301 void ChunkDemuxer::OnSourceInitDone(bool success, TimeDelta duration) {
1302 DVLOG(1) << "OnSourceInitDone(" << success << ", "
1303 << duration.InSecondsF() << ")";
1304 lock_.AssertAcquired();
1305 DCHECK_EQ(state_, INITIALIZING);
1306 if (!success || (!audio_ && !video_)) {
1307 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
1308 return;
1311 if (duration != TimeDelta() && duration_ == kNoTimestamp())
1312 UpdateDuration(duration);
1314 // Wait until all streams have initialized.
1315 if ((!source_id_audio_.empty() && !audio_) ||
1316 (!source_id_video_.empty() && !video_))
1317 return;
1319 SeekAllSources(GetStartTime());
1320 StartReturningData();
1322 if (duration_ == kNoTimestamp())
1323 duration_ = kInfiniteDuration();
1325 // The demuxer is now initialized after the |start_timestamp_| was set.
1326 ChangeState_Locked(INITIALIZED);
1327 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK);
1330 ChunkDemuxerStream*
1331 ChunkDemuxer::CreateDemuxerStream(DemuxerStream::Type type) {
1332 switch (type) {
1333 case DemuxerStream::AUDIO:
1334 if (audio_)
1335 return NULL;
1336 audio_.reset(new ChunkDemuxerStream(DemuxerStream::AUDIO));
1337 return audio_.get();
1338 break;
1339 case DemuxerStream::VIDEO:
1340 if (video_)
1341 return NULL;
1342 video_.reset(new ChunkDemuxerStream(DemuxerStream::VIDEO));
1343 return video_.get();
1344 break;
1345 case DemuxerStream::UNKNOWN:
1346 case DemuxerStream::NUM_TYPES:
1347 NOTREACHED();
1348 return NULL;
1350 NOTREACHED();
1351 return NULL;
1354 bool ChunkDemuxer::OnTextBuffers(
1355 TextTrack* text_track,
1356 const StreamParser::BufferQueue& buffers) {
1357 lock_.AssertAcquired();
1358 DCHECK_NE(state_, SHUTDOWN);
1360 // TODO(matthewjheaney): IncreaseDurationIfNecessary
1362 for (StreamParser::BufferQueue::const_iterator itr = buffers.begin();
1363 itr != buffers.end(); ++itr) {
1364 const StreamParserBuffer* const buffer = itr->get();
1365 const TimeDelta start = buffer->timestamp();
1366 const TimeDelta end = start + buffer->duration();
1368 std::string id, settings, content;
1370 WebMWebVTTParser::Parse(buffer->data(),
1371 buffer->data_size(),
1372 &id, &settings, &content);
1374 text_track->addWebVTTCue(start, end, id, content, settings);
1377 return true;
1380 bool ChunkDemuxer::IsValidId(const std::string& source_id) const {
1381 lock_.AssertAcquired();
1382 return source_state_map_.count(source_id) > 0u;
1385 void ChunkDemuxer::UpdateDuration(TimeDelta new_duration) {
1386 DCHECK(duration_ != new_duration);
1387 user_specified_duration_ = -1;
1388 duration_ = new_duration;
1389 host_->SetDuration(new_duration);
1392 void ChunkDemuxer::IncreaseDurationIfNecessary(
1393 TimeDelta last_appended_buffer_timestamp,
1394 ChunkDemuxerStream* stream) {
1395 DCHECK(last_appended_buffer_timestamp != kNoTimestamp());
1396 if (last_appended_buffer_timestamp <= duration_)
1397 return;
1399 Ranges<TimeDelta> ranges = stream->GetBufferedRanges(kInfiniteDuration());
1400 DCHECK_GT(ranges.size(), 0u);
1402 TimeDelta last_timestamp_buffered = ranges.end(ranges.size() - 1);
1403 if (last_timestamp_buffered > duration_)
1404 UpdateDuration(last_timestamp_buffered);
1407 void ChunkDemuxer::DecreaseDurationIfNecessary() {
1408 Ranges<TimeDelta> ranges = GetBufferedRanges();
1409 if (ranges.size() == 0u)
1410 return;
1412 TimeDelta last_timestamp_buffered = ranges.end(ranges.size() - 1);
1413 if (last_timestamp_buffered < duration_)
1414 UpdateDuration(last_timestamp_buffered);
1417 Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges() const {
1418 if (audio_ && !video_)
1419 return audio_->GetBufferedRanges(duration_);
1420 else if (!audio_ && video_)
1421 return video_->GetBufferedRanges(duration_);
1422 return ComputeIntersection();
1425 void ChunkDemuxer::StartReturningData() {
1426 if (audio_)
1427 audio_->StartReturningData();
1429 if (video_)
1430 video_->StartReturningData();
1433 void ChunkDemuxer::AbortPendingReads() {
1434 if (audio_)
1435 audio_->AbortReads();
1437 if (video_)
1438 video_->AbortReads();
1441 void ChunkDemuxer::SeekAllSources(TimeDelta seek_time) {
1442 if (audio_)
1443 audio_->Seek(seek_time);
1445 if (video_)
1446 video_->Seek(seek_time);
1449 void ChunkDemuxer::CompletePendingReadsIfPossible() {
1450 if (audio_)
1451 audio_->CompletePendingReadIfPossible();
1453 if (video_)
1454 video_->CompletePendingReadIfPossible();
1457 } // namespace media