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 |