| 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..7364878dfdc2d38d8bca27d12f8e95926a12adcf 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
|
|
|