1*103e46e4SHarish Mahendrakar // Copyright (c) 2016 The WebM project authors. All Rights Reserved.
2*103e46e4SHarish Mahendrakar //
3*103e46e4SHarish Mahendrakar // Use of this source code is governed by a BSD-style license
4*103e46e4SHarish Mahendrakar // that can be found in the LICENSE file in the root of the source
5*103e46e4SHarish Mahendrakar // tree. An additional intellectual property rights grant can be found
6*103e46e4SHarish Mahendrakar // in the file PATENTS. All contributing project authors may
7*103e46e4SHarish Mahendrakar // be found in the AUTHORS file in the root of the source tree.
8*103e46e4SHarish Mahendrakar #include "testing/test_util.h"
9*103e46e4SHarish Mahendrakar
10*103e46e4SHarish Mahendrakar #include <cstdint>
11*103e46e4SHarish Mahendrakar #include <cstdio>
12*103e46e4SHarish Mahendrakar #include <cstdlib>
13*103e46e4SHarish Mahendrakar #include <cstring>
14*103e46e4SHarish Mahendrakar #include <ios>
15*103e46e4SHarish Mahendrakar #include <string>
16*103e46e4SHarish Mahendrakar
17*103e46e4SHarish Mahendrakar #include "common/libwebm_util.h"
18*103e46e4SHarish Mahendrakar #include "common/webmids.h"
19*103e46e4SHarish Mahendrakar
20*103e46e4SHarish Mahendrakar #include "mkvparser/mkvparser.h"
21*103e46e4SHarish Mahendrakar #include "mkvparser/mkvreader.h"
22*103e46e4SHarish Mahendrakar
23*103e46e4SHarish Mahendrakar namespace test {
24*103e46e4SHarish Mahendrakar
GetTestDataDir()25*103e46e4SHarish Mahendrakar std::string GetTestDataDir() {
26*103e46e4SHarish Mahendrakar const char* test_data_path = std::getenv("LIBWEBM_TEST_DATA_PATH");
27*103e46e4SHarish Mahendrakar return test_data_path ? std::string(test_data_path) : std::string();
28*103e46e4SHarish Mahendrakar }
29*103e46e4SHarish Mahendrakar
GetTestFilePath(const std::string & name)30*103e46e4SHarish Mahendrakar std::string GetTestFilePath(const std::string& name) {
31*103e46e4SHarish Mahendrakar const std::string libwebm_testdata_dir = GetTestDataDir();
32*103e46e4SHarish Mahendrakar return libwebm_testdata_dir + "/" + name;
33*103e46e4SHarish Mahendrakar }
34*103e46e4SHarish Mahendrakar
CompareFiles(const std::string & file1,const std::string & file2)35*103e46e4SHarish Mahendrakar bool CompareFiles(const std::string& file1, const std::string& file2) {
36*103e46e4SHarish Mahendrakar const std::size_t kBlockSize = 4096;
37*103e46e4SHarish Mahendrakar std::uint8_t buf1[kBlockSize] = {0};
38*103e46e4SHarish Mahendrakar std::uint8_t buf2[kBlockSize] = {0};
39*103e46e4SHarish Mahendrakar
40*103e46e4SHarish Mahendrakar libwebm::FilePtr f1 =
41*103e46e4SHarish Mahendrakar libwebm::FilePtr(std::fopen(file1.c_str(), "rb"), libwebm::FILEDeleter());
42*103e46e4SHarish Mahendrakar libwebm::FilePtr f2 =
43*103e46e4SHarish Mahendrakar libwebm::FilePtr(std::fopen(file2.c_str(), "rb"), libwebm::FILEDeleter());
44*103e46e4SHarish Mahendrakar
45*103e46e4SHarish Mahendrakar if (!f1.get() || !f2.get()) {
46*103e46e4SHarish Mahendrakar // Files cannot match if one or both couldn't be opened.
47*103e46e4SHarish Mahendrakar return false;
48*103e46e4SHarish Mahendrakar }
49*103e46e4SHarish Mahendrakar
50*103e46e4SHarish Mahendrakar do {
51*103e46e4SHarish Mahendrakar const std::size_t r1 = std::fread(buf1, 1, kBlockSize, f1.get());
52*103e46e4SHarish Mahendrakar const std::size_t r2 = std::fread(buf2, 1, kBlockSize, f2.get());
53*103e46e4SHarish Mahendrakar
54*103e46e4SHarish Mahendrakar // TODO(fgalligan): Add output of which byte differs.
55*103e46e4SHarish Mahendrakar if (r1 != r2 || std::memcmp(buf1, buf2, r1)) {
56*103e46e4SHarish Mahendrakar return 0; // Files are not equal
57*103e46e4SHarish Mahendrakar }
58*103e46e4SHarish Mahendrakar } while (!std::feof(f1.get()) && !std::feof(f2.get()));
59*103e46e4SHarish Mahendrakar
60*103e46e4SHarish Mahendrakar return std::feof(f1.get()) && std::feof(f2.get());
61*103e46e4SHarish Mahendrakar }
62*103e46e4SHarish Mahendrakar
HasCuePoints(const mkvparser::Segment * segment,std::int64_t * cues_offset)63*103e46e4SHarish Mahendrakar bool HasCuePoints(const mkvparser::Segment* segment,
64*103e46e4SHarish Mahendrakar std::int64_t* cues_offset) {
65*103e46e4SHarish Mahendrakar if (!segment || !cues_offset) {
66*103e46e4SHarish Mahendrakar return false;
67*103e46e4SHarish Mahendrakar }
68*103e46e4SHarish Mahendrakar using mkvparser::SeekHead;
69*103e46e4SHarish Mahendrakar const SeekHead* const seek_head = segment->GetSeekHead();
70*103e46e4SHarish Mahendrakar if (!seek_head) {
71*103e46e4SHarish Mahendrakar return false;
72*103e46e4SHarish Mahendrakar }
73*103e46e4SHarish Mahendrakar
74*103e46e4SHarish Mahendrakar std::int64_t offset = 0;
75*103e46e4SHarish Mahendrakar for (int i = 0; i < seek_head->GetCount(); ++i) {
76*103e46e4SHarish Mahendrakar const SeekHead::Entry* const entry = seek_head->GetEntry(i);
77*103e46e4SHarish Mahendrakar if (entry->id == libwebm::kMkvCues) {
78*103e46e4SHarish Mahendrakar offset = entry->pos;
79*103e46e4SHarish Mahendrakar }
80*103e46e4SHarish Mahendrakar }
81*103e46e4SHarish Mahendrakar
82*103e46e4SHarish Mahendrakar if (offset <= 0) {
83*103e46e4SHarish Mahendrakar // No Cues found.
84*103e46e4SHarish Mahendrakar return false;
85*103e46e4SHarish Mahendrakar }
86*103e46e4SHarish Mahendrakar
87*103e46e4SHarish Mahendrakar *cues_offset = offset;
88*103e46e4SHarish Mahendrakar return true;
89*103e46e4SHarish Mahendrakar }
90*103e46e4SHarish Mahendrakar
ValidateCues(mkvparser::Segment * segment,mkvparser::IMkvReader * reader)91*103e46e4SHarish Mahendrakar bool ValidateCues(mkvparser::Segment* segment, mkvparser::IMkvReader* reader) {
92*103e46e4SHarish Mahendrakar if (!segment) {
93*103e46e4SHarish Mahendrakar return false;
94*103e46e4SHarish Mahendrakar }
95*103e46e4SHarish Mahendrakar
96*103e46e4SHarish Mahendrakar std::int64_t cues_offset = 0;
97*103e46e4SHarish Mahendrakar if (!HasCuePoints(segment, &cues_offset)) {
98*103e46e4SHarish Mahendrakar // No cues to validate, everything is OK.
99*103e46e4SHarish Mahendrakar return true;
100*103e46e4SHarish Mahendrakar }
101*103e46e4SHarish Mahendrakar
102*103e46e4SHarish Mahendrakar // Parse Cues.
103*103e46e4SHarish Mahendrakar long long cues_pos = 0; // NOLINT
104*103e46e4SHarish Mahendrakar long cues_len = 0; // NOLINT
105*103e46e4SHarish Mahendrakar if (segment->ParseCues(cues_offset, cues_pos, cues_len)) {
106*103e46e4SHarish Mahendrakar return false;
107*103e46e4SHarish Mahendrakar }
108*103e46e4SHarish Mahendrakar
109*103e46e4SHarish Mahendrakar // Get a pointer to the video track if it exists. Otherwise, we assume
110*103e46e4SHarish Mahendrakar // that Cues are based on the first track (which is true for all our test
111*103e46e4SHarish Mahendrakar // files).
112*103e46e4SHarish Mahendrakar const mkvparser::Tracks* const tracks = segment->GetTracks();
113*103e46e4SHarish Mahendrakar const mkvparser::Track* cues_track = tracks->GetTrackByIndex(0);
114*103e46e4SHarish Mahendrakar for (int i = 1; i < static_cast<int>(tracks->GetTracksCount()); ++i) {
115*103e46e4SHarish Mahendrakar const mkvparser::Track* const track = tracks->GetTrackByIndex(i);
116*103e46e4SHarish Mahendrakar if (track->GetType() == mkvparser::Track::kVideo) {
117*103e46e4SHarish Mahendrakar cues_track = track;
118*103e46e4SHarish Mahendrakar break;
119*103e46e4SHarish Mahendrakar }
120*103e46e4SHarish Mahendrakar }
121*103e46e4SHarish Mahendrakar
122*103e46e4SHarish Mahendrakar // Iterate through Cues and verify if they are pointing to the correct
123*103e46e4SHarish Mahendrakar // Cluster position.
124*103e46e4SHarish Mahendrakar const mkvparser::Cues* const cues = segment->GetCues();
125*103e46e4SHarish Mahendrakar const mkvparser::CuePoint* cue_point = NULL;
126*103e46e4SHarish Mahendrakar while (cues->LoadCuePoint()) {
127*103e46e4SHarish Mahendrakar if (!cue_point) {
128*103e46e4SHarish Mahendrakar cue_point = cues->GetFirst();
129*103e46e4SHarish Mahendrakar } else {
130*103e46e4SHarish Mahendrakar cue_point = cues->GetNext(cue_point);
131*103e46e4SHarish Mahendrakar }
132*103e46e4SHarish Mahendrakar const mkvparser::CuePoint::TrackPosition* const track_position =
133*103e46e4SHarish Mahendrakar cue_point->Find(cues_track);
134*103e46e4SHarish Mahendrakar const long long cluster_pos = track_position->m_pos + // NOLINT
135*103e46e4SHarish Mahendrakar segment->m_start;
136*103e46e4SHarish Mahendrakar
137*103e46e4SHarish Mahendrakar // If a cluster does not begin at |cluster_pos|, then the file is
138*103e46e4SHarish Mahendrakar // incorrect.
139*103e46e4SHarish Mahendrakar long length; // NOLINT
140*103e46e4SHarish Mahendrakar const std::int64_t id = mkvparser::ReadID(reader, cluster_pos, length);
141*103e46e4SHarish Mahendrakar if (id != libwebm::kMkvCluster) {
142*103e46e4SHarish Mahendrakar return false;
143*103e46e4SHarish Mahendrakar }
144*103e46e4SHarish Mahendrakar }
145*103e46e4SHarish Mahendrakar return true;
146*103e46e4SHarish Mahendrakar }
147*103e46e4SHarish Mahendrakar
~MkvParser()148*103e46e4SHarish Mahendrakar MkvParser::~MkvParser() {
149*103e46e4SHarish Mahendrakar delete segment;
150*103e46e4SHarish Mahendrakar delete reader;
151*103e46e4SHarish Mahendrakar }
152*103e46e4SHarish Mahendrakar
ParseMkvFileReleaseParser(const std::string & webm_file,MkvParser * parser_out)153*103e46e4SHarish Mahendrakar bool ParseMkvFileReleaseParser(const std::string& webm_file,
154*103e46e4SHarish Mahendrakar MkvParser* parser_out) {
155*103e46e4SHarish Mahendrakar parser_out->reader = new (std::nothrow) mkvparser::MkvReader;
156*103e46e4SHarish Mahendrakar mkvparser::MkvReader& reader = *parser_out->reader;
157*103e46e4SHarish Mahendrakar if (!parser_out->reader || reader.Open(webm_file.c_str()) < 0) {
158*103e46e4SHarish Mahendrakar return false;
159*103e46e4SHarish Mahendrakar }
160*103e46e4SHarish Mahendrakar
161*103e46e4SHarish Mahendrakar long long pos = 0; // NOLINT
162*103e46e4SHarish Mahendrakar mkvparser::EBMLHeader ebml_header;
163*103e46e4SHarish Mahendrakar if (ebml_header.Parse(&reader, pos)) {
164*103e46e4SHarish Mahendrakar return false;
165*103e46e4SHarish Mahendrakar }
166*103e46e4SHarish Mahendrakar
167*103e46e4SHarish Mahendrakar using mkvparser::Segment;
168*103e46e4SHarish Mahendrakar Segment* segment_ptr = nullptr;
169*103e46e4SHarish Mahendrakar if (Segment::CreateInstance(&reader, pos, segment_ptr)) {
170*103e46e4SHarish Mahendrakar return false;
171*103e46e4SHarish Mahendrakar }
172*103e46e4SHarish Mahendrakar
173*103e46e4SHarish Mahendrakar std::unique_ptr<Segment> segment(segment_ptr);
174*103e46e4SHarish Mahendrakar long result;
175*103e46e4SHarish Mahendrakar if ((result = segment->Load()) < 0) {
176*103e46e4SHarish Mahendrakar return false;
177*103e46e4SHarish Mahendrakar }
178*103e46e4SHarish Mahendrakar
179*103e46e4SHarish Mahendrakar const mkvparser::Cluster* cluster = segment->GetFirst();
180*103e46e4SHarish Mahendrakar if (!cluster || cluster->EOS()) {
181*103e46e4SHarish Mahendrakar return false;
182*103e46e4SHarish Mahendrakar }
183*103e46e4SHarish Mahendrakar
184*103e46e4SHarish Mahendrakar while (cluster && cluster->EOS() == false) {
185*103e46e4SHarish Mahendrakar if (cluster->GetTimeCode() < 0) {
186*103e46e4SHarish Mahendrakar return false;
187*103e46e4SHarish Mahendrakar }
188*103e46e4SHarish Mahendrakar
189*103e46e4SHarish Mahendrakar const mkvparser::BlockEntry* block = nullptr;
190*103e46e4SHarish Mahendrakar if (cluster->GetFirst(block) < 0) {
191*103e46e4SHarish Mahendrakar return false;
192*103e46e4SHarish Mahendrakar }
193*103e46e4SHarish Mahendrakar
194*103e46e4SHarish Mahendrakar while (block != NULL && block->EOS() == false) {
195*103e46e4SHarish Mahendrakar if (cluster->GetNext(block, block) < 0) {
196*103e46e4SHarish Mahendrakar return false;
197*103e46e4SHarish Mahendrakar }
198*103e46e4SHarish Mahendrakar }
199*103e46e4SHarish Mahendrakar
200*103e46e4SHarish Mahendrakar cluster = segment->GetNext(cluster);
201*103e46e4SHarish Mahendrakar }
202*103e46e4SHarish Mahendrakar
203*103e46e4SHarish Mahendrakar parser_out->segment = segment.release();
204*103e46e4SHarish Mahendrakar return true;
205*103e46e4SHarish Mahendrakar }
206*103e46e4SHarish Mahendrakar
ParseMkvFile(const std::string & webm_file)207*103e46e4SHarish Mahendrakar bool ParseMkvFile(const std::string& webm_file) {
208*103e46e4SHarish Mahendrakar MkvParser parser;
209*103e46e4SHarish Mahendrakar const bool result = ParseMkvFileReleaseParser(webm_file, &parser);
210*103e46e4SHarish Mahendrakar delete parser.segment;
211*103e46e4SHarish Mahendrakar delete parser.reader;
212*103e46e4SHarish Mahendrakar return result;
213*103e46e4SHarish Mahendrakar }
214*103e46e4SHarish Mahendrakar
215*103e46e4SHarish Mahendrakar } // namespace test
216