blob: a650f36242d69ad356a8e00a4378c4e0ba30f241 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <string>
#include "base/files/file_path.h"
#include "base/files/memory_mapped_file.h"
#include "base/logging.h"
#include "media/base/subsample_entry.h"
#include "media/base/test_data_util.h"
#include "media/video/h265_nalu_parser.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace {
struct HevcTestData {
std::string file_name;
// Number of NALUs in the test stream to be parsed.
int num_nalus;
};
} // namespace
class H265NaluParserTest : public ::testing::Test {
protected:
void LoadParserFile(std::string file_name) {
parser_.Reset();
base::FilePath file_path = GetTestDataFilePath(file_name);
stream_ = std::make_unique<base::MemoryMappedFile>();
ASSERT_TRUE(stream_->Initialize(file_path))
<< "Couldn't open stream file: " << file_path.MaybeAsASCII();
parser_.SetStream(stream_->data(), stream_->length());
}
bool ParseNalusUntilNut(H265NALU* target_nalu, H265NALU::Type nalu_type) {
while (true) {
H265NaluParser::Result res = parser_.AdvanceToNextNALU(target_nalu);
if (res == H265NaluParser::kEOStream) {
return false;
}
EXPECT_EQ(res, H265NaluParser::kOk);
if (target_nalu->nal_unit_type == nalu_type)
return true;
}
}
H265NaluParser parser_;
std::unique_ptr<base::MemoryMappedFile> stream_;
};
TEST_F(H265NaluParserTest, RawHevcStreamFileParsing) {
HevcTestData test_data[] = {
{"bear.hevc", 35},
{"bbb.hevc", 64},
};
for (const auto& data : test_data) {
LoadParserFile(data.file_name);
// Parse until the end of stream/unsupported stream/error in stream is
// found.
int num_parsed_nalus = 0;
while (true) {
H265NALU nalu;
H265NaluParser::Result res = parser_.AdvanceToNextNALU(&nalu);
if (res == H265NaluParser::kEOStream) {
DVLOG(1) << "Number of successfully parsed NALUs before EOS: "
<< num_parsed_nalus;
EXPECT_EQ(data.num_nalus, num_parsed_nalus);
break;
}
EXPECT_EQ(res, H265NaluParser::kOk);
++num_parsed_nalus;
DVLOG(4) << "Found NALU " << nalu.nal_unit_type;
EXPECT_EQ(res, H265NaluParser::kOk);
}
}
}
// Verify that GetCurrentSubsamples works.
TEST_F(H265NaluParserTest, GetCurrentSubsamplesNormal) {
constexpr uint8_t kStream[] = {
// First NALU.
// Clear bytes = 5.
0x00, 0x00, 0x01, // start code.
0x28, 0x00, // Nalu type = 20, IDR slice.
// Below is bogus data.
// Encrypted bytes = 15.
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06,
// Clear bytes = 5.
0x07, 0x00, 0x01, 0x02, 0x03,
// Encrypted until next NALU. Encrypted bytes = 20.
0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
// Note that this is still in the encrypted region but looks like a start
// code.
0x00, 0x00, 0x01, 0x03, 0x04, 0x05, 0x06, 0x07,
// Second NALU. Completely clear.
// Clear bytes = 11.
0x00, 0x00, 0x01, // start code.
0x42, 0x00, // nalu type = 33, SPS.
// Bogus data.
0xff, 0xfe, 0xfd, 0xee, 0x12, 0x33,
};
std::vector<SubsampleEntry> subsamples;
subsamples.emplace_back(5u, 15u);
subsamples.emplace_back(5u, 20u);
subsamples.emplace_back(11u, 0u);
H265NaluParser parser;
parser.SetEncryptedStream(kStream, base::size(kStream), subsamples);
H265NALU nalu;
EXPECT_EQ(H265NaluParser::kOk, parser.AdvanceToNextNALU(&nalu));
EXPECT_EQ(H265NALU::IDR_N_LP, nalu.nal_unit_type);
auto nalu_subsamples = parser.GetCurrentSubsamples();
EXPECT_EQ(2u, nalu_subsamples.size());
// Note that nalu->data starts from the NALU header, i.e. does not include
// the start code.
EXPECT_EQ(2u, nalu_subsamples[0].clear_bytes);
EXPECT_EQ(15u, nalu_subsamples[0].cypher_bytes);
EXPECT_EQ(5u, nalu_subsamples[1].clear_bytes);
EXPECT_EQ(20u, nalu_subsamples[1].cypher_bytes);
// Make sure that it reached the next NALU.
EXPECT_EQ(H265NaluParser::kOk, parser.AdvanceToNextNALU(&nalu));
EXPECT_EQ(H265NALU::SPS_NUT, nalu.nal_unit_type);
nalu_subsamples = parser.GetCurrentSubsamples();
EXPECT_EQ(1u, nalu_subsamples.size());
EXPECT_EQ(8u, nalu_subsamples[0].clear_bytes);
EXPECT_EQ(0u, nalu_subsamples[0].cypher_bytes);
}
// Verify that subsamples starting at non-NALU boundary also works.
TEST_F(H265NaluParserTest,
GetCurrentSubsamplesSubsampleNotStartingAtNaluBoundary) {
constexpr uint8_t kStream[] = {
// First NALU.
// Clear bytes = 5.
0x00, 0x00, 0x01, // start code.
0x28, 0x00, // Nalu type = 20, IDR slice.
// Below is bogus data.
// Encrypted bytes = 24.
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
// Clear bytes = 19. The rest is in the clear. Note that this is not at
// a NALU boundary and a NALU starts below.
0xaa, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
// Second NALU. Completely clear.
0x00, 0x00, 0x01, // start code.
0x42, 0x00, // nalu type = 33, SPS.
// Bogus data.
0xff, 0xfe, 0xfd, 0xee, 0x12, 0x33,
};
std::vector<SubsampleEntry> subsamples;
subsamples.emplace_back(5u, 24u);
subsamples.emplace_back(19u, 0u);
H265NaluParser parser;
parser.SetEncryptedStream(kStream, base::size(kStream), subsamples);
H265NALU nalu;
EXPECT_EQ(H265NaluParser::kOk, parser.AdvanceToNextNALU(&nalu));
EXPECT_EQ(H265NALU::IDR_N_LP, nalu.nal_unit_type);
auto nalu_subsamples = parser.GetCurrentSubsamples();
EXPECT_EQ(2u, nalu_subsamples.size());
// Note that nalu->data starts from the NALU header, i.e. does not include
// the start code.
EXPECT_EQ(2u, nalu_subsamples[0].clear_bytes);
EXPECT_EQ(24u, nalu_subsamples[0].cypher_bytes);
// The nalu ends with 8 more clear bytes. The last 10 bytes should be
// associated with the next nalu.
EXPECT_EQ(8u, nalu_subsamples[1].clear_bytes);
EXPECT_EQ(0u, nalu_subsamples[1].cypher_bytes);
EXPECT_EQ(H265NaluParser::kOk, parser.AdvanceToNextNALU(&nalu));
EXPECT_EQ(H265NALU::SPS_NUT, nalu.nal_unit_type);
nalu_subsamples = parser.GetCurrentSubsamples();
EXPECT_EQ(1u, nalu_subsamples.size());
// Although the input had 10 more bytes, since nalu->data starts from the nalu
// header, there's only 7 more bytes left.
EXPECT_EQ(8u, nalu_subsamples[0].clear_bytes);
EXPECT_EQ(0u, nalu_subsamples[0].cypher_bytes);
}
} // namespace media