diff options
author | wolenetz@chromium.org <wolenetz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-22 20:06:41 +0000 |
---|---|---|
committer | wolenetz@chromium.org <wolenetz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-22 20:06:41 +0000 |
commit | 4485999873892de22a88dcd1db13d079a1141f4b (patch) | |
tree | 077ef3e1e831109058ae29b25c9a770cc9ba1c46 | |
parent | 38c27283f9e6a9b4f7045c408025396b648fe1f3 (diff) | |
download | chromium_src-4485999873892de22a88dcd1db13d079a1141f4b.zip chromium_src-4485999873892de22a88dcd1db13d079a1141f4b.tar.gz chromium_src-4485999873892de22a88dcd1db13d079a1141f4b.tar.bz2 |
Add WebMClusterParserTest coverage for duration default/estimation/fallback logic
WebMClusterParser recently began supporting using TrackEntry
DefaultDurations, estimating missing durations from inter-buffer
timestamp deltas in a track, or falling back to reusing largest
duration so far or a hardcoded frame duration. This change adds
missing test coverage. The test cluster builder is augmented to allow
generation of clusters containing BlockGroups with blocks optionally
missing the BlockDuration field, to support some of these new tests.
R=acolwell@chromium.org
BUG=361786,351166
TEST=All media_unittests pass locally on Linux
Review URL: https://codereview.chromium.org/238273002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@265340 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | media/formats/webm/cluster_builder.cc | 50 | ||||
-rw-r--r-- | media/formats/webm/cluster_builder.h | 5 | ||||
-rw-r--r-- | media/formats/webm/webm_cluster_parser.cc | 7 | ||||
-rw-r--r-- | media/formats/webm/webm_cluster_parser.h | 9 | ||||
-rw-r--r-- | media/formats/webm/webm_cluster_parser_unittest.cc | 324 |
5 files changed, 371 insertions, 24 deletions
diff --git a/media/formats/webm/cluster_builder.cc b/media/formats/webm/cluster_builder.cc index ec95616..bada9e2 100644 --- a/media/formats/webm/cluster_builder.cc +++ b/media/formats/webm/cluster_builder.cc @@ -32,6 +32,13 @@ static const uint8 kBlockGroupHeader[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block(size = 0) }; +static const uint8 kBlockGroupHeaderWithoutBlockDuration[] = { + 0xA0, // BlockGroup ID + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // BlockGroup(size = 0) + 0xA1, // Block ID + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block(size = 0) +}; + enum { kClusterSizeOffset = 4, kClusterTimecodeOffset = 14, @@ -39,6 +46,7 @@ enum { kSimpleBlockSizeOffset = 1, kBlockGroupSizeOffset = 1, + kBlockGroupWithoutBlockDurationBlockSizeOffset = 10, kBlockGroupDurationOffset = 11, kBlockGroupBlockSizeOffset = 20, @@ -85,8 +93,30 @@ void ClusterBuilder::AddSimpleBlock(int track_num, int64 timecode, int flags, void ClusterBuilder::AddBlockGroup(int track_num, int64 timecode, int duration, int flags, const uint8* data, int size) { + AddBlockGroupInternal(track_num, timecode, true, duration, flags, data, size); +} + +void ClusterBuilder::AddBlockGroupWithoutBlockDuration(int track_num, + int64 timecode, + int flags, + const uint8* data, + int size) { + AddBlockGroupInternal(track_num, timecode, false, 0, flags, data, size); +} + + +void ClusterBuilder::AddBlockGroupInternal(int track_num, int64 timecode, + bool include_block_duration, + int duration, int flags, + const uint8* data, int size) { int block_size = size + 4; - int bytes_needed = sizeof(kBlockGroupHeader) + block_size; + int bytes_needed = block_size; + if (include_block_duration) { + bytes_needed += sizeof(kBlockGroupHeader); + } else { + bytes_needed += sizeof(kBlockGroupHeaderWithoutBlockDuration); + } + int block_group_size = bytes_needed - 9; if (bytes_needed > (buffer_size_ - bytes_used_)) @@ -94,11 +124,21 @@ void ClusterBuilder::AddBlockGroup(int track_num, int64 timecode, int duration, uint8* buf = buffer_.get() + bytes_used_; int block_group_offset = bytes_used_; - memcpy(buf, kBlockGroupHeader, sizeof(kBlockGroupHeader)); + if (include_block_duration) { + memcpy(buf, kBlockGroupHeader, sizeof(kBlockGroupHeader)); + UpdateUInt64(block_group_offset + kBlockGroupDurationOffset, duration); + UpdateUInt64(block_group_offset + kBlockGroupBlockSizeOffset, block_size); + buf += sizeof(kBlockGroupHeader); + } else { + memcpy(buf, kBlockGroupHeaderWithoutBlockDuration, + sizeof(kBlockGroupHeaderWithoutBlockDuration)); + UpdateUInt64( + block_group_offset + kBlockGroupWithoutBlockDurationBlockSizeOffset, + block_size); + buf += sizeof(kBlockGroupHeaderWithoutBlockDuration); + } + UpdateUInt64(block_group_offset + kBlockGroupSizeOffset, block_group_size); - UpdateUInt64(block_group_offset + kBlockGroupDurationOffset, duration); - UpdateUInt64(block_group_offset + kBlockGroupBlockSizeOffset, block_size); - buf += sizeof(kBlockGroupHeader); // Make sure the 4 most-significant bits are 0. // http://www.matroska.org/technical/specs/index.html#block_structure diff --git a/media/formats/webm/cluster_builder.h b/media/formats/webm/cluster_builder.h index 98f0f6c..ac5464a 100644 --- a/media/formats/webm/cluster_builder.h +++ b/media/formats/webm/cluster_builder.h @@ -36,10 +36,15 @@ class ClusterBuilder { const uint8* data, int size); void AddBlockGroup(int track_num, int64 timecode, int duration, int flags, const uint8* data, int size); + void AddBlockGroupWithoutBlockDuration(int track_num, int64 timecode, + int flags, const uint8* data, int size); scoped_ptr<Cluster> Finish(); private: + void AddBlockGroupInternal(int track_num, int64 timecode, + bool include_block_duration, int duration, + int flags, const uint8* data, int size); void Reset(); void ExtendBuffer(int bytes_needed); void UpdateUInt64(int offset, int64 value); diff --git a/media/formats/webm/webm_cluster_parser.cc b/media/formats/webm/webm_cluster_parser.cc index 5a3adc3..e206107 100644 --- a/media/formats/webm/webm_cluster_parser.cc +++ b/media/formats/webm/webm_cluster_parser.cc @@ -15,13 +15,6 @@ #include "media/formats/webm/webm_crypto_helpers.h" #include "media/formats/webm/webm_webvtt_parser.h" -// Arbitrarily-chosen numbers to estimate the duration of a buffer if none is -// set and there is not enough information to get a better estimate. -// TODO(wolenetz/acolwell): Parse audio codebook to determine missing audio -// frame durations. See http://crbug.com/351166. -static int kDefaultAudioBufferDurationInMs = 23; // Common 1k samples @44.1kHz -static int kDefaultVideoBufferDurationInMs = 42; // Low 24fps to reduce stalls - namespace media { WebMClusterParser::WebMClusterParser( diff --git a/media/formats/webm/webm_cluster_parser.h b/media/formats/webm/webm_cluster_parser.h index 5657e90..add21f2 100644 --- a/media/formats/webm/webm_cluster_parser.h +++ b/media/formats/webm/webm_cluster_parser.h @@ -24,6 +24,15 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient { public: typedef StreamParser::TrackId TrackId; + // Arbitrarily-chosen numbers to estimate the duration of a buffer if none is + // set and there is not enough information to get a better estimate. + // TODO(wolenetz/acolwell): Parse audio codebook to determine missing audio + // frame durations. See http://crbug.com/351166. + enum { + kDefaultAudioBufferDurationInMs = 23, // Common 1k samples @44.1kHz + kDefaultVideoBufferDurationInMs = 42 // Low 24fps to reduce stalls + }; + private: // Helper class that manages per-track state. class Track { diff --git a/media/formats/webm/webm_cluster_parser_unittest.cc b/media/formats/webm/webm_cluster_parser_unittest.cc index 8b32f5d..7364878 100644 --- a/media/formats/webm/webm_cluster_parser_unittest.cc +++ b/media/formats/webm/webm_cluster_parser_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include <algorithm> +#include <cstdlib> #include "base/bind.h" #include "base/logging.h" @@ -19,16 +20,35 @@ using ::testing::_; namespace media { +typedef WebMTracksParser::TextTracks TextTracks; + enum { kTimecodeScale = 1000000, // Timecode scale for millisecond timestamps. kAudioTrackNum = 1, kVideoTrackNum = 2, kTextTrackNum = 3, + kTestAudioFrameDefaultDurationInMs = 13, + kTestVideoFrameDefaultDurationInMs = 17 }; +COMPILE_ASSERT( + static_cast<int>(kTestAudioFrameDefaultDurationInMs) != + static_cast<int>(WebMClusterParser::kDefaultAudioBufferDurationInMs), + test_default_is_same_as_estimation_fallback_audio_duration); +COMPILE_ASSERT( + static_cast<int>(kTestVideoFrameDefaultDurationInMs) != + static_cast<int>(WebMClusterParser::kDefaultVideoBufferDurationInMs), + test_default_is_same_as_estimation_fallback_video_duration); + struct BlockInfo { int track_num; int timestamp; + + // Negative value is allowed only for block groups (not simple blocks) and + // directs CreateCluster() to exclude BlockDuration entry from the cluster for + // this BlockGroup. The absolute value is used for parser verification. + // For simple blocks, this value must be non-negative, and is used only for + // parser verification. int duration; bool use_simple_block; }; @@ -57,13 +77,20 @@ static scoped_ptr<Cluster> CreateCluster(int timecode, for (int i = 0; i < block_count; i++) { uint8 data[] = { 0x00 }; if (block_info[i].use_simple_block) { + CHECK_GE(block_info[i].duration, 0); cb.AddSimpleBlock(block_info[i].track_num, block_info[i].timestamp, 0, data, sizeof(data)); continue; } - CHECK_GE(block_info[i].duration, 0); + if (block_info[i].duration < 0) { + cb.AddBlockGroupWithoutBlockDuration(block_info[i].track_num, + block_info[i].timestamp, + 0, data, sizeof(data)); + continue; + } + cb.AddBlockGroup(block_info[i].track_num, block_info[i].timestamp, block_info[i].duration, @@ -121,7 +148,8 @@ static bool VerifyBuffers(const WebMClusterParser::BufferQueue& audio_buffers, scoped_refptr<StreamParserBuffer> buffer = (*buffers)[(*offset)++]; EXPECT_EQ(block_info[i].timestamp, buffer->timestamp().InMilliseconds()); - EXPECT_EQ(block_info[i].duration, buffer->duration().InMilliseconds()); + EXPECT_EQ(std::abs(block_info[i].duration), + buffer->duration().InMilliseconds()); EXPECT_EQ(expected_type, buffer->type()); EXPECT_EQ(block_info[i].track_num, buffer->track_id()); } @@ -171,7 +199,8 @@ static bool VerifyTextBuffers( const scoped_refptr<StreamParserBuffer> buffer = *buffer_iter++; EXPECT_EQ(block_info.timestamp, buffer->timestamp().InMilliseconds()); - EXPECT_EQ(block_info.duration, buffer->duration().InMilliseconds()); + EXPECT_EQ(std::abs(block_info.duration), + buffer->duration().InMilliseconds()); EXPECT_EQ(DemuxerStream::TEXT, buffer->type()); EXPECT_EQ(text_track_num, buffer->track_id()); } @@ -203,14 +232,39 @@ class WebMClusterParserTest : public testing::Test { kNoTimestamp(), kVideoTrackNum, kNoTimestamp(), - WebMTracksParser::TextTracks(), + TextTracks(), std::set<int64>(), std::string(), std::string(), LogCB())) {} protected: + void ResetParserToHaveDefaultDurations() { + base::TimeDelta default_audio_duration = base::TimeDelta::FromMilliseconds( + kTestAudioFrameDefaultDurationInMs); + base::TimeDelta default_video_duration = base::TimeDelta::FromMilliseconds( + kTestVideoFrameDefaultDurationInMs); + ASSERT_GE(default_audio_duration, base::TimeDelta()); + ASSERT_GE(default_video_duration, base::TimeDelta()); + ASSERT_NE(kNoTimestamp(), default_audio_duration); + ASSERT_NE(kNoTimestamp(), default_video_duration); + + parser_.reset(new WebMClusterParser(kTimecodeScale, + kAudioTrackNum, + default_audio_duration, + kVideoTrackNum, + default_video_duration, + TextTracks(), + std::set<int64>(), + std::string(), + std::string(), + LogCB())); + } + scoped_ptr<WebMClusterParser> parser_; + + private: + DISALLOW_COPY_AND_ASSIGN(WebMClusterParserTest); }; TEST_F(WebMClusterParserTest, Reset) { @@ -337,7 +391,7 @@ TEST_F(WebMClusterParserTest, IgnoredTracks) { kNoTimestamp(), kVideoTrackNum, kNoTimestamp(), - WebMTracksParser::TextTracks(), + TextTracks(), ignored_tracks, std::string(), std::string(), @@ -371,7 +425,6 @@ TEST_F(WebMClusterParserTest, IgnoredTracks) { } TEST_F(WebMClusterParserTest, ParseTextTracks) { - typedef WebMTracksParser::TextTracks TextTracks; TextTracks text_tracks; text_tracks.insert(std::make_pair(TextTracks::key_type(kTextTrackNum), @@ -409,8 +462,7 @@ TEST_F(WebMClusterParserTest, ParseTextTracks) { } TEST_F(WebMClusterParserTest, TextTracksSimpleBlock) { - typedef WebMTracksParser::TextTracks TextTracks; - WebMTracksParser::TextTracks text_tracks; + TextTracks text_tracks; text_tracks.insert(std::make_pair(TextTracks::key_type(kTextTrackNum), TextTrackConfig(kTextSubtitles, "", "", @@ -440,7 +492,6 @@ TEST_F(WebMClusterParserTest, TextTracksSimpleBlock) { } TEST_F(WebMClusterParserTest, ParseMultipleTextTracks) { - typedef WebMTracksParser::TextTracks TextTracks; TextTracks text_tracks; const int kSubtitleTextTrackNum = kTextTrackNum; @@ -489,7 +540,7 @@ TEST_F(WebMClusterParserTest, ParseMultipleTextTracks) { text_map.begin(); itr != text_map.end(); ++itr) { - const WebMTracksParser::TextTracks::const_iterator find_result = + const TextTracks::const_iterator find_result = text_tracks.find(itr->first); ASSERT_TRUE(find_result != text_tracks.end()); ASSERT_TRUE(VerifyTextBuffers(parser_, kInputBlockInfo, input_block_count, @@ -505,7 +556,7 @@ TEST_F(WebMClusterParserTest, ParseEncryptedBlock) { kNoTimestamp(), kVideoTrackNum, kNoTimestamp(), - WebMTracksParser::TextTracks(), + TextTracks(), std::set<int64>(), std::string(), "video_key_id", @@ -526,7 +577,7 @@ TEST_F(WebMClusterParserTest, ParseBadEncryptedBlock) { kNoTimestamp(), kVideoTrackNum, kNoTimestamp(), - WebMTracksParser::TextTracks(), + TextTracks(), std::set<int64>(), std::string(), "video_key_id", @@ -552,4 +603,253 @@ TEST_F(WebMClusterParserTest, ParseInvalidUnknownButActuallyZeroSizedCluster) { EXPECT_EQ(-1, parser_->Parse(kBuffer, sizeof(kBuffer))); } +TEST_F(WebMClusterParserTest, ParseInvalidTextBlockGroupWithoutDuration) { + // Text track frames must have explicitly specified BlockGroup BlockDurations. + TextTracks text_tracks; + + text_tracks.insert(std::make_pair(TextTracks::key_type(kTextTrackNum), + TextTrackConfig(kTextSubtitles, "", "", + ""))); + + parser_.reset(new WebMClusterParser(kTimecodeScale, + kAudioTrackNum, + kNoTimestamp(), + kVideoTrackNum, + kNoTimestamp(), + text_tracks, + std::set<int64>(), + std::string(), + std::string(), + LogCB())); + + const BlockInfo kBlockInfo[] = { + { kTextTrackNum, 33, -42, false }, + }; + int block_count = arraysize(kBlockInfo); + scoped_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count)); + int result = parser_->Parse(cluster->data(), cluster->size()); + EXPECT_LT(result, 0); +} + +TEST_F(WebMClusterParserTest, ParseWithDefaultDurationsSimpleBlocks) { + InSequence s; + ResetParserToHaveDefaultDurations(); + + EXPECT_LT(kTestAudioFrameDefaultDurationInMs, 23); + EXPECT_LT(kTestVideoFrameDefaultDurationInMs, 33); + + const BlockInfo kBlockInfo[] = { + { kAudioTrackNum, 0, kTestAudioFrameDefaultDurationInMs, true }, + { kAudioTrackNum, 23, kTestAudioFrameDefaultDurationInMs, true }, + { kVideoTrackNum, 33, kTestVideoFrameDefaultDurationInMs, true }, + { kAudioTrackNum, 46, kTestAudioFrameDefaultDurationInMs, true }, + { kVideoTrackNum, 67, kTestVideoFrameDefaultDurationInMs, true }, + { kAudioTrackNum, 69, kTestAudioFrameDefaultDurationInMs, true }, + { kVideoTrackNum, 100, kTestVideoFrameDefaultDurationInMs, true }, + }; + + int block_count = arraysize(kBlockInfo); + scoped_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count)); + + // Send slightly less than the full cluster so all but the last block is + // parsed. Though all the blocks are simple blocks, none should be held aside + // for duration estimation prior to end of cluster detection because all the + // tracks have DefaultDurations. + int result = parser_->Parse(cluster->data(), cluster->size() - 1); + EXPECT_GT(result, 0); + EXPECT_LT(result, cluster->size()); + ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count - 1)); + + parser_->Reset(); + + // Now parse a whole cluster to verify that all the blocks will get parsed. + result = parser_->Parse(cluster->data(), cluster->size()); + EXPECT_EQ(cluster->size(), result); + ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count)); +} + +TEST_F(WebMClusterParserTest, ParseWithoutAnyDurationsSimpleBlocks) { + InSequence s; + + // Absent DefaultDuration information, SimpleBlock durations are derived from + // inter-buffer track timestamp delta if within the cluster, and are estimated + // as the lowest non-zero duration seen so far if the last buffer in the track + // in the cluster (independently for each track in the cluster). + const BlockInfo kBlockInfo1[] = { + { kAudioTrackNum, 0, 23, true }, + { kAudioTrackNum, 23, 22, true }, + { kVideoTrackNum, 33, 33, true }, + { kAudioTrackNum, 45, 23, true }, + { kVideoTrackNum, 66, 34, true }, + { kAudioTrackNum, 68, 22, true }, // Estimated from minimum audio dur + { kVideoTrackNum, 100, 33, true }, // Estimated from minimum video dur + }; + + int block_count1 = arraysize(kBlockInfo1); + scoped_ptr<Cluster> cluster1(CreateCluster(0, kBlockInfo1, block_count1)); + + // Send slightly less than the first full cluster so all but the last video + // block is parsed. Verify the last fully parsed audio and video buffer are + // both missing from the result (parser should hold them aside for duration + // estimation prior to end of cluster detection in the absence of + // DefaultDurations.) + int result = parser_->Parse(cluster1->data(), cluster1->size() - 1); + EXPECT_GT(result, 0); + EXPECT_LT(result, cluster1->size()); + ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo1, block_count1 - 3)); + EXPECT_EQ(3UL, parser_->GetAudioBuffers().size()); + EXPECT_EQ(1UL, parser_->GetVideoBuffers().size()); + + parser_->Reset(); + + // Now parse the full first cluster and verify all the blocks are parsed. + result = parser_->Parse(cluster1->data(), cluster1->size()); + EXPECT_EQ(cluster1->size(), result); + ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo1, block_count1)); + + // Verify that the estimated frame duration is tracked across clusters for + // each track. + const BlockInfo kBlockInfo2[] = { + { kAudioTrackNum, 200, 22, true }, // Estimate carries over across clusters + { kVideoTrackNum, 201, 33, true }, // Estimate carries over across clusters + }; + + int block_count2 = arraysize(kBlockInfo2); + scoped_ptr<Cluster> cluster2(CreateCluster(0, kBlockInfo2, block_count2)); + result = parser_->Parse(cluster2->data(), cluster2->size()); + EXPECT_EQ(cluster2->size(), result); + ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo2, block_count2)); +} + +TEST_F(WebMClusterParserTest, ParseWithoutAnyDurationsBlockGroups) { + InSequence s; + + // Absent DefaultDuration and BlockDuration information, BlockGroup block + // durations are derived from inter-buffer track timestamp delta if within the + // cluster, and are estimated as the lowest non-zero duration seen so far if + // the last buffer in the track in the cluster (independently for each track + // in the cluster). + const BlockInfo kBlockInfo1[] = { + { kAudioTrackNum, 0, -23, false }, + { kAudioTrackNum, 23, -22, false }, + { kVideoTrackNum, 33, -33, false }, + { kAudioTrackNum, 45, -23, false }, + { kVideoTrackNum, 66, -34, false }, + { kAudioTrackNum, 68, -22, false }, // Estimated from minimum audio dur + { kVideoTrackNum, 100, -33, false }, // Estimated from minimum video dur + }; + + int block_count1 = arraysize(kBlockInfo1); + scoped_ptr<Cluster> cluster1(CreateCluster(0, kBlockInfo1, block_count1)); + + // Send slightly less than the first full cluster so all but the last video + // block is parsed. Verify the last fully parsed audio and video buffer are + // both missing from the result (parser should hold them aside for duration + // estimation prior to end of cluster detection in the absence of + // DefaultDurations.) + int result = parser_->Parse(cluster1->data(), cluster1->size() - 1); + EXPECT_GT(result, 0); + EXPECT_LT(result, cluster1->size()); + ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo1, block_count1 - 3)); + EXPECT_EQ(3UL, parser_->GetAudioBuffers().size()); + EXPECT_EQ(1UL, parser_->GetVideoBuffers().size()); + + parser_->Reset(); + + // Now parse the full first cluster and verify all the blocks are parsed. + result = parser_->Parse(cluster1->data(), cluster1->size()); + EXPECT_EQ(cluster1->size(), result); + ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo1, block_count1)); + + // Verify that the estimated frame duration is tracked across clusters for + // each track. + const BlockInfo kBlockInfo2[] = { + { kAudioTrackNum, 200, -22, false }, + { kVideoTrackNum, 201, -33, false }, + }; + + int block_count2 = arraysize(kBlockInfo2); + scoped_ptr<Cluster> cluster2(CreateCluster(0, kBlockInfo2, block_count2)); + result = parser_->Parse(cluster2->data(), cluster2->size()); + EXPECT_EQ(cluster2->size(), result); + ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo2, block_count2)); +} + +// TODO(wolenetz): Is parser behavior correct? See http://crbug.com/363433. +TEST_F(WebMClusterParserTest, + ParseWithDefaultDurationsBlockGroupsWithoutDurations) { + InSequence s; + ResetParserToHaveDefaultDurations(); + + EXPECT_LT(kTestAudioFrameDefaultDurationInMs, 23); + EXPECT_LT(kTestVideoFrameDefaultDurationInMs, 33); + + const BlockInfo kBlockInfo[] = { + { kAudioTrackNum, 0, -kTestAudioFrameDefaultDurationInMs, false }, + { kAudioTrackNum, 23, -kTestAudioFrameDefaultDurationInMs, false }, + { kVideoTrackNum, 33, -kTestVideoFrameDefaultDurationInMs, false }, + { kAudioTrackNum, 46, -kTestAudioFrameDefaultDurationInMs, false }, + { kVideoTrackNum, 67, -kTestVideoFrameDefaultDurationInMs, false }, + { kAudioTrackNum, 69, -kTestAudioFrameDefaultDurationInMs, false }, + { kVideoTrackNum, 100, -kTestVideoFrameDefaultDurationInMs, false }, + }; + + int block_count = arraysize(kBlockInfo); + scoped_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count)); + + // Send slightly less than the full cluster so all but the last block is + // parsed. None should be held aside for duration estimation prior to end of + // cluster detection because all the tracks have DefaultDurations. + int result = parser_->Parse(cluster->data(), cluster->size() - 1); + EXPECT_GT(result, 0); + EXPECT_LT(result, cluster->size()); + ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count - 1)); + + parser_->Reset(); + + // Now parse a whole cluster to verify that all the blocks will get parsed. + result = parser_->Parse(cluster->data(), cluster->size()); + EXPECT_EQ(cluster->size(), result); + ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count)); +} + +TEST_F(WebMClusterParserTest, + ParseDegenerateClusterYieldsHardcodedEstimatedDurations) { + const BlockInfo kBlockInfo[] = { + { + kAudioTrackNum, + 0, + WebMClusterParser::kDefaultAudioBufferDurationInMs, + true + }, { + kVideoTrackNum, + 0, + WebMClusterParser::kDefaultVideoBufferDurationInMs, + true + }, + }; + + int block_count = arraysize(kBlockInfo); + scoped_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count)); + int result = parser_->Parse(cluster->data(), cluster->size()); + EXPECT_EQ(cluster->size(), result); + ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count)); +} + +TEST_F(WebMClusterParserTest, + ParseDegenerateClusterWithDefaultDurationsYieldsDefaultDurations) { + ResetParserToHaveDefaultDurations(); + + const BlockInfo kBlockInfo[] = { + { kAudioTrackNum, 0, kTestAudioFrameDefaultDurationInMs, true }, + { kVideoTrackNum, 0, kTestVideoFrameDefaultDurationInMs, true }, + }; + + int block_count = arraysize(kBlockInfo); + scoped_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count)); + int result = parser_->Parse(cluster->data(), cluster->size()); + EXPECT_EQ(cluster->size(), result); + ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count)); +} + } // namespace media |