diff options
author | acolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-18 05:19:40 +0000 |
---|---|---|
committer | acolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-18 05:19:40 +0000 |
commit | 0e1d89e963f250df7c7c18b651986f7715a8e509 (patch) | |
tree | 284adc4d75827f2e449d7cde116e5970ebe0955d /media | |
parent | 925dec1cf8e2574f24d09cb39fe2f1ad8346d28b (diff) | |
download | chromium_src-0e1d89e963f250df7c7c18b651986f7715a8e509.zip chromium_src-0e1d89e963f250df7c7c18b651986f7715a8e509.tar.gz chromium_src-0e1d89e963f250df7c7c18b651986f7715a8e509.tar.bz2 |
Update WebMClusterParser to compute durations for all StreamParserBuffers.
BUG=127916
TEST=WebMClusterParserTest.*
Review URL: https://chromiumcodereview.appspot.com/10382200
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@137826 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/base/stream_parser_buffer.cc | 1 | ||||
-rw-r--r-- | media/filters/chunk_demuxer_unittest.cc | 252 | ||||
-rw-r--r-- | media/filters/pipeline_integration_test.cc | 2 | ||||
-rw-r--r-- | media/webm/webm_cluster_parser.cc | 97 | ||||
-rw-r--r-- | media/webm/webm_cluster_parser.h | 37 | ||||
-rw-r--r-- | media/webm/webm_cluster_parser_unittest.cc | 8 | ||||
-rw-r--r-- | media/webm/webm_tracks_parser.cc | 11 |
7 files changed, 218 insertions, 190 deletions
diff --git a/media/base/stream_parser_buffer.cc b/media/base/stream_parser_buffer.cc index 3c765b1..ed4150c 100644 --- a/media/base/stream_parser_buffer.cc +++ b/media/base/stream_parser_buffer.cc @@ -12,6 +12,7 @@ StreamParserBuffer::StreamParserBuffer(const uint8* data, int data_size, bool is_keyframe) : DataBuffer(data, data_size), is_keyframe_(is_keyframe) { + SetDuration(kNoTimestamp()); } scoped_refptr<StreamParserBuffer> StreamParserBuffer::CreateEOSBuffer() { diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc index 43ec4ad..45c3a7d 100644 --- a/media/filters/chunk_demuxer_unittest.cc +++ b/media/filters/chunk_demuxer_unittest.cc @@ -39,6 +39,9 @@ static const int kVideoTrackEntryHeaderSize = kVideoTrackSizeOffset + static const int kVideoTrackNum = 1; static const int kAudioTrackNum = 2; +static const int kAudioBlockDuration = 23; +static const int kVideoBlockDuration = 33; + static const char* kSourceId = "SourceId"; base::TimeDelta kDefaultDuration() { @@ -59,7 +62,7 @@ static void WriteInt64(uint8* buffer, int64 number) { } MATCHER_P(HasTimestamp, timestamp_in_ms, "") { - return !arg->IsEndOfStream() && + return arg && !arg->IsEndOfStream() && arg->GetTimestamp().InMilliseconds() == timestamp_in_ms; } @@ -177,6 +180,7 @@ class ChunkDemuxerTest : public testing::Test { } bool AppendData(const uint8* data, size_t length) { + CHECK(length); EXPECT_CALL(host_, SetBufferedBytes(_)).Times(AnyNumber()); EXPECT_CALL(host_, SetNetworkActivity(true)) .Times(AnyNumber()); @@ -253,10 +257,74 @@ class ChunkDemuxerTest : public testing::Test { cb->AddSimpleBlock(track_num, timecode, 0, data, sizeof(data)); } - void AddSimpleBlock(ClusterBuilder* cb, int track_num, int64 timecode, - int size) { + scoped_ptr<Cluster> GenerateCluster(int timecode, int block_count) { + CHECK_GT(block_count, 0); + + int size = 10; scoped_array<uint8> data(new uint8[size]); - cb->AddSimpleBlock(track_num, timecode, 0, data.get(), size); + + ClusterBuilder cb; + cb.SetClusterTimecode(timecode); + int audio_timecode = timecode; + int video_timecode = timecode + 1; + + if (block_count == 1) { + cb.AddBlockGroup(kAudioTrackNum, audio_timecode, kAudioBlockDuration, 0, + data.get(), size); + return cb.Finish(); + } + + // Create simple blocks for everything except the last 2 blocks. + for (int i = 0; i < block_count - 2; i++) { + if (audio_timecode <= video_timecode) { + cb.AddSimpleBlock(kAudioTrackNum, audio_timecode, 0, data.get(), size); + audio_timecode += kAudioBlockDuration; + continue; + } + + cb.AddSimpleBlock(kVideoTrackNum, video_timecode, 0, data.get(), size); + video_timecode += kVideoBlockDuration; + } + + // 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); + } else { + cb.AddBlockGroup(kVideoTrackNum, video_timecode, kVideoBlockDuration, 0, + data.get(), size); + cb.AddBlockGroup(kAudioTrackNum, audio_timecode, kAudioBlockDuration, 0, + data.get(), size); + } + + return cb.Finish(); + } + + void GenerateExpectedReads(int timecode, int block_count, + DemuxerStream* audio, + DemuxerStream* video) { + CHECK_GT(block_count, 0); + int audio_timecode = timecode; + int video_timecode = timecode + 1; + + if (block_count == 1) { + ExpectRead(audio, audio_timecode); + return; + } + + for (int i = 0; i < block_count; i++) { + if (audio_timecode <= video_timecode) { + ExpectRead(audio, audio_timecode); + audio_timecode += kAudioBlockDuration; + continue; + } + + ExpectRead(video, video_timecode); + video_timecode += kVideoBlockDuration; + } } MOCK_METHOD1(ReadDone, void(const scoped_refptr<Buffer>&)); @@ -416,10 +484,7 @@ TEST_F(ChunkDemuxerTest, TestAppendDataAfterSeek) { EXPECT_CALL(*this, Checkpoint(2)); - ClusterBuilder cb; - cb.SetClusterTimecode(0); - AddSimpleBlock(&cb, kVideoTrackNum, 0); - scoped_ptr<Cluster> cluster(cb.Finish()); + scoped_ptr<Cluster> cluster(GenerateCluster(0, 4)); Checkpoint(1); @@ -442,28 +507,20 @@ TEST_F(ChunkDemuxerTest, TestSeekWhileParsingCluster) { InSequence s; - ClusterBuilder cb; - cb.SetClusterTimecode(0); - AddSimpleBlock(&cb, kAudioTrackNum, 1); - AddSimpleBlock(&cb, kVideoTrackNum, 2); - AddSimpleBlock(&cb, kAudioTrackNum, 10); - AddSimpleBlock(&cb, kVideoTrackNum, 20); - scoped_ptr<Cluster> cluster_a(cb.Finish()); - - cb.SetClusterTimecode(5000); - AddSimpleBlock(&cb, kAudioTrackNum, 5000); - AddSimpleBlock(&cb, kVideoTrackNum, 5005); - AddSimpleBlock(&cb, kAudioTrackNum, 5007); - AddSimpleBlock(&cb, kVideoTrackNum, 5035); - scoped_ptr<Cluster> cluster_b(cb.Finish()); + scoped_ptr<Cluster> cluster_a(GenerateCluster(0, 6)); + scoped_ptr<Cluster> cluster_b(GenerateCluster(5000, 6)); // Append all but the last byte so that everything but // the last block can be parsed. ASSERT_TRUE(AppendData(cluster_a->data(), cluster_a->size() - 1)); - ExpectRead(audio, 1); - ExpectRead(video, 2); - ExpectRead(audio, 10); + ExpectRead(audio, 0); + ExpectRead(video, 1); + 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_->Seek(base::TimeDelta::FromSeconds(5), @@ -473,10 +530,7 @@ TEST_F(ChunkDemuxerTest, TestSeekWhileParsingCluster) { // Append the new cluster and verify that only the blocks // in the new cluster are returned. ASSERT_TRUE(AppendData(cluster_b->data(), cluster_b->size())); - ExpectRead(audio, 5000); - ExpectRead(video, 5005); - ExpectRead(audio, 5007); - ExpectRead(video, 5035); + GenerateExpectedReads(5000, 6, audio, video); } // Test the case where AppendData() is called before Init(). @@ -501,18 +555,14 @@ TEST_F(ChunkDemuxerTest, TestRead) { bool audio_read_done = false; bool video_read_done = false; audio->Read(base::Bind(&OnReadDone, - base::TimeDelta::FromMilliseconds(32), + base::TimeDelta::FromMilliseconds(0), &audio_read_done)); video->Read(base::Bind(&OnReadDone, - base::TimeDelta::FromMilliseconds(123), + base::TimeDelta::FromMilliseconds(1), &video_read_done)); - ClusterBuilder cb; - cb.SetClusterTimecode(0); - AddSimpleBlock(&cb, kAudioTrackNum, 32); - AddSimpleBlock(&cb, kVideoTrackNum, 123); - scoped_ptr<Cluster> cluster(cb.Finish()); + scoped_ptr<Cluster> cluster(GenerateCluster(0, 4)); ASSERT_TRUE(AppendData(cluster->data(), cluster->size())); @@ -523,25 +573,13 @@ TEST_F(ChunkDemuxerTest, TestRead) { TEST_F(ChunkDemuxerTest, TestOutOfOrderClusters) { ASSERT_TRUE(InitDemuxer(true, true, false)); - ClusterBuilder cb; - - cb.SetClusterTimecode(10); - AddSimpleBlock(&cb, kAudioTrackNum, 10); - AddSimpleBlock(&cb, kVideoTrackNum, 10); - AddSimpleBlock(&cb, kAudioTrackNum, 33); - AddSimpleBlock(&cb, kVideoTrackNum, 43); - scoped_ptr<Cluster> cluster_a(cb.Finish()); + scoped_ptr<Cluster> cluster_a(GenerateCluster(10, 4)); ASSERT_TRUE(AppendData(cluster_a->data(), cluster_a->size())); // Cluster B starts before cluster_a and has data // that overlaps. - cb.SetClusterTimecode(5); - AddSimpleBlock(&cb, kAudioTrackNum, 5); - AddSimpleBlock(&cb, kVideoTrackNum, 7); - AddSimpleBlock(&cb, kAudioTrackNum, 28); - AddSimpleBlock(&cb, kVideoTrackNum, 40); - scoped_ptr<Cluster> cluster_b(cb.Finish()); + scoped_ptr<Cluster> cluster_b(GenerateCluster(5, 4)); // Make sure that AppendData() fails because this cluster data // is before previous data. @@ -549,10 +587,7 @@ TEST_F(ChunkDemuxerTest, TestOutOfOrderClusters) { ASSERT_TRUE(AppendData(cluster_b->data(), cluster_b->size())); // Verify that AppendData() doesn't accept more data now. - cb.SetClusterTimecode(45); - AddSimpleBlock(&cb, kAudioTrackNum, 45); - AddSimpleBlock(&cb, kVideoTrackNum, 45); - scoped_ptr<Cluster> cluster_c(cb.Finish()); + scoped_ptr<Cluster> cluster_c(GenerateCluster(45, 2)); EXPECT_FALSE(demuxer_->AppendData(kSourceId, cluster_c->data(), cluster_c->size())); } @@ -575,10 +610,7 @@ TEST_F(ChunkDemuxerTest, TestNonMonotonicButAboveClusterTimecode) { ASSERT_TRUE(AppendData(cluster_a->data(), cluster_a->size())); // Verify that AppendData() doesn't accept more data now. - cb.SetClusterTimecode(20); - AddSimpleBlock(&cb, kAudioTrackNum, 20); - AddSimpleBlock(&cb, kVideoTrackNum, 20); - scoped_ptr<Cluster> cluster_b(cb.Finish()); + scoped_ptr<Cluster> cluster_b(GenerateCluster(20, 2)); EXPECT_FALSE(demuxer_->AppendData(kSourceId, cluster_b->data(), cluster_b->size())); } @@ -601,10 +633,7 @@ TEST_F(ChunkDemuxerTest, TestBackwardsAndBeforeClusterTimecode) { ASSERT_TRUE(AppendData(cluster_a->data(), cluster_a->size())); // Verify that AppendData() doesn't accept more data now. - cb.SetClusterTimecode(6); - AddSimpleBlock(&cb, kAudioTrackNum, 6); - AddSimpleBlock(&cb, kVideoTrackNum, 6); - scoped_ptr<Cluster> cluster_b(cb.Finish()); + scoped_ptr<Cluster> cluster_b(GenerateCluster(6, 2)); EXPECT_FALSE(demuxer_->AppendData(kSourceId, cluster_b->data(), cluster_b->size())); } @@ -651,10 +680,7 @@ TEST_F(ChunkDemuxerTest, TestMonotonicallyIncreasingTimestampsAcrossClusters) { ASSERT_TRUE(AppendData(cluster_b->data(), cluster_b->size())); // Verify that AppendData() doesn't accept more data now. - cb.SetClusterTimecode(10); - AddSimpleBlock(&cb, kAudioTrackNum, 10); - AddSimpleBlock(&cb, kVideoTrackNum, 10); - scoped_ptr<Cluster> cluster_c(cb.Finish()); + scoped_ptr<Cluster> cluster_c(GenerateCluster(10, 2)); EXPECT_FALSE(demuxer_->AppendData(kSourceId, cluster_c->data(), cluster_c->size())); } @@ -668,10 +694,7 @@ TEST_F(ChunkDemuxerTest, TestClusterBeforeInfoTracks) { ASSERT_EQ(AddId(), ChunkDemuxer::kOk); - ClusterBuilder cb; - cb.SetClusterTimecode(0); - AddSimpleBlock(&cb, kVideoTrackNum, 0); - scoped_ptr<Cluster> cluster(cb.Finish()); + scoped_ptr<Cluster> cluster(GenerateCluster(0, 1)); ASSERT_TRUE(AppendData(cluster->data(), cluster->size())); } @@ -687,13 +710,7 @@ TEST_F(ChunkDemuxerTest, TestEOSDuringInit) { TEST_F(ChunkDemuxerTest, TestDecodeErrorEndOfStream) { ASSERT_TRUE(InitDemuxer(true, true, false)); - ClusterBuilder cb; - cb.SetClusterTimecode(0); - AddSimpleBlock(&cb, kAudioTrackNum, 0); - AddSimpleBlock(&cb, kVideoTrackNum, 0); - AddSimpleBlock(&cb, kAudioTrackNum, 23); - AddSimpleBlock(&cb, kVideoTrackNum, 33); - scoped_ptr<Cluster> cluster(cb.Finish()); + scoped_ptr<Cluster> cluster(GenerateCluster(0, 4)); ASSERT_TRUE(AppendData(cluster->data(), cluster->size())); EXPECT_CALL(host_, OnDemuxerError(PIPELINE_ERROR_DECODE)); @@ -703,13 +720,7 @@ TEST_F(ChunkDemuxerTest, TestDecodeErrorEndOfStream) { TEST_F(ChunkDemuxerTest, TestNetworkErrorEndOfStream) { ASSERT_TRUE(InitDemuxer(true, true, false)); - ClusterBuilder cb; - cb.SetClusterTimecode(0); - AddSimpleBlock(&cb, kAudioTrackNum, 0); - AddSimpleBlock(&cb, kVideoTrackNum, 0); - AddSimpleBlock(&cb, kAudioTrackNum, 23); - AddSimpleBlock(&cb, kVideoTrackNum, 33); - scoped_ptr<Cluster> cluster(cb.Finish()); + scoped_ptr<Cluster> cluster(GenerateCluster(0, 4)); ASSERT_TRUE(AppendData(cluster->data(), cluster->size())); EXPECT_CALL(host_, OnDemuxerError(PIPELINE_ERROR_NETWORK)); @@ -780,21 +791,17 @@ TEST_F(ChunkDemuxerTest, TestEndOfStreamWithPendingReads) { EndOfStreamHelper end_of_stream_helper_2(demuxer_); audio->Read(base::Bind(&OnReadDone, - base::TimeDelta::FromMilliseconds(32), + base::TimeDelta::FromMilliseconds(0), &audio_read_done_1)); video->Read(base::Bind(&OnReadDone, - base::TimeDelta::FromMilliseconds(123), + base::TimeDelta::FromMilliseconds(1), &video_read_done_1)); end_of_stream_helper_1.RequestReads(); end_of_stream_helper_2.RequestReads(); - ClusterBuilder cb; - cb.SetClusterTimecode(0); - AddSimpleBlock(&cb, kAudioTrackNum, 32); - AddSimpleBlock(&cb, kVideoTrackNum, 123); - scoped_ptr<Cluster> cluster(cb.Finish()); + scoped_ptr<Cluster> cluster(GenerateCluster(0, 2)); ASSERT_TRUE(AppendData(cluster->data(), cluster->size())); @@ -826,20 +833,16 @@ TEST_F(ChunkDemuxerTest, TestReadsAfterEndOfStream) { EndOfStreamHelper end_of_stream_helper_3(demuxer_); audio->Read(base::Bind(&OnReadDone, - base::TimeDelta::FromMilliseconds(32), + base::TimeDelta::FromMilliseconds(0), &audio_read_done_1)); video->Read(base::Bind(&OnReadDone, - base::TimeDelta::FromMilliseconds(123), + base::TimeDelta::FromMilliseconds(1), &video_read_done_1)); end_of_stream_helper_1.RequestReads(); - ClusterBuilder cb; - cb.SetClusterTimecode(0); - AddSimpleBlock(&cb, kAudioTrackNum, 32); - AddSimpleBlock(&cb, kVideoTrackNum, 123); - scoped_ptr<Cluster> cluster(cb.Finish()); + scoped_ptr<Cluster> cluster(GenerateCluster(0, 2)); ASSERT_TRUE(AppendData(cluster->data(), cluster->size())); @@ -873,16 +876,8 @@ TEST_F(ChunkDemuxerTest, TestAppendingInPieces) { int info_tracks_size = 0; CreateInfoTracks(true, true, false, &info_tracks, &info_tracks_size); - ClusterBuilder cb; - cb.SetClusterTimecode(0); - AddSimpleBlock(&cb, kAudioTrackNum, 32, 512); - AddSimpleBlock(&cb, kVideoTrackNum, 123, 1024); - scoped_ptr<Cluster> cluster_a(cb.Finish()); - - cb.SetClusterTimecode(125); - AddSimpleBlock(&cb, kAudioTrackNum, 125, 2048); - AddSimpleBlock(&cb, kVideoTrackNum, 150, 2048); - scoped_ptr<Cluster> cluster_b(cb.Finish()); + scoped_ptr<Cluster> cluster_a(GenerateCluster(0, 4)); + scoped_ptr<Cluster> cluster_b(GenerateCluster(68, 4)); size_t buffer_size = info_tracks_size + cluster_a->size() + cluster_b->size(); scoped_array<uint8> buffer(new uint8[buffer_size]); @@ -906,31 +901,8 @@ TEST_F(ChunkDemuxerTest, TestAppendingInPieces) { ASSERT_TRUE(audio); ASSERT_TRUE(video); - bool audio_read_done = false; - bool video_read_done = false; - audio->Read(base::Bind(&OnReadDone, - base::TimeDelta::FromMilliseconds(32), - &audio_read_done)); - - video->Read(base::Bind(&OnReadDone, - base::TimeDelta::FromMilliseconds(123), - &video_read_done)); - - EXPECT_TRUE(audio_read_done); - EXPECT_TRUE(video_read_done); - - audio_read_done = false; - video_read_done = false; - audio->Read(base::Bind(&OnReadDone, - base::TimeDelta::FromMilliseconds(125), - &audio_read_done)); - - video->Read(base::Bind(&OnReadDone, - base::TimeDelta::FromMilliseconds(150), - &video_read_done)); - - EXPECT_TRUE(audio_read_done); - EXPECT_TRUE(video_read_done); + GenerateExpectedReads(0, 4, audio, video); + GenerateExpectedReads(68, 4, audio, video); } TEST_F(ChunkDemuxerTest, TestWebMFile_AudioAndVideo) { @@ -993,14 +965,7 @@ TEST_F(ChunkDemuxerTest, TestWebMFile_VideoOnly) { TEST_F(ChunkDemuxerTest, TestIncrementalClusterParsing) { ASSERT_TRUE(InitDemuxer(true, true, false)); - ClusterBuilder cb; - cb.SetClusterTimecode(0); - AddSimpleBlock(&cb, kAudioTrackNum, 0, 10); - AddSimpleBlock(&cb, kVideoTrackNum, 1, 10); - AddSimpleBlock(&cb, kVideoTrackNum, 2, 10); - AddSimpleBlock(&cb, kAudioTrackNum, 3, 10); - scoped_ptr<Cluster> cluster(cb.Finish()); - + scoped_ptr<Cluster> cluster(GenerateCluster(0, 6)); scoped_refptr<DemuxerStream> audio = demuxer_->GetStream(DemuxerStream::AUDIO); scoped_refptr<DemuxerStream> video = @@ -1042,11 +1007,11 @@ TEST_F(ChunkDemuxerTest, TestIncrementalClusterParsing) { audio_read_done = false; video_read_done = false; audio->Read(base::Bind(&OnReadDone, - base::TimeDelta::FromMilliseconds(3), + base::TimeDelta::FromMilliseconds(23), &audio_read_done)); video->Read(base::Bind(&OnReadDone, - base::TimeDelta::FromMilliseconds(2), + base::TimeDelta::FromMilliseconds(34), &video_read_done)); // Make sure the reads haven't completed yet. @@ -1054,6 +1019,7 @@ TEST_F(ChunkDemuxerTest, TestIncrementalClusterParsing) { EXPECT_FALSE(video_read_done); // Append the remaining data. + ASSERT_LT(i, cluster->size()); ASSERT_TRUE(AppendData(cluster->data() + i, cluster->size() - i)); EXPECT_TRUE(audio_read_done); diff --git a/media/filters/pipeline_integration_test.cc b/media/filters/pipeline_integration_test.cc index e0d679b..a9347ef 100644 --- a/media/filters/pipeline_integration_test.cc +++ b/media/filters/pipeline_integration_test.cc @@ -226,7 +226,7 @@ TEST_F(PipelineIntegrationTest, DISABLED_SeekWhilePlaying) { // Verify audio decoder & renderer can handle aborted demuxer reads. TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_AudioOnly) { ASSERT_TRUE(TestSeekDuringRead("bear-320x240-audio-only.webm", 8192, - base::TimeDelta::FromMilliseconds(477), + base::TimeDelta::FromMilliseconds(464), base::TimeDelta::FromMilliseconds(617), 0x10CA, 19730)); } diff --git a/media/webm/webm_cluster_parser.cc b/media/webm/webm_cluster_parser.cc index ed1e94e..8271899 100644 --- a/media/webm/webm_cluster_parser.cc +++ b/media/webm/webm_cluster_parser.cc @@ -19,16 +19,14 @@ WebMClusterParser::WebMClusterParser(int64 timecode_scale, const uint8* video_encryption_key_id, int video_encryption_key_id_size) : timecode_multiplier_(timecode_scale / 1000.0), - audio_track_num_(audio_track_num), - audio_default_duration_(audio_default_duration), - video_track_num_(video_track_num), - video_default_duration_(video_default_duration), video_encryption_key_id_size_(video_encryption_key_id_size), parser_(kWebMIdCluster, this), last_block_timecode_(-1), block_data_size_(-1), block_duration_(-1), - cluster_timecode_(-1) { + cluster_timecode_(-1), + audio_(audio_track_num, audio_default_duration), + video_(video_track_num, video_default_duration) { CHECK_GE(video_encryption_key_id_size, 0); if (video_encryption_key_id_size > 0) { video_encryption_key_id_.reset(new uint8[video_encryption_key_id_size]); @@ -40,16 +38,16 @@ WebMClusterParser::WebMClusterParser(int64 timecode_scale, WebMClusterParser::~WebMClusterParser() {} void WebMClusterParser::Reset() { - audio_buffers_.clear(); - video_buffers_.clear(); last_block_timecode_ = -1; cluster_timecode_ = -1; parser_.Reset(); + audio_.Reset(); + video_.Reset(); } int WebMClusterParser::Parse(const uint8* buf, int size) { - audio_buffers_.clear(); - video_buffers_.clear(); + audio_.ClearBufferQueue(); + video_.ClearBufferQueue(); int result = parser_.Parse(buf, size); @@ -166,6 +164,7 @@ bool WebMClusterParser::OnBinary(int id, const uint8* data, int size) { block_data_size_ = size; return true; } + bool WebMClusterParser::OnBlock(int track_num, int timecode, int block_duration, int flags, @@ -196,42 +195,80 @@ bool WebMClusterParser::OnBlock(int track_num, int timecode, scoped_refptr<StreamParserBuffer> buffer = StreamParserBuffer::CopyFrom(data, size, is_keyframe); - if (track_num == video_track_num_ && video_encryption_key_id_.get()) { + if (track_num == video_.track_num() && video_encryption_key_id_.get()) { buffer->SetDecryptConfig(scoped_ptr<DecryptConfig>(new DecryptConfig( video_encryption_key_id_.get(), video_encryption_key_id_size_))); } buffer->SetTimestamp(timestamp); - BufferQueue* queue = NULL; - base::TimeDelta duration = kNoTimestamp(); - - if (track_num == audio_track_num_) { - duration = audio_default_duration_; - queue = &audio_buffers_; - } else if (track_num == video_track_num_) { - duration = video_default_duration_; - queue = &video_buffers_; - } else { - DVLOG(1) << "Unexpected track number " << track_num; - return false; - } if (block_duration >= 0) { - duration = base::TimeDelta::FromMicroseconds( - block_duration * timecode_multiplier_); + buffer->SetDuration(base::TimeDelta::FromMicroseconds( + block_duration * timecode_multiplier_)); } - buffer->SetDuration(duration); + if (track_num == audio_.track_num()) { + return audio_.AddBuffer(buffer); + } else if (track_num == video_.track_num()) { + return video_.AddBuffer(buffer); + } + + DVLOG(1) << "Unexpected track number " << track_num; + return false; +} + +WebMClusterParser::Track::Track(int track_num, + base::TimeDelta default_duration) + : track_num_(track_num), + default_duration_(default_duration) { +} + +WebMClusterParser::Track::~Track() {} - if (!queue->empty() && - buffer->GetTimestamp() == queue->back()->GetTimestamp()) { +bool WebMClusterParser::Track::AddBuffer( + const scoped_refptr<StreamParserBuffer>& buffer) { + if (!buffers_.empty() && + buffer->GetTimestamp() == buffers_.back()->GetTimestamp()) { DVLOG(1) << "Got a block timecode that is not strictly monotonically " - << "increasing for track " << track_num; + << "increasing for track " << track_num_; return false; } - queue->push_back(buffer); + 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 = + buffer->GetTimestamp() - delayed_buffer_->GetTimestamp(); + + if (new_duration <= base::TimeDelta()) + return false; + + delayed_buffer_->SetDuration(new_duration); + buffers_.push_back(delayed_buffer_); + + delayed_buffer_ = NULL; + } + + // Place the buffer in delayed buffer slot if we don't know + // its duration. + if (buffer->GetDuration() == kNoTimestamp()) { + delayed_buffer_ = buffer; + return true; + } + + buffers_.push_back(buffer); return true; } +void WebMClusterParser::Track::Reset() { + buffers_.clear(); + delayed_buffer_ = NULL; +} + +void WebMClusterParser::Track::ClearBufferQueue() { + buffers_.clear(); +} + } // namespace media diff --git a/media/webm/webm_cluster_parser.h b/media/webm/webm_cluster_parser.h index 806e276..851f95c 100644 --- a/media/webm/webm_cluster_parser.h +++ b/media/webm/webm_cluster_parser.h @@ -38,10 +38,34 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient { // Returns the number of bytes parsed on success. int Parse(const uint8* buf, int size); - const BufferQueue& audio_buffers() const { return audio_buffers_; } - const BufferQueue& video_buffers() const { return video_buffers_; } + const BufferQueue& audio_buffers() const { return audio_.buffers(); } + const BufferQueue& video_buffers() const { return video_.buffers(); } private: + // Helper class that manages per-track state. + class Track { + public: + Track(int track_num, base::TimeDelta default_duration); + ~Track(); + + int track_num() const { return track_num_; } + const BufferQueue& buffers() const { return buffers_; } + + bool AddBuffer(const scoped_refptr<StreamParserBuffer>& buffer); + + // Clears all buffer state. + void Reset(); + + // Clears only the |buffers_|. + void ClearBufferQueue(); + + private: + int track_num_; + base::TimeDelta default_duration_; + BufferQueue buffers_; + scoped_refptr<StreamParserBuffer> delayed_buffer_; + }; + // WebMParserClient methods. virtual WebMParserClient* OnListStart(int id) OVERRIDE; virtual bool OnListEnd(int id) OVERRIDE; @@ -54,10 +78,6 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient { double timecode_multiplier_; // Multiplier used to convert timecodes into // microseconds. - int audio_track_num_; - base::TimeDelta audio_default_duration_; - int video_track_num_; - base::TimeDelta video_default_duration_; scoped_array<uint8> video_encryption_key_id_; int video_encryption_key_id_size_; @@ -69,8 +89,9 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient { int64 block_duration_; int64 cluster_timecode_; - BufferQueue audio_buffers_; - BufferQueue video_buffers_; + + Track audio_; + Track video_; DISALLOW_IMPLICIT_CONSTRUCTORS(WebMClusterParser); }; diff --git a/media/webm/webm_cluster_parser_unittest.cc b/media/webm/webm_cluster_parser_unittest.cc index d553c1d..cbf3150 100644 --- a/media/webm/webm_cluster_parser_unittest.cc +++ b/media/webm/webm_cluster_parser_unittest.cc @@ -38,11 +38,11 @@ struct BlockInfo { }; const BlockInfo kDefaultBlockInfo[] = { - { kAudioTrackNum, 0, -1, true }, - { kAudioTrackNum, 23, -1, true }, + { kAudioTrackNum, 0, 23, true }, + { kAudioTrackNum, 23, 23, true }, { kVideoTrackNum, kVideoDefaultDurationInMs, kVideoDefaultDurationInMs, true }, - { kAudioTrackNum, 46, -1, true }, + { kAudioTrackNum, 46, 23, false }, { kVideoTrackNum, 2 * kVideoDefaultDurationInMs, kVideoDefaultDurationInMs, true }, }; @@ -242,7 +242,7 @@ TEST_F(WebMClusterParserTest, ParseBlockGroup) { TEST_F(WebMClusterParserTest, ParseSimpleBlockAndBlockGroupMixture) { const BlockInfo kBlockInfo[] = { - { kAudioTrackNum, 0, -1, true }, + { kAudioTrackNum, 0, 23, true }, { kAudioTrackNum, 23, 23, false }, { kVideoTrackNum, kVideoDefaultDurationInMs, kVideoDefaultDurationInMs, true }, diff --git a/media/webm/webm_tracks_parser.cc b/media/webm/webm_tracks_parser.cc index 7aa85d5..d7ab5af 100644 --- a/media/webm/webm_tracks_parser.cc +++ b/media/webm/webm_tracks_parser.cc @@ -6,6 +6,7 @@ #include "base/logging.h" #include "base/string_util.h" +#include "media/base/buffers.h" #include "media/webm/webm_constants.h" #include "media/webm/webm_content_encodings.h" @@ -21,7 +22,9 @@ WebMTracksParser::WebMTracksParser(int64 timecode_scale) track_num_(-1), track_default_duration_(-1), audio_track_num_(-1), - video_track_num_(-1) { + audio_default_duration_(kNoTimestamp()), + video_track_num_(-1), + video_default_duration_(kNoTimestamp()) { } WebMTracksParser::~WebMTracksParser() {} @@ -49,9 +52,9 @@ int WebMTracksParser::Parse(const uint8* buf, int size) { track_num_ = -1; track_default_duration_ = -1; audio_track_num_ = -1; - audio_default_duration_ = base::TimeDelta(); + audio_default_duration_ = kNoTimestamp(); video_track_num_ = -1; - video_default_duration_ = base::TimeDelta(); + video_default_duration_ = kNoTimestamp(); WebMListParser parser(kWebMIdTracks, this); int result = parser.Parse(buf, size); @@ -95,7 +98,7 @@ bool WebMTracksParser::OnListEnd(int id) { return false; } - base::TimeDelta default_duration; + base::TimeDelta default_duration = kNoTimestamp(); if (track_default_duration_ > 0) { // Convert nanoseconds to base::TimeDelta. |