summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--media/base/stream_parser.h7
-rw-r--r--media/filters/chunk_demuxer.cc42
-rw-r--r--media/filters/chunk_demuxer.h2
-rw-r--r--media/filters/chunk_demuxer_unittest.cc57
-rw-r--r--media/filters/source_buffer_stream.cc54
-rw-r--r--media/filters/source_buffer_stream.h10
-rw-r--r--media/filters/source_buffer_stream_unittest.cc69
-rw-r--r--media/webm/webm_stream_parser.cc31
-rw-r--r--media/webm/webm_stream_parser.h8
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_;