blob: cbf3150b50e180cb3b66663f8ca9d1008fdcb79e [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/logging.h"
#include "media/webm/cluster_builder.h"
#include "media/webm/webm_cluster_parser.h"
#include "media/webm/webm_constants.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::InSequence;
using ::testing::Return;
using ::testing::_;
namespace media {
enum {
kTimecodeScale = 1000000, // Timecode scale for millisecond timestamps.
kAudioTrackNum = 1,
kVideoTrackNum = 2,
kVideoDefaultDurationInMs = 33,
};
static base::TimeDelta kAudioDefaultDuration() {
return kNoTimestamp();
}
static base::TimeDelta kVideoDefaultDuration() {
return base::TimeDelta::FromMilliseconds(kVideoDefaultDurationInMs);
}
struct BlockInfo {
int track_num;
int timestamp;
int duration;
bool use_simple_block;
};
const BlockInfo kDefaultBlockInfo[] = {
{ kAudioTrackNum, 0, 23, true },
{ kAudioTrackNum, 23, 23, true },
{ kVideoTrackNum, kVideoDefaultDurationInMs, kVideoDefaultDurationInMs,
true },
{ kAudioTrackNum, 46, 23, false },
{ kVideoTrackNum, 2 * kVideoDefaultDurationInMs, kVideoDefaultDurationInMs,
true },
};
static scoped_ptr<Cluster> CreateCluster(int timecode,
const BlockInfo* block_info,
int block_count) {
ClusterBuilder cb;
cb.SetClusterTimecode(0);
for (int i = 0; i < block_count; i++) {
uint8 data[] = { 0x00 };
if (block_info[i].use_simple_block) {
cb.AddSimpleBlock(block_info[i].track_num,
block_info[i].timestamp,
0, data, sizeof(data));
continue;
}
CHECK_GE(block_info[i].duration, 0);
cb.AddBlockGroup(block_info[i].track_num,
block_info[i].timestamp,
block_info[i].duration,
0, data, sizeof(data));
}
return cb.Finish();
}
static bool VerifyBuffers(const WebMClusterParser::BufferQueue& audio_buffers,
const WebMClusterParser::BufferQueue& video_buffers,
const BlockInfo* block_info,
int block_count) {
size_t audio_offset = 0;
size_t video_offset = 0;
for (int i = 0; i < block_count; i++) {
const WebMClusterParser::BufferQueue* buffers = NULL;
size_t* offset;
if (block_info[i].track_num == kAudioTrackNum) {
buffers = &audio_buffers;
offset = &audio_offset;
} else if (block_info[i].track_num == kVideoTrackNum) {
buffers = &video_buffers;
offset = &video_offset;
} else {
LOG(ERROR) << "Unexpected track number " << block_info[i].track_num;
return false;
}
if (*offset >= buffers->size())
return false;
scoped_refptr<StreamParserBuffer> buffer = (*buffers)[(*offset)++];
EXPECT_EQ(buffer->GetTimestamp().InMilliseconds(), block_info[i].timestamp);
if (!block_info[i].use_simple_block)
EXPECT_NE(buffer->GetDuration(), kNoTimestamp());
if (buffer->GetDuration() != kNoTimestamp())
EXPECT_EQ(buffer->GetDuration().InMilliseconds(), block_info[i].duration);
}
return true;
}
static bool VerifyBuffers(const scoped_ptr<WebMClusterParser>& parser,
const BlockInfo* block_info,
int block_count) {
return VerifyBuffers(parser->audio_buffers(),
parser->video_buffers(),
block_info,
block_count);
}
static void AppendToEnd(const WebMClusterParser::BufferQueue& src,
WebMClusterParser::BufferQueue* dest) {
for (WebMClusterParser::BufferQueue::const_iterator itr = src.begin();
itr != src.end(); ++itr) {
dest->push_back(*itr);
}
}
class WebMClusterParserTest : public testing::Test {
public:
WebMClusterParserTest()
: parser_(new WebMClusterParser(kTimecodeScale,
kAudioTrackNum,
kAudioDefaultDuration(),
kVideoTrackNum,
kVideoDefaultDuration(),
NULL, 0)) {
}
protected:
scoped_ptr<WebMClusterParser> parser_;
};
TEST_F(WebMClusterParserTest, TestReset) {
InSequence s;
int block_count = arraysize(kDefaultBlockInfo);
scoped_ptr<Cluster> cluster(CreateCluster(0, kDefaultBlockInfo, block_count));
// Send slightly less than the full cluster so all but the last block is
// parsed.
int result = parser_->Parse(cluster->data(), cluster->size() - 1);
EXPECT_GT(result, 0);
EXPECT_LT(result, cluster->size());
ASSERT_TRUE(VerifyBuffers(parser_, kDefaultBlockInfo, 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(result, cluster->size());
ASSERT_TRUE(VerifyBuffers(parser_, kDefaultBlockInfo, block_count));
}
TEST_F(WebMClusterParserTest, ParseClusterWithSingleCall) {
int block_count = arraysize(kDefaultBlockInfo);
scoped_ptr<Cluster> cluster(CreateCluster(0, kDefaultBlockInfo, block_count));
int result = parser_->Parse(cluster->data(), cluster->size());
EXPECT_EQ(cluster->size(), result);
ASSERT_TRUE(VerifyBuffers(parser_, kDefaultBlockInfo, block_count));
}
TEST_F(WebMClusterParserTest, ParseClusterWithMultipleCalls) {
int block_count = arraysize(kDefaultBlockInfo);
scoped_ptr<Cluster> cluster(CreateCluster(0, kDefaultBlockInfo, block_count));
WebMClusterParser::BufferQueue audio_buffers;
WebMClusterParser::BufferQueue video_buffers;
const uint8* data = cluster->data();
int size = cluster->size();
int default_parse_size = 3;
int parse_size = std::min(default_parse_size, size);
while (size > 0) {
int result = parser_->Parse(data, parse_size);
ASSERT_GE(result, 0);
ASSERT_LE(result, parse_size);
if (result == 0) {
// The parser needs more data so increase the parse_size a little.
parse_size += default_parse_size;
parse_size = std::min(parse_size, size);
continue;
}
AppendToEnd(parser_->audio_buffers(), &audio_buffers);
AppendToEnd(parser_->video_buffers(), &video_buffers);
parse_size = default_parse_size;
data += result;
size -= result;
}
ASSERT_TRUE(VerifyBuffers(audio_buffers, video_buffers, kDefaultBlockInfo,
block_count));
}
// Verify that both BlockGroups with the BlockDuration before the Block
// and BlockGroups with the BlockDuration after the Block are supported
// correctly.
// Note: Raw bytes are use here because ClusterBuilder only generates
// one of these scenarios.
TEST_F(WebMClusterParserTest, ParseBlockGroup) {
const BlockInfo kBlockInfo[] = {
{ kAudioTrackNum, 0, 23, false },
{ kVideoTrackNum, 33, 34, false },
};
int block_count = arraysize(kBlockInfo);
const uint8 kClusterData[] = {
0x1F, 0x43, 0xB6, 0x75, 0x9B, // Cluster(size=27)
0xE7, 0x81, 0x00, // Timecode(size=1, value=0)
// BlockGroup with BlockDuration before Block.
0xA0, 0x8A, // BlockGroup(size=10)
0x9B, 0x81, 0x17, // BlockDuration(size=1, value=23)
0xA1, 0x85, 0x81, 0x00, 0x00, 0x00, 0xaa, // Block(size=5, track=1, ts=0)
// BlockGroup with BlockDuration after Block.
0xA0, 0x8A, // BlockGroup(size=10)
0xA1, 0x85, 0x82, 0x00, 0x21, 0x00, 0x55, // Block(size=5, track=2, ts=33)
0x9B, 0x81, 0x22, // BlockDuration(size=1, value=34)
};
const int kClusterSize = sizeof(kClusterData);
int result = parser_->Parse(kClusterData, kClusterSize);
EXPECT_EQ(result, kClusterSize);
ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
}
TEST_F(WebMClusterParserTest, ParseSimpleBlockAndBlockGroupMixture) {
const BlockInfo kBlockInfo[] = {
{ kAudioTrackNum, 0, 23, true },
{ kAudioTrackNum, 23, 23, false },
{ kVideoTrackNum, kVideoDefaultDurationInMs, kVideoDefaultDurationInMs,
true },
{ kAudioTrackNum, 46, 23, false },
{ kVideoTrackNum, 2 * kVideoDefaultDurationInMs, 34,
false },
};
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