2014-04-01 01:34:59 +00:00
|
|
|
// Copyright 2014 The Chromium Authors. All rights reserved.
|
2023-10-11 08:49:50 +00:00
|
|
|
//
|
2014-04-01 01:34:59 +00:00
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
2023-10-10 23:51:11 +00:00
|
|
|
#include <packager/media/codecs/h264_parser.h>
|
2022-12-16 00:53:36 +00:00
|
|
|
|
2023-10-13 19:42:47 +00:00
|
|
|
#include <absl/log/log.h>
|
2014-08-21 22:40:44 +00:00
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
2023-10-13 19:42:47 +00:00
|
|
|
#include <packager/macros.h>
|
2023-10-10 23:51:11 +00:00
|
|
|
#include <packager/media/test/test_data_util.h>
|
2014-04-01 01:34:59 +00:00
|
|
|
|
2016-05-20 21:19:33 +00:00
|
|
|
namespace shaka {
|
2014-04-01 01:34:59 +00:00
|
|
|
namespace media {
|
|
|
|
|
2016-12-06 20:23:37 +00:00
|
|
|
namespace {
|
2017-03-21 00:46:36 +00:00
|
|
|
// This is the prefix of a video slice (including the nalu header) that only has
|
|
|
|
// the slice header. The actual slice header size is 30 bits (not including the
|
|
|
|
// nalu header).
|
2016-12-06 20:23:37 +00:00
|
|
|
const uint8_t kVideoSliceTrimmed[] = {
|
|
|
|
0x25, 0xB8, 0x20, 0x20, 0x63,
|
|
|
|
};
|
2017-03-21 00:46:36 +00:00
|
|
|
|
|
|
|
// This is another prefix of a video slice (including the nalu header).
|
|
|
|
// The slice header is 67 bits. So the first 10 bytes is the data before
|
|
|
|
// slice_data().
|
|
|
|
// Note also that this is from a real video slice and
|
|
|
|
// PPS's entropy_coding_mode_flag is true. So slice_data() starts from the 11th
|
|
|
|
// byte.
|
|
|
|
const uint8_t kVideoSliceTrimmedMultipleLumaWeights[] = {
|
|
|
|
0x41, 0x9A, 0x72, 0x78, 0x43, 0xC9, 0x94, 0xC0,
|
|
|
|
0x8C, 0xFF, 0xC1, 0x54,
|
|
|
|
};
|
|
|
|
|
|
|
|
// SPS for KVideoSliceTrimmedMultipleLumaWeights.
|
|
|
|
const uint8_t kSps2[] = {
|
|
|
|
0x67, 0x64, 0x00, 0x28, 0xAC, 0xB2, 0x00, 0xF0, 0x04, 0x4F,
|
|
|
|
0xCB, 0x80, 0xB5, 0x01, 0x01, 0x01, 0x40, 0x00, 0x00, 0x03,
|
|
|
|
0x00, 0x40, 0x00, 0x00, 0x0F, 0x03, 0xC6, 0x0C, 0x92,
|
|
|
|
};
|
|
|
|
|
|
|
|
// PPS for KVideoSliceTrimmedMultipleLumaWeights.
|
|
|
|
const uint8_t kPps2[] = {
|
|
|
|
0x68, 0xEB, 0xCC, 0xB2, 0x2C,
|
|
|
|
};
|
2016-12-06 20:23:37 +00:00
|
|
|
} // namespace
|
|
|
|
|
2014-04-01 01:34:59 +00:00
|
|
|
TEST(H264ParserTest, StreamFileParsing) {
|
2014-10-07 21:41:40 +00:00
|
|
|
std::vector<uint8_t> buffer = ReadTestDataFile("test-25fps.h264");
|
2023-07-17 20:16:22 +00:00
|
|
|
ASSERT_FALSE(buffer.empty());
|
2014-10-07 21:41:40 +00:00
|
|
|
|
2014-04-01 01:34:59 +00:00
|
|
|
// Number of NALUs in the test stream to be parsed.
|
|
|
|
int num_nalus = 759;
|
|
|
|
|
|
|
|
H264Parser parser;
|
2016-04-21 20:35:49 +00:00
|
|
|
NaluReader reader(Nalu::kH264, kIsAnnexbByteStream, buffer.data(),
|
2016-02-26 22:34:14 +00:00
|
|
|
buffer.size());
|
2014-04-01 01:34:59 +00:00
|
|
|
|
|
|
|
// Parse until the end of stream/unsupported stream/error in stream is found.
|
|
|
|
int num_parsed_nalus = 0;
|
|
|
|
while (true) {
|
2014-04-01 21:18:11 +00:00
|
|
|
H264SliceHeader shdr;
|
|
|
|
H264SEIMessage sei_msg;
|
2016-02-05 19:08:07 +00:00
|
|
|
Nalu nalu;
|
|
|
|
NaluReader::Result res = reader.Advance(&nalu);
|
|
|
|
if (res == NaluReader::kEOStream) {
|
2014-04-01 01:34:59 +00:00
|
|
|
DVLOG(1) << "Number of successfully parsed NALUs before EOS: "
|
|
|
|
<< num_parsed_nalus;
|
|
|
|
ASSERT_EQ(num_nalus, num_parsed_nalus);
|
|
|
|
return;
|
|
|
|
}
|
2016-02-05 19:08:07 +00:00
|
|
|
ASSERT_EQ(res, NaluReader::kOk);
|
2014-04-01 01:34:59 +00:00
|
|
|
|
|
|
|
++num_parsed_nalus;
|
|
|
|
|
|
|
|
int id;
|
2016-02-05 19:08:07 +00:00
|
|
|
switch (nalu.type()) {
|
|
|
|
case Nalu::H264_IDRSlice:
|
|
|
|
case Nalu::H264_NonIDRSlice:
|
2014-04-01 01:34:59 +00:00
|
|
|
ASSERT_EQ(parser.ParseSliceHeader(nalu, &shdr), H264Parser::kOk);
|
|
|
|
break;
|
|
|
|
|
2016-02-05 19:08:07 +00:00
|
|
|
case Nalu::H264_SPS:
|
2016-03-14 19:09:26 +00:00
|
|
|
ASSERT_EQ(parser.ParseSps(nalu, &id), H264Parser::kOk);
|
2014-04-01 01:34:59 +00:00
|
|
|
break;
|
|
|
|
|
2016-02-05 19:08:07 +00:00
|
|
|
case Nalu::H264_PPS:
|
2016-03-14 19:09:26 +00:00
|
|
|
ASSERT_EQ(parser.ParsePps(nalu, &id), H264Parser::kOk);
|
2014-04-01 01:34:59 +00:00
|
|
|
break;
|
|
|
|
|
2016-02-05 19:08:07 +00:00
|
|
|
case Nalu::H264_SEIMessage:
|
|
|
|
ASSERT_EQ(parser.ParseSEI(nalu, &sei_msg), H264Parser::kOk);
|
2014-04-01 01:34:59 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
// Skip unsupported NALU.
|
|
|
|
DVLOG(4) << "Skipping unsupported NALU";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-06 20:23:37 +00:00
|
|
|
// Verify that SliceHeader::nalu_data points to the beginning of nal unit.
|
|
|
|
// Also verify that header_bit_size is set correctly.
|
|
|
|
TEST(H264ParserTest, SliceHeaderSize) {
|
2022-12-16 00:53:36 +00:00
|
|
|
const uint8_t kSps[] = {
|
|
|
|
0x27, 0x4D, 0x40, 0x0D, 0xA9, 0x18, 0x28, 0x3E, 0x60, 0x0D,
|
|
|
|
0x41, 0x80, 0x41, 0xAD, 0xB0, 0xAD, 0x7B, 0xDF, 0x01,
|
|
|
|
};
|
|
|
|
const uint8_t kPps[] = {
|
|
|
|
0x28,
|
|
|
|
0xDE,
|
|
|
|
0x9,
|
|
|
|
0x88,
|
|
|
|
};
|
|
|
|
|
2016-12-06 20:23:37 +00:00
|
|
|
H264Parser parser;
|
|
|
|
int unused_id;
|
|
|
|
Nalu nalu;
|
2022-12-16 00:53:36 +00:00
|
|
|
ASSERT_TRUE(nalu.Initialize(Nalu::kH264, kSps, std::size(kSps)));
|
2016-12-06 20:23:37 +00:00
|
|
|
ASSERT_EQ(H264Parser::kOk, parser.ParseSps(nalu, &unused_id));
|
2022-12-16 00:53:36 +00:00
|
|
|
ASSERT_TRUE(nalu.Initialize(Nalu::kH264, kPps, std::size(kPps)));
|
2016-12-06 20:23:37 +00:00
|
|
|
ASSERT_EQ(H264Parser::kOk, parser.ParsePps(nalu, &unused_id));
|
|
|
|
ASSERT_TRUE(nalu.Initialize(Nalu::kH264, kVideoSliceTrimmed,
|
2022-12-16 00:53:36 +00:00
|
|
|
std::size(kVideoSliceTrimmed)));
|
2016-12-06 20:23:37 +00:00
|
|
|
|
|
|
|
H264SliceHeader slice_header;
|
|
|
|
ASSERT_EQ(H264Parser::kOk, parser.ParseSliceHeader(nalu, &slice_header));
|
|
|
|
EXPECT_EQ(nalu.data(), slice_header.nalu_data);
|
|
|
|
EXPECT_EQ(30u, slice_header.header_bit_size);
|
|
|
|
}
|
|
|
|
|
2017-03-21 00:46:36 +00:00
|
|
|
TEST(H264ParserTest, PredWeightTable) {
|
|
|
|
H264Parser parser;
|
|
|
|
int unused_id;
|
|
|
|
Nalu nalu;
|
2022-12-16 00:53:36 +00:00
|
|
|
ASSERT_TRUE(nalu.Initialize(Nalu::kH264, kSps2, std::size(kSps2)));
|
2017-03-21 00:46:36 +00:00
|
|
|
ASSERT_EQ(H264Parser::kOk, parser.ParseSps(nalu, &unused_id));
|
2022-12-16 00:53:36 +00:00
|
|
|
ASSERT_TRUE(nalu.Initialize(Nalu::kH264, kPps2, std::size(kPps2)));
|
2017-03-21 00:46:36 +00:00
|
|
|
ASSERT_EQ(H264Parser::kOk, parser.ParsePps(nalu, &unused_id));
|
|
|
|
ASSERT_TRUE(
|
|
|
|
nalu.Initialize(Nalu::kH264, kVideoSliceTrimmedMultipleLumaWeights,
|
2022-12-16 00:53:36 +00:00
|
|
|
std::size(kVideoSliceTrimmedMultipleLumaWeights)));
|
2017-03-21 00:46:36 +00:00
|
|
|
|
|
|
|
H264SliceHeader slice_header;
|
|
|
|
ASSERT_EQ(H264Parser::kOk, parser.ParseSliceHeader(nalu, &slice_header));
|
|
|
|
|
|
|
|
EXPECT_TRUE(slice_header.num_ref_idx_active_override_flag);
|
|
|
|
ASSERT_EQ(3, slice_header.num_ref_idx_l0_active_minus1);
|
|
|
|
|
|
|
|
const H264WeightingFactors& pred_weight_table =
|
|
|
|
slice_header.pred_weight_table_l0;
|
|
|
|
|
|
|
|
EXPECT_FALSE(pred_weight_table.luma_weight_flag[0]);
|
|
|
|
EXPECT_TRUE(pred_weight_table.luma_weight_flag[1]);
|
|
|
|
EXPECT_FALSE(pred_weight_table.luma_weight_flag[2]);
|
|
|
|
EXPECT_FALSE(pred_weight_table.luma_weight_flag[3]);
|
|
|
|
|
|
|
|
// Luma checks.
|
|
|
|
EXPECT_EQ(1, pred_weight_table.luma_weight[0]);
|
|
|
|
EXPECT_EQ(1, pred_weight_table.luma_weight[1]);
|
|
|
|
EXPECT_EQ(1, pred_weight_table.luma_weight[2]);
|
|
|
|
EXPECT_EQ(1, pred_weight_table.luma_weight[3]);
|
|
|
|
EXPECT_EQ(0, pred_weight_table.luma_offset[0]);
|
|
|
|
EXPECT_EQ(-1, pred_weight_table.luma_offset[1]);
|
|
|
|
EXPECT_EQ(0, pred_weight_table.luma_offset[2]);
|
|
|
|
EXPECT_EQ(0, pred_weight_table.luma_offset[3]);
|
|
|
|
|
|
|
|
EXPECT_FALSE(pred_weight_table.chroma_weight_flag[0]);
|
|
|
|
EXPECT_FALSE(pred_weight_table.chroma_weight_flag[1]);
|
|
|
|
EXPECT_FALSE(pred_weight_table.chroma_weight_flag[2]);
|
|
|
|
EXPECT_FALSE(pred_weight_table.chroma_weight_flag[3]);
|
|
|
|
|
|
|
|
// Chroma checks.
|
|
|
|
// U plane.
|
|
|
|
EXPECT_EQ(1, pred_weight_table.chroma_weight[0][0]);
|
|
|
|
EXPECT_EQ(1, pred_weight_table.chroma_weight[1][0]);
|
|
|
|
EXPECT_EQ(1, pred_weight_table.chroma_weight[2][0]);
|
|
|
|
EXPECT_EQ(1, pred_weight_table.chroma_weight[3][0]);
|
|
|
|
EXPECT_EQ(0, pred_weight_table.chroma_offset[0][0]);
|
|
|
|
EXPECT_EQ(0, pred_weight_table.chroma_offset[1][0]);
|
|
|
|
EXPECT_EQ(0, pred_weight_table.chroma_offset[2][0]);
|
|
|
|
EXPECT_EQ(0, pred_weight_table.chroma_offset[3][0]);
|
|
|
|
|
|
|
|
// V plane.
|
|
|
|
EXPECT_EQ(1, pred_weight_table.chroma_weight[0][1]);
|
|
|
|
EXPECT_EQ(1, pred_weight_table.chroma_weight[1][1]);
|
|
|
|
EXPECT_EQ(1, pred_weight_table.chroma_weight[2][1]);
|
|
|
|
EXPECT_EQ(1, pred_weight_table.chroma_weight[3][1]);
|
|
|
|
EXPECT_EQ(0, pred_weight_table.chroma_offset[0][1]);
|
|
|
|
EXPECT_EQ(0, pred_weight_table.chroma_offset[1][1]);
|
|
|
|
EXPECT_EQ(0, pred_weight_table.chroma_offset[2][1]);
|
|
|
|
EXPECT_EQ(0, pred_weight_table.chroma_offset[3][1]);
|
|
|
|
}
|
|
|
|
|
2019-09-23 06:24:33 +00:00
|
|
|
TEST(H264ParserTest, ParseSps) {
|
|
|
|
const uint8_t kSps[] = {0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xB4,
|
|
|
|
0x2F, 0xF9, 0x7F, 0xF0, 0x00, 0x80, 0x00, 0x91,
|
|
|
|
0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA,
|
|
|
|
0x60, 0x0F, 0x16, 0x2D, 0x96};
|
|
|
|
|
|
|
|
H264Parser parser;
|
|
|
|
int sps_id = 0;
|
|
|
|
Nalu nalu;
|
2022-12-16 00:53:36 +00:00
|
|
|
ASSERT_TRUE(nalu.Initialize(Nalu::kH264, kSps, std::size(kSps)));
|
2019-09-23 06:24:33 +00:00
|
|
|
ASSERT_EQ(H264Parser::kOk, parser.ParseSps(nalu, &sps_id));
|
|
|
|
|
|
|
|
const H264Sps* sps = parser.GetSps(sps_id);
|
|
|
|
ASSERT_TRUE(sps);
|
|
|
|
|
|
|
|
EXPECT_EQ(100, sps->profile_idc);
|
|
|
|
EXPECT_EQ(30, sps->level_idc);
|
|
|
|
EXPECT_EQ(0, sps->transfer_characteristics);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(H264ParserTest, ParseSpsWithTransferCharacteristics) {
|
|
|
|
const uint8_t kSps[] = {
|
|
|
|
0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xB4, 0x2F, 0xF9, 0x7F, 0xF0,
|
|
|
|
0x00, 0x80, 0x00, 0x96, 0xA1, 0x22, 0x01, 0x28, 0x00, 0x00, 0x03, 0x00,
|
|
|
|
0x08, 0x00, 0x00, 0x03, 0x01, 0x80, 0x78, 0xB1, 0x6C, 0xB0,
|
|
|
|
};
|
|
|
|
|
|
|
|
H264Parser parser;
|
|
|
|
int sps_id = 0;
|
|
|
|
Nalu nalu;
|
2022-12-16 00:53:36 +00:00
|
|
|
ASSERT_TRUE(nalu.Initialize(Nalu::kH264, kSps, std::size(kSps)));
|
2019-09-23 06:24:33 +00:00
|
|
|
ASSERT_EQ(H264Parser::kOk, parser.ParseSps(nalu, &sps_id));
|
|
|
|
|
|
|
|
const H264Sps* sps = parser.GetSps(sps_id);
|
|
|
|
ASSERT_TRUE(sps);
|
|
|
|
|
|
|
|
EXPECT_EQ(100, sps->profile_idc);
|
|
|
|
EXPECT_EQ(30, sps->level_idc);
|
|
|
|
EXPECT_EQ(16, sps->transfer_characteristics);
|
|
|
|
}
|
|
|
|
|
2015-09-16 22:22:22 +00:00
|
|
|
TEST(H264ParserTest, ExtractResolutionFromSpsData) {
|
2015-08-20 19:06:02 +00:00
|
|
|
const uint8_t kSps[] = {0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xB4,
|
|
|
|
0x2F, 0xF9, 0x7F, 0xF0, 0x00, 0x80, 0x00, 0x91,
|
|
|
|
0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA,
|
|
|
|
0x60, 0x0F, 0x16, 0x2D, 0x96};
|
|
|
|
|
2015-10-27 00:52:57 +00:00
|
|
|
H264Parser parser;
|
|
|
|
int sps_id = 0;
|
2016-02-05 19:08:07 +00:00
|
|
|
Nalu nalu;
|
2022-12-16 00:53:36 +00:00
|
|
|
ASSERT_TRUE(nalu.Initialize(Nalu::kH264, kSps, std::size(kSps)));
|
2016-03-14 19:09:26 +00:00
|
|
|
ASSERT_EQ(H264Parser::kOk, parser.ParseSps(nalu, &sps_id));
|
2015-10-27 00:52:57 +00:00
|
|
|
|
2015-09-16 22:22:22 +00:00
|
|
|
uint32_t coded_width = 0;
|
|
|
|
uint32_t coded_height = 0;
|
2015-08-20 19:06:02 +00:00
|
|
|
uint32_t pixel_width = 0;
|
|
|
|
uint32_t pixel_height = 0;
|
2016-03-14 19:09:26 +00:00
|
|
|
ASSERT_TRUE(ExtractResolutionFromSps(*parser.GetSps(sps_id), &coded_width,
|
2015-10-27 00:52:57 +00:00
|
|
|
&coded_height, &pixel_width,
|
|
|
|
&pixel_height));
|
2015-09-16 22:22:22 +00:00
|
|
|
EXPECT_EQ(720u, coded_width);
|
|
|
|
EXPECT_EQ(360u, coded_height);
|
2015-08-20 19:06:02 +00:00
|
|
|
EXPECT_EQ(8u, pixel_width);
|
|
|
|
EXPECT_EQ(9u, pixel_height);
|
|
|
|
}
|
|
|
|
|
2015-09-16 22:22:22 +00:00
|
|
|
TEST(H264ParserTest, ExtractResolutionFromSpsDataWithCropping) {
|
|
|
|
// 320x192 with frame_crop_bottom_offset of 6.
|
|
|
|
const uint8_t kSps[] = {0x67, 0x64, 0x00, 0x0C, 0xAC, 0xD9, 0x41, 0x41, 0x9F,
|
|
|
|
0x9F, 0x01, 0x10, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00,
|
|
|
|
0x00, 0x03, 0x03, 0x00, 0xF1, 0x42, 0x99, 0x60};
|
|
|
|
|
2015-10-27 00:52:57 +00:00
|
|
|
H264Parser parser;
|
|
|
|
int sps_id = 0;
|
2016-02-05 19:08:07 +00:00
|
|
|
Nalu nalu;
|
2022-12-16 00:53:36 +00:00
|
|
|
ASSERT_TRUE(nalu.Initialize(Nalu::kH264, kSps, std::size(kSps)));
|
2016-03-14 19:09:26 +00:00
|
|
|
ASSERT_EQ(H264Parser::kOk, parser.ParseSps(nalu, &sps_id));
|
2015-10-27 00:52:57 +00:00
|
|
|
|
2015-09-16 22:22:22 +00:00
|
|
|
uint32_t coded_width = 0;
|
|
|
|
uint32_t coded_height = 0;
|
|
|
|
uint32_t pixel_width = 0;
|
|
|
|
uint32_t pixel_height = 0;
|
2016-03-14 19:09:26 +00:00
|
|
|
ASSERT_TRUE(ExtractResolutionFromSps(*parser.GetSps(sps_id), &coded_width,
|
2015-10-27 00:52:57 +00:00
|
|
|
&coded_height, &pixel_width,
|
|
|
|
&pixel_height));
|
2015-09-16 22:22:22 +00:00
|
|
|
EXPECT_EQ(320u, coded_width);
|
|
|
|
EXPECT_EQ(180u, coded_height);
|
|
|
|
EXPECT_EQ(1u, pixel_width);
|
|
|
|
EXPECT_EQ(1u, pixel_height);
|
|
|
|
}
|
|
|
|
|
2014-04-01 01:34:59 +00:00
|
|
|
} // namespace media
|
2016-05-20 21:19:33 +00:00
|
|
|
} // namespace shaka
|