You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
824 lines
31 KiB
824 lines
31 KiB
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
|
|
//
|
|
// Use of this source code is governed by a BSD-style license
|
|
// that can be found in the LICENSE file in the root of the source
|
|
// tree. An additional intellectual property rights grant can be found
|
|
// in the file PATENTS. All contributing project authors may
|
|
// be found in the AUTHORS file in the root of the source tree.
|
|
#include "gtest/gtest.h"
|
|
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <iomanip>
|
|
#include <string>
|
|
|
|
#include "common/hdr_util.h"
|
|
#include "mkvparser/mkvparser.h"
|
|
#include "mkvparser/mkvreader.h"
|
|
#include "testing/test_util.h"
|
|
|
|
using mkvparser::AudioTrack;
|
|
using mkvparser::Block;
|
|
using mkvparser::BlockEntry;
|
|
using mkvparser::BlockGroup;
|
|
using mkvparser::Cluster;
|
|
using mkvparser::CuePoint;
|
|
using mkvparser::Cues;
|
|
using mkvparser::MkvReader;
|
|
using mkvparser::Segment;
|
|
using mkvparser::SegmentInfo;
|
|
using mkvparser::Track;
|
|
using mkvparser::Tracks;
|
|
using mkvparser::VideoTrack;
|
|
|
|
namespace test {
|
|
|
|
// Base class containing boiler plate stuff.
|
|
class ParserTest : public testing::Test {
|
|
public:
|
|
ParserTest() : is_reader_open_(false), segment_(NULL) {
|
|
memset(dummy_data_, -1, kFrameLength);
|
|
memset(gold_frame_, 0, kFrameLength);
|
|
}
|
|
|
|
virtual ~ParserTest() {
|
|
CloseReader();
|
|
if (segment_ != NULL) {
|
|
delete segment_;
|
|
segment_ = NULL;
|
|
}
|
|
}
|
|
|
|
void CloseReader() {
|
|
if (is_reader_open_) {
|
|
reader_.Close();
|
|
}
|
|
is_reader_open_ = false;
|
|
}
|
|
|
|
bool CreateAndLoadSegment(const std::string& filename,
|
|
int expected_doc_type_ver) {
|
|
filename_ = GetTestFilePath(filename);
|
|
if (reader_.Open(filename_.c_str())) {
|
|
return false;
|
|
}
|
|
is_reader_open_ = true;
|
|
pos_ = 0;
|
|
mkvparser::EBMLHeader ebml_header;
|
|
ebml_header.Parse(&reader_, pos_);
|
|
EXPECT_EQ(1, ebml_header.m_version);
|
|
EXPECT_EQ(1, ebml_header.m_readVersion);
|
|
EXPECT_STREQ("webm", ebml_header.m_docType);
|
|
EXPECT_EQ(expected_doc_type_ver, ebml_header.m_docTypeVersion);
|
|
EXPECT_EQ(2, ebml_header.m_docTypeReadVersion);
|
|
|
|
if (mkvparser::Segment::CreateInstance(&reader_, pos_, segment_)) {
|
|
return false;
|
|
}
|
|
return !HasFailure() && segment_->Load() >= 0;
|
|
}
|
|
|
|
bool CreateAndLoadSegment(const std::string& filename) {
|
|
return CreateAndLoadSegment(filename, 4);
|
|
}
|
|
|
|
void CreateSegmentNoHeaderChecks(const std::string& filename) {
|
|
filename_ = GetTestFilePath(filename);
|
|
ASSERT_NE(0u, filename_.length());
|
|
ASSERT_EQ(0, reader_.Open(filename_.c_str()));
|
|
mkvparser::EBMLHeader ebml_header;
|
|
ASSERT_EQ(0, ebml_header.Parse(&reader_, pos_));
|
|
ASSERT_EQ(0, mkvparser::Segment::CreateInstance(&reader_, pos_, segment_));
|
|
}
|
|
|
|
void CompareBlockContents(const Cluster* const cluster,
|
|
const Block* const block, std::uint64_t timestamp,
|
|
int track_number, bool is_key, int frame_count) {
|
|
ASSERT_TRUE(block != NULL);
|
|
EXPECT_EQ(track_number, block->GetTrackNumber());
|
|
EXPECT_EQ(static_cast<long long>(timestamp), block->GetTime(cluster));
|
|
EXPECT_EQ(is_key, block->IsKey());
|
|
EXPECT_EQ(frame_count, block->GetFrameCount());
|
|
const Block::Frame& frame = block->GetFrame(0);
|
|
EXPECT_EQ(kFrameLength, frame.len);
|
|
std::memset(dummy_data_, -1, kFrameLength);
|
|
frame.Read(&reader_, dummy_data_);
|
|
EXPECT_EQ(0, std::memcmp(gold_frame_, dummy_data_, kFrameLength));
|
|
}
|
|
|
|
void CompareCuePointContents(const Track* const track,
|
|
const CuePoint* const cue_point,
|
|
std::uint64_t timestamp, int track_number,
|
|
std::uint64_t pos) {
|
|
ASSERT_TRUE(cue_point != NULL);
|
|
EXPECT_EQ(static_cast<long long>(timestamp), cue_point->GetTime(segment_));
|
|
const CuePoint::TrackPosition* const track_position =
|
|
cue_point->Find(track);
|
|
EXPECT_EQ(track_number, track_position->m_track);
|
|
EXPECT_EQ(static_cast<long long>(pos), track_position->m_pos);
|
|
}
|
|
|
|
protected:
|
|
MkvReader reader_;
|
|
bool is_reader_open_;
|
|
Segment* segment_;
|
|
std::string filename_;
|
|
long long pos_;
|
|
std::uint8_t dummy_data_[kFrameLength];
|
|
std::uint8_t gold_frame_[kFrameLength];
|
|
};
|
|
|
|
TEST_F(ParserTest, SegmentInfo) {
|
|
ASSERT_TRUE(CreateAndLoadSegment("segment_info.webm"));
|
|
const SegmentInfo* const info = segment_->GetInfo();
|
|
EXPECT_EQ(kTimeCodeScale, info->GetTimeCodeScale());
|
|
EXPECT_STREQ(kAppString, info->GetMuxingAppAsUTF8());
|
|
EXPECT_STREQ(kAppString, info->GetWritingAppAsUTF8());
|
|
}
|
|
|
|
TEST_F(ParserTest, TrackEntries) {
|
|
ASSERT_TRUE(CreateAndLoadSegment("tracks.webm"));
|
|
const Tracks* const tracks = segment_->GetTracks();
|
|
const unsigned int kTracksCount = 2;
|
|
EXPECT_EQ(kTracksCount, tracks->GetTracksCount());
|
|
for (int i = 0; i < 2; ++i) {
|
|
const Track* const track = tracks->GetTrackByIndex(i);
|
|
ASSERT_TRUE(track != NULL);
|
|
EXPECT_STREQ(kTrackName, track->GetNameAsUTF8());
|
|
if (track->GetType() == Track::kVideo) {
|
|
const VideoTrack* const video_track =
|
|
dynamic_cast<const VideoTrack*>(track);
|
|
EXPECT_EQ(kWidth, static_cast<int>(video_track->GetWidth()));
|
|
EXPECT_EQ(kHeight, static_cast<int>(video_track->GetHeight()));
|
|
EXPECT_STREQ(kVP8CodecId, video_track->GetCodecId());
|
|
EXPECT_DOUBLE_EQ(kVideoFrameRate, video_track->GetFrameRate());
|
|
const unsigned int kTrackUid = 1;
|
|
EXPECT_EQ(kTrackUid, video_track->GetUid());
|
|
} else if (track->GetType() == Track::kAudio) {
|
|
const AudioTrack* const audio_track =
|
|
dynamic_cast<const AudioTrack*>(track);
|
|
EXPECT_EQ(kSampleRate, audio_track->GetSamplingRate());
|
|
EXPECT_EQ(kChannels, audio_track->GetChannels());
|
|
EXPECT_EQ(kBitDepth, audio_track->GetBitDepth());
|
|
EXPECT_STREQ(kVorbisCodecId, audio_track->GetCodecId());
|
|
const unsigned int kTrackUid = 2;
|
|
EXPECT_EQ(kTrackUid, audio_track->GetUid());
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(ParserTest, SimpleBlock) {
|
|
ASSERT_TRUE(CreateAndLoadSegment("simple_block.webm"));
|
|
const unsigned int kTracksCount = 1;
|
|
EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount());
|
|
|
|
// Get the cluster
|
|
const Cluster* cluster = segment_->GetFirst();
|
|
ASSERT_TRUE(cluster != NULL);
|
|
EXPECT_FALSE(cluster->EOS());
|
|
|
|
// Get the first block
|
|
const BlockEntry* block_entry;
|
|
EXPECT_EQ(0, cluster->GetFirst(block_entry));
|
|
ASSERT_TRUE(block_entry != NULL);
|
|
EXPECT_FALSE(block_entry->EOS());
|
|
CompareBlockContents(cluster, block_entry->GetBlock(), 0, kVideoTrackNumber,
|
|
false, 1);
|
|
|
|
// Get the second block
|
|
EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry));
|
|
ASSERT_TRUE(block_entry != NULL);
|
|
EXPECT_FALSE(block_entry->EOS());
|
|
CompareBlockContents(cluster, block_entry->GetBlock(), 2000000,
|
|
kVideoTrackNumber, false, 1);
|
|
|
|
// End of Stream
|
|
EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry));
|
|
ASSERT_EQ(NULL, block_entry);
|
|
cluster = segment_->GetNext(cluster);
|
|
EXPECT_TRUE(cluster->EOS());
|
|
}
|
|
|
|
TEST_F(ParserTest, MultipleClusters) {
|
|
ASSERT_TRUE(CreateAndLoadSegment("force_new_cluster.webm"));
|
|
const unsigned int kTracksCount = 1;
|
|
EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount());
|
|
|
|
// Get the first cluster
|
|
const Cluster* cluster = segment_->GetFirst();
|
|
ASSERT_TRUE(cluster != NULL);
|
|
EXPECT_FALSE(cluster->EOS());
|
|
|
|
// Get the first block
|
|
const BlockEntry* block_entry;
|
|
EXPECT_EQ(0, cluster->GetFirst(block_entry));
|
|
ASSERT_TRUE(block_entry != NULL);
|
|
EXPECT_FALSE(block_entry->EOS());
|
|
CompareBlockContents(cluster, block_entry->GetBlock(), 0, kVideoTrackNumber,
|
|
false, 1);
|
|
|
|
// Get the second cluster
|
|
EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry));
|
|
EXPECT_EQ(NULL, block_entry);
|
|
cluster = segment_->GetNext(cluster);
|
|
ASSERT_TRUE(cluster != NULL);
|
|
EXPECT_FALSE(cluster->EOS());
|
|
|
|
// Get the second block
|
|
EXPECT_EQ(0, cluster->GetFirst(block_entry));
|
|
ASSERT_TRUE(block_entry != NULL);
|
|
EXPECT_FALSE(block_entry->EOS());
|
|
CompareBlockContents(cluster, block_entry->GetBlock(), 2000000,
|
|
kVideoTrackNumber, false, 1);
|
|
|
|
// Get the third block
|
|
EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry));
|
|
ASSERT_TRUE(block_entry != NULL);
|
|
EXPECT_FALSE(block_entry->EOS());
|
|
CompareBlockContents(cluster, block_entry->GetBlock(), 4000000,
|
|
kVideoTrackNumber, false, 1);
|
|
|
|
// Get the third cluster
|
|
EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry));
|
|
EXPECT_EQ(NULL, block_entry);
|
|
cluster = segment_->GetNext(cluster);
|
|
ASSERT_TRUE(cluster != NULL);
|
|
EXPECT_FALSE(cluster->EOS());
|
|
|
|
// Get the fourth block
|
|
EXPECT_EQ(0, cluster->GetFirst(block_entry));
|
|
ASSERT_TRUE(block_entry != NULL);
|
|
EXPECT_FALSE(block_entry->EOS());
|
|
CompareBlockContents(cluster, block_entry->GetBlock(), 6000000,
|
|
kVideoTrackNumber, false, 1);
|
|
|
|
// End of Stream
|
|
EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry));
|
|
EXPECT_EQ(NULL, block_entry);
|
|
cluster = segment_->GetNext(cluster);
|
|
EXPECT_TRUE(cluster->EOS());
|
|
}
|
|
|
|
TEST_F(ParserTest, BlockGroup) {
|
|
ASSERT_TRUE(CreateAndLoadSegment("metadata_block.webm"));
|
|
const unsigned int kTracksCount = 1;
|
|
EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount());
|
|
|
|
// Get the cluster
|
|
const Cluster* cluster = segment_->GetFirst();
|
|
ASSERT_TRUE(cluster != NULL);
|
|
EXPECT_FALSE(cluster->EOS());
|
|
|
|
// Get the first block
|
|
const BlockEntry* block_entry;
|
|
EXPECT_EQ(0, cluster->GetFirst(block_entry));
|
|
ASSERT_TRUE(block_entry != NULL);
|
|
EXPECT_FALSE(block_entry->EOS());
|
|
EXPECT_EQ(BlockEntry::Kind::kBlockGroup, block_entry->GetKind());
|
|
const BlockGroup* block_group = static_cast<const BlockGroup*>(block_entry);
|
|
EXPECT_EQ(2, block_group->GetDurationTimeCode());
|
|
CompareBlockContents(cluster, block_group->GetBlock(), 0,
|
|
kMetadataTrackNumber, true, 1);
|
|
|
|
// Get the second block
|
|
EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry));
|
|
ASSERT_TRUE(block_entry != NULL);
|
|
EXPECT_FALSE(block_entry->EOS());
|
|
EXPECT_EQ(BlockEntry::Kind::kBlockGroup, block_entry->GetKind());
|
|
block_group = static_cast<const BlockGroup*>(block_entry);
|
|
EXPECT_EQ(6, block_group->GetDurationTimeCode());
|
|
CompareBlockContents(cluster, block_group->GetBlock(), 2000000,
|
|
kMetadataTrackNumber, true, 1);
|
|
|
|
// End of Stream
|
|
EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry));
|
|
EXPECT_EQ(NULL, block_entry);
|
|
cluster = segment_->GetNext(cluster);
|
|
EXPECT_TRUE(cluster->EOS());
|
|
}
|
|
|
|
TEST_F(ParserTest, Cues) {
|
|
ASSERT_TRUE(CreateAndLoadSegment("output_cues.webm"));
|
|
const unsigned int kTracksCount = 1;
|
|
EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount());
|
|
|
|
const Track* const track = segment_->GetTracks()->GetTrackByIndex(0);
|
|
const Cues* const cues = segment_->GetCues();
|
|
ASSERT_TRUE(cues != NULL);
|
|
while (!cues->DoneParsing()) {
|
|
cues->LoadCuePoint();
|
|
}
|
|
EXPECT_EQ(3, cues->GetCount());
|
|
|
|
// Get first Cue Point
|
|
const CuePoint* cue_point = cues->GetFirst();
|
|
CompareCuePointContents(track, cue_point, 0, kVideoTrackNumber, 206);
|
|
|
|
// Get second Cue Point
|
|
cue_point = cues->GetNext(cue_point);
|
|
CompareCuePointContents(track, cue_point, 6000000, kVideoTrackNumber, 269);
|
|
|
|
// Get third (also last) Cue Point
|
|
cue_point = cues->GetNext(cue_point);
|
|
const CuePoint* last_cue_point = cues->GetLast();
|
|
EXPECT_TRUE(cue_point == last_cue_point);
|
|
CompareCuePointContents(track, cue_point, 4000000, kVideoTrackNumber, 269);
|
|
|
|
EXPECT_TRUE(ValidateCues(segment_, &reader_));
|
|
}
|
|
|
|
TEST_F(ParserTest, CuesBeforeClusters) {
|
|
ASSERT_TRUE(CreateAndLoadSegment("cues_before_clusters.webm"));
|
|
const unsigned int kTracksCount = 1;
|
|
EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount());
|
|
|
|
const Track* const track = segment_->GetTracks()->GetTrackByIndex(0);
|
|
const Cues* const cues = segment_->GetCues();
|
|
ASSERT_TRUE(cues != NULL);
|
|
while (!cues->DoneParsing()) {
|
|
cues->LoadCuePoint();
|
|
}
|
|
EXPECT_EQ(2, cues->GetCount());
|
|
|
|
// Get first Cue Point
|
|
const CuePoint* cue_point = cues->GetFirst();
|
|
CompareCuePointContents(track, cue_point, 0, kVideoTrackNumber, 238);
|
|
|
|
// Get second (also last) Cue Point
|
|
cue_point = cues->GetNext(cue_point);
|
|
const CuePoint* last_cue_point = cues->GetLast();
|
|
EXPECT_TRUE(cue_point == last_cue_point);
|
|
CompareCuePointContents(track, cue_point, 6000000, kVideoTrackNumber, 301);
|
|
|
|
EXPECT_TRUE(ValidateCues(segment_, &reader_));
|
|
}
|
|
|
|
TEST_F(ParserTest, CuesTrackNumber) {
|
|
ASSERT_TRUE(CreateAndLoadSegment("set_cues_track_number.webm"));
|
|
const unsigned int kTracksCount = 1;
|
|
EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount());
|
|
|
|
const Track* const track = segment_->GetTracks()->GetTrackByIndex(0);
|
|
const Cues* const cues = segment_->GetCues();
|
|
ASSERT_TRUE(cues != NULL);
|
|
while (!cues->DoneParsing()) {
|
|
cues->LoadCuePoint();
|
|
}
|
|
EXPECT_EQ(2, cues->GetCount());
|
|
|
|
// Get first Cue Point
|
|
const CuePoint* cue_point = cues->GetFirst();
|
|
CompareCuePointContents(track, cue_point, 0, 10, 206);
|
|
|
|
// Get second (also last) Cue Point
|
|
cue_point = cues->GetNext(cue_point);
|
|
const CuePoint* last_cue_point = cues->GetLast();
|
|
EXPECT_TRUE(cue_point == last_cue_point);
|
|
CompareCuePointContents(track, cue_point, 6000000, 10, 269);
|
|
|
|
EXPECT_TRUE(ValidateCues(segment_, &reader_));
|
|
}
|
|
|
|
TEST_F(ParserTest, Opus) {
|
|
ASSERT_TRUE(CreateAndLoadSegment("bbb_480p_vp9_opus_1second.webm", 4));
|
|
const unsigned int kTracksCount = 2;
|
|
EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount());
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Track Header validation.
|
|
const Tracks* const tracks = segment_->GetTracks();
|
|
EXPECT_EQ(kTracksCount, tracks->GetTracksCount());
|
|
for (int i = 0; i < 2; ++i) {
|
|
const Track* const track = tracks->GetTrackByIndex(i);
|
|
ASSERT_TRUE(track != NULL);
|
|
|
|
EXPECT_EQ(NULL, track->GetNameAsUTF8());
|
|
EXPECT_STREQ("und", track->GetLanguage());
|
|
EXPECT_EQ(i + 1, track->GetNumber());
|
|
EXPECT_FALSE(track->GetLacing());
|
|
|
|
if (track->GetType() == Track::kVideo) {
|
|
const VideoTrack* const video_track =
|
|
dynamic_cast<const VideoTrack*>(track);
|
|
EXPECT_EQ(854, static_cast<int>(video_track->GetWidth()));
|
|
EXPECT_EQ(480, static_cast<int>(video_track->GetHeight()));
|
|
EXPECT_STREQ(kVP9CodecId, video_track->GetCodecId());
|
|
EXPECT_DOUBLE_EQ(0., video_track->GetFrameRate());
|
|
EXPECT_EQ(41666666,
|
|
static_cast<int>(video_track->GetDefaultDuration())); // 24.000
|
|
const unsigned int kVideoUid = kVideoTrackNumber;
|
|
EXPECT_EQ(kVideoUid, video_track->GetUid());
|
|
const unsigned int kCodecDelay = 0;
|
|
EXPECT_EQ(kCodecDelay, video_track->GetCodecDelay());
|
|
const unsigned int kSeekPreRoll = 0;
|
|
EXPECT_EQ(kSeekPreRoll, video_track->GetSeekPreRoll());
|
|
|
|
size_t video_codec_private_size;
|
|
EXPECT_EQ(NULL, video_track->GetCodecPrivate(video_codec_private_size));
|
|
const unsigned int kPrivateSize = 0;
|
|
EXPECT_EQ(kPrivateSize, video_codec_private_size);
|
|
} else if (track->GetType() == Track::kAudio) {
|
|
const AudioTrack* const audio_track =
|
|
dynamic_cast<const AudioTrack*>(track);
|
|
EXPECT_EQ(48000, audio_track->GetSamplingRate());
|
|
EXPECT_EQ(6, audio_track->GetChannels());
|
|
EXPECT_EQ(32, audio_track->GetBitDepth());
|
|
EXPECT_STREQ(kOpusCodecId, audio_track->GetCodecId());
|
|
EXPECT_EQ(kAudioTrackNumber, static_cast<int>(audio_track->GetUid()));
|
|
const unsigned int kDefaultDuration = 0;
|
|
EXPECT_EQ(kDefaultDuration, audio_track->GetDefaultDuration());
|
|
EXPECT_EQ(kOpusCodecDelay, audio_track->GetCodecDelay());
|
|
EXPECT_EQ(kOpusSeekPreroll, audio_track->GetSeekPreRoll());
|
|
|
|
size_t audio_codec_private_size;
|
|
EXPECT_TRUE(audio_track->GetCodecPrivate(audio_codec_private_size) !=
|
|
NULL);
|
|
EXPECT_GE(audio_codec_private_size, kOpusPrivateDataSizeMinimum);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Parse the file to do block-level validation.
|
|
const Cluster* cluster = segment_->GetFirst();
|
|
ASSERT_TRUE(cluster != NULL);
|
|
EXPECT_FALSE(cluster->EOS());
|
|
|
|
for (; cluster != NULL && !cluster->EOS();
|
|
cluster = segment_->GetNext(cluster)) {
|
|
// Get the first block
|
|
const BlockEntry* block_entry;
|
|
EXPECT_EQ(0, cluster->GetFirst(block_entry));
|
|
ASSERT_TRUE(block_entry != NULL);
|
|
EXPECT_FALSE(block_entry->EOS());
|
|
|
|
while (block_entry != NULL && !block_entry->EOS()) {
|
|
const Block* const block = block_entry->GetBlock();
|
|
ASSERT_TRUE(block != NULL);
|
|
EXPECT_FALSE(block->IsInvisible());
|
|
EXPECT_EQ(Block::kLacingNone, block->GetLacing());
|
|
|
|
const std::uint32_t track_number =
|
|
static_cast<std::uint32_t>(block->GetTrackNumber());
|
|
const Track* const track = tracks->GetTrackByNumber(track_number);
|
|
ASSERT_TRUE(track != NULL);
|
|
EXPECT_EQ(track->GetNumber(), block->GetTrackNumber());
|
|
const unsigned int kContentEncodingCount = 0;
|
|
EXPECT_EQ(kContentEncodingCount,
|
|
track->GetContentEncodingCount()); // no encryption
|
|
|
|
const std::int64_t track_type = track->GetType();
|
|
EXPECT_TRUE(track_type == Track::kVideo || track_type == Track::kAudio);
|
|
if (track_type == Track::kVideo) {
|
|
EXPECT_EQ(BlockEntry::kBlockSimple, block_entry->GetKind());
|
|
EXPECT_EQ(0, block->GetDiscardPadding());
|
|
} else {
|
|
EXPECT_TRUE(block->IsKey());
|
|
const std::int64_t kLastAudioTimecode = 1001;
|
|
const std::int64_t timecode = block->GetTimeCode(cluster);
|
|
// Only the final Opus block should have discard padding.
|
|
if (timecode == kLastAudioTimecode) {
|
|
EXPECT_EQ(BlockEntry::kBlockGroup, block_entry->GetKind());
|
|
EXPECT_EQ(13500000, block->GetDiscardPadding());
|
|
} else {
|
|
EXPECT_EQ(BlockEntry::kBlockSimple, block_entry->GetKind());
|
|
EXPECT_EQ(0, block->GetDiscardPadding());
|
|
}
|
|
}
|
|
|
|
const int frame_count = block->GetFrameCount();
|
|
const Block::Frame& frame = block->GetFrame(0);
|
|
EXPECT_EQ(1, frame_count);
|
|
EXPECT_GT(frame.len, 0);
|
|
|
|
EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry));
|
|
}
|
|
}
|
|
|
|
ASSERT_TRUE(cluster != NULL);
|
|
EXPECT_TRUE(cluster->EOS());
|
|
}
|
|
|
|
TEST_F(ParserTest, DiscardPadding) {
|
|
// Test an artificial file with some extreme DiscardPadding values.
|
|
const std::string file = "discard_padding.webm";
|
|
ASSERT_TRUE(CreateAndLoadSegment(file, 4));
|
|
const unsigned int kTracksCount = 1;
|
|
EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount());
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Track Header validation.
|
|
const Tracks* const tracks = segment_->GetTracks();
|
|
EXPECT_EQ(kTracksCount, tracks->GetTracksCount());
|
|
const Track* const track = tracks->GetTrackByIndex(0);
|
|
ASSERT_TRUE(track != NULL);
|
|
|
|
EXPECT_STREQ(NULL, track->GetNameAsUTF8());
|
|
EXPECT_EQ(NULL, track->GetLanguage());
|
|
EXPECT_EQ(kAudioTrackNumber, track->GetNumber());
|
|
EXPECT_TRUE(track->GetLacing());
|
|
|
|
EXPECT_EQ(Track::kAudio, track->GetType());
|
|
const AudioTrack* const audio_track = dynamic_cast<const AudioTrack*>(track);
|
|
EXPECT_EQ(30, audio_track->GetSamplingRate());
|
|
EXPECT_EQ(2, audio_track->GetChannels());
|
|
EXPECT_STREQ(kOpusCodecId, audio_track->GetCodecId());
|
|
EXPECT_EQ(kAudioTrackNumber, static_cast<int>(audio_track->GetUid()));
|
|
const unsigned int kDefaultDuration = 0;
|
|
EXPECT_EQ(kDefaultDuration, audio_track->GetDefaultDuration());
|
|
const unsigned int kCodecDelay = 0;
|
|
EXPECT_EQ(kCodecDelay, audio_track->GetCodecDelay());
|
|
const unsigned int kSeekPreRoll = 0;
|
|
EXPECT_EQ(kSeekPreRoll, audio_track->GetSeekPreRoll());
|
|
|
|
size_t audio_codec_private_size;
|
|
EXPECT_EQ(NULL, audio_track->GetCodecPrivate(audio_codec_private_size));
|
|
const unsigned int kPrivateSize = 0;
|
|
EXPECT_EQ(kPrivateSize, audio_codec_private_size);
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Parse the file to do block-level validation.
|
|
const Cluster* cluster = segment_->GetFirst();
|
|
ASSERT_TRUE(cluster != NULL);
|
|
EXPECT_FALSE(cluster->EOS());
|
|
const unsigned int kSegmentCount = 1;
|
|
EXPECT_EQ(kSegmentCount, segment_->GetCount());
|
|
|
|
// Get the first block
|
|
const BlockEntry* block_entry;
|
|
EXPECT_EQ(0, cluster->GetFirst(block_entry));
|
|
ASSERT_TRUE(block_entry != NULL);
|
|
EXPECT_FALSE(block_entry->EOS());
|
|
|
|
const std::array<int, 3> discard_padding = {{12810000, 127, -128}};
|
|
int index = 0;
|
|
while (block_entry != NULL && !block_entry->EOS()) {
|
|
const Block* const block = block_entry->GetBlock();
|
|
ASSERT_TRUE(block != NULL);
|
|
EXPECT_FALSE(block->IsInvisible());
|
|
EXPECT_EQ(Block::kLacingNone, block->GetLacing());
|
|
|
|
const std::uint32_t track_number =
|
|
static_cast<std::uint32_t>(block->GetTrackNumber());
|
|
const Track* const track = tracks->GetTrackByNumber(track_number);
|
|
ASSERT_TRUE(track != NULL);
|
|
EXPECT_EQ(track->GetNumber(), block->GetTrackNumber());
|
|
const unsigned int kContentEncodingCount = 0;
|
|
EXPECT_EQ(kContentEncodingCount,
|
|
track->GetContentEncodingCount()); // no encryption
|
|
|
|
const std::int64_t track_type = track->GetType();
|
|
EXPECT_EQ(Track::kAudio, track_type);
|
|
EXPECT_TRUE(block->IsKey());
|
|
|
|
// All blocks have DiscardPadding.
|
|
EXPECT_EQ(BlockEntry::kBlockGroup, block_entry->GetKind());
|
|
ASSERT_LT(index, static_cast<int>(discard_padding.size()));
|
|
EXPECT_EQ(discard_padding[index], block->GetDiscardPadding());
|
|
++index;
|
|
|
|
const int frame_count = block->GetFrameCount();
|
|
const Block::Frame& frame = block->GetFrame(0);
|
|
EXPECT_EQ(1, frame_count);
|
|
EXPECT_GT(frame.len, 0);
|
|
|
|
EXPECT_EQ(0, cluster->GetNext(block_entry, block_entry));
|
|
}
|
|
|
|
cluster = segment_->GetNext(cluster);
|
|
ASSERT_TRUE(cluster != NULL);
|
|
EXPECT_TRUE(cluster->EOS());
|
|
}
|
|
|
|
TEST_F(ParserTest, StereoModeParsedCorrectly) {
|
|
ASSERT_TRUE(CreateAndLoadSegment("test_stereo_left_right.webm"));
|
|
const unsigned int kTracksCount = 1;
|
|
EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount());
|
|
|
|
const VideoTrack* const video_track = dynamic_cast<const VideoTrack*>(
|
|
segment_->GetTracks()->GetTrackByIndex(0));
|
|
|
|
EXPECT_EQ(1, video_track->GetStereoMode());
|
|
EXPECT_EQ(256, video_track->GetWidth());
|
|
EXPECT_EQ(144, video_track->GetHeight());
|
|
EXPECT_EQ(128, video_track->GetDisplayWidth());
|
|
EXPECT_EQ(144, video_track->GetDisplayHeight());
|
|
}
|
|
|
|
TEST_F(ParserTest, CanParseColour) {
|
|
ASSERT_TRUE(CreateAndLoadSegment("colour.webm"));
|
|
const unsigned int kTracksCount = 1;
|
|
EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount());
|
|
const VideoTrack* const video_track = dynamic_cast<const VideoTrack*>(
|
|
segment_->GetTracks()->GetTrackByIndex(0));
|
|
|
|
const mkvparser::Colour* const colour = video_track->GetColour();
|
|
ASSERT_TRUE(colour != nullptr);
|
|
EXPECT_EQ(0u, colour->matrix_coefficients);
|
|
EXPECT_EQ(1u, colour->bits_per_channel);
|
|
EXPECT_EQ(2u, colour->chroma_subsampling_horz);
|
|
EXPECT_EQ(3u, colour->chroma_subsampling_vert);
|
|
EXPECT_EQ(4u, colour->cb_subsampling_horz);
|
|
EXPECT_EQ(5u, colour->cb_subsampling_vert);
|
|
EXPECT_EQ(1u, colour->chroma_siting_horz);
|
|
EXPECT_EQ(1u, colour->chroma_siting_vert);
|
|
EXPECT_EQ(2u, colour->range);
|
|
EXPECT_EQ(9u, colour->transfer_characteristics);
|
|
EXPECT_EQ(10u, colour->primaries);
|
|
EXPECT_EQ(11u, colour->max_cll);
|
|
EXPECT_EQ(12u, colour->max_fall);
|
|
|
|
const mkvparser::MasteringMetadata* const mm =
|
|
video_track->GetColour()->mastering_metadata;
|
|
ASSERT_TRUE(mm != nullptr);
|
|
ASSERT_TRUE(mm->r != nullptr);
|
|
ASSERT_TRUE(mm->g != nullptr);
|
|
ASSERT_TRUE(mm->b != nullptr);
|
|
ASSERT_TRUE(mm->white_point != nullptr);
|
|
EXPECT_FLOAT_EQ(.1, mm->r->x);
|
|
EXPECT_FLOAT_EQ(.2, mm->r->y);
|
|
EXPECT_FLOAT_EQ(.1, mm->g->x);
|
|
EXPECT_FLOAT_EQ(.2, mm->g->y);
|
|
EXPECT_FLOAT_EQ(.1, mm->b->x);
|
|
EXPECT_FLOAT_EQ(.2, mm->b->y);
|
|
EXPECT_FLOAT_EQ(.1, mm->white_point->x);
|
|
EXPECT_FLOAT_EQ(.2, mm->white_point->y);
|
|
EXPECT_FLOAT_EQ(30.0, mm->luminance_min);
|
|
EXPECT_FLOAT_EQ(40.0, mm->luminance_max);
|
|
}
|
|
|
|
TEST_F(ParserTest, CanParseProjection) {
|
|
ASSERT_TRUE(CreateAndLoadSegment("projection.webm"));
|
|
const unsigned int kTracksCount = 1;
|
|
EXPECT_EQ(kTracksCount, segment_->GetTracks()->GetTracksCount());
|
|
const VideoTrack* const video_track =
|
|
static_cast<const VideoTrack*>(segment_->GetTracks()->GetTrackByIndex(0));
|
|
|
|
const mkvparser::Projection* const projection = video_track->GetProjection();
|
|
ASSERT_TRUE(projection != nullptr);
|
|
EXPECT_EQ(mkvparser::Projection::kRectangular, projection->type);
|
|
EXPECT_FLOAT_EQ(1, projection->pose_yaw);
|
|
EXPECT_FLOAT_EQ(2, projection->pose_pitch);
|
|
EXPECT_FLOAT_EQ(3, projection->pose_roll);
|
|
EXPECT_EQ(1u, projection->private_data_length);
|
|
ASSERT_TRUE(projection->private_data != nullptr);
|
|
EXPECT_EQ(4u, projection->private_data[0]);
|
|
}
|
|
|
|
TEST_F(ParserTest, Vp9CodecLevelTest) {
|
|
const int kCodecPrivateLength = 3;
|
|
const uint8_t good_codec_private_level[kCodecPrivateLength] = {2, 1, 11};
|
|
libwebm::Vp9CodecFeatures features;
|
|
EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&good_codec_private_level[0],
|
|
kCodecPrivateLength, &features));
|
|
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.profile);
|
|
EXPECT_EQ(11, features.level);
|
|
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.bit_depth);
|
|
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent,
|
|
features.chroma_subsampling);
|
|
}
|
|
|
|
TEST_F(ParserTest, Vp9CodecProfileTest) {
|
|
const int kCodecPrivateLength = 3;
|
|
const uint8_t good_codec_private_profile[kCodecPrivateLength] = {1, 1, 1};
|
|
libwebm::Vp9CodecFeatures features;
|
|
EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&good_codec_private_profile[0],
|
|
kCodecPrivateLength, &features));
|
|
EXPECT_EQ(1, features.profile);
|
|
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.level);
|
|
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.bit_depth);
|
|
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent,
|
|
features.chroma_subsampling);
|
|
}
|
|
|
|
TEST_F(ParserTest, Vp9CodecBitDepthTest) {
|
|
const int kCodecPrivateLength = 3;
|
|
const uint8_t good_codec_private_profile[kCodecPrivateLength] = {3, 1, 8};
|
|
libwebm::Vp9CodecFeatures features;
|
|
EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&good_codec_private_profile[0],
|
|
kCodecPrivateLength, &features));
|
|
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.profile);
|
|
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.level);
|
|
EXPECT_EQ(8, features.bit_depth);
|
|
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent,
|
|
features.chroma_subsampling);
|
|
}
|
|
|
|
TEST_F(ParserTest, Vp9CodecChromaSubsamplingTest) {
|
|
const int kCodecPrivateLength = 3;
|
|
const uint8_t good_codec_private_profile[kCodecPrivateLength] = {4, 1, 0};
|
|
libwebm::Vp9CodecFeatures features;
|
|
EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&good_codec_private_profile[0],
|
|
kCodecPrivateLength, &features));
|
|
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.profile);
|
|
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.level);
|
|
EXPECT_EQ(libwebm::Vp9CodecFeatures::kValueNotPresent, features.bit_depth);
|
|
EXPECT_EQ(0, features.chroma_subsampling);
|
|
}
|
|
|
|
TEST_F(ParserTest, Vp9CodecProfileLevelTest) {
|
|
const int kCodecPrivateLength = 6;
|
|
const uint8_t codec_private[kCodecPrivateLength] = {1, 1, 1, 2, 1, 11};
|
|
libwebm::Vp9CodecFeatures features;
|
|
EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&codec_private[0],
|
|
kCodecPrivateLength, &features));
|
|
EXPECT_EQ(1, features.profile);
|
|
EXPECT_EQ(11, features.level);
|
|
}
|
|
|
|
TEST_F(ParserTest, Vp9CodecAllTest) {
|
|
const int kCodecPrivateLength = 12;
|
|
const uint8_t codec_private[kCodecPrivateLength] = {1, 1, 1, 2, 1, 11,
|
|
3, 1, 8, 4, 1, 0};
|
|
libwebm::Vp9CodecFeatures features;
|
|
EXPECT_TRUE(libwebm::ParseVpxCodecPrivate(&codec_private[0],
|
|
kCodecPrivateLength, &features));
|
|
EXPECT_EQ(1, features.profile);
|
|
EXPECT_EQ(11, features.level);
|
|
EXPECT_EQ(8, features.bit_depth);
|
|
EXPECT_EQ(0, features.chroma_subsampling);
|
|
}
|
|
|
|
TEST_F(ParserTest, Vp9CodecPrivateBadTest) {
|
|
const int kCodecPrivateLength = 3;
|
|
libwebm::Vp9CodecFeatures features;
|
|
// Test invalid codec private data; all of these should return false.
|
|
const uint8_t bad_codec_private[kCodecPrivateLength] = {0, 0, 0};
|
|
EXPECT_FALSE(
|
|
libwebm::ParseVpxCodecPrivate(NULL, kCodecPrivateLength, &features));
|
|
EXPECT_FALSE(
|
|
libwebm::ParseVpxCodecPrivate(&bad_codec_private[0], 0, &features));
|
|
EXPECT_FALSE(libwebm::ParseVpxCodecPrivate(&bad_codec_private[0],
|
|
kCodecPrivateLength, &features));
|
|
const uint8_t good_codec_private_level[kCodecPrivateLength] = {2, 1, 11};
|
|
|
|
// Test parse of codec private chunks, but lie about length.
|
|
EXPECT_FALSE(
|
|
libwebm::ParseVpxCodecPrivate(&bad_codec_private[0], 0, &features));
|
|
EXPECT_FALSE(libwebm::ParseVpxCodecPrivate(&good_codec_private_level[0], 0,
|
|
&features));
|
|
EXPECT_FALSE(libwebm::ParseVpxCodecPrivate(&good_codec_private_level[0],
|
|
kCodecPrivateLength, NULL));
|
|
}
|
|
|
|
TEST_F(ParserTest, InvalidTruncatedChapterString) {
|
|
ASSERT_NO_FATAL_FAILURE(CreateSegmentNoHeaderChecks(
|
|
"invalid/chapters_truncated_chapter_string.mkv"));
|
|
EXPECT_EQ(mkvparser::E_PARSE_FAILED, segment_->Load());
|
|
}
|
|
|
|
TEST_F(ParserTest, InvalidTruncatedChapterString2) {
|
|
ASSERT_NO_FATAL_FAILURE(CreateSegmentNoHeaderChecks(
|
|
"invalid/chapters_truncated_chapter_string_2.mkv"));
|
|
EXPECT_EQ(mkvparser::E_FILE_FORMAT_INVALID, segment_->Load());
|
|
}
|
|
|
|
TEST_F(ParserTest, InvalidFixedLacingSize) {
|
|
ASSERT_NO_FATAL_FAILURE(
|
|
CreateSegmentNoHeaderChecks("invalid/fixed_lacing_bad_lace_size.mkv"));
|
|
ASSERT_EQ(0, segment_->Load());
|
|
const mkvparser::BlockEntry* block_entry = NULL;
|
|
EXPECT_EQ(mkvparser::E_FILE_FORMAT_INVALID,
|
|
segment_->GetFirst()->GetFirst(block_entry));
|
|
}
|
|
|
|
TEST_F(ParserTest, InvalidBlockEndsBeyondCluster) {
|
|
ASSERT_NO_FATAL_FAILURE(
|
|
CreateSegmentNoHeaderChecks("invalid/block_ends_beyond_cluster.mkv"));
|
|
ASSERT_EQ(0, segment_->Load());
|
|
const mkvparser::BlockEntry* block_entry = NULL;
|
|
EXPECT_EQ(0, segment_->GetFirst()->GetFirst(block_entry));
|
|
EXPECT_EQ(mkvparser::E_FILE_FORMAT_INVALID,
|
|
segment_->GetFirst()->GetNext(block_entry, block_entry));
|
|
}
|
|
|
|
TEST_F(ParserTest, InvalidBlockGroupBlockEndsBlockGroup) {
|
|
ASSERT_NO_FATAL_FAILURE(CreateSegmentNoHeaderChecks(
|
|
"invalid/blockgroup_block_ends_beyond_blockgroup.mkv"));
|
|
ASSERT_EQ(0, segment_->Load());
|
|
const mkvparser::BlockEntry* block_entry = NULL;
|
|
EXPECT_EQ(0, segment_->GetFirst()->GetFirst(block_entry));
|
|
EXPECT_EQ(mkvparser::E_FILE_FORMAT_INVALID,
|
|
segment_->GetFirst()->GetNext(block_entry, block_entry));
|
|
}
|
|
|
|
TEST_F(ParserTest, InvalidProjectionFloatOverflow) {
|
|
ASSERT_NO_FATAL_FAILURE(
|
|
CreateSegmentNoHeaderChecks("invalid/projection_float_overflow.webm"));
|
|
EXPECT_EQ(mkvparser::E_FILE_FORMAT_INVALID, segment_->Load());
|
|
}
|
|
|
|
TEST_F(ParserTest, InvalidPrimaryChromaticityParseFail) {
|
|
ASSERT_NO_FATAL_FAILURE(CreateSegmentNoHeaderChecks(
|
|
"invalid/primarychromaticity_fieldtoolarge.webm"));
|
|
EXPECT_EQ(mkvparser::E_FILE_FORMAT_INVALID, segment_->Load());
|
|
}
|
|
|
|
} // namespace test
|
|
|
|
int main(int argc, char* argv[]) {
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
return RUN_ALL_TESTS();
|
|
}
|