(61d00a474) v0.9.7.1
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,823 @@
|
||||
// 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();
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
// 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 "testing/test_util.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ios>
|
||||
#include <string>
|
||||
|
||||
#include "common/libwebm_util.h"
|
||||
#include "common/webmids.h"
|
||||
|
||||
#include "mkvparser/mkvparser.h"
|
||||
#include "mkvparser/mkvreader.h"
|
||||
|
||||
namespace test {
|
||||
|
||||
std::string GetTestDataDir() {
|
||||
const char* test_data_path = std::getenv("LIBWEBM_TEST_DATA_PATH");
|
||||
return test_data_path ? std::string(test_data_path) : std::string();
|
||||
}
|
||||
|
||||
std::string GetTestFilePath(const std::string& name) {
|
||||
const std::string libwebm_testdata_dir = GetTestDataDir();
|
||||
return libwebm_testdata_dir + "/" + name;
|
||||
}
|
||||
|
||||
bool CompareFiles(const std::string& file1, const std::string& file2) {
|
||||
const std::size_t kBlockSize = 4096;
|
||||
std::uint8_t buf1[kBlockSize] = {0};
|
||||
std::uint8_t buf2[kBlockSize] = {0};
|
||||
|
||||
libwebm::FilePtr f1 =
|
||||
libwebm::FilePtr(std::fopen(file1.c_str(), "rb"), libwebm::FILEDeleter());
|
||||
libwebm::FilePtr f2 =
|
||||
libwebm::FilePtr(std::fopen(file2.c_str(), "rb"), libwebm::FILEDeleter());
|
||||
|
||||
if (!f1.get() || !f2.get()) {
|
||||
// Files cannot match if one or both couldn't be opened.
|
||||
return false;
|
||||
}
|
||||
|
||||
do {
|
||||
const std::size_t r1 = std::fread(buf1, 1, kBlockSize, f1.get());
|
||||
const std::size_t r2 = std::fread(buf2, 1, kBlockSize, f2.get());
|
||||
|
||||
// TODO(fgalligan): Add output of which byte differs.
|
||||
if (r1 != r2 || std::memcmp(buf1, buf2, r1)) {
|
||||
return 0; // Files are not equal
|
||||
}
|
||||
} while (!std::feof(f1.get()) && !std::feof(f2.get()));
|
||||
|
||||
return std::feof(f1.get()) && std::feof(f2.get());
|
||||
}
|
||||
|
||||
bool HasCuePoints(const mkvparser::Segment* segment,
|
||||
std::int64_t* cues_offset) {
|
||||
if (!segment || !cues_offset) {
|
||||
return false;
|
||||
}
|
||||
using mkvparser::SeekHead;
|
||||
const SeekHead* const seek_head = segment->GetSeekHead();
|
||||
if (!seek_head) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::int64_t offset = 0;
|
||||
for (int i = 0; i < seek_head->GetCount(); ++i) {
|
||||
const SeekHead::Entry* const entry = seek_head->GetEntry(i);
|
||||
if (entry->id == libwebm::kMkvCues) {
|
||||
offset = entry->pos;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset <= 0) {
|
||||
// No Cues found.
|
||||
return false;
|
||||
}
|
||||
|
||||
*cues_offset = offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidateCues(mkvparser::Segment* segment, mkvparser::IMkvReader* reader) {
|
||||
if (!segment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::int64_t cues_offset = 0;
|
||||
if (!HasCuePoints(segment, &cues_offset)) {
|
||||
// No cues to validate, everything is OK.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parse Cues.
|
||||
long long cues_pos = 0; // NOLINT
|
||||
long cues_len = 0; // NOLINT
|
||||
if (segment->ParseCues(cues_offset, cues_pos, cues_len)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get a pointer to the video track if it exists. Otherwise, we assume
|
||||
// that Cues are based on the first track (which is true for all our test
|
||||
// files).
|
||||
const mkvparser::Tracks* const tracks = segment->GetTracks();
|
||||
const mkvparser::Track* cues_track = tracks->GetTrackByIndex(0);
|
||||
for (int i = 1; i < static_cast<int>(tracks->GetTracksCount()); ++i) {
|
||||
const mkvparser::Track* const track = tracks->GetTrackByIndex(i);
|
||||
if (track->GetType() == mkvparser::Track::kVideo) {
|
||||
cues_track = track;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate through Cues and verify if they are pointing to the correct
|
||||
// Cluster position.
|
||||
const mkvparser::Cues* const cues = segment->GetCues();
|
||||
const mkvparser::CuePoint* cue_point = NULL;
|
||||
while (cues->LoadCuePoint()) {
|
||||
if (!cue_point) {
|
||||
cue_point = cues->GetFirst();
|
||||
} else {
|
||||
cue_point = cues->GetNext(cue_point);
|
||||
}
|
||||
const mkvparser::CuePoint::TrackPosition* const track_position =
|
||||
cue_point->Find(cues_track);
|
||||
const long long cluster_pos = track_position->m_pos + // NOLINT
|
||||
segment->m_start;
|
||||
|
||||
// If a cluster does not begin at |cluster_pos|, then the file is
|
||||
// incorrect.
|
||||
long length; // NOLINT
|
||||
const std::int64_t id = mkvparser::ReadID(reader, cluster_pos, length);
|
||||
if (id != libwebm::kMkvCluster) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MkvParser::~MkvParser() {
|
||||
delete segment;
|
||||
delete reader;
|
||||
}
|
||||
|
||||
bool ParseMkvFileReleaseParser(const std::string& webm_file,
|
||||
MkvParser* parser_out) {
|
||||
parser_out->reader = new (std::nothrow) mkvparser::MkvReader;
|
||||
mkvparser::MkvReader& reader = *parser_out->reader;
|
||||
if (!parser_out->reader || reader.Open(webm_file.c_str()) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long long pos = 0; // NOLINT
|
||||
mkvparser::EBMLHeader ebml_header;
|
||||
if (ebml_header.Parse(&reader, pos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
using mkvparser::Segment;
|
||||
Segment* segment_ptr = nullptr;
|
||||
if (Segment::CreateInstance(&reader, pos, segment_ptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<Segment> segment(segment_ptr);
|
||||
long result;
|
||||
if ((result = segment->Load()) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const mkvparser::Cluster* cluster = segment->GetFirst();
|
||||
if (!cluster || cluster->EOS()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (cluster && cluster->EOS() == false) {
|
||||
if (cluster->GetTimeCode() < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const mkvparser::BlockEntry* block = nullptr;
|
||||
if (cluster->GetFirst(block) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (block != NULL && block->EOS() == false) {
|
||||
if (cluster->GetNext(block, block) < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
cluster = segment->GetNext(cluster);
|
||||
}
|
||||
|
||||
parser_out->segment = segment.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseMkvFile(const std::string& webm_file) {
|
||||
MkvParser parser;
|
||||
const bool result = ParseMkvFileReleaseParser(webm_file, &parser);
|
||||
delete parser.segment;
|
||||
delete parser.reader;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
@@ -0,0 +1,88 @@
|
||||
// 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.
|
||||
#ifndef LIBWEBM_TESTING_TEST_UTIL_H_
|
||||
#define LIBWEBM_TESTING_TEST_UTIL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
namespace mkvparser {
|
||||
class IMkvReader;
|
||||
class MkvReader;
|
||||
class Segment;
|
||||
} // namespace mkvparser
|
||||
|
||||
namespace test {
|
||||
|
||||
// constants for muxer and parser tests
|
||||
const char kAppString[] = "mkvmuxer_unit_tests";
|
||||
const char kOpusCodecId[] = "A_OPUS";
|
||||
const char kVorbisCodecId[] = "A_VORBIS";
|
||||
const int kAudioTrackNumber = 2;
|
||||
const int kBitDepth = 2;
|
||||
const int kChannels = 2;
|
||||
const double kDuration = 2.345;
|
||||
const int kFrameLength = 10;
|
||||
const int kHeight = 180;
|
||||
const int kInvalidTrackNumber = 100;
|
||||
const std::uint64_t kOpusCodecDelay = 6500000;
|
||||
const std::size_t kOpusPrivateDataSizeMinimum = 19;
|
||||
const std::uint64_t kOpusSeekPreroll = 80000000;
|
||||
const char kMetadataCodecId[] = "D_WEBVTT/METADATA";
|
||||
const int kMetadataTrackNumber = 3;
|
||||
const int kMetadataTrackType = 0x21;
|
||||
const int kSampleRate = 30;
|
||||
const int kTimeCodeScale = 1000;
|
||||
const char kTrackName[] = "unit_test";
|
||||
const char kVP8CodecId[] = "V_VP8";
|
||||
const char kVP9CodecId[] = "V_VP9";
|
||||
const double kVideoFrameRate = 0.5;
|
||||
const int kVideoTrackNumber = 1;
|
||||
const int kWidth = 320;
|
||||
|
||||
// Returns the path to the test data directory by reading and returning the
|
||||
// contents the LIBWEBM_TESTDATA_DIR environment variable.
|
||||
std::string GetTestDataDir();
|
||||
|
||||
// Returns the absolute path to the file of |name| in LIBWEBM_TESTDATA_DIR.
|
||||
std::string GetTestFilePath(const std::string& name);
|
||||
|
||||
// Byte-wise comparison of two files |file1| and |file2|. Returns true if the
|
||||
// files match exactly, false otherwise.
|
||||
bool CompareFiles(const std::string& file1, const std::string& file2);
|
||||
|
||||
// Returns true and sets |cues_offset| to the cues location within the MKV file
|
||||
// parsed by |segment| when the MKV file has cue points.
|
||||
bool HasCuePoints(const mkvparser::Segment* segment, std::int64_t* cues_offset);
|
||||
|
||||
// Validates cue points. Assumes caller has already called Load() on |segment|.
|
||||
// Returns true when:
|
||||
// All cue points point at clusters, OR
|
||||
// Data parsed by |segment| has no cue points.
|
||||
bool ValidateCues(mkvparser::Segment* segment, mkvparser::IMkvReader* reader);
|
||||
|
||||
// Parses |webm_file| using mkvparser and returns true when file parses
|
||||
// successfully (all clusters and blocks can be successfully walked). Second
|
||||
// variant allows further interaction with the parsed file via transferring
|
||||
// ownership of the mkvparser Segment and MkvReader to the caller via
|
||||
// |parser_out|.
|
||||
struct MkvParser {
|
||||
MkvParser() = default;
|
||||
~MkvParser();
|
||||
mkvparser::Segment* segment = nullptr;
|
||||
mkvparser::MkvReader* reader = nullptr;
|
||||
};
|
||||
bool ParseMkvFile(const std::string& webm_file);
|
||||
bool ParseMkvFileReleaseParser(const std::string& webm_file,
|
||||
MkvParser* parser_out);
|
||||
|
||||
} // namespace test
|
||||
|
||||
#endif // LIBWEBM_TESTING_TEST_UTIL_H_
|
||||
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,24 @@
|
||||
Why the files in this directory are considered invalid:
|
||||
|
||||
block_ends_beyond_cluster.mkv -
|
||||
File containing a single cluster with two simple blocks. One valid, and the
|
||||
second reporting a size that would cause the block to end far beyond the end
|
||||
of its parent cluster.
|
||||
|
||||
blockgroup_block_ends_beyond_blockgroup.mkv -
|
||||
File containing a single cluster and two blockgroups. The first blockgroup is
|
||||
valid. The second blockgroup contains a block reporting a size that spans well
|
||||
past the block and the end of the file.
|
||||
|
||||
chapters_truncated_chapter_string.mkv -
|
||||
File with a Chapters element that ends with a ChapterAtom whose ChapterDisplay
|
||||
element contains a truncated ChapterString.
|
||||
|
||||
chapters_truncated_chapter_string_2.mkv -
|
||||
Nearly identical to chapters_truncated_chapter_string.mkv, but with a void
|
||||
element and a partial cluster. Causes mkvparser to fail in a slightly
|
||||
different manner.
|
||||
|
||||
fixed_lacing_bad_lace_size.mkv -
|
||||
File containing a BlockGroup with fixed lacing, but reports a total laced size
|
||||
that is not evenly divisible by the number of laced frames.
|
||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
+1
@@ -0,0 +1 @@
|
||||
EߣŸB0�0B0�0B0�0B0�0B‚„webmB0�0B0�0S€g0000000»‹00000000000»Œ000000000000ì¼000000000000000000000000000000000000000000000000000000000000€“0000000000000000000W0“0000000000000000000T®k@ž®@›×�0s0�0ƒ�†…00000à@‡°‚00º�0U°ýU¸�0U¹�0Uº�0U»�0U¼�0U½�0UÐÆUÙ„0000UÚ„0000UÑ„0000UÒ000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
Vendored
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,75 @@
|
||||
// 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 "common/video_frame.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
const libwebm::VideoFrame::Codec kCodec = libwebm::VideoFrame::kVP8;
|
||||
const std::int64_t kPts = 12345;
|
||||
const std::size_t kSize = 1;
|
||||
const std::size_t kEmptySize = 0;
|
||||
|
||||
TEST(VideoFrameTests, DefaultsTest) {
|
||||
libwebm::VideoFrame frame;
|
||||
EXPECT_EQ(kEmptySize, frame.buffer().capacity);
|
||||
EXPECT_EQ(kEmptySize, frame.buffer().length);
|
||||
EXPECT_EQ(nullptr, frame.buffer().data.get());
|
||||
EXPECT_FALSE(frame.keyframe());
|
||||
EXPECT_EQ(0, frame.nanosecond_pts());
|
||||
EXPECT_EQ(libwebm::VideoFrame::kVP9, frame.codec());
|
||||
}
|
||||
|
||||
TEST(VideoFrameTests, SizeTest) {
|
||||
libwebm::VideoFrame frame;
|
||||
EXPECT_TRUE(frame.Init(kSize));
|
||||
|
||||
// Buffer inits empty, length should be 0, aka |kEmpty|.
|
||||
EXPECT_GT(kSize, frame.buffer().length);
|
||||
EXPECT_EQ(kEmptySize, frame.buffer().length);
|
||||
|
||||
// Capacity should be equal to |kSize|.
|
||||
EXPECT_EQ(kSize, frame.buffer().capacity);
|
||||
EXPECT_FALSE(frame.SetBufferLength(kSize + 1));
|
||||
|
||||
// Write a byte into the buffer via the raw data pointer, update length, and
|
||||
// verify expected behavior.
|
||||
uint8_t* write_ptr = reinterpret_cast<uint8_t*>(frame.buffer().data.get());
|
||||
*write_ptr = 0xFF;
|
||||
EXPECT_TRUE(frame.SetBufferLength(1));
|
||||
EXPECT_EQ(frame.buffer().length, frame.buffer().capacity);
|
||||
}
|
||||
|
||||
TEST(VideoFrameTests, OverloadsTest) {
|
||||
const bool kKeyframe = true;
|
||||
|
||||
// Test VideoFrame::VideoFrame(bool keyframe, int64_t nano_pts, Codec c).
|
||||
libwebm::VideoFrame keyframe(kKeyframe, kPts, kCodec);
|
||||
EXPECT_EQ(kKeyframe, keyframe.keyframe());
|
||||
EXPECT_EQ(kPts, keyframe.nanosecond_pts());
|
||||
EXPECT_EQ(kCodec, keyframe.codec());
|
||||
EXPECT_EQ(kEmptySize, keyframe.buffer().capacity);
|
||||
EXPECT_EQ(kEmptySize, keyframe.buffer().length);
|
||||
EXPECT_EQ(nullptr, keyframe.buffer().data.get());
|
||||
|
||||
// Test VideoFrame::Init(std::size_t length).
|
||||
EXPECT_TRUE(keyframe.Init(kSize));
|
||||
EXPECT_EQ(kKeyframe, keyframe.keyframe());
|
||||
EXPECT_EQ(kPts, keyframe.nanosecond_pts());
|
||||
EXPECT_EQ(kCodec, keyframe.codec());
|
||||
EXPECT_NE(nullptr, keyframe.buffer().data.get());
|
||||
|
||||
// Test VideoFrame::Init(size_t length, int64_t nano_pts, Codec c).
|
||||
EXPECT_TRUE(keyframe.Init(kSize, kPts + 1, libwebm::VideoFrame::kVP9));
|
||||
EXPECT_EQ(kSize, keyframe.buffer().capacity);
|
||||
EXPECT_GT(kSize, keyframe.buffer().length);
|
||||
EXPECT_NE(kPts, keyframe.nanosecond_pts());
|
||||
EXPECT_NE(kCodec, keyframe.codec());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user