// 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/vp9_header_parser.h" #include #include "gtest/gtest.h" #include "common/hdr_util.h" #include "mkvparser/mkvparser.h" #include "mkvparser/mkvreader.h" #include "testing/test_util.h" namespace { class Vp9HeaderParserTests : public ::testing::Test { public: Vp9HeaderParserTests() : is_reader_open_(false), segment_(NULL) {} ~Vp9HeaderParserTests() override { CloseReader(); if (segment_ != NULL) { delete segment_; segment_ = NULL; } } void CloseReader() { if (is_reader_open_) { reader_.Close(); } is_reader_open_ = false; } void CreateAndLoadSegment(const std::string& filename, int expected_doc_type_ver) { filename_ = test::GetTestFilePath(filename); ASSERT_EQ(0, reader_.Open(filename_.c_str())); is_reader_open_ = true; pos_ = 0; mkvparser::EBMLHeader ebml_header; ebml_header.Parse(&reader_, pos_); ASSERT_EQ(1, ebml_header.m_version); ASSERT_EQ(1, ebml_header.m_readVersion); ASSERT_STREQ("webm", ebml_header.m_docType); ASSERT_EQ(expected_doc_type_ver, ebml_header.m_docTypeVersion); ASSERT_EQ(2, ebml_header.m_docTypeReadVersion); ASSERT_EQ(0, mkvparser::Segment::CreateInstance(&reader_, pos_, segment_)); ASSERT_FALSE(HasFailure()); ASSERT_GE(0, segment_->Load()); } void CreateAndLoadSegment(const std::string& filename) { CreateAndLoadSegment(filename, 4); } // Load a corrupted segment with no expectation of correctness. void CreateAndLoadInvalidSegment(const std::string& filename) { filename_ = test::GetTestFilePath(filename); ASSERT_EQ(0, reader_.Open(filename_.c_str())); is_reader_open_ = true; pos_ = 0; mkvparser::EBMLHeader ebml_header; ebml_header.Parse(&reader_, pos_); ASSERT_EQ(0, mkvparser::Segment::CreateInstance(&reader_, pos_, segment_)); ASSERT_GE(0, segment_->Load()); } void ProcessTheFrames(bool invalid_bitstream) { unsigned char* data = NULL; size_t data_len = 0; const mkvparser::Tracks* const parser_tracks = segment_->GetTracks(); ASSERT_TRUE(parser_tracks != NULL); const mkvparser::Cluster* cluster = segment_->GetFirst(); ASSERT_TRUE(cluster != NULL); while ((cluster != NULL) && !cluster->EOS()) { const mkvparser::BlockEntry* block_entry; long status = cluster->GetFirst(block_entry); // NOLINT ASSERT_EQ(0, status); while ((block_entry != NULL) && !block_entry->EOS()) { const mkvparser::Block* const block = block_entry->GetBlock(); ASSERT_TRUE(block != NULL); const long long trackNum = block->GetTrackNumber(); // NOLINT const mkvparser::Track* const parser_track = parser_tracks->GetTrackByNumber( static_cast(trackNum)); // NOLINT ASSERT_TRUE(parser_track != NULL); const long long track_type = parser_track->GetType(); // NOLINT if (track_type == mkvparser::Track::kVideo) { const int frame_count = block->GetFrameCount(); for (int i = 0; i < frame_count; ++i) { const mkvparser::Block::Frame& frame = block->GetFrame(i); if (static_cast(frame.len) > data_len) { delete[] data; data = new unsigned char[frame.len]; ASSERT_TRUE(data != NULL); data_len = static_cast(frame.len); } ASSERT_FALSE(frame.Read(&reader_, data)); ASSERT_EQ(parser_.ParseUncompressedHeader(data, data_len), !invalid_bitstream); } } status = cluster->GetNext(block_entry, block_entry); ASSERT_EQ(0, status); } cluster = segment_->GetNext(cluster); } delete[] data; } protected: mkvparser::MkvReader reader_; bool is_reader_open_; mkvparser::Segment* segment_; std::string filename_; long long pos_; // NOLINT vp9_parser::Vp9HeaderParser parser_; }; TEST_F(Vp9HeaderParserTests, VideoOnlyFile) { ASSERT_NO_FATAL_FAILURE(CreateAndLoadSegment("test_stereo_left_right.webm")); ProcessTheFrames(false); EXPECT_EQ(256, parser_.width()); EXPECT_EQ(144, parser_.height()); EXPECT_EQ(1, parser_.column_tiles()); EXPECT_EQ(0, parser_.frame_parallel_mode()); } TEST_F(Vp9HeaderParserTests, Muxed) { ASSERT_NO_FATAL_FAILURE( CreateAndLoadSegment("bbb_480p_vp9_opus_1second.webm", 4)); ProcessTheFrames(false); EXPECT_EQ(854, parser_.width()); EXPECT_EQ(480, parser_.height()); EXPECT_EQ(2, parser_.column_tiles()); EXPECT_EQ(1, parser_.frame_parallel_mode()); } TEST_F(Vp9HeaderParserTests, Invalid) { const char* files[] = { "invalid/invalid_vp9_bitstream-bug_1416.webm", "invalid/invalid_vp9_bitstream-bug_1417.webm", }; for (int i = 0; i < static_cast(sizeof(files) / sizeof(files[0])); ++i) { SCOPED_TRACE(files[i]); ASSERT_NO_FATAL_FAILURE(CreateAndLoadInvalidSegment(files[i])); ProcessTheFrames(true); CloseReader(); delete segment_; segment_ = NULL; } } TEST_F(Vp9HeaderParserTests, API) { vp9_parser::Vp9HeaderParser parser; uint8_t data; EXPECT_FALSE(parser.ParseUncompressedHeader(NULL, 0)); EXPECT_FALSE(parser.ParseUncompressedHeader(NULL, 10)); EXPECT_FALSE(parser.ParseUncompressedHeader(&data, 0)); } } // namespace int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }