#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/parsers/h266_nalu_parser.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace {
struct VvcTestData {
std::string file_name;
int num_nalus;
};
}
class H266NaluParserTest : 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(H266NALU* target_nalu, H266NALU::Type nalu_type) {
while (true) {
H266NaluParser::Result res = parser_.AdvanceToNextNALU(target_nalu);
if (res == H266NaluParser::kEndOfStream) {
return false;
}
EXPECT_EQ(res, H266NaluParser::kOk);
if (target_nalu->nal_unit_type == nalu_type) {
return true;
}
}
}
H266NaluParser parser_;
std::unique_ptr<base::MemoryMappedFile> stream_;
};
TEST_F(H266NaluParserTest, RawVvcStreamFileParsingShouldSucceed) {
VvcTestData test_data[] = {
{"bear_180p.vvc", 54},
{"bbb_360p.vvc", 87},
};
for (const auto& data : test_data) {
LoadParserFile(data.file_name);
int num_parsed_nalus = 0;
while (true) {
H266NALU nalu;
H266NaluParser::Result res = parser_.AdvanceToNextNALU(&nalu);
if (res == H266NaluParser::kEndOfStream) {
DVLOG(1) << "Number of successfully parsed NALUs before EOS: "
<< num_parsed_nalus;
EXPECT_EQ(data.num_nalus, num_parsed_nalus);
break;
}
EXPECT_EQ(res, H266NaluParser::kOk);
++num_parsed_nalus;
DVLOG(4) << "Found NALU " << nalu.nal_unit_type;
EXPECT_EQ(res, H266NaluParser::kOk);
}
}
}
TEST_F(H266NaluParserTest, GetCurrentSubsamplesNormalShouldSucceed) {
constexpr uint8_t kStream[] = {
0x00,
0x00,
0x01,
0x00,
0x41,
0x00,
0x01,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x00,
0x01,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x00,
0x01,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x00,
0x01,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x00,
0x00,
0x01,
0x03,
0x04,
0x05,
0x06,
0x07,
0x00,
0x00,
0x01,
0x00,
0x79,
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);
H266NaluParser parser;
parser.SetEncryptedStream(kStream, std::size(kStream), subsamples);
H266NALU nalu;
EXPECT_EQ(H266NaluParser::kOk, parser.AdvanceToNextNALU(&nalu));
EXPECT_EQ(H266NALU::kIDRNoLeadingPicture, nalu.nal_unit_type);
auto nalu_subsamples = parser.GetCurrentSubsamples();
EXPECT_EQ(2u, nalu_subsamples.size());
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);
EXPECT_EQ(H266NaluParser::kOk, parser.AdvanceToNextNALU(&nalu));
EXPECT_EQ(H266NALU::kSPS, 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);
}
TEST_F(H266NaluParserTest,
GetCurrentSubsamplesSubsampleNotStartingAtNaluBoundaryShouldSucceed) {
constexpr uint8_t kStream[] = {
0x00,
0x00,
0x01,
0x00,
0x41,
0x00,
0x01,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x00,
0x01,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x00,
0x01,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0xaa,
0x01,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x00,
0x00,
0x01,
0x00,
0x79,
0xff,
0xfe,
0xfd,
0xee,
0x12,
0x33,
};
std::vector<SubsampleEntry> subsamples;
subsamples.emplace_back(5u, 24u);
subsamples.emplace_back(19u, 0u);
H266NaluParser parser;
parser.SetEncryptedStream(kStream, std::size(kStream), subsamples);
H266NALU nalu;
EXPECT_EQ(H266NaluParser::kOk, parser.AdvanceToNextNALU(&nalu));
EXPECT_EQ(H266NALU::kIDRNoLeadingPicture, nalu.nal_unit_type);
auto nalu_subsamples = parser.GetCurrentSubsamples();
EXPECT_EQ(2u, nalu_subsamples.size());
EXPECT_EQ(2u, nalu_subsamples[0].clear_bytes);
EXPECT_EQ(24u, nalu_subsamples[0].cypher_bytes);
EXPECT_EQ(8u, nalu_subsamples[1].clear_bytes);
EXPECT_EQ(0u, nalu_subsamples[1].cypher_bytes);
EXPECT_EQ(H266NaluParser::kOk, parser.AdvanceToNextNALU(&nalu));
EXPECT_EQ(H266NALU::kSPS, 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);
}
TEST_F(H266NaluParserTest, NaluWithOutOfRangeNuhLayerIdShouldBeIgnored) {
constexpr uint8_t kStream[] = {0x00, 0x00,
0x01,
0x56,
0x41,
0x00, 0x01, 0x02, 0x03, 0x04};
H266NaluParser parser;
parser.SetStream(kStream, std::size(kStream));
H266NALU nalu;
EXPECT_EQ(H266NaluParser::kIgnored, parser.AdvanceToNextNALU(&nalu));
}
TEST_F(H266NaluParserTest, ParseNaluWithInvalidTemporalIdShouldFail) {
constexpr uint8_t kStream[] = {
0x00, 0x00,
0x01,
0x00,
0x00,
0x50, 0x01, 0x02, 0x03, 0x04,
0x00, 0x00,
0x01,
0x00,
0x42,
0x00, 0x01, 0x02, 0x03, 0x04};
H266NaluParser parser;
parser.SetStream(kStream, std::size(kStream));
H266NALU nalu;
EXPECT_EQ(H266NaluParser::kInvalidStream, parser.AdvanceToNextNALU(&nalu));
EXPECT_EQ(H266NaluParser::kInvalidStream, parser.AdvanceToNextNALU(&nalu));
}
TEST_F(H266NaluParserTest, ParseNonIrapNaluWithNonZeroTemporalIdShouldSucceed) {
constexpr uint8_t kStream[] = {
0x00, 0x00,
0x01,
0x00,
0x0e,
0x94, 0x55, 0x48, 0x03, 0x04};
H266NaluParser parser;
parser.SetStream(kStream, std::size(kStream));
H266NALU nalu;
EXPECT_EQ(H266NaluParser::kOk, parser.AdvanceToNextNALU(&nalu));
}
}