02bc809f9d
The code will estimate the file duration if the last block duration is 0. This is not totally correct, but better then what we currently have. BUG=https://bugs.chromium.org/p/webm/issues/detail?id=1100 Change-Id: I8f81df0bd592e6f7b1925fa2637a2e09cf182742
216 lines
5.9 KiB
C++
216 lines
5.9 KiB
C++
// 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
|