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"
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
;
26 // Contains state belonging to a source id.
29 explicit SourceState(scoped_ptr
<StreamParser
> stream_parser
);
31 void Init(const StreamParser::InitCB
& init_cb
,
32 const StreamParser::NewConfigCB
& config_cb
,
33 const StreamParser::NewBuffersCB
& audio_cb
,
34 const StreamParser::NewBuffersCB
& video_cb
,
35 const StreamParser::NewTextBuffersCB
& text_cb
,
36 const StreamParser::NeedKeyCB
& need_key_cb
,
37 const AddTextTrackCB
& add_text_track_cb
,
38 const StreamParser::NewMediaSegmentCB
& new_segment_cb
,
41 // Appends new data to the StreamParser.
42 // Returns true if the data was successfully appended. Returns false if an
44 bool Append(const uint8
* data
, size_t length
);
46 // Aborts the current append sequence and resets the parser.
49 // Sets |timestamp_offset_| if possible.
50 // Returns if the offset was set. Returns false if the offset could not be
51 // updated at this time.
52 bool SetTimestampOffset(TimeDelta timestamp_offset
);
54 TimeDelta
timestamp_offset() const { return timestamp_offset_
; }
57 // Called by the |stream_parser_| at the beginning of a new media segment.
58 // |timestamp| is the timestamp on the first buffer in the segment.
59 // It modifies the state of this object and then calls |new_segment_cb| with
60 // modified version of |timestamp|.
61 void OnNewMediaSegment(const StreamParser::NewMediaSegmentCB
& new_segment_cb
,
64 // Called by the |stream_parser_| at the end of a media segment.
65 void OnEndOfMediaSegment();
67 // Called by the |stream_parser_| when new buffers have been parsed. It
68 // applies |timestamp_offset_| to all buffers in |buffers| and then calls
69 // |new_buffers_cb| with the modified buffers.
70 // Returns true on a successful call. Returns false if an error occured while
71 // processing the buffers.
72 bool OnBuffers(const StreamParser::NewBuffersCB
& new_buffers_cb
,
73 const StreamParser::BufferQueue
& buffers
);
75 // Called by the |stream_parser_| when new text buffers have been parsed. It
76 // applies |timestamp_offset_| to all buffers in |buffers| and then calls
77 // |new_buffers_cb| with the modified buffers.
78 // Returns true on a successful call. Returns false if an error occured while
79 // processing the buffers.
80 bool OnTextBuffers(const StreamParser::NewTextBuffersCB
& new_buffers_cb
,
81 TextTrack
* text_track
,
82 const StreamParser::BufferQueue
& buffers
);
84 // Helper function that adds |timestamp_offset_| to each buffer in |buffers|.
85 void AdjustBufferTimestamps(const StreamParser::BufferQueue
& buffers
);
87 // The offset to apply to media segment timestamps.
88 TimeDelta timestamp_offset_
;
90 // Keeps track of whether |timestamp_offset_| can be modified.
91 bool can_update_offset_
;
93 // The object used to parse appended data.
94 scoped_ptr
<StreamParser
> stream_parser_
;
96 DISALLOW_COPY_AND_ASSIGN(SourceState
);
99 SourceState::SourceState(scoped_ptr
<StreamParser
> stream_parser
)
100 : can_update_offset_(true),
101 stream_parser_(stream_parser
.release()) {
104 void SourceState::Init(const StreamParser::InitCB
& init_cb
,
105 const StreamParser::NewConfigCB
& config_cb
,
106 const StreamParser::NewBuffersCB
& audio_cb
,
107 const StreamParser::NewBuffersCB
& video_cb
,
108 const StreamParser::NewTextBuffersCB
& text_cb
,
109 const StreamParser::NeedKeyCB
& need_key_cb
,
110 const AddTextTrackCB
& add_text_track_cb
,
111 const StreamParser::NewMediaSegmentCB
& new_segment_cb
,
112 const LogCB
& log_cb
) {
113 stream_parser_
->Init(init_cb
, config_cb
,
114 base::Bind(&SourceState::OnBuffers
,
115 base::Unretained(this), audio_cb
),
116 base::Bind(&SourceState::OnBuffers
,
117 base::Unretained(this), video_cb
),
118 base::Bind(&SourceState::OnTextBuffers
,
119 base::Unretained(this), text_cb
),
122 base::Bind(&SourceState::OnNewMediaSegment
,
123 base::Unretained(this), new_segment_cb
),
124 base::Bind(&SourceState::OnEndOfMediaSegment
,
125 base::Unretained(this)),
129 bool SourceState::SetTimestampOffset(TimeDelta timestamp_offset
) {
130 if (!can_update_offset_
)
133 timestamp_offset_
= timestamp_offset
;
137 bool SourceState::Append(const uint8
* data
, size_t length
) {
138 return stream_parser_
->Parse(data
, length
);
141 void SourceState::Abort() {
142 stream_parser_
->Flush();
143 can_update_offset_
= true;
146 void SourceState::AdjustBufferTimestamps(
147 const StreamParser::BufferQueue
& buffers
) {
148 if (timestamp_offset_
== TimeDelta())
151 for (StreamParser::BufferQueue::const_iterator itr
= buffers
.begin();
152 itr
!= buffers
.end(); ++itr
) {
153 (*itr
)->SetDecodeTimestamp(
154 (*itr
)->GetDecodeTimestamp() + timestamp_offset_
);
155 (*itr
)->SetTimestamp((*itr
)->GetTimestamp() + timestamp_offset_
);
159 void SourceState::OnNewMediaSegment(
160 const StreamParser::NewMediaSegmentCB
& new_segment_cb
,
161 TimeDelta timestamp
) {
162 DCHECK(timestamp
!= kNoTimestamp());
163 DVLOG(2) << "OnNewMediaSegment(" << timestamp
.InSecondsF() << ")";
165 can_update_offset_
= false;
166 new_segment_cb
.Run(timestamp
+ timestamp_offset_
);
169 void SourceState::OnEndOfMediaSegment() {
170 DVLOG(2) << "OnEndOfMediaSegment()";
171 can_update_offset_
= true;
174 bool SourceState::OnBuffers(const StreamParser::NewBuffersCB
& new_buffers_cb
,
175 const StreamParser::BufferQueue
& buffers
) {
176 if (new_buffers_cb
.is_null())
179 AdjustBufferTimestamps(buffers
);
181 return new_buffers_cb
.Run(buffers
);
184 bool SourceState::OnTextBuffers(
185 const StreamParser::NewTextBuffersCB
& new_buffers_cb
,
186 TextTrack
* text_track
,
187 const StreamParser::BufferQueue
& buffers
) {
188 if (new_buffers_cb
.is_null())
191 AdjustBufferTimestamps(buffers
);
193 return new_buffers_cb
.Run(text_track
, buffers
);
196 class ChunkDemuxerStream
: public DemuxerStream
{
198 typedef std::deque
<scoped_refptr
<StreamParserBuffer
> > BufferQueue
;
199 typedef std::deque
<ReadCB
> ReadCBQueue
;
200 typedef std::deque
<base::Closure
> ClosureQueue
;
202 ChunkDemuxerStream(const AudioDecoderConfig
& audio_config
,
203 const LogCB
& log_cb
);
204 ChunkDemuxerStream(const VideoDecoderConfig
& video_config
,
205 const LogCB
& log_cb
);
206 virtual ~ChunkDemuxerStream();
208 void AbortReadsAndSeekStream(TimeDelta seek_time
);
209 void Seek(TimeDelta time
);
210 bool IsSeekWaitingForData() const;
212 // Add buffers to this stream. Buffers are stored in SourceBufferStreams,
213 // which handle ordering and overlap resolution.
214 // Returns true if buffers were successfully added.
215 bool Append(const StreamParser::BufferQueue
& buffers
);
217 // Signal to the stream that duration has changed to |duration|.
218 void OnSetDuration(TimeDelta duration
);
220 // Returns the range of buffered data in this stream, capped at |duration|.
221 Ranges
<TimeDelta
> GetBufferedRanges(TimeDelta duration
) const;
223 // Signal to the stream that buffers handed in through subsequent calls to
224 // Append() belong to a media segment that starts at |start_timestamp|.
225 void OnNewMediaSegment(TimeDelta start_timestamp
);
227 // Called when midstream config updates occur.
228 // Returns true if the new config is accepted.
229 // Returns false if the new config should trigger an error.
230 bool UpdateAudioConfig(const AudioDecoderConfig
& config
);
231 bool UpdateVideoConfig(const VideoDecoderConfig
& config
);
234 void CancelEndOfStream();
238 // DemuxerStream methods.
239 virtual void Read(const ReadCB
& read_cb
) OVERRIDE
;
240 virtual Type
type() OVERRIDE
;
241 virtual void EnableBitstreamConverter() OVERRIDE
;
242 virtual AudioDecoderConfig
audio_decoder_config() OVERRIDE
;
243 virtual VideoDecoderConfig
video_decoder_config() OVERRIDE
;
245 void set_memory_limit_for_testing(int memory_limit
) {
246 stream_
->set_memory_limit_for_testing(memory_limit
);
252 RETURNING_DATA_FOR_READS
,
253 RETURNING_ABORT_FOR_READS
,
257 // Assigns |state_| to |state|
258 void ChangeState_Locked(State state
);
260 // Adds the callback to |read_cbs_| so it can be called later when we
262 void DeferRead_Locked(const ReadCB
& read_cb
);
264 // Creates closures that bind ReadCBs in |read_cbs_| to data in
265 // |buffers_| and pops the callbacks & buffers from the respective queues.
266 void CreateReadDoneClosures_Locked(ClosureQueue
* closures
);
268 // Gets the value to pass to the next Read() callback. Returns true if
269 // |status| and |buffer| should be passed to the callback. False indicates
270 // that |status| and |buffer| were not set and more data is needed.
271 bool GetNextBuffer_Locked(DemuxerStream::Status
* status
,
272 scoped_refptr
<StreamParserBuffer
>* buffer
);
274 // Specifies the type of the stream (must be AUDIO or VIDEO for now).
277 scoped_ptr
<SourceBufferStream
> stream_
;
279 mutable base::Lock lock_
;
281 ReadCBQueue read_cbs_
;
283 DISALLOW_IMPLICIT_CONSTRUCTORS(ChunkDemuxerStream
);
286 ChunkDemuxerStream::ChunkDemuxerStream(const AudioDecoderConfig
& audio_config
,
289 state_(UNINITIALIZED
) {
290 stream_
.reset(new SourceBufferStream(audio_config
, log_cb
));
293 ChunkDemuxerStream::ChunkDemuxerStream(const VideoDecoderConfig
& video_config
,
296 state_(UNINITIALIZED
) {
297 stream_
.reset(new SourceBufferStream(video_config
, log_cb
));
300 void ChunkDemuxerStream::AbortReadsAndSeekStream(TimeDelta seek_time
) {
301 DVLOG(1) << "ChunkDemuxerStream::AbortReads()";
302 ReadCBQueue read_cbs
;
304 base::AutoLock
auto_lock(lock_
);
305 ChangeState_Locked(RETURNING_ABORT_FOR_READS
);
306 stream_
->Seek(seek_time
);
307 std::swap(read_cbs_
, read_cbs
);
310 for (ReadCBQueue::iterator it
= read_cbs
.begin(); it
!= read_cbs
.end(); ++it
)
311 it
->Run(kAborted
, NULL
);
314 void ChunkDemuxerStream::Seek(TimeDelta time
) {
315 base::AutoLock
auto_lock(lock_
);
316 DCHECK(read_cbs_
.empty());
317 DCHECK(state_
== UNINITIALIZED
|| state_
== RETURNING_ABORT_FOR_READS
);
320 ChangeState_Locked(RETURNING_DATA_FOR_READS
);
323 bool ChunkDemuxerStream::IsSeekWaitingForData() const {
324 base::AutoLock
auto_lock(lock_
);
325 return stream_
->IsSeekPending();
328 void ChunkDemuxerStream::OnNewMediaSegment(TimeDelta start_timestamp
) {
329 base::AutoLock
auto_lock(lock_
);
330 stream_
->OnNewMediaSegment(start_timestamp
);
333 bool ChunkDemuxerStream::Append(const StreamParser::BufferQueue
& buffers
) {
337 ClosureQueue closures
;
339 base::AutoLock
auto_lock(lock_
);
340 DCHECK_NE(state_
, SHUTDOWN
);
341 if (!stream_
->Append(buffers
)) {
342 DVLOG(1) << "ChunkDemuxerStream::Append() : stream append failed";
345 CreateReadDoneClosures_Locked(&closures
);
348 for (ClosureQueue::iterator it
= closures
.begin(); it
!= closures
.end(); ++it
)
354 void ChunkDemuxerStream::OnSetDuration(TimeDelta duration
) {
355 base::AutoLock
auto_lock(lock_
);
356 stream_
->OnSetDuration(duration
);
359 Ranges
<TimeDelta
> ChunkDemuxerStream::GetBufferedRanges(
360 TimeDelta duration
) const {
361 base::AutoLock
auto_lock(lock_
);
362 Ranges
<TimeDelta
> range
= stream_
->GetBufferedTime();
364 if (range
.size() == 0u)
367 // Clamp the end of the stream's buffered ranges to fit within the duration.
368 // This can be done by intersecting the stream's range with the valid time
370 Ranges
<TimeDelta
> valid_time_range
;
371 valid_time_range
.Add(range
.start(0), duration
);
372 return range
.IntersectionWith(valid_time_range
);
375 bool ChunkDemuxerStream::UpdateAudioConfig(const AudioDecoderConfig
& config
) {
376 DCHECK(config
.IsValidConfig());
377 DCHECK_EQ(type_
, AUDIO
);
378 base::AutoLock
auto_lock(lock_
);
379 return stream_
->UpdateAudioConfig(config
);
382 bool ChunkDemuxerStream::UpdateVideoConfig(const VideoDecoderConfig
& config
) {
383 DCHECK(config
.IsValidConfig());
384 DCHECK_EQ(type_
, VIDEO
);
385 base::AutoLock
auto_lock(lock_
);
386 return stream_
->UpdateVideoConfig(config
);
389 void ChunkDemuxerStream::EndOfStream() {
390 ClosureQueue closures
;
392 base::AutoLock
auto_lock(lock_
);
393 stream_
->EndOfStream();
394 CreateReadDoneClosures_Locked(&closures
);
397 for (ClosureQueue::iterator it
= closures
.begin(); it
!= closures
.end(); ++it
)
401 void ChunkDemuxerStream::CancelEndOfStream() {
402 base::AutoLock
auto_lock(lock_
);
403 stream_
->CancelEndOfStream();
406 void ChunkDemuxerStream::Shutdown() {
407 ReadCBQueue read_cbs
;
409 base::AutoLock
auto_lock(lock_
);
410 ChangeState_Locked(SHUTDOWN
);
411 std::swap(read_cbs_
, read_cbs
);
414 // Pass end of stream buffers to all callbacks to signal that no more data
416 for (ReadCBQueue::iterator it
= read_cbs
.begin(); it
!= read_cbs
.end(); ++it
)
417 it
->Run(DemuxerStream::kOk
, StreamParserBuffer::CreateEOSBuffer());
420 // Helper function that makes sure |read_cb| runs on |message_loop_proxy|.
421 static void RunOnMessageLoop(
422 const DemuxerStream::ReadCB
& read_cb
,
423 const scoped_refptr
<base::MessageLoopProxy
>& message_loop_proxy
,
424 DemuxerStream::Status status
,
425 const scoped_refptr
<DecoderBuffer
>& buffer
) {
426 if (!message_loop_proxy
->BelongsToCurrentThread()) {
427 message_loop_proxy
->PostTask(FROM_HERE
, base::Bind(
428 &RunOnMessageLoop
, read_cb
, message_loop_proxy
, status
, buffer
));
432 read_cb
.Run(status
, buffer
);
435 // DemuxerStream methods.
436 void ChunkDemuxerStream::Read(const ReadCB
& read_cb
) {
437 base::AutoLock
auto_lock(lock_
);
438 DCHECK_NE(state_
, UNINITIALIZED
);
440 DemuxerStream::Status status
= kOk
;
441 scoped_refptr
<StreamParserBuffer
> buffer
;
443 if (!read_cbs_
.empty() || !GetNextBuffer_Locked(&status
, &buffer
)) {
444 DeferRead_Locked(read_cb
);
448 base::MessageLoopProxy::current()->PostTask(FROM_HERE
, base::Bind(
449 read_cb
, status
, buffer
));
452 DemuxerStream::Type
ChunkDemuxerStream::type() { return type_
; }
454 void ChunkDemuxerStream::EnableBitstreamConverter() {}
456 AudioDecoderConfig
ChunkDemuxerStream::audio_decoder_config() {
457 CHECK_EQ(type_
, AUDIO
);
458 base::AutoLock
auto_lock(lock_
);
459 return stream_
->GetCurrentAudioDecoderConfig();
462 VideoDecoderConfig
ChunkDemuxerStream::video_decoder_config() {
463 CHECK_EQ(type_
, VIDEO
);
464 base::AutoLock
auto_lock(lock_
);
465 return stream_
->GetCurrentVideoDecoderConfig();
468 void ChunkDemuxerStream::ChangeState_Locked(State state
) {
469 lock_
.AssertAcquired();
470 DVLOG(1) << "ChunkDemuxerStream::ChangeState_Locked() : "
472 << " - " << state_
<< " -> " << state
;
476 ChunkDemuxerStream::~ChunkDemuxerStream() {}
478 void ChunkDemuxerStream::DeferRead_Locked(const ReadCB
& read_cb
) {
479 lock_
.AssertAcquired();
480 // Wrap & store |read_cb| so that it will
481 // get called on the current MessageLoop.
482 read_cbs_
.push_back(base::Bind(&RunOnMessageLoop
, read_cb
,
483 base::MessageLoopProxy::current()));
486 void ChunkDemuxerStream::CreateReadDoneClosures_Locked(ClosureQueue
* closures
) {
487 lock_
.AssertAcquired();
489 if (state_
!= RETURNING_DATA_FOR_READS
)
492 DemuxerStream::Status status
;
493 scoped_refptr
<StreamParserBuffer
> buffer
;
494 while (!read_cbs_
.empty()) {
495 if (!GetNextBuffer_Locked(&status
, &buffer
))
497 closures
->push_back(base::Bind(read_cbs_
.front(),
499 read_cbs_
.pop_front();
503 bool ChunkDemuxerStream::GetNextBuffer_Locked(
504 DemuxerStream::Status
* status
,
505 scoped_refptr
<StreamParserBuffer
>* buffer
) {
506 lock_
.AssertAcquired();
512 case RETURNING_DATA_FOR_READS
:
513 switch (stream_
->GetNextBuffer(buffer
)) {
514 case SourceBufferStream::kSuccess
:
515 *status
= DemuxerStream::kOk
;
517 case SourceBufferStream::kNeedBuffer
:
519 case SourceBufferStream::kEndOfStream
:
520 *status
= DemuxerStream::kOk
;
521 *buffer
= StreamParserBuffer::CreateEOSBuffer();
523 case SourceBufferStream::kConfigChange
:
524 DVLOG(2) << "Config change reported to ChunkDemuxerStream.";
525 *status
= kConfigChanged
;
530 case RETURNING_ABORT_FOR_READS
:
531 // Null buffers should be returned in this state since we are waiting
532 // for a seek. Any buffers in the SourceBuffer should NOT be returned
533 // because they are associated with the seek.
534 DCHECK(read_cbs_
.empty());
535 *status
= DemuxerStream::kAborted
;
539 DCHECK(read_cbs_
.empty());
540 *status
= DemuxerStream::kOk
;
541 *buffer
= StreamParserBuffer::CreateEOSBuffer();
549 ChunkDemuxer::ChunkDemuxer(const base::Closure
& open_cb
,
550 const NeedKeyCB
& need_key_cb
,
551 const AddTextTrackCB
& add_text_track_cb
,
553 : state_(WAITING_FOR_INIT
),
554 cancel_next_seek_(false),
557 need_key_cb_(need_key_cb
),
558 add_text_track_cb_(add_text_track_cb
),
560 duration_(kNoTimestamp()),
561 user_specified_duration_(-1) {
562 DCHECK(!open_cb_
.is_null());
563 DCHECK(!need_key_cb_
.is_null());
566 void ChunkDemuxer::Initialize(DemuxerHost
* host
, const PipelineStatusCB
& cb
) {
567 DVLOG(1) << "Init()";
569 base::AutoLock
auto_lock(lock_
);
571 init_cb_
= BindToCurrentLoop(cb
);
572 if (state_
== SHUTDOWN
) {
573 base::ResetAndReturn(&init_cb_
).Run(DEMUXER_ERROR_COULD_NOT_OPEN
);
576 DCHECK_EQ(state_
, WAITING_FOR_INIT
);
579 ChangeState_Locked(INITIALIZING
);
581 base::ResetAndReturn(&open_cb_
).Run();
584 void ChunkDemuxer::Stop(const base::Closure
& callback
) {
585 DVLOG(1) << "Stop()";
590 void ChunkDemuxer::Seek(TimeDelta time
, const PipelineStatusCB
& cb
) {
591 DVLOG(1) << "Seek(" << time
.InSecondsF() << ")";
592 DCHECK(time
>= TimeDelta());
594 base::AutoLock
auto_lock(lock_
);
595 DCHECK(seek_cb_
.is_null());
597 seek_cb_
= BindToCurrentLoop(cb
);
598 if (state_
!= INITIALIZED
&& state_
!= ENDED
) {
599 base::ResetAndReturn(&seek_cb_
).Run(PIPELINE_ERROR_INVALID_STATE
);
603 if (cancel_next_seek_
) {
604 cancel_next_seek_
= false;
605 base::ResetAndReturn(&seek_cb_
).Run(PIPELINE_OK
);
615 if (IsSeekWaitingForData_Locked()) {
616 DVLOG(1) << "Seek() : waiting for more data to arrive.";
620 base::ResetAndReturn(&seek_cb_
).Run(PIPELINE_OK
);
623 void ChunkDemuxer::OnAudioRendererDisabled() {
624 base::AutoLock
auto_lock(lock_
);
626 disabled_audio_
= audio_
.Pass();
629 // Demuxer implementation.
630 DemuxerStream
* ChunkDemuxer::GetStream(DemuxerStream::Type type
) {
631 base::AutoLock
auto_lock(lock_
);
632 if (type
== DemuxerStream::VIDEO
)
635 if (type
== DemuxerStream::AUDIO
)
641 TimeDelta
ChunkDemuxer::GetStartTime() const {
645 void ChunkDemuxer::StartWaitingForSeek(TimeDelta seek_time
) {
646 DVLOG(1) << "StartWaitingForSeek()";
647 base::AutoLock
auto_lock(lock_
);
648 DCHECK(state_
== INITIALIZED
|| state_
== ENDED
|| state_
== SHUTDOWN
);
649 DCHECK(seek_cb_
.is_null());
651 if (state_
== SHUTDOWN
)
655 audio_
->AbortReadsAndSeekStream(seek_time
);
658 video_
->AbortReadsAndSeekStream(seek_time
);
660 // Cancel state set in CancelPendingSeek() since we want to
661 // accept the next Seek().
662 cancel_next_seek_
= false;
665 void ChunkDemuxer::CancelPendingSeek(TimeDelta seek_time
) {
666 base::AutoLock
auto_lock(lock_
);
667 DCHECK_NE(state_
, INITIALIZING
);
668 DCHECK(seek_cb_
.is_null() || IsSeekWaitingForData_Locked());
670 if (cancel_next_seek_
)
674 audio_
->AbortReadsAndSeekStream(seek_time
);
677 video_
->AbortReadsAndSeekStream(seek_time
);
679 if (seek_cb_
.is_null()) {
680 cancel_next_seek_
= true;
684 base::ResetAndReturn(&seek_cb_
).Run(PIPELINE_OK
);
687 ChunkDemuxer::Status
ChunkDemuxer::AddId(const std::string
& id
,
688 const std::string
& type
,
689 std::vector
<std::string
>& codecs
) {
690 DCHECK_GT(codecs
.size(), 0u);
691 base::AutoLock
auto_lock(lock_
);
693 if ((state_
!= WAITING_FOR_INIT
&& state_
!= INITIALIZING
) || IsValidId(id
))
694 return kReachedIdLimit
;
696 bool has_audio
= false;
697 bool has_video
= false;
698 scoped_ptr
<media::StreamParser
> stream_parser(
699 StreamParserFactory::Create(type
, codecs
, log_cb_
,
700 &has_audio
, &has_video
));
703 return ChunkDemuxer::kNotSupported
;
705 if ((has_audio
&& !source_id_audio_
.empty()) ||
706 (has_video
&& !source_id_video_
.empty()))
707 return kReachedIdLimit
;
709 StreamParser::NewBuffersCB audio_cb
;
710 StreamParser::NewBuffersCB video_cb
;
713 source_id_audio_
= id
;
714 audio_cb
= base::Bind(&ChunkDemuxer::OnAudioBuffers
,
715 base::Unretained(this));
719 source_id_video_
= id
;
720 video_cb
= base::Bind(&ChunkDemuxer::OnVideoBuffers
,
721 base::Unretained(this));
724 scoped_ptr
<SourceState
> source_state(new SourceState(stream_parser
.Pass()));
726 base::Bind(&ChunkDemuxer::OnSourceInitDone
, base::Unretained(this)),
727 base::Bind(&ChunkDemuxer::OnNewConfigs
, base::Unretained(this),
728 has_audio
, has_video
),
731 base::Bind(&ChunkDemuxer::OnTextBuffers
, base::Unretained(this)),
732 base::Bind(&ChunkDemuxer::OnNeedKey
, base::Unretained(this)),
734 base::Bind(&ChunkDemuxer::OnNewMediaSegment
, base::Unretained(this), id
),
737 source_state_map_
[id
] = source_state
.release();
741 void ChunkDemuxer::RemoveId(const std::string
& id
) {
742 base::AutoLock
auto_lock(lock_
);
743 CHECK(IsValidId(id
));
745 delete source_state_map_
[id
];
746 source_state_map_
.erase(id
);
748 if (source_id_audio_
== id
) {
751 source_id_audio_
.clear();
754 if (source_id_video_
== id
) {
757 source_id_video_
.clear();
761 Ranges
<TimeDelta
> ChunkDemuxer::GetBufferedRanges(const std::string
& id
) const {
762 base::AutoLock
auto_lock(lock_
);
764 DCHECK(IsValidId(id
));
765 DCHECK(id
== source_id_audio_
|| id
== source_id_video_
);
767 if (id
== source_id_audio_
&& id
!= source_id_video_
) {
768 // Only include ranges that have been buffered in |audio_|
769 return audio_
? audio_
->GetBufferedRanges(duration_
) : Ranges
<TimeDelta
>();
772 if (id
!= source_id_audio_
&& id
== source_id_video_
) {
773 // Only include ranges that have been buffered in |video_|
774 return video_
? video_
->GetBufferedRanges(duration_
) : Ranges
<TimeDelta
>();
777 return ComputeIntersection();
780 Ranges
<TimeDelta
> ChunkDemuxer::ComputeIntersection() const {
781 lock_
.AssertAcquired();
783 if (!audio_
|| !video_
)
784 return Ranges
<TimeDelta
>();
786 // Include ranges that have been buffered in both |audio_| and |video_|.
787 Ranges
<TimeDelta
> audio_ranges
= audio_
->GetBufferedRanges(duration_
);
788 Ranges
<TimeDelta
> video_ranges
= video_
->GetBufferedRanges(duration_
);
789 Ranges
<TimeDelta
> result
= audio_ranges
.IntersectionWith(video_ranges
);
791 if (state_
== ENDED
&& result
.size() > 0) {
792 // If appending has ended, extend the last intersection range to include the
793 // max end time of the last audio/video range. This allows the buffered
794 // information to match the actual time range that will get played out if
795 // the streams have slightly different lengths.
796 TimeDelta audio_start
= audio_ranges
.start(audio_ranges
.size() - 1);
797 TimeDelta audio_end
= audio_ranges
.end(audio_ranges
.size() - 1);
798 TimeDelta video_start
= video_ranges
.start(video_ranges
.size() - 1);
799 TimeDelta video_end
= video_ranges
.end(video_ranges
.size() - 1);
801 // Verify the last audio range overlaps with the last video range.
802 // This is enforced by the logic that controls the transition to ENDED.
803 DCHECK((audio_start
<= video_start
&& video_start
<= audio_end
) ||
804 (video_start
<= audio_start
&& audio_start
<= video_end
));
805 result
.Add(result
.end(result
.size()-1), std::max(audio_end
, video_end
));
811 void ChunkDemuxer::AppendData(const std::string
& id
,
814 DVLOG(1) << "AppendData(" << id
<< ", " << length
<< ")";
818 Ranges
<TimeDelta
> ranges
;
821 base::AutoLock
auto_lock(lock_
);
823 // Capture if any of the SourceBuffers are waiting for data before we start
825 bool old_waiting_for_data
= IsSeekWaitingForData_Locked();
827 if (state_
== ENDED
) {
828 ChangeState_Locked(INITIALIZED
);
831 audio_
->CancelEndOfStream();
834 video_
->CancelEndOfStream();
844 DCHECK(IsValidId(id
));
845 if (!source_state_map_
[id
]->Append(data
, length
)) {
846 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN
);
852 DCHECK(IsValidId(id
));
853 if (!source_state_map_
[id
]->Append(data
, length
)) {
854 ReportError_Locked(PIPELINE_ERROR_DECODE
);
860 DVLOG(1) << "AppendData(): Ignoring data after a parse error.";
863 case WAITING_FOR_INIT
:
866 DVLOG(1) << "AppendData(): called in unexpected state " << state_
;
870 // Check to see if data was appended at the pending seek point. This
871 // indicates we have parsed enough data to complete the seek.
872 if (old_waiting_for_data
&& !IsSeekWaitingForData_Locked() &&
873 !seek_cb_
.is_null()) {
874 base::ResetAndReturn(&seek_cb_
).Run(PIPELINE_OK
);
877 ranges
= GetBufferedRanges();
880 for (size_t i
= 0; i
< ranges
.size(); ++i
)
881 host_
->AddBufferedTimeRange(ranges
.start(i
), ranges
.end(i
));
884 void ChunkDemuxer::Abort(const std::string
& id
) {
885 DVLOG(1) << "Abort(" << id
<< ")";
886 base::AutoLock
auto_lock(lock_
);
888 CHECK(IsValidId(id
));
889 source_state_map_
[id
]->Abort();
892 double ChunkDemuxer::GetDuration() {
893 base::AutoLock
auto_lock(lock_
);
894 return GetDuration_Locked();
897 double ChunkDemuxer::GetDuration_Locked() {
898 lock_
.AssertAcquired();
899 if (duration_
== kNoTimestamp())
900 return std::numeric_limits
<double>::quiet_NaN();
902 // Return positive infinity if the resource is unbounded.
903 // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#dom-media-duration
904 if (duration_
== kInfiniteDuration())
905 return std::numeric_limits
<double>::infinity();
907 if (user_specified_duration_
>= 0)
908 return user_specified_duration_
;
910 return duration_
.InSecondsF();
913 void ChunkDemuxer::SetDuration(double duration
) {
914 base::AutoLock
auto_lock(lock_
);
915 DVLOG(1) << "SetDuration(" << duration
<< ")";
916 DCHECK_GE(duration
, 0);
918 if (duration
== GetDuration_Locked())
921 // Compute & bounds check the TimeDelta representation of duration.
922 // This can be different if the value of |duration| doesn't fit the range or
923 // precision of TimeDelta.
924 TimeDelta min_duration
= TimeDelta::FromInternalValue(1);
925 TimeDelta max_duration
= TimeDelta::FromInternalValue(kint64max
- 1);
926 double min_duration_in_seconds
= min_duration
.InSecondsF();
927 double max_duration_in_seconds
= max_duration
.InSecondsF();
929 TimeDelta duration_td
;
930 if (duration
== std::numeric_limits
<double>::infinity()) {
931 duration_td
= media::kInfiniteDuration();
932 } else if (duration
< min_duration_in_seconds
) {
933 duration_td
= min_duration
;
934 } else if (duration
> max_duration_in_seconds
) {
935 duration_td
= max_duration
;
937 duration_td
= TimeDelta::FromMicroseconds(
938 duration
* base::Time::kMicrosecondsPerSecond
);
941 DCHECK(duration_td
> TimeDelta());
943 user_specified_duration_
= duration
;
944 duration_
= duration_td
;
945 host_
->SetDuration(duration_
);
948 audio_
->OnSetDuration(duration_
);
951 video_
->OnSetDuration(duration_
);
954 bool ChunkDemuxer::SetTimestampOffset(const std::string
& id
, TimeDelta offset
) {
955 base::AutoLock
auto_lock(lock_
);
956 DVLOG(1) << "SetTimestampOffset(" << id
<< ", " << offset
.InSecondsF() << ")";
957 CHECK(IsValidId(id
));
959 return source_state_map_
[id
]->SetTimestampOffset(offset
);
962 void ChunkDemuxer::EndOfStream(PipelineStatus status
) {
963 DVLOG(1) << "EndOfStream(" << status
<< ")";
964 base::AutoLock
auto_lock(lock_
);
965 DCHECK_NE(state_
, WAITING_FOR_INIT
);
966 DCHECK_NE(state_
, ENDED
);
968 if (state_
== SHUTDOWN
|| state_
== PARSE_ERROR
)
971 if (state_
== INITIALIZING
) {
972 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN
);
976 bool old_waiting_for_data
= IsSeekWaitingForData_Locked();
978 audio_
->EndOfStream();
981 video_
->EndOfStream();
983 // Give a chance to resume the pending seek process.
984 if (status
!= PIPELINE_OK
) {
985 ReportError_Locked(status
);
989 ChangeState_Locked(ENDED
);
990 DecreaseDurationIfNecessary();
992 if (old_waiting_for_data
&& !IsSeekWaitingForData_Locked() &&
993 !seek_cb_
.is_null()) {
994 base::ResetAndReturn(&seek_cb_
).Run(PIPELINE_OK
);
998 void ChunkDemuxer::Shutdown() {
999 DVLOG(1) << "Shutdown()";
1000 base::AutoLock
auto_lock(lock_
);
1002 if (state_
== SHUTDOWN
)
1011 ChangeState_Locked(SHUTDOWN
);
1013 if(!seek_cb_
.is_null())
1014 base::ResetAndReturn(&seek_cb_
).Run(PIPELINE_ERROR_ABORT
);
1017 void ChunkDemuxer::SetMemoryLimitsForTesting(int memory_limit
) {
1019 audio_
->set_memory_limit_for_testing(memory_limit
);
1022 video_
->set_memory_limit_for_testing(memory_limit
);
1025 void ChunkDemuxer::ChangeState_Locked(State new_state
) {
1026 lock_
.AssertAcquired();
1027 DVLOG(1) << "ChunkDemuxer::ChangeState_Locked() : "
1028 << state_
<< " -> " << new_state
;
1032 ChunkDemuxer::~ChunkDemuxer() {
1033 DCHECK_NE(state_
, INITIALIZED
);
1034 for (SourceStateMap::iterator it
= source_state_map_
.begin();
1035 it
!= source_state_map_
.end(); ++it
) {
1038 source_state_map_
.clear();
1041 void ChunkDemuxer::ReportError_Locked(PipelineStatus error
) {
1042 DVLOG(1) << "ReportError_Locked(" << error
<< ")";
1043 lock_
.AssertAcquired();
1044 DCHECK_NE(error
, PIPELINE_OK
);
1046 ChangeState_Locked(PARSE_ERROR
);
1048 PipelineStatusCB cb
;
1050 if (!init_cb_
.is_null()) {
1051 std::swap(cb
, init_cb_
);
1053 if (!seek_cb_
.is_null())
1054 std::swap(cb
, seek_cb_
);
1063 if (!cb
.is_null()) {
1068 base::AutoUnlock
auto_unlock(lock_
);
1069 host_
->OnDemuxerError(error
);
1072 bool ChunkDemuxer::IsSeekWaitingForData_Locked() const {
1073 lock_
.AssertAcquired();
1074 bool waiting_for_data
= false;
1077 waiting_for_data
= audio_
->IsSeekWaitingForData();
1079 if (!waiting_for_data
&& video_
)
1080 waiting_for_data
= video_
->IsSeekWaitingForData();
1082 return waiting_for_data
;
1085 void ChunkDemuxer::OnSourceInitDone(bool success
, TimeDelta duration
) {
1086 DVLOG(1) << "OnSourceInitDone(" << success
<< ", "
1087 << duration
.InSecondsF() << ")";
1088 lock_
.AssertAcquired();
1089 DCHECK_EQ(state_
, INITIALIZING
);
1090 if (!success
|| (!audio_
&& !video_
)) {
1091 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN
);
1095 if (duration
!= TimeDelta() && duration_
== kNoTimestamp())
1096 UpdateDuration(duration
);
1098 // Wait until all streams have initialized.
1099 if ((!source_id_audio_
.empty() && !audio_
) ||
1100 (!source_id_video_
.empty() && !video_
))
1103 TimeDelta start_time
= GetStartTime();
1105 audio_
->Seek(start_time
);
1108 video_
->Seek(start_time
);
1110 if (duration_
== kNoTimestamp())
1111 duration_
= kInfiniteDuration();
1113 // The demuxer is now initialized after the |start_timestamp_| was set.
1114 ChangeState_Locked(INITIALIZED
);
1115 base::ResetAndReturn(&init_cb_
).Run(PIPELINE_OK
);
1118 bool ChunkDemuxer::OnNewConfigs(bool has_audio
, bool has_video
,
1119 const AudioDecoderConfig
& audio_config
,
1120 const VideoDecoderConfig
& video_config
) {
1121 DVLOG(1) << "OnNewConfigs(" << has_audio
<< ", " << has_video
1122 << ", " << audio_config
.IsValidConfig()
1123 << ", " << video_config
.IsValidConfig() << ")";
1124 lock_
.AssertAcquired();
1126 if (!audio_config
.IsValidConfig() && !video_config
.IsValidConfig()) {
1127 DVLOG(1) << "OnNewConfigs() : Audio & video config are not valid!";
1131 // Signal an error if we get configuration info for stream types that weren't
1132 // specified in AddId() or more configs after a stream is initialized.
1133 // Only allow a single audio config for now.
1134 if (has_audio
!= audio_config
.IsValidConfig()) {
1136 << "Initialization segment"
1137 << (audio_config
.IsValidConfig() ? " has" : " does not have")
1138 << " an audio track, but the mimetype"
1139 << (has_audio
? " specifies" : " does not specify")
1140 << " an audio codec.";
1144 // Only allow a single video config for now.
1145 if (has_video
!= video_config
.IsValidConfig()) {
1147 << "Initialization segment"
1148 << (video_config
.IsValidConfig() ? " has" : " does not have")
1149 << " a video track, but the mimetype"
1150 << (has_video
? " specifies" : " does not specify")
1151 << " a video codec.";
1155 bool success
= true;
1156 if (audio_config
.IsValidConfig()) {
1158 success
&= audio_
->UpdateAudioConfig(audio_config
);
1160 audio_
.reset(new ChunkDemuxerStream(audio_config
, log_cb_
));
1164 if (video_config
.IsValidConfig()) {
1166 success
&= video_
->UpdateVideoConfig(video_config
);
1168 video_
.reset(new ChunkDemuxerStream(video_config
, log_cb_
));
1172 DVLOG(1) << "OnNewConfigs() : " << (success
? "success" : "failed");
1176 bool ChunkDemuxer::OnAudioBuffers(const StreamParser::BufferQueue
& buffers
) {
1177 lock_
.AssertAcquired();
1178 DCHECK_NE(state_
, SHUTDOWN
);
1183 CHECK(IsValidId(source_id_audio_
));
1184 if (!audio_
->Append(buffers
))
1187 IncreaseDurationIfNecessary(buffers
, audio_
.get());
1191 bool ChunkDemuxer::OnVideoBuffers(const StreamParser::BufferQueue
& buffers
) {
1192 lock_
.AssertAcquired();
1193 DCHECK_NE(state_
, SHUTDOWN
);
1198 CHECK(IsValidId(source_id_video_
));
1199 if (!video_
->Append(buffers
))
1202 IncreaseDurationIfNecessary(buffers
, video_
.get());
1206 bool ChunkDemuxer::OnTextBuffers(
1207 TextTrack
* text_track
,
1208 const StreamParser::BufferQueue
& buffers
) {
1209 lock_
.AssertAcquired();
1210 DCHECK_NE(state_
, SHUTDOWN
);
1212 // TODO(matthewjheaney): IncreaseDurationIfNecessary
1214 for (StreamParser::BufferQueue::const_iterator itr
= buffers
.begin();
1215 itr
!= buffers
.end(); ++itr
) {
1216 const StreamParserBuffer
* const buffer
= itr
->get();
1217 const TimeDelta start
= buffer
->GetTimestamp();
1218 const TimeDelta end
= start
+ buffer
->GetDuration();
1220 std::string id
, settings
, content
;
1222 WebMWebVTTParser::Parse(buffer
->GetData(),
1223 buffer
->GetDataSize(),
1224 &id
, &settings
, &content
);
1226 text_track
->addWebVTTCue(start
, end
, id
, content
, settings
);
1232 // TODO(acolwell): Remove bool from StreamParser::NeedKeyCB so that
1233 // this method can be removed and need_key_cb_ can be passed directly
1235 bool ChunkDemuxer::OnNeedKey(const std::string
& type
,
1236 scoped_ptr
<uint8
[]> init_data
,
1237 int init_data_size
) {
1238 lock_
.AssertAcquired();
1239 need_key_cb_
.Run(type
, init_data
.Pass(), init_data_size
);
1243 void ChunkDemuxer::OnNewMediaSegment(const std::string
& source_id
,
1244 TimeDelta timestamp
) {
1245 DCHECK(timestamp
!= kNoTimestamp());
1246 DVLOG(2) << "OnNewMediaSegment(" << source_id
<< ", "
1247 << timestamp
.InSecondsF() << ")";
1248 lock_
.AssertAcquired();
1250 CHECK(IsValidId(source_id
));
1251 if (audio_
&& source_id
== source_id_audio_
)
1252 audio_
->OnNewMediaSegment(timestamp
);
1253 if (video_
&& source_id
== source_id_video_
)
1254 video_
->OnNewMediaSegment(timestamp
);
1257 bool ChunkDemuxer::IsValidId(const std::string
& source_id
) const {
1258 lock_
.AssertAcquired();
1259 return source_state_map_
.count(source_id
) > 0u;
1262 void ChunkDemuxer::UpdateDuration(TimeDelta new_duration
) {
1263 DCHECK(duration_
!= new_duration
);
1264 user_specified_duration_
= -1;
1265 duration_
= new_duration
;
1266 host_
->SetDuration(new_duration
);
1269 void ChunkDemuxer::IncreaseDurationIfNecessary(
1270 const StreamParser::BufferQueue
& buffers
,
1271 ChunkDemuxerStream
* stream
) {
1272 DCHECK(!buffers
.empty());
1273 if (buffers
.back()->GetTimestamp() <= duration_
)
1276 Ranges
<TimeDelta
> ranges
= stream
->GetBufferedRanges(kInfiniteDuration());
1277 DCHECK_GT(ranges
.size(), 0u);
1279 TimeDelta last_timestamp_buffered
= ranges
.end(ranges
.size() - 1);
1280 if (last_timestamp_buffered
> duration_
)
1281 UpdateDuration(last_timestamp_buffered
);
1284 void ChunkDemuxer::DecreaseDurationIfNecessary() {
1285 Ranges
<TimeDelta
> ranges
= GetBufferedRanges();
1286 if (ranges
.size() == 0u)
1289 TimeDelta last_timestamp_buffered
= ranges
.end(ranges
.size() - 1);
1290 if (last_timestamp_buffered
< duration_
)
1291 UpdateDuration(last_timestamp_buffered
);
1294 Ranges
<TimeDelta
> ChunkDemuxer::GetBufferedRanges() const {
1295 if (audio_
&& !video_
)
1296 return audio_
->GetBufferedRanges(duration_
);
1297 else if (!audio_
&& video_
)
1298 return video_
->GetBufferedRanges(duration_
);
1299 return ComputeIntersection();
1302 } // namespace media