Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(743)

Unified Diff: media/formats/webm/webm_cluster_parser_unittest.cc

Issue 238273002: Adds WebMClusterParserTest coverage for duration default/estimation/fallback logic (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: cast to int to compare enum values in COMPILE_ASSERTs Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/formats/webm/webm_cluster_parser.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « media/formats/webm/webm_cluster_parser.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698