Chromium Code Reviews| Index: media/formats/webm/webm_cluster_parser_unittest.cc |
| diff --git a/media/formats/webm/webm_cluster_parser_unittest.cc b/media/formats/webm/webm_cluster_parser_unittest.cc |
| index 8b32f5defbd5ee6f1d46dfa806525b0b1df5899c..3f8520d45c91218256738d7b1ff6b859f3a12857 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,33 @@ using ::testing::_; |
| namespace media { |
| +typedef WebMTracksParser::TextTracks TextTracks; |
|
acolwell GONE FROM CHROMIUM
2014/04/15 00:32:24
nit: How about "using" instead of a typedef?
wolenetz
2014/04/15 01:49:12
using only worked as an alias. Keeping as typedef.
|
| + |
| enum { |
| kTimecodeScale = 1000000, // Timecode scale for millisecond timestamps. |
| kAudioTrackNum = 1, |
| kVideoTrackNum = 2, |
| kTextTrackNum = 3, |
| + kTestAudioFrameDefaultDurationInMs = 13, |
| + kTestVideoFrameDefaultDurationInMs = 17 |
| }; |
| +COMPILE_ASSERT(kTestAudioFrameDefaultDurationInMs != |
| + WebMClusterParser::kDefaultAudioBufferDurationInMs, |
| + test_default_is_same_as_estimation_fallback_audio_duration); |
| +COMPILE_ASSERT(kTestVideoFrameDefaultDurationInMs != |
| + 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 +75,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 +146,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 +197,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 +230,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 +389,7 @@ TEST_F(WebMClusterParserTest, IgnoredTracks) { |
| kNoTimestamp(), |
| kVideoTrackNum, |
| kNoTimestamp(), |
| - WebMTracksParser::TextTracks(), |
| + TextTracks(), |
| ignored_tracks, |
| std::string(), |
| std::string(), |
| @@ -371,7 +423,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 +460,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 +490,6 @@ TEST_F(WebMClusterParserTest, TextTracksSimpleBlock) { |
| } |
| TEST_F(WebMClusterParserTest, ParseMultipleTextTracks) { |
| - typedef WebMTracksParser::TextTracks TextTracks; |
| TextTracks text_tracks; |
| const int kSubtitleTextTrackNum = kTextTrackNum; |
| @@ -489,7 +538,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 +554,7 @@ TEST_F(WebMClusterParserTest, ParseEncryptedBlock) { |
| kNoTimestamp(), |
| kVideoTrackNum, |
| kNoTimestamp(), |
| - WebMTracksParser::TextTracks(), |
| + TextTracks(), |
| std::set<int64>(), |
| std::string(), |
| "video_key_id", |
| @@ -526,7 +575,7 @@ TEST_F(WebMClusterParserTest, ParseBadEncryptedBlock) { |
| kNoTimestamp(), |
| kVideoTrackNum, |
| kNoTimestamp(), |
| - WebMTracksParser::TextTracks(), |
| + TextTracks(), |
| std::set<int64>(), |
| std::string(), |
| "video_key_id", |
| @@ -552,4 +601,255 @@ 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 highest 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, 23, true }, // Estimated from maximum audio dur |
| + { kVideoTrackNum, 100, 34, true }, // Estimated from maximum 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, 23, true }, // Estimate carries over across clusters |
| + { kVideoTrackNum, 201, 34, 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 highest duration seen so far if the last |
|
acolwell GONE FROM CHROMIUM
2014/04/15 00:32:24
Doesn't this become lowest w/ Dale's change?
wolenetz
2014/04/15 01:49:12
Correct. In patch set 3, I've rebased onto his CL
|
| + // 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, -23, false }, // Estimated from maximum audio dur |
| + { kVideoTrackNum, 100, -34, false }, // Estimated from maximum 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, -23, false }, |
| + { kVideoTrackNum, 201, -34, 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(); |
| + EXPECT_LT(kTestAudioFrameDefaultDurationInMs, 23); |
| + EXPECT_LT(kTestVideoFrameDefaultDurationInMs, 33); |
| + |
| + 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 |