diff options
-rw-r--r-- | media/base/stream_parser.h | 7 | ||||
-rw-r--r-- | media/filters/chunk_demuxer.cc | 42 | ||||
-rw-r--r-- | media/filters/chunk_demuxer.h | 2 | ||||
-rw-r--r-- | media/filters/chunk_demuxer_unittest.cc | 57 | ||||
-rw-r--r-- | media/filters/source_buffer_stream.cc | 54 | ||||
-rw-r--r-- | media/filters/source_buffer_stream.h | 10 | ||||
-rw-r--r-- | media/filters/source_buffer_stream_unittest.cc | 69 | ||||
-rw-r--r-- | media/webm/webm_stream_parser.cc | 31 | ||||
-rw-r--r-- | media/webm/webm_stream_parser.h | 8 |
9 files changed, 245 insertions, 35 deletions
diff --git a/media/base/stream_parser.h b/media/base/stream_parser.h index 22fc0c2..cdf146d 100644 --- a/media/base/stream_parser.h +++ b/media/base/stream_parser.h @@ -53,6 +53,10 @@ class MEDIA_EXPORT StreamParser { // error should be signalled. typedef base::Callback<bool(const BufferQueue&)> NewBuffersCB; + // Signals the beginning of a new media segment. + // First parameter - The earliest timestamp of all the streams in the segment. + typedef base::Callback<void(base::TimeDelta)> NewMediaSegmentCB; + // A new potentially encrypted stream has been parsed. // First parameter - The initialization data associated with the stream. // Second parameter - Number of bytes of the initialization data. @@ -69,7 +73,8 @@ class MEDIA_EXPORT StreamParser { const NewConfigCB& config_cb, const NewBuffersCB& audio_cb, const NewBuffersCB& video_cb, - const KeyNeededCB& key_needed_cb) = 0; + const KeyNeededCB& key_needed_cb, + const NewMediaSegmentCB& new_segment_cb) = 0; // Called when a seek occurs. This flushes the current parser state // and puts the parser in a state where it can receive data for the new seek diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc index 9a27f52..2d47cc2 100644 --- a/media/filters/chunk_demuxer.cc +++ b/media/filters/chunk_demuxer.cc @@ -127,6 +127,7 @@ class ChunkDemuxerStream : public DemuxerStream { void StartWaitingForSeek(); void Seek(base::TimeDelta time); bool IsSeekPending() const; + void Flush(); // Add buffers to this stream. Buffers are stored in SourceBufferStreams, // which handle ordering and overlap resolution. @@ -136,6 +137,10 @@ class ChunkDemuxerStream : public DemuxerStream { // Returns a list of the buffered time ranges. SourceBufferStream::TimespanList GetBufferedTime() const; + // Signal to the stream that buffers handed in through subsequent calls to + // Append() belong to a media segment that starts at |start_timestamp|. + void OnNewMediaSegment(base::TimeDelta start_timestamp); + // Called when mid-stream config updates occur. // Returns true if the new config is accepted. // Returns false if the new config should trigger an error. @@ -183,12 +188,17 @@ class ChunkDemuxerStream : public DemuxerStream { State state_; ReadCBQueue read_cbs_; + // The timestamp of the current media segment being parsed by + // |stream_parser_|. + base::TimeDelta media_segment_start_time_; + DISALLOW_IMPLICIT_CONSTRUCTORS(ChunkDemuxerStream); }; ChunkDemuxerStream::ChunkDemuxerStream(const AudioDecoderConfig& audio_config) : type_(AUDIO), - state_(RETURNING_DATA_FOR_READS) { + state_(RETURNING_DATA_FOR_READS), + media_segment_start_time_(kNoTimestamp()) { stream_.reset(new SourceBufferStream(audio_config)); } @@ -228,6 +238,14 @@ bool ChunkDemuxerStream::IsSeekPending() const { return stream_->IsSeekPending(); } +void ChunkDemuxerStream::Flush() { + media_segment_start_time_ = kNoTimestamp(); +} + +void ChunkDemuxerStream::OnNewMediaSegment(base::TimeDelta start_timestamp) { + media_segment_start_time_ = start_timestamp; +} + bool ChunkDemuxerStream::Append(const StreamParser::BufferQueue& buffers) { if (buffers.empty()) return false; @@ -236,7 +254,8 @@ bool ChunkDemuxerStream::Append(const StreamParser::BufferQueue& buffers) { { base::AutoLock auto_lock(lock_); DCHECK_NE(state_, SHUTDOWN); - if (!stream_->Append(buffers)) { + DCHECK(media_segment_start_time_ != kNoTimestamp()); + if (!stream_->Append(buffers, media_segment_start_time_)) { DVLOG(1) << "ChunkDemuxerStream::Append() : stream append failed"; return false; } @@ -500,6 +519,7 @@ scoped_refptr<DemuxerStream> ChunkDemuxer::GetStream( base::TimeDelta ChunkDemuxer::GetStartTime() const { DVLOG(1) << "GetStartTime()"; // TODO(acolwell) : Fix this so it uses the time on the first packet. + // (crbug.com/132815) return base::TimeDelta(); } @@ -563,7 +583,8 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, has_audio, has_video), audio_cb, video_cb, - base::Bind(&ChunkDemuxer::OnKeyNeeded, base::Unretained(this))); + base::Bind(&ChunkDemuxer::OnKeyNeeded, base::Unretained(this)), + base::Bind(&ChunkDemuxer::OnNewMediaSegment, base::Unretained(this), id)); stream_parser_map_[id] = stream_parser.release(); @@ -749,6 +770,10 @@ void ChunkDemuxer::Abort(const std::string& id) { DCHECK_GT(stream_parser_map_.count(id), 0u); stream_parser_map_[id]->Flush(); + if (audio_ && source_id_audio_ == id) + audio_->Flush(); + if (video_ && source_id_video_ == id) + video_->Flush(); } bool ChunkDemuxer::EndOfStream(PipelineStatus status) { @@ -976,4 +1001,15 @@ bool ChunkDemuxer::OnKeyNeeded(scoped_array<uint8> init_data, return true; } +void ChunkDemuxer::OnNewMediaSegment(const std::string& source_id, + base::TimeDelta start_timestamp) { + // TODO(vrk): There should be a special case for the first appends where all + // streams (for both demuxed and muxed case) begin at the earliest stream + // timestamp. (crbug.com/132815) + if (audio_ && source_id == source_id_audio_) + audio_->OnNewMediaSegment(start_timestamp); + if (video_ && source_id == source_id_video_) + video_->OnNewMediaSegment(start_timestamp); +} + } // namespace media diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h index e53bcd5..5945526 100644 --- a/media/filters/chunk_demuxer.h +++ b/media/filters/chunk_demuxer.h @@ -118,6 +118,8 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { bool OnAudioBuffers(const StreamParser::BufferQueue& buffers); bool OnVideoBuffers(const StreamParser::BufferQueue& buffers); bool OnKeyNeeded(scoped_array<uint8> init_data, int init_data_size); + void OnNewMediaSegment(const std::string& source_id, + base::TimeDelta start_timestamp); // Helper functions for calculating GetBufferedRanges(). bool CopyIntoRanges( diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc index 0b518cd..d242aae 100644 --- a/media/filters/chunk_demuxer_unittest.cc +++ b/media/filters/chunk_demuxer_unittest.cc @@ -338,7 +338,8 @@ class ChunkDemuxerTest : public testing::Test { return GenerateCluster(timecode, timecode, block_count); } - scoped_ptr<Cluster> GenerateCluster(int audio_timecode, int video_timecode, + scoped_ptr<Cluster> GenerateCluster(int first_audio_timecode, + int first_video_timecode, int block_count) { CHECK_GT(block_count, 0); @@ -346,14 +347,18 @@ class ChunkDemuxerTest : public testing::Test { scoped_array<uint8> data(new uint8[size]); ClusterBuilder cb; - cb.SetClusterTimecode(std::min(audio_timecode, video_timecode)); + cb.SetClusterTimecode(std::min(first_audio_timecode, first_video_timecode)); if (block_count == 1) { - cb.AddBlockGroup(kAudioTrackNum, audio_timecode, kAudioBlockDuration, - kWebMFlagKeyframe, data.get(), size); + cb.AddBlockGroup(kAudioTrackNum, first_audio_timecode, + kAudioBlockDuration, kWebMFlagKeyframe, + data.get(), size); return cb.Finish(); } + int audio_timecode = first_audio_timecode; + int video_timecode = first_video_timecode; + // Create simple blocks for everything except the last 2 blocks. // The first video frame must be a keyframe. uint8 video_flag = kWebMFlagKeyframe; @@ -417,15 +422,23 @@ class ChunkDemuxerTest : public testing::Test { void GenerateExpectedReads(int timecode, int block_count, DemuxerStream* audio, DemuxerStream* video) { + GenerateExpectedReads(timecode, timecode, block_count, audio, video); + } + + void GenerateExpectedReads(int start_audio_timecode, + int start_video_timecode, + int block_count, DemuxerStream* audio, + DemuxerStream* video) { CHECK_GT(block_count, 0); - int audio_timecode = timecode; - int video_timecode = timecode; if (block_count == 1) { - ExpectRead(audio, audio_timecode); + ExpectRead(audio, start_audio_timecode); return; } + int audio_timecode = start_audio_timecode; + int video_timecode = start_video_timecode; + for (int i = 0; i < block_count; i++) { if (audio_timecode <= video_timecode) { ExpectRead(audio, audio_timecode); @@ -1579,4 +1592,34 @@ TEST_F(ChunkDemuxerTest, GetBufferedRanges_EndOfStream) { CheckExpectedRanges(expected); } +TEST_F(ChunkDemuxerTest, TestDifferentStreamTimecodes) { + ASSERT_TRUE(InitDemuxer(true, true, false)); + + scoped_refptr<DemuxerStream> audio = + demuxer_->GetStream(DemuxerStream::AUDIO); + scoped_refptr<DemuxerStream> video = + demuxer_->GetStream(DemuxerStream::VIDEO); + + demuxer_->Seek(base::TimeDelta::FromSeconds(0), + NewExpectedStatusCB(PIPELINE_OK)); + + // Create a cluster where the video timecode begins 25ms after the audio. + scoped_ptr<Cluster> start_cluster( + GenerateCluster(0, 25, 8)); + + ASSERT_TRUE(AppendData(start_cluster->data(), start_cluster->size())); + GenerateExpectedReads(0, 25, 8, audio, video); + + // Seek to 5 seconds. + demuxer_->StartWaitingForSeek(); + demuxer_->Seek(base::TimeDelta::FromSeconds(5), + NewExpectedStatusCB(PIPELINE_OK)); + + // Generate a cluster to fulfill this seek, where audio timecode begins 25ms + // after the video. + scoped_ptr<Cluster> middle_cluster(GenerateCluster(5025, 5000, 8)); + ASSERT_TRUE(AppendData(middle_cluster->data(), middle_cluster->size())); + GenerateExpectedReads(5025, 5000, 8, audio, video); +} + } // namespace media diff --git a/media/filters/source_buffer_stream.cc b/media/filters/source_buffer_stream.cc index cf7233e..47899ee 100644 --- a/media/filters/source_buffer_stream.cc +++ b/media/filters/source_buffer_stream.cc @@ -20,7 +20,10 @@ class SourceBufferRange { // Creates a source buffer range with |new_buffers|. |new_buffers| cannot be // empty and the front of |new_buffers| must be a keyframe. - explicit SourceBufferRange(const BufferQueue& new_buffers); + // |media_segment_start_time| refers to the starting timestamp for the media + // segment to which these buffers belong. + SourceBufferRange(const BufferQueue& new_buffers, + base::TimeDelta media_segment_start_time); // Appends |buffers| to the end of the range and updates |keyframe_map_| as // it encounters new keyframes. Assumes |buffers| belongs at the end of the @@ -138,6 +141,16 @@ class SourceBufferRange { // keyframe with timestamp >= |next_keyframe_timestamp_|. base::TimeDelta next_keyframe_timestamp_; + // If the first buffer in this range is the beginning of a media segment, + // |media_segment_start_time_| is the time when the media segment begins. + // |media_segment_start_time_| may be <= the timestamp of the first buffer in + // |buffers_|. |media_segment_start_time_| is kNoTimestamp() if this range + // does not start at the beginning of a media segment, which can only happen + // garbage collection or after an end overlap that results in a split range + // (we don't have a way of knowing the media segment timestamp for the new + // range). + base::TimeDelta media_segment_start_time_; + DISALLOW_COPY_AND_ASSIGN(SourceBufferRange); }; @@ -188,14 +201,14 @@ static bool IsNextInSequence( namespace media { SourceBufferStream::SourceBufferStream() - : seek_pending_(true), + : seek_pending_(false), seek_buffer_timestamp_(base::TimeDelta()), selected_range_(NULL), end_of_stream_(false) { } SourceBufferStream::SourceBufferStream(const AudioDecoderConfig& audio_config) - : seek_pending_(true), + : seek_pending_(false), seek_buffer_timestamp_(base::TimeDelta()), selected_range_(NULL), end_of_stream_(false) { @@ -203,7 +216,7 @@ SourceBufferStream::SourceBufferStream(const AudioDecoderConfig& audio_config) } SourceBufferStream::SourceBufferStream(const VideoDecoderConfig& video_config) - : seek_pending_(true), + : seek_pending_(false), seek_buffer_timestamp_(base::TimeDelta()), selected_range_(NULL), end_of_stream_(false) { @@ -218,8 +231,10 @@ SourceBufferStream::~SourceBufferStream() { } bool SourceBufferStream::Append( - const SourceBufferStream::BufferQueue& buffers) { + const SourceBufferStream::BufferQueue& buffers, + base::TimeDelta media_segment_start_time) { DCHECK(!buffers.empty()); + DCHECK(media_segment_start_time != kNoTimestamp()); RangeList::iterator range_for_new_buffers = ranges_.end(); RangeList::iterator itr = ranges_.end(); @@ -244,7 +259,8 @@ bool SourceBufferStream::Append( if (!buffers.front()->IsKeyframe()) return false; range_for_new_buffers = - ranges_.insert(itr, new SourceBufferRange(buffers)); + ranges_.insert(itr, new SourceBufferRange( + buffers, media_segment_start_time)); } else { InsertIntoExistingRange(range_for_new_buffers, buffers); } @@ -254,6 +270,14 @@ bool SourceBufferStream::Append( ResolveEndOverlap(range_for_new_buffers); MergeWithAdjacentRangeIfNecessary(range_for_new_buffers); + // If these were the first buffers appended to the stream, seek to the + // beginning of the range. + // TODO(vrk): This should be done by ChunkDemuxer. (crbug.com/132815) + if (!seek_pending_ && !selected_range_) { + selected_range_ = *range_for_new_buffers; + selected_range_->Seek(start_timestamp); + } + // Finally, try to complete pending seek if one exists. if (seek_pending_) Seek(seek_buffer_timestamp_); @@ -497,10 +521,12 @@ bool SourceBufferStream::CanEndOfStream() const { return ranges_.empty() || selected_range_ == ranges_.back(); } -SourceBufferRange::SourceBufferRange(const BufferQueue& new_buffers) +SourceBufferRange::SourceBufferRange(const BufferQueue& new_buffers, + base::TimeDelta media_segment_start_time) : next_buffer_index_(-1), waiting_for_keyframe_(false), - next_keyframe_timestamp_(kNoTimestamp()) { + next_keyframe_timestamp_(kNoTimestamp()), + media_segment_start_time_(media_segment_start_time) { DCHECK(!new_buffers.empty()); DCHECK(new_buffers.front()->IsKeyframe()); AppendToEnd(new_buffers); @@ -537,8 +563,8 @@ void SourceBufferRange::Seek(base::TimeDelta timestamp) { // lower_bound() returns the first element >= |timestamp|, so we want the // previous element if it did not return the element exactly equal to // |timestamp|. - if (result == keyframe_map_.end() || result->first != timestamp) { - DCHECK(result != keyframe_map_.begin()); + if (result != keyframe_map_.begin() && + (result == keyframe_map_.end() || result->first != timestamp)) { result--; } next_buffer_index_ = result->second; @@ -582,7 +608,8 @@ SourceBufferRange* SourceBufferRange::SplitRange(base::TimeDelta timestamp) { DeleteAfter( buffers_.begin() + keyframe_index, &removed_buffers, &next_buffer); - SourceBufferRange* split_range = new SourceBufferRange(removed_buffers); + SourceBufferRange* split_range = + new SourceBufferRange(removed_buffers, kNoTimestamp()); if (next_buffer != removed_buffers.end()) { split_range->next_buffer_index_ = next_buffer - removed_buffers.begin(); } @@ -734,7 +761,10 @@ bool SourceBufferRange::EndOverlaps(const SourceBufferRange& range) const { base::TimeDelta SourceBufferRange::GetStartTimestamp() const { DCHECK(!buffers_.empty()); - return buffers_.front()->GetTimestamp(); + base::TimeDelta start_timestamp = media_segment_start_time_; + if (start_timestamp == kNoTimestamp()) + start_timestamp = buffers_.front()->GetTimestamp(); + return start_timestamp; } base::TimeDelta SourceBufferRange::GetEndTimestamp() const { diff --git a/media/filters/source_buffer_stream.h b/media/filters/source_buffer_stream.h index c67ecf6..6d7c179 100644 --- a/media/filters/source_buffer_stream.h +++ b/media/filters/source_buffer_stream.h @@ -39,9 +39,12 @@ class MEDIA_EXPORT SourceBufferStream { // expected to be in order, but multiple calls to Append() may add buffers out // of order or overlapping. Assumes all buffers within |buffers| are in // presentation order and are non-overlapping. + // |media_segment_start_time| refers to the starting timestamp for the media + // segment to which these buffers belong. // Returns true if Append() was successful, false if |buffers| are not added. // TODO(vrk): Implement garbage collection. (crbug.com/125070) - bool Append(const BufferQueue& buffers); + bool Append(const BufferQueue& buffers, + base::TimeDelta media_segment_start_time); // Changes the SourceBufferStream's state so that it will start returning // buffers starting from the closest keyframe before |timestamp|. @@ -51,8 +54,9 @@ class MEDIA_EXPORT SourceBufferStream { // 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(). + // Fills |out_buffer| with a new buffer. Buffers are presented in order from + // the last call to Seek(), or starting with the first buffer appended if + // Seek() has not been called yet. // |out_buffer|'s timestamp may be earlier than the |timestamp| passed to // the last Seek() call. // Returns true if |out_buffer| is filled with a valid buffer, false if diff --git a/media/filters/source_buffer_stream_unittest.cc b/media/filters/source_buffer_stream_unittest.cc index 07fc967..eaf1ef5 100644 --- a/media/filters/source_buffer_stream_unittest.cc +++ b/media/filters/source_buffer_stream_unittest.cc @@ -29,17 +29,26 @@ class SourceBufferStreamTest : public testing::Test { } void AppendBuffers(int starting_position, int number_of_buffers) { - AppendBuffers(starting_position, number_of_buffers, true, NULL, 0); + AppendBuffers(starting_position, number_of_buffers, + base::TimeDelta(), true, NULL, 0); } void AppendBuffers(int starting_position, int number_of_buffers, const uint8* data) { - AppendBuffers(starting_position, number_of_buffers, true, data, kDataSize); + AppendBuffers(starting_position, number_of_buffers, + base::TimeDelta(), true, data, kDataSize); + } + + void AppendBuffers(int starting_position, int number_of_buffers, + base::TimeDelta first_buffer_offset) { + AppendBuffers(starting_position, number_of_buffers, + first_buffer_offset, true, NULL, 0); } void AppendBuffers_ExpectFailure( int starting_position, int number_of_buffers) { - AppendBuffers(starting_position, number_of_buffers, false, NULL, 0); + AppendBuffers(starting_position, number_of_buffers, + base::TimeDelta(), false, NULL, 0); } void Seek(int position) { @@ -142,8 +151,11 @@ class SourceBufferStreamTest : public testing::Test { } void AppendBuffers(int starting_position, int number_of_buffers, + base::TimeDelta first_buffer_offset, bool expect_success, const uint8* data, int size) { int keyframe_interval = frames_per_second_ / keyframes_per_second_; + base::TimeDelta media_segment_start_time = + starting_position * frame_duration_; SourceBufferStream::BufferQueue queue; for (int i = 0; i < number_of_buffers; i++) { @@ -151,11 +163,17 @@ class SourceBufferStreamTest : public testing::Test { bool is_keyframe = position % keyframe_interval == 0; scoped_refptr<StreamParserBuffer> buffer = StreamParserBuffer::CopyFrom(data, size, is_keyframe); - buffer->SetDuration(frame_duration_); - buffer->SetTimestamp(frame_duration_ * position); + base::TimeDelta duration = frame_duration_; + base::TimeDelta timestamp = frame_duration_ * position; + if (i == 0) { + duration -= first_buffer_offset; + timestamp += first_buffer_offset; + } + buffer->SetDuration(duration); + buffer->SetTimestamp(timestamp); queue.push_back(buffer); } - EXPECT_EQ(stream_.Append(queue), expect_success); + EXPECT_EQ(stream_.Append(queue, media_segment_start_time), expect_success); } int frames_per_second_; @@ -1076,6 +1094,37 @@ TEST_F(SourceBufferStreamTest, Seek_After_TrackBuffer_Filled) { CheckExpectedTimespan(0, 19); } +TEST_F(SourceBufferStreamTest, Seek_StartOfSegment) { + base::TimeDelta bump = frame_duration() / 4; + CHECK(bump > base::TimeDelta()); + + // Append 5 buffers at position (5 + |bump|) through 9, where the media + // segment begins at position 5. + AppendBuffers(5, 5, bump); + scoped_refptr<StreamParserBuffer> buffer; + + // GetNextBuffer() should return the next buffer at position (5 + |bump|). + EXPECT_TRUE(stream_.GetNextBuffer(&buffer)); + EXPECT_EQ(buffer->GetTimestamp(), 5 * frame_duration() + bump); + + // Check rest of buffers. + CheckExpectedBuffers(6, 9); + + // Seek to position 15. + Seek(15); + + // Append 5 buffers at positions (15 + |bump|) through 19, where the media + // segment begins at 15. + AppendBuffers(15, 5, bump); + + // GetNextBuffer() should return the next buffer at position (15 + |bump|). + EXPECT_TRUE(stream_.GetNextBuffer(&buffer)); + EXPECT_EQ(buffer->GetTimestamp(), 15 * frame_duration() + bump); + + // Check rest of buffers. + CheckExpectedBuffers(16, 19); +} + TEST_F(SourceBufferStreamTest, GetNextBuffer_AfterMerges) { // Append 5 buffers at positions 10 through 14. AppendBuffers(10, 5); @@ -1144,4 +1193,12 @@ TEST_F(SourceBufferStreamTest, GetNextBuffer_Overlap_Selected_Complete) { CheckExpectedBuffers(10, 14, &kDataB); } +TEST_F(SourceBufferStreamTest, GetNextBuffer_NoSeek) { + // Append 5 buffers at position 5. + AppendBuffers(5, 5); + + // Should receive buffers from the start without needing to seek. + CheckExpectedBuffers(5, 5); +} + } // namespace media diff --git a/media/webm/webm_stream_parser.cc b/media/webm/webm_stream_parser.cc index c8ca10e..b063a26 100644 --- a/media/webm/webm_stream_parser.cc +++ b/media/webm/webm_stream_parser.cc @@ -181,7 +181,8 @@ bool FFmpegConfigHelper::SetupStreamConfigs() { } WebMStreamParser::WebMStreamParser() - : state_(kWaitingForInit) { + : state_(kWaitingForInit), + waiting_for_buffers_(false) { } WebMStreamParser::~WebMStreamParser() {} @@ -190,13 +191,15 @@ void WebMStreamParser::Init(const InitCB& init_cb, const NewConfigCB& config_cb, const NewBuffersCB& audio_cb, const NewBuffersCB& video_cb, - const KeyNeededCB& key_needed_cb) { + const KeyNeededCB& key_needed_cb, + const NewMediaSegmentCB& new_segment_cb) { DCHECK_EQ(state_, kWaitingForInit); DCHECK(init_cb_.is_null()); DCHECK(!init_cb.is_null()); DCHECK(!config_cb.is_null()); DCHECK(!audio_cb.is_null() || !video_cb.is_null()); DCHECK(!key_needed_cb.is_null()); + DCHECK(!new_segment_cb.is_null()); ChangeState(kParsingHeaders); init_cb_ = init_cb; @@ -204,6 +207,7 @@ void WebMStreamParser::Init(const InitCB& init_cb, audio_cb_ = audio_cb; video_cb_ = video_cb; key_needed_cb_ = key_needed_cb; + new_segment_cb_ = new_segment_cb; } void WebMStreamParser::Flush() { @@ -381,6 +385,9 @@ int WebMStreamParser::ParseCluster(const uint8* data, int size) { if (result <= 0) return result; + if (id == kWebMIdCluster) + waiting_for_buffers_ = true; + if (id == kWebMIdCues) { if (size < (result + element_size)) { // We don't have the whole element yet. Signal we need more data. @@ -403,6 +410,26 @@ int WebMStreamParser::ParseCluster(const uint8* data, int size) { const BufferQueue& audio_buffers = cluster_parser_->audio_buffers(); const BufferQueue& video_buffers = cluster_parser_->video_buffers(); + if (waiting_for_buffers_) { + base::TimeDelta audio_start_timestamp = kInfiniteDuration(); + base::TimeDelta video_start_timestamp = kInfiniteDuration(); + + if (!audio_buffers.empty()) + audio_start_timestamp = audio_buffers.front()->GetTimestamp(); + if (!video_buffers.empty()) + video_start_timestamp = video_buffers.front()->GetTimestamp(); + + base::TimeDelta cluster_start_timestamp = + std::min(audio_start_timestamp, video_start_timestamp); + + // If we haven't gotten any buffers yet, return early. + if (cluster_start_timestamp == kInfiniteDuration()) + return bytes_parsed; + + new_segment_cb_.Run(cluster_start_timestamp); + waiting_for_buffers_ = false; + } + if (!audio_buffers.empty() && !audio_cb_.Run(audio_buffers)) return -1; diff --git a/media/webm/webm_stream_parser.h b/media/webm/webm_stream_parser.h index 0d37992..003171e 100644 --- a/media/webm/webm_stream_parser.h +++ b/media/webm/webm_stream_parser.h @@ -25,7 +25,8 @@ class WebMStreamParser : public StreamParser { virtual void Init(const InitCB& init_cb, const NewConfigCB& config_cb, const NewBuffersCB& audio_cb, const NewBuffersCB& video_cb, - const KeyNeededCB& key_needed_cb) OVERRIDE; + const KeyNeededCB& key_needed_cb, + const NewMediaSegmentCB& new_segment_cb) OVERRIDE; virtual void Flush() OVERRIDE; virtual bool Parse(const uint8* buf, int size) OVERRIDE; @@ -64,6 +65,11 @@ class WebMStreamParser : public StreamParser { NewBuffersCB audio_cb_; NewBuffersCB video_cb_; KeyNeededCB key_needed_cb_; + NewMediaSegmentCB new_segment_cb_; + + // True if a new cluster id has been seen, but no audio or video buffers have + // been parsed yet. + bool waiting_for_buffers_; scoped_ptr<WebMClusterParser> cluster_parser_; ByteQueue byte_queue_; |