diff options
author | annacc@chromium.org <annacc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-29 22:49:31 +0000 |
---|---|---|
committer | annacc@chromium.org <annacc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-29 22:49:31 +0000 |
commit | a4dcf06e3e0d6fd5be7376d191e6a5291fe4a416 (patch) | |
tree | abe2ef62e756a4065c57d8f90111a71f25cf94c2 /media | |
parent | 7c5b9b43fac48a72a8481658a3adbcbd1d2b87f6 (diff) | |
download | chromium_src-a4dcf06e3e0d6fd5be7376d191e6a5291fe4a416.zip chromium_src-a4dcf06e3e0d6fd5be7376d191e6a5291fe4a416.tar.gz chromium_src-a4dcf06e3e0d6fd5be7376d191e6a5291fe4a416.tar.bz2 |
This patch integrates SourceBufferStreams into SourceBuffer, removes all buffering functionality from ChunkDemuxerStream so that it now refers to SourceBufferStream (through ChunkDemuxer/SourceBuffer) for buffer data.
A few notes about this patch:
* We are still assuming only 1 source ID (and 1 SourceBuffer)
* SourceBuffer still has only 1 audio and 1 video streams
* ChunkDemuxerStream now has a handle to its ChunkDemuxer so that it can ask for data from ChunkDemuxer's SourceBuffer.
BUG=125208
TEST=source_buffer_unittest.cc (coming soon)
Review URL: https://chromiumcodereview.appspot.com/10388099
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@139403 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/filters/chunk_demuxer.cc | 346 | ||||
-rw-r--r-- | media/filters/chunk_demuxer.h | 39 | ||||
-rw-r--r-- | media/filters/chunk_demuxer_unittest.cc | 72 | ||||
-rw-r--r-- | media/filters/pipeline_integration_test.cc | 2 | ||||
-rw-r--r-- | media/filters/source_buffer.cc | 100 | ||||
-rw-r--r-- | media/filters/source_buffer.h | 57 | ||||
-rw-r--r-- | media/filters/source_buffer_stream.cc | 53 | ||||
-rw-r--r-- | media/filters/source_buffer_stream.h | 33 | ||||
-rw-r--r-- | media/webm/webm_cluster_parser.cc | 3 | ||||
-rw-r--r-- | media/webm/webm_cluster_parser_unittest.cc | 10 | ||||
-rw-r--r-- | media/webm/webm_constants.h | 2 |
11 files changed, 407 insertions, 310 deletions
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc index 6af153a..1ceba74 100644 --- a/media/filters/chunk_demuxer.cc +++ b/media/filters/chunk_demuxer.cc @@ -121,25 +121,16 @@ class ChunkDemuxerStream : public DemuxerStream { typedef std::deque<ReadCB> ReadCBQueue; typedef std::deque<base::Closure> ClosureQueue; - explicit ChunkDemuxerStream(const AudioDecoderConfig& audio_config); - explicit ChunkDemuxerStream(const VideoDecoderConfig& video_config); + ChunkDemuxerStream(Type type, ChunkDemuxer* chunk_demuxer); - void Flush(); + void StartWaitingForSeek(); void Seek(base::TimeDelta time); - // Checks if it is ok to add the |buffers| to the stream. - bool CanAddBuffers(const BufferQueue& buffers) const; + // Notifies this stream of new buffers. + void OnBuffersAvailable(); - void AddBuffers(const BufferQueue& buffers); void Shutdown(); - // Gets the time range buffered by this object. - // Returns true if there is buffered data. |start_out| & |end_out| are set to - // the start and end time of the buffered data respectively. - // Returns false if no data is buffered. - bool GetBufferedRange(base::TimeDelta* start_out, - base::TimeDelta* end_out) const; - // DemuxerStream methods. virtual void Read(const ReadCB& read_cb) OVERRIDE; virtual Type type() OVERRIDE; @@ -154,9 +145,6 @@ class ChunkDemuxerStream : public DemuxerStream { enum State { RETURNING_DATA_FOR_READS, WAITING_FOR_SEEK, - RECEIVED_EOS_WHILE_WAITING_FOR_SEEK, // EOS = End of stream. - RECEIVED_EOS, - RETURNING_EOS_FOR_READS, SHUTDOWN, }; @@ -171,46 +159,31 @@ class ChunkDemuxerStream : public DemuxerStream { // |buffers_| and pops the callbacks & buffers from the respecive queues. void CreateReadDoneClosures_Locked(ClosureQueue* closures); + // Specifies the type of the stream (must be AUDIO or VIDEO for now). Type type_; - AudioDecoderConfig audio_config_; - VideoDecoderConfig video_config_; + + // Pointer to the ChunkDemuxer that owns this ChunkDemuxerStream. Used for + // read requests to SourceBuffer. + ChunkDemuxer* chunk_demuxer_; mutable base::Lock lock_; State state_; ReadCBQueue read_cbs_; - BufferQueue buffers_; - - // Keeps track of the timestamp of the last buffer we have - // added to |buffers_|. This is used to enforce buffers with strictly - // monotonically increasing timestamps. - base::TimeDelta last_buffer_timestamp_; DISALLOW_IMPLICIT_CONSTRUCTORS(ChunkDemuxerStream); }; -ChunkDemuxerStream::ChunkDemuxerStream(const AudioDecoderConfig& audio_config) - : type_(AUDIO), - state_(RETURNING_DATA_FOR_READS), - last_buffer_timestamp_(kNoTimestamp()) { - audio_config_.CopyFrom(audio_config); -} - - -ChunkDemuxerStream::ChunkDemuxerStream(const VideoDecoderConfig& video_config) - : type_(VIDEO), - state_(RETURNING_DATA_FOR_READS), - last_buffer_timestamp_(kNoTimestamp()) { - video_config_.CopyFrom(video_config); -} +ChunkDemuxerStream::ChunkDemuxerStream(Type type, ChunkDemuxer* chunk_demuxer) + : type_(type), + chunk_demuxer_(chunk_demuxer), + state_(RETURNING_DATA_FOR_READS) {} -void ChunkDemuxerStream::Flush() { - DVLOG(1) << "Flush()"; +void ChunkDemuxerStream::StartWaitingForSeek() { + DVLOG(1) << "StartWaitingForSeek()"; ReadCBQueue read_cbs; { base::AutoLock auto_lock(lock_); - buffers_.clear(); ChangeState_Locked(WAITING_FOR_SEEK); - last_buffer_timestamp_ = kNoTimestamp(); std::swap(read_cbs_, read_cbs); } @@ -224,64 +197,15 @@ void ChunkDemuxerStream::Seek(base::TimeDelta time) { DCHECK(read_cbs_.empty()); - if (state_ == WAITING_FOR_SEEK) { + if (state_ == WAITING_FOR_SEEK) ChangeState_Locked(RETURNING_DATA_FOR_READS); - return; - } - - if (state_ == RECEIVED_EOS_WHILE_WAITING_FOR_SEEK) { - ChangeState_Locked(RECEIVED_EOS); - return; - } } -bool ChunkDemuxerStream::CanAddBuffers(const BufferQueue& buffers) const { - base::AutoLock auto_lock(lock_); - - // If we haven't seen any buffers yet, then anything can be added. - if (last_buffer_timestamp_ == kNoTimestamp()) - return true; - - if (buffers.empty()) - return true; - - return (buffers.front()->GetTimestamp() > last_buffer_timestamp_); -} - -void ChunkDemuxerStream::AddBuffers(const BufferQueue& buffers) { - if (buffers.empty()) - return; - +void ChunkDemuxerStream::OnBuffersAvailable() { ClosureQueue closures; { base::AutoLock auto_lock(lock_); - - for (BufferQueue::const_iterator itr = buffers.begin(); - itr != buffers.end(); itr++) { - // Make sure we aren't trying to add a buffer after we have received and - // "end of stream" buffer. - DCHECK_NE(state_, RECEIVED_EOS_WHILE_WAITING_FOR_SEEK); - DCHECK_NE(state_, RECEIVED_EOS); - DCHECK_NE(state_, RETURNING_EOS_FOR_READS); - - if ((*itr)->IsEndOfStream()) { - if (state_ == WAITING_FOR_SEEK) { - ChangeState_Locked(RECEIVED_EOS_WHILE_WAITING_FOR_SEEK); - } else { - ChangeState_Locked(RECEIVED_EOS); - } - } else { - base::TimeDelta current_ts = (*itr)->GetTimestamp(); - if (last_buffer_timestamp_ != kNoTimestamp()) { - DCHECK_GT(current_ts.ToInternalValue(), - last_buffer_timestamp_.ToInternalValue()); - } - - last_buffer_timestamp_ = current_ts; - buffers_.push_back(*itr); - } - } - + DCHECK_NE(state_, SHUTDOWN); CreateReadDoneClosures_Locked(&closures); } @@ -296,7 +220,6 @@ void ChunkDemuxerStream::Shutdown() { ChangeState_Locked(SHUTDOWN); std::swap(read_cbs_, read_cbs); - buffers_.clear(); } // Pass end of stream buffers to all callbacks to signal that no more data @@ -305,23 +228,6 @@ void ChunkDemuxerStream::Shutdown() { it->Run(StreamParserBuffer::CreateEOSBuffer()); } -bool ChunkDemuxerStream::GetBufferedRange( - base::TimeDelta* start_out, base::TimeDelta* end_out) const { - base::AutoLock auto_lock(lock_); - - if (buffers_.empty()) - return false; - - *start_out = buffers_.front()->GetTimestamp(); - *end_out = buffers_.back()->GetTimestamp(); - - base::TimeDelta end_duration = buffers_.back()->GetDuration(); - if (end_duration != kNoTimestamp()) - *end_out += end_duration; - - return true; -} - // Helper function that makes sure |read_cb| runs on |message_loop|. static void RunOnMessageLoop(const DemuxerStream::ReadCB& read_cb, MessageLoop* message_loop, @@ -337,46 +243,50 @@ static void RunOnMessageLoop(const DemuxerStream::ReadCB& read_cb, // DemuxerStream methods. void ChunkDemuxerStream::Read(const ReadCB& read_cb) { - scoped_refptr<Buffer> buffer; - + scoped_refptr<StreamParserBuffer> buffer; { base::AutoLock auto_lock(lock_); switch (state_) { case RETURNING_DATA_FOR_READS: - // If we don't have any buffers ready or already have - // pending reads, then defer this read. - if (buffers_.empty() || !read_cbs_.empty()) { + // If we already have pending reads, then defer this read. + if (!read_cbs_.empty()) { DeferRead_Locked(read_cb); return; } - buffer = buffers_.front(); - buffers_.pop_front(); - break; + bool read_success; + { + // Unlock is necessary to avoid deadlock with OnBuffersAvailable(). + base::AutoUnlock auto_unlock(lock_); + read_success = chunk_demuxer_->SourceBufferRead(type_, &buffer); + } + + // Check whether the state may have changed during the unlock. + switch (state_) { + case RETURNING_DATA_FOR_READS: + if (!read_success) { + DeferRead_Locked(read_cb); + return; + } + break; + case WAITING_FOR_SEEK: + DCHECK(read_cbs_.empty()); + buffer = NULL; + break; + case SHUTDOWN: + DCHECK(read_cbs_.empty()); + buffer = StreamParserBuffer::CreateEOSBuffer(); + } + break; case WAITING_FOR_SEEK: - case RECEIVED_EOS_WHILE_WAITING_FOR_SEEK: // Null buffers should be returned in this state since we are waiting - // for a seek. Any buffers in |buffers_| should NOT be returned because - // they are associated with the seek. - DCHECK(read_cbs_.empty()); - break; - case RECEIVED_EOS: + // for a seek. Any buffers in the SourceBuffer should NOT be returned + // because they are associated with the seek. DCHECK(read_cbs_.empty()); - - if (buffers_.empty()) { - ChangeState_Locked(RETURNING_EOS_FOR_READS); - buffer = StreamParserBuffer::CreateEOSBuffer(); - } else { - buffer = buffers_.front(); - buffers_.pop_front(); - } break; - - case RETURNING_EOS_FOR_READS: case SHUTDOWN: - DCHECK(buffers_.empty()); DCHECK(read_cbs_.empty()); buffer = StreamParserBuffer::CreateEOSBuffer(); } @@ -391,12 +301,12 @@ void ChunkDemuxerStream::EnableBitstreamConverter() {} const AudioDecoderConfig& ChunkDemuxerStream::audio_decoder_config() { CHECK_EQ(type_, AUDIO); - return audio_config_; + return chunk_demuxer_->GetCurrentAudioDecoderConfig(); } const VideoDecoderConfig& ChunkDemuxerStream::video_decoder_config() { CHECK_EQ(type_, VIDEO); - return video_config_; + return chunk_demuxer_->GetCurrentVideoDecoderConfig(); } void ChunkDemuxerStream::ChangeState_Locked(State state) { @@ -417,35 +327,23 @@ void ChunkDemuxerStream::DeferRead_Locked(const ReadCB& read_cb) { void ChunkDemuxerStream::CreateReadDoneClosures_Locked(ClosureQueue* closures) { lock_.AssertAcquired(); - if (state_ != RETURNING_DATA_FOR_READS && state_ != RECEIVED_EOS) + if (state_ != RETURNING_DATA_FOR_READS) return; - while (!buffers_.empty() && !read_cbs_.empty()) { - closures->push_back(base::Bind(read_cbs_.front(), buffers_.front())); - buffers_.pop_front(); - read_cbs_.pop_front(); - } - - if (state_ != RECEIVED_EOS || !buffers_.empty() || read_cbs_.empty()) - return; - - // Push enough EOS buffers to satisfy outstanding Read() requests. - scoped_refptr<Buffer> end_of_stream_buffer = - StreamParserBuffer::CreateEOSBuffer(); + scoped_refptr<StreamParserBuffer> buffer; while (!read_cbs_.empty()) { - closures->push_back(base::Bind(read_cbs_.front(), end_of_stream_buffer)); + if (!chunk_demuxer_->SourceBufferRead_Locked(type_, &buffer)) + return; + closures->push_back(base::Bind(read_cbs_.front(), buffer)); read_cbs_.pop_front(); } - - ChangeState_Locked(RETURNING_EOS_FOR_READS); } ChunkDemuxer::ChunkDemuxer(ChunkDemuxerClient* client) : state_(WAITING_FOR_INIT), host_(NULL), client_(client), - buffered_bytes_(0), - seek_waits_for_data_(true) { + buffered_bytes_(0) { DCHECK(client); } @@ -484,7 +382,9 @@ void ChunkDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { if (video_) video_->Seek(time); - if (seek_waits_for_data_) { + source_buffer_->Seek(time); + + if (source_buffer_->IsSeekPending()) { DVLOG(1) << "Seek() : waiting for more data to arrive."; seek_cb_ = cb; return; @@ -525,8 +425,8 @@ base::TimeDelta ChunkDemuxer::GetStartTime() const { return base::TimeDelta(); } -void ChunkDemuxer::FlushData() { - DVLOG(1) << "FlushData()"; +void ChunkDemuxer::StartWaitingForSeek() { + DVLOG(1) << "StartWaitingForSeek()"; base::AutoLock auto_lock(lock_); DCHECK(state_ == INITIALIZED || state_ == ENDED || state_ == SHUTDOWN); @@ -534,14 +434,13 @@ void ChunkDemuxer::FlushData() { return; if (audio_.get()) - audio_->Flush(); + audio_->StartWaitingForSeek(); if (video_.get()) - video_->Flush(); + video_->StartWaitingForSeek(); - source_buffer_->Flush(); + source_buffer_->ResetParser(); - seek_waits_for_data_ = true; ChangeState_Locked(INITIALIZED); } @@ -563,8 +462,8 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, source_id_ = id; - StreamParser::NewBuffersCB audio_cb; - StreamParser::NewBuffersCB video_cb; + SourceBuffer::NewBuffersCB audio_cb; + SourceBuffer::NewBuffersCB video_cb; if (has_audio) { audio_cb = base::Bind(&ChunkDemuxer::OnAudioBuffers, @@ -605,33 +504,8 @@ bool ChunkDemuxer::GetBufferedRanges(const std::string& id, DCHECK(ranges_out); base::AutoLock auto_lock(lock_); - base::TimeDelta start = kNoTimestamp(); - base::TimeDelta end; - base::TimeDelta tmp_start; - base::TimeDelta tmp_end; - - if (audio_ && audio_->GetBufferedRange(&tmp_start, &tmp_end)) { - start = tmp_start; - end = tmp_end; - } - - if (video_ && video_->GetBufferedRange(&tmp_start, &tmp_end)) { - if (start == kNoTimestamp()) { - start = tmp_start; - end = tmp_end; - } else { - start = std::min(start, tmp_start); - end = std::max(end, tmp_end); - } - } - if (start == kNoTimestamp()) - return false; - - ranges_out->resize(1); - (*ranges_out)[0].first = start; - (*ranges_out)[0].second = end; - return true; + return source_buffer_->GetBufferedRanges(ranges_out); } bool ChunkDemuxer::AppendData(const std::string& id, @@ -659,10 +533,8 @@ bool ChunkDemuxer::AppendData(const std::string& id, { base::AutoLock auto_lock(lock_); - // Capture |seek_waits_for_data_| state before we start parsing. - // Its state can be changed by OnAudioBuffers() or OnVideoBuffers() - // calls during the parse. - bool old_seek_waits_for_data = seek_waits_for_data_; + // Capture if the SourceBuffer has a pending seek before we start parsing. + bool old_seek_pending = source_buffer_->IsSeekPending(); switch (state_) { case INITIALIZING: @@ -688,9 +560,9 @@ bool ChunkDemuxer::AppendData(const std::string& id, return false; } - // Check to see if parsing triggered seek_waits_for_data_ to go from true to - // false. This indicates we have parsed enough data to complete the seek. - if (old_seek_waits_for_data && !seek_waits_for_data_ && + // Check to see if data was appended at the pending seek point. This + // indicates we have parsed enough data to complete the seek. + if (old_seek_pending && !source_buffer_->IsSeekPending() && !seek_cb_.is_null()) { std::swap(cb, seek_cb_); } @@ -714,44 +586,54 @@ void ChunkDemuxer::Abort(const std::string& id) { DCHECK(!id.empty()); DCHECK_EQ(source_id_, id); - source_buffer_->Flush(); + source_buffer_->ResetParser(); +} + +const AudioDecoderConfig& ChunkDemuxer::GetCurrentAudioDecoderConfig() { + base::AutoLock auto_lock(lock_); + return source_buffer_->GetCurrentAudioDecoderConfig(); +} + +const VideoDecoderConfig& ChunkDemuxer::GetCurrentVideoDecoderConfig() { + base::AutoLock auto_lock(lock_); + return source_buffer_->GetCurrentVideoDecoderConfig(); +} + +bool ChunkDemuxer::SourceBufferRead(DemuxerStream::Type type, + scoped_refptr<StreamParserBuffer>* out_buffer) { + base::AutoLock auto_lock(lock_); + return SourceBufferRead_Locked(type, out_buffer); +} + +bool ChunkDemuxer::SourceBufferRead_Locked(DemuxerStream::Type type, + scoped_refptr<StreamParserBuffer>* out_buffer) { + lock_.AssertAcquired(); + return source_buffer_->Read(type, out_buffer); } -void ChunkDemuxer::EndOfStream(PipelineStatus status) { +bool ChunkDemuxer::EndOfStream(PipelineStatus status) { DVLOG(1) << "EndOfStream(" << status << ")"; base::AutoLock auto_lock(lock_); DCHECK_NE(state_, WAITING_FOR_INIT); DCHECK_NE(state_, ENDED); if (state_ == SHUTDOWN || state_ == PARSE_ERROR) - return; + return true; if (state_ == INITIALIZING) { ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); - return; + return true; } + if (!source_buffer_->EndOfStream() && status == PIPELINE_OK) + return false; + ChangeState_Locked(ENDED); - if (status != PIPELINE_OK) { + if (status != PIPELINE_OK) ReportError_Locked(status); - return; - } - - // Create an end of stream buffer. - ChunkDemuxerStream::BufferQueue buffers; - buffers.push_back(StreamParserBuffer::CreateEOSBuffer()); - if (audio_.get()) - audio_->AddBuffers(buffers); - - if (video_.get()) - video_->AddBuffers(buffers); -} - -bool ChunkDemuxer::HasEnded() { - base::AutoLock auto_lock(lock_); - return (state_ == ENDED); + return true; } void ChunkDemuxer::Shutdown() { @@ -851,7 +733,7 @@ bool ChunkDemuxer::OnNewConfigs(const AudioDecoderConfig& audio_config, if (audio_.get()) return false; - audio_ = new ChunkDemuxerStream(audio_config); + audio_ = new ChunkDemuxerStream(DemuxerStream::AUDIO, this); } // Only allow a single video config for now. @@ -859,34 +741,30 @@ bool ChunkDemuxer::OnNewConfigs(const AudioDecoderConfig& audio_config, if (video_.get()) return false; - video_ = new ChunkDemuxerStream(video_config); + video_ = new ChunkDemuxerStream(DemuxerStream::VIDEO, this); } return true; } -bool ChunkDemuxer::OnAudioBuffers(const StreamParser::BufferQueue& buffers) { - if (!audio_.get()) - return false; +bool ChunkDemuxer::OnAudioBuffers() { + DCHECK_NE(state_, SHUTDOWN); - if (!audio_->CanAddBuffers(buffers)) + if (!audio_.get()) return false; - audio_->AddBuffers(buffers); - seek_waits_for_data_ = false; + audio_->OnBuffersAvailable(); return true; } -bool ChunkDemuxer::OnVideoBuffers(const StreamParser::BufferQueue& buffers) { - if (!video_.get()) - return false; +bool ChunkDemuxer::OnVideoBuffers() { + DCHECK_NE(state_, SHUTDOWN); - if (!video_->CanAddBuffers(buffers)) + if (!video_.get()) return false; - video_->AddBuffers(buffers); - seek_waits_for_data_ = false; + video_->OnBuffersAvailable(); return true; } diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h index 9f570a8..d57ec7d 100644 --- a/media/filters/chunk_demuxer.h +++ b/media/filters/chunk_demuxer.h @@ -47,7 +47,7 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { virtual int GetBitrate() OVERRIDE; // Methods used by an external object to control this demuxer. - void FlushData(); + void StartWaitingForSeek(); // Registers a new |id| to use for AppendData() calls. |type| indicates // the MIME type for the data that we intend to append for this ID. @@ -77,8 +77,30 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { // it can accept a new segment. void Abort(const std::string& id); - void EndOfStream(PipelineStatus status); - bool HasEnded(); + // Accesses the configs associated with the current playback buffers. + const AudioDecoderConfig& GetCurrentAudioDecoderConfig(); + const VideoDecoderConfig& GetCurrentVideoDecoderConfig(); + + // Fills |out_buffer| with a new buffer from the current SourceBufferStream + // indicated by |type|. + // Returns true if |out_buffer| is filled with a valid buffer. + // Returns false if SourceBuffer can not fulfill the request or if |type| is + // not AUDIO or VIDEO. + // Two versions needed (Locked and not Locked) because this method is called + // during a Read() when ChunkDemuxer IS NOT locked and from + // ChunkDemuxerStream::CreateReadDoneClosures_Locked() when + // OnBuffersAvailable() is called and ChunkDemuxer IS locked. + // TODO(acolwell): Investigate a cleaner solution to SourceBufferRead locking + // requirements. crbug.com/129849 + bool SourceBufferRead_Locked(DemuxerStream::Type type, + scoped_refptr<StreamParserBuffer>* out_buffer); + bool SourceBufferRead(DemuxerStream::Type type, + scoped_refptr<StreamParserBuffer>* out_buffer); + + // Signals an EndOfStream request. + // Returns false if called in an unexpected state or if there is a gap between + // the current position and the end of the buffered data. + bool EndOfStream(PipelineStatus status); void Shutdown(); protected: @@ -100,13 +122,12 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { // data. void ReportError_Locked(PipelineStatus error); - void OnSourceBufferInitDone(bool success, base::TimeDelta duration); - // SourceBuffer callbacks. + void OnSourceBufferInitDone(bool success, base::TimeDelta duration); bool OnNewConfigs(const AudioDecoderConfig& audio_config, const VideoDecoderConfig& video_config); - bool OnAudioBuffers(const StreamParser::BufferQueue& buffer); - bool OnVideoBuffers(const StreamParser::BufferQueue& buffer); + bool OnAudioBuffers(); + bool OnVideoBuffers(); bool OnKeyNeeded(scoped_array<uint8> init_data, int init_data_size); mutable base::Lock lock_; @@ -126,10 +147,6 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { scoped_ptr<SourceBuffer> source_buffer_; - // Should a Seek() call wait for more data before calling the - // callback. - bool seek_waits_for_data_; - // TODO(acolwell): Remove this when fixing http://crbug.com/122909 std::string source_id_; diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc index 61ee9c2..ae59363 100644 --- a/media/filters/chunk_demuxer_unittest.cc +++ b/media/filters/chunk_demuxer_unittest.cc @@ -10,6 +10,7 @@ #include "media/filters/chunk_demuxer.h" #include "media/filters/chunk_demuxer_client.h" #include "media/webm/cluster_builder.h" +#include "media/webm/webm_constants.h" #include "testing/gtest/include/gtest/gtest.h" using ::testing::AnyNumber; @@ -265,46 +266,54 @@ class ChunkDemuxerTest : public testing::Test { } scoped_ptr<Cluster> GenerateCluster(int timecode, int block_count) { + return GenerateCluster(timecode, timecode, block_count); + } + + scoped_ptr<Cluster> GenerateCluster(int audio_timecode, int video_timecode, + int block_count) { CHECK_GT(block_count, 0); int size = 10; scoped_array<uint8> data(new uint8[size]); ClusterBuilder cb; - cb.SetClusterTimecode(timecode); - int audio_timecode = timecode; - int video_timecode = timecode + 1; + cb.SetClusterTimecode(std::min(audio_timecode, video_timecode)); if (block_count == 1) { - cb.AddBlockGroup(kAudioTrackNum, audio_timecode, kAudioBlockDuration, 0, - data.get(), size); + cb.AddBlockGroup(kAudioTrackNum, audio_timecode, kAudioBlockDuration, + kWebMFlagKeyframe, data.get(), size); return cb.Finish(); } // Create simple blocks for everything except the last 2 blocks. + // The first video frame must be a keyframe. + uint8 video_flag = kWebMFlagKeyframe; for (int i = 0; i < block_count - 2; i++) { if (audio_timecode <= video_timecode) { - cb.AddSimpleBlock(kAudioTrackNum, audio_timecode, 0, data.get(), size); + cb.AddSimpleBlock(kAudioTrackNum, audio_timecode, kWebMFlagKeyframe, + data.get(), size); audio_timecode += kAudioBlockDuration; continue; } - cb.AddSimpleBlock(kVideoTrackNum, video_timecode, 0, data.get(), size); + cb.AddSimpleBlock(kVideoTrackNum, video_timecode, video_flag, data.get(), + size); video_timecode += kVideoBlockDuration; + video_flag = 0; } // Make the last 2 blocks BlockGroups so that they don't get delayed by the // block duration calculation logic. if (audio_timecode <= video_timecode) { - cb.AddBlockGroup(kAudioTrackNum, audio_timecode, kAudioBlockDuration, 0, - data.get(), size); - cb.AddBlockGroup(kVideoTrackNum, video_timecode, kVideoBlockDuration, 0, - data.get(), size); + cb.AddBlockGroup(kAudioTrackNum, audio_timecode, kAudioBlockDuration, + kWebMFlagKeyframe, data.get(), size); + cb.AddBlockGroup(kVideoTrackNum, video_timecode, kVideoBlockDuration, + video_flag, data.get(), size); } else { - cb.AddBlockGroup(kVideoTrackNum, video_timecode, kVideoBlockDuration, 0, - data.get(), size); - cb.AddBlockGroup(kAudioTrackNum, audio_timecode, kAudioBlockDuration, 0, - data.get(), size); + cb.AddBlockGroup(kVideoTrackNum, video_timecode, kVideoBlockDuration, + video_flag, data.get(), size); + cb.AddBlockGroup(kAudioTrackNum, audio_timecode, kAudioBlockDuration, + kWebMFlagKeyframe, data.get(), size); } return cb.Finish(); @@ -315,7 +324,7 @@ class ChunkDemuxerTest : public testing::Test { DemuxerStream* video) { CHECK_GT(block_count, 0); int audio_timecode = timecode; - int video_timecode = timecode + 1; + int video_timecode = timecode; if (block_count == 1) { ExpectRead(audio, audio_timecode); @@ -523,14 +532,14 @@ TEST_F(ChunkDemuxerTest, TestSeekWhileParsingCluster) { ASSERT_TRUE(AppendData(cluster_a->data(), cluster_a->size() - 1)); ExpectRead(audio, 0); - ExpectRead(video, 1); + ExpectRead(video, 0); ExpectRead(audio, kAudioBlockDuration); // Note: We skip trying to read a video buffer here because computing // the duration for this block relies on successfully parsing the last block // in the cluster the cluster. ExpectRead(audio, 2 * kAudioBlockDuration); - demuxer_->FlushData(); + demuxer_->StartWaitingForSeek(); demuxer_->Seek(base::TimeDelta::FromSeconds(5), NewExpectedStatusCB(PIPELINE_OK)); @@ -567,7 +576,7 @@ TEST_F(ChunkDemuxerTest, TestRead) { &audio_read_done)); video->Read(base::Bind(&OnReadDone, - base::TimeDelta::FromMilliseconds(1), + base::TimeDelta::FromMilliseconds(0), &video_read_done)); scoped_ptr<Cluster> cluster(GenerateCluster(0, 4)); @@ -589,15 +598,13 @@ TEST_F(ChunkDemuxerTest, TestOutOfOrderClusters) { // that overlaps. scoped_ptr<Cluster> cluster_b(GenerateCluster(5, 4)); - // Make sure that AppendData() fails because this cluster data - // is before previous data. - EXPECT_CALL(host_, OnDemuxerError(PIPELINE_ERROR_DECODE)); + // Make sure that AppendData() does not fail. ASSERT_TRUE(AppendData(cluster_b->data(), cluster_b->size())); - // Verify that AppendData() doesn't accept more data now. + // Verify that AppendData() can still accept more data. scoped_ptr<Cluster> cluster_c(GenerateCluster(45, 2)); - EXPECT_FALSE(demuxer_->AppendData(kSourceId, cluster_c->data(), - cluster_c->size())); + ASSERT_TRUE(demuxer_->AppendData(kSourceId, cluster_c->data(), + cluster_c->size())); } TEST_F(ChunkDemuxerTest, TestNonMonotonicButAboveClusterTimecode) { @@ -803,7 +810,7 @@ TEST_F(ChunkDemuxerTest, TestEndOfStreamWithPendingReads) { &audio_read_done_1)); video->Read(base::Bind(&OnReadDone, - base::TimeDelta::FromMilliseconds(1), + base::TimeDelta::FromMilliseconds(0), &video_read_done_1)); end_of_stream_helper_1.RequestReads(); @@ -845,7 +852,7 @@ TEST_F(ChunkDemuxerTest, TestReadsAfterEndOfStream) { &audio_read_done_1)); video->Read(base::Bind(&OnReadDone, - base::TimeDelta::FromMilliseconds(1), + base::TimeDelta::FromMilliseconds(0), &video_read_done_1)); end_of_stream_helper_1.RequestReads(); @@ -858,7 +865,7 @@ TEST_F(ChunkDemuxerTest, TestReadsAfterEndOfStream) { EXPECT_TRUE(video_read_done_1); end_of_stream_helper_1.CheckIfReadDonesWereCalled(false); - demuxer_->EndOfStream(PIPELINE_OK); + EXPECT_TRUE(demuxer_->EndOfStream(PIPELINE_OK)); end_of_stream_helper_1.CheckIfReadDonesWereCalled(true); @@ -885,7 +892,7 @@ TEST_F(ChunkDemuxerTest, TestAppendingInPieces) { CreateInfoTracks(true, true, false, &info_tracks, &info_tracks_size); scoped_ptr<Cluster> cluster_a(GenerateCluster(0, 4)); - scoped_ptr<Cluster> cluster_b(GenerateCluster(68, 4)); + scoped_ptr<Cluster> cluster_b(GenerateCluster(46, 66, 5)); size_t buffer_size = info_tracks_size + cluster_a->size() + cluster_b->size(); scoped_array<uint8> buffer(new uint8[buffer_size]); @@ -909,8 +916,7 @@ TEST_F(ChunkDemuxerTest, TestAppendingInPieces) { ASSERT_TRUE(audio); ASSERT_TRUE(video); - GenerateExpectedReads(0, 4, audio, video); - GenerateExpectedReads(68, 4, audio, video); + GenerateExpectedReads(0, 9, audio, video); } TEST_F(ChunkDemuxerTest, TestWebMFile_AudioAndVideo) { @@ -986,7 +992,7 @@ TEST_F(ChunkDemuxerTest, TestIncrementalClusterParsing) { &audio_read_done)); video->Read(base::Bind(&OnReadDone, - base::TimeDelta::FromMilliseconds(1), + base::TimeDelta::FromMilliseconds(0), &video_read_done)); // Make sure the reads haven't completed yet. @@ -1019,7 +1025,7 @@ TEST_F(ChunkDemuxerTest, TestIncrementalClusterParsing) { &audio_read_done)); video->Read(base::Bind(&OnReadDone, - base::TimeDelta::FromMilliseconds(34), + base::TimeDelta::FromMilliseconds(33), &video_read_done)); // Make sure the reads haven't completed yet. diff --git a/media/filters/pipeline_integration_test.cc b/media/filters/pipeline_integration_test.cc index a9347ef..2ca8fd7 100644 --- a/media/filters/pipeline_integration_test.cc +++ b/media/filters/pipeline_integration_test.cc @@ -42,7 +42,7 @@ class MockMediaSource : public ChunkDemuxerClient { const std::string& url() const { return url_; } void Seek(int new_position, int seek_append_size) { - chunk_demuxer_->FlushData(); + chunk_demuxer_->StartWaitingForSeek(); DCHECK_GE(new_position, 0); DCHECK_LT(new_position, file_data_size_); diff --git a/media/filters/source_buffer.cc b/media/filters/source_buffer.cc index 90f0434..84efa91 100644 --- a/media/filters/source_buffer.cc +++ b/media/filters/source_buffer.cc @@ -47,10 +47,75 @@ bool SourceBuffer::AppendData(const uint8* data, size_t length) { return stream_parser_->Parse(data, length); } -void SourceBuffer::Flush() { +bool SourceBuffer::Read(DemuxerStream::Type type, + scoped_refptr<StreamParserBuffer>* out_buffer) { + if (type == DemuxerStream::AUDIO) { + return audio_->GetNextBuffer(out_buffer); + } else if (type == DemuxerStream::VIDEO) { + return video_->GetNextBuffer(out_buffer); + } + return false; +} + +void SourceBuffer::Seek(base::TimeDelta time) { + if (audio_.get()) + audio_->Seek(time); + + if (video_.get()) + video_->Seek(time); +} + +bool SourceBuffer::IsSeekPending() const { + bool seek_pending = false; + + if (audio_.get()) + seek_pending = audio_->IsSeekPending(); + + if (!seek_pending && video_.get()) + seek_pending = video_->IsSeekPending(); + + return seek_pending; +} + +void SourceBuffer::ResetParser() { stream_parser_->Flush(); } +bool SourceBuffer::GetBufferedRanges(Ranges* ranges_out) const { + // TODO(annacc): calculate buffered ranges. (crbug.com/129852) + return false; +} + +bool SourceBuffer::EndOfStream() { + + if ((audio_.get() && !audio_->CanEndOfStream()) || + (video_.get() && !video_->CanEndOfStream())) + return false; + + if (audio_.get()) + audio_->EndOfStream(); + + if (video_.get()) + video_->EndOfStream(); + + // Run callbacks so that any pending read requests can be fulfilled. + if (!audio_cb_.is_null()) + audio_cb_.Run(); + + if (!video_cb_.is_null()) + video_cb_.Run(); + + return true; +} + +const AudioDecoderConfig& SourceBuffer::GetCurrentAudioDecoderConfig() { + return audio_->GetCurrentAudioDecoderConfig(); +} + +const VideoDecoderConfig& SourceBuffer::GetCurrentVideoDecoderConfig() { + return video_->GetCurrentVideoDecoderConfig(); +} + void SourceBuffer::OnStreamParserInitDone(bool success, base::TimeDelta duration) { init_cb_.Run(success, duration); @@ -58,25 +123,46 @@ void SourceBuffer::OnStreamParserInitDone(bool success, } bool SourceBuffer::OnNewConfigs(const AudioDecoderConfig& audio_config, - const VideoDecoderConfig& video_config) { + const VideoDecoderConfig& video_config) { CHECK(audio_config.IsValidConfig() || video_config.IsValidConfig()); // Signal an error if we get configuration info for stream types // we don't have a callback to handle. - if ((audio_config.IsValidConfig() && audio_cb_.is_null()) || - (video_config.IsValidConfig() && video_cb_.is_null())) { - return false; + if (audio_config.IsValidConfig()) { + // Disallow multiple audio configs and new audio configs after stream is + // initialized. + if (audio_cb_.is_null() || audio_.get()) + return false; + + audio_.reset(new SourceBufferStream(audio_config)); + } + + if (video_config.IsValidConfig()) { + // Disallow multiple video configs and new video configs after stream is + // initialized. + if (video_cb_.is_null() || video_.get()) + return false; + + video_.reset(new SourceBufferStream(video_config)); } return config_cb_.Run(audio_config, video_config); } bool SourceBuffer::OnAudioBuffers(const StreamParser::BufferQueue& buffer) { - return audio_cb_.Run(buffer); + if (!audio_.get()) + return false; + audio_->Append(buffer); + + return audio_cb_.Run(); } bool SourceBuffer::OnVideoBuffers(const StreamParser::BufferQueue& buffer) { - return video_cb_.Run(buffer); + if (!video_.get()) + return false; + video_->Append(buffer); + + return video_cb_.Run(); } bool SourceBuffer::OnKeyNeeded(scoped_array<uint8> init_data, diff --git a/media/filters/source_buffer.h b/media/filters/source_buffer.h index a03c75e..78e02ec 100644 --- a/media/filters/source_buffer.h +++ b/media/filters/source_buffer.h @@ -5,24 +5,26 @@ #ifndef MEDIA_FILTERS_SOURCE_BUFFER_H_ #define MEDIA_FILTERS_SOURCE_BUFFER_H_ -#include "media/base/audio_decoder_config.h" +#include "media/base/demuxer_stream.h" #include "media/base/stream_parser.h" -#include "media/base/video_decoder_config.h" +#include "media/filters/source_buffer_stream.h" namespace media { -// SourceBuffer will store media segments and initialization segments that are -// dynamically appended to a particular source ID, hand them to a StreamParser -// to be parsed, and provide buffers to ChunkDemuxerStreams during playback. +// SourceBuffer stores media segments and initialization segments that are +// dynamically appended to a particular source ID, hands them to a StreamParser +// to be parsed, and provides buffers to ChunkDemuxerStreams during playback. class MEDIA_EXPORT SourceBuffer { public: + typedef std::vector<std::pair<base::TimeDelta, base::TimeDelta> > Ranges; + SourceBuffer(); ~SourceBuffer(); typedef base::Callback<void(bool, base::TimeDelta)> InitCB; typedef base::Callback<bool(const AudioDecoderConfig&, const VideoDecoderConfig&)> NewConfigCB; - typedef base::Callback<bool(const StreamParser::BufferQueue&)> NewBuffersCB; + typedef base::Callback<bool()> NewBuffersCB; typedef base::Callback<bool(scoped_array<uint8>, int)> KeyNeededCB; void Init(scoped_ptr<StreamParser> parser, @@ -32,14 +34,42 @@ class MEDIA_EXPORT SourceBuffer { const NewBuffersCB& video_cb, const KeyNeededCB& key_needed_cb); - // Add buffers to this source. Incoming data will be parsed and then stored - // in SourceBufferStreams, which will handle ordering and overlap resolution. - // Returns true if data was successfully appended, false if the parse failed. + // Add buffers to this source. Incoming data is parsed and stored in + // SourceBufferStreams, which handle ordering and overlap resolution. + // Returns true if parsing was successful. bool AppendData(const uint8* data, size_t length); - // Clears the data from this buffer. Used during a seek to ensure the next - // buffers provided during a read are the buffers appended after the seek. - void Flush(); + // Fills |out_buffer| with a new buffer from the SourceBufferStream indicated + // by |type|. + // Returns true if |out_buffer| is filled with a valid buffer, false if + // there is not enough data buffered to fulfill the request or if |type| is + // not a supported stream type. + bool Read(DemuxerStream::Type type, + scoped_refptr<StreamParserBuffer>* out_buffer); + + // Seeks the SourceBufferStreams to a new |time|. + void Seek(base::TimeDelta time); + + // Returns true if this SourceBuffer has seeked to a time without buffered + // data and is waiting for more data to be appended. + bool IsSeekPending() const; + + // Resets StreamParser so it can accept a new segment. + void ResetParser(); + + // Fills |ranges_out| with the Ranges that are currently buffered. + // Returns false if no data is buffered. + bool GetBufferedRanges(Ranges* ranges_out) const; + + // Notifies all the streams in this SourceBuffer that EndOfStream has been + // called so that they can return EOS buffers for reads requested after the + // last buffer in the stream. + // Returns false if called when there is a gap between the current position + // and the end of the buffered data. + bool EndOfStream(); + + const AudioDecoderConfig& GetCurrentAudioDecoderConfig(); + const VideoDecoderConfig& GetCurrentVideoDecoderConfig(); private: // StreamParser callbacks. @@ -58,6 +88,9 @@ class MEDIA_EXPORT SourceBuffer { NewBuffersCB video_cb_; KeyNeededCB key_needed_cb_; + scoped_ptr<SourceBufferStream> audio_; + scoped_ptr<SourceBufferStream> video_; + DISALLOW_COPY_AND_ASSIGN(SourceBuffer); }; diff --git a/media/filters/source_buffer_stream.cc b/media/filters/source_buffer_stream.cc index 52ce36c..e12af6b 100644 --- a/media/filters/source_buffer_stream.cc +++ b/media/filters/source_buffer_stream.cc @@ -40,6 +40,7 @@ class SourceBufferRange { // filled with a valid buffer, false if there is not enough data to fulfill // the request. bool GetNextBuffer(scoped_refptr<StreamParserBuffer>* out_buffer); + bool HasNextBuffer() const; base::TimeDelta GetNextTimestamp() const; // Returns the Timespan of buffered time in this range. @@ -156,10 +157,29 @@ static bool IsNextInSequence( namespace media { SourceBufferStream::SourceBufferStream() - : seek_pending_(false), - seek_buffer_timestamp_(kNoTimestamp()), + : seek_pending_(true), + seek_buffer_timestamp_(base::TimeDelta()), selected_range_(NULL), - waiting_for_keyframe_(false) { + waiting_for_keyframe_(false), + end_of_stream_(false) { +} + +SourceBufferStream::SourceBufferStream(const AudioDecoderConfig& audio_config) + : seek_pending_(true), + seek_buffer_timestamp_(base::TimeDelta()), + selected_range_(NULL), + waiting_for_keyframe_(false), + end_of_stream_(false) { + audio_config_.CopyFrom(audio_config); +} + +SourceBufferStream::SourceBufferStream(const VideoDecoderConfig& video_config) + : seek_pending_(true), + seek_buffer_timestamp_(base::TimeDelta()), + selected_range_(NULL), + waiting_for_keyframe_(false), + end_of_stream_(false) { + video_config_.CopyFrom(video_config); } SourceBufferStream::~SourceBufferStream() { @@ -325,6 +345,11 @@ void SourceBufferStream::Seek(base::TimeDelta timestamp) { selected_range_ = *itr; selected_range_->Seek(timestamp); seek_pending_ = false; + end_of_stream_ = false; +} + +bool SourceBufferStream::IsSeekPending() const { + return seek_pending_; } bool SourceBufferStream::GetNextBuffer( @@ -334,6 +359,13 @@ bool SourceBufferStream::GetNextBuffer( track_buffer_.pop_front(); return true; } + + if (end_of_stream_ && (!selected_range_ || + !selected_range_->HasNextBuffer())) { + *out_buffer = StreamParserBuffer::CreateEOSBuffer(); + return true; + } + return selected_range_ && selected_range_->GetNextBuffer(out_buffer); } @@ -347,6 +379,15 @@ SourceBufferStream::GetBufferedTime() const { return timespans; } +void SourceBufferStream::EndOfStream() { + DCHECK(CanEndOfStream()); + end_of_stream_ = true; +} + +bool SourceBufferStream::CanEndOfStream() const { + return ranges_.empty() || selected_range_ == ranges_.back(); +} + SourceBufferRange::SourceBufferRange() : next_buffer_index_(-1) { } @@ -419,7 +460,6 @@ base::TimeDelta SourceBufferRange::SeekAfter(base::TimeDelta timestamp) { return result->first; } - bool SourceBufferRange::GetNextBuffer( scoped_refptr<StreamParserBuffer>* out_buffer) { DCHECK_GE(next_buffer_index_, 0); @@ -431,6 +471,11 @@ bool SourceBufferRange::GetNextBuffer( return true; } +bool SourceBufferRange::HasNextBuffer() const { + return next_buffer_index_ >= 0 && + next_buffer_index_ < static_cast<int>(buffers_.size()); +} + base::TimeDelta SourceBufferRange::GetNextTimestamp() const { DCHECK_GE(next_buffer_index_, 0); DCHECK(!buffers_.empty()); diff --git a/media/filters/source_buffer_stream.h b/media/filters/source_buffer_stream.h index 8e80414..ed3dfc3 100644 --- a/media/filters/source_buffer_stream.h +++ b/media/filters/source_buffer_stream.h @@ -10,8 +10,10 @@ #include <utility> #include "base/memory/ref_counted.h" +#include "media/base/audio_decoder_config.h" #include "media/base/media_export.h" #include "media/base/stream_parser_buffer.h" +#include "media/base/video_decoder_config.h" namespace media { @@ -28,6 +30,9 @@ class MEDIA_EXPORT SourceBufferStream { typedef std::list<Timespan> TimespanList; SourceBufferStream(); + explicit SourceBufferStream(const AudioDecoderConfig& audio_config); + explicit SourceBufferStream(const VideoDecoderConfig& video_config); + ~SourceBufferStream(); // Add the |buffers| to the SourceBufferStream. Buffers within the queue are @@ -44,6 +49,10 @@ class MEDIA_EXPORT SourceBufferStream { // buffers starting from the closest keyframe before |timestamp|. void Seek(base::TimeDelta timestamp); + // Returns true if the SourceBufferStream has seeked to a time without + // buffered data and is waiting for more data to be appended. + bool IsSeekPending() const; + // Fills |out_buffer| with a new buffer. Seek() must be called before calling // this method. Buffers are presented in order from the last call to Seek(). // |out_buffer|'s timestamp may be earlier than the |timestamp| passed to @@ -55,6 +64,23 @@ class MEDIA_EXPORT SourceBufferStream { // Returns a list of the buffered time ranges. TimespanList GetBufferedTime() const; + // Notifies this SourceBufferStream that EndOfStream has been called and that + // GetNextBuffer() should return EOS buffers after all other buffered data. + // Returns false if called when there is a gap between the current position + // and the end of the buffered data. + void EndOfStream(); + + // Returns true if this SourceBufferStream can successfully call EndOfStream() + // (if there are no gaps between the current position and the remaining data). + bool CanEndOfStream() const; + + const AudioDecoderConfig& GetCurrentAudioDecoderConfig() { + return audio_config_; + } + const VideoDecoderConfig& GetCurrentVideoDecoderConfig() { + return video_config_; + } + private: typedef std::list<SourceBufferRange*> RangeList; @@ -76,6 +102,9 @@ class MEDIA_EXPORT SourceBufferStream { // List of disjoint buffered ranges, ordered by start time. RangeList ranges_; + AudioDecoderConfig audio_config_; + VideoDecoderConfig video_config_; + // True if more data needs to be appended before the Seek() can complete, // false if no Seek() has been requested or the Seek() is completed. bool seek_pending_; @@ -97,6 +126,10 @@ class MEDIA_EXPORT SourceBufferStream { // |track_buffer_| to be appended. bool waiting_for_keyframe_; + // True when EndOfStream() has been called and GetNextBuffer() should return + // EOS buffers for read requests beyond the buffered data. False initially. + bool end_of_stream_; + DISALLOW_COPY_AND_ASSIGN(SourceBufferStream); }; diff --git a/media/webm/webm_cluster_parser.cc b/media/webm/webm_cluster_parser.cc index 8271899..67ae9a7 100644 --- a/media/webm/webm_cluster_parser.cc +++ b/media/webm/webm_cluster_parser.cc @@ -234,9 +234,6 @@ bool WebMClusterParser::Track::AddBuffer( return false; } - if (buffer->GetDuration() == kNoTimestamp()) - buffer->SetDuration(default_duration_); - if (delayed_buffer_) { // Update the duration of the delayed buffer and place it into the queue. base::TimeDelta new_duration = diff --git a/media/webm/webm_cluster_parser_unittest.cc b/media/webm/webm_cluster_parser_unittest.cc index cbf3150..96586d1 100644 --- a/media/webm/webm_cluster_parser_unittest.cc +++ b/media/webm/webm_cluster_parser_unittest.cc @@ -40,11 +40,11 @@ struct BlockInfo { const BlockInfo kDefaultBlockInfo[] = { { kAudioTrackNum, 0, 23, true }, { kAudioTrackNum, 23, 23, true }, - { kVideoTrackNum, kVideoDefaultDurationInMs, kVideoDefaultDurationInMs, - true }, - { kAudioTrackNum, 46, 23, false }, - { kVideoTrackNum, 2 * kVideoDefaultDurationInMs, kVideoDefaultDurationInMs, - true }, + { kVideoTrackNum, 33, 34, true }, + { kAudioTrackNum, 46, 23, true }, + { kVideoTrackNum, 67, 33, false }, + { kAudioTrackNum, 69, 23, false }, + { kVideoTrackNum, 100, 33, false }, }; static scoped_ptr<Cluster> CreateCluster(int timecode, diff --git a/media/webm/webm_constants.h b/media/webm/webm_constants.h index f6afef4..b6ba0a2 100644 --- a/media/webm/webm_constants.h +++ b/media/webm/webm_constants.h @@ -195,6 +195,8 @@ const int kWebMIdWritingApp = 0x5741; const int64 kWebMReservedId = 0x1FFFFFFF; const int64 kWebMUnknownSize = GG_LONGLONG(0x00FFFFFFFFFFFFFF); +const uint8 kWebMFlagKeyframe = 0x80; + } // namespace media #endif // MEDIA_WEBM_WEBM_CONSTANTS_H_ |