/* * Copyright (c) 2023 The WebRTC 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_video/h265/h265_pps_parser.h" #include #include "common_video/h265/h265_common.h" #include "rtc_base/arraysize.h" #include "rtc_base/bit_buffer.h" #include "rtc_base/buffer.h" #include "rtc_base/checks.h" #include "test/gtest.h" namespace webrtc { namespace { constexpr size_t kPpsBufferMaxSize = 256; constexpr uint32_t kIgnored = 0; } // namespace void WritePps(const H265PpsParser::PpsState& pps, bool cu_qp_delta_enabled_flag, bool tiles_enabled_flag, bool uniform_spacing_flag, bool deblocking_filter_control_present_flag, bool pps_deblocking_filter_disabled_flag, bool pps_scaling_list_data_present_flag, bool scaling_list_pred_mode_flag, rtc::Buffer* out_buffer) { uint8_t data[kPpsBufferMaxSize] = {0}; rtc::BitBufferWriter bit_buffer(data, kPpsBufferMaxSize); // pic_parameter_set_id: ue(v) bit_buffer.WriteExponentialGolomb(pps.pps_id); // seq_parameter_set_id: ue(v) bit_buffer.WriteExponentialGolomb(pps.sps_id); // dependent_slice_segments_enabled_flag: u(1) bit_buffer.WriteBits(pps.dependent_slice_segments_enabled_flag, 1); // output_flag_present_flag: u(1) bit_buffer.WriteBits(pps.output_flag_present_flag, 1); // num_extra_slice_header_bits: u(3) bit_buffer.WriteBits(pps.num_extra_slice_header_bits, 3); // sign_data_hiding_enabled_flag: u(1) bit_buffer.WriteBits(1, 1); // cabac_init_present_flag: u(1) bit_buffer.WriteBits(pps.cabac_init_present_flag, 1); // num_ref_idx_l0_default_active_minus1: ue(v) bit_buffer.WriteExponentialGolomb(pps.num_ref_idx_l0_default_active_minus1); // num_ref_idx_l1_default_active_minus1: ue(v) bit_buffer.WriteExponentialGolomb(pps.num_ref_idx_l1_default_active_minus1); // init_qp_minus26: se(v) bit_buffer.WriteSignedExponentialGolomb(pps.init_qp_minus26); // constrained_intra_pred_flag: u(1) bit_buffer.WriteBits(0, 1); // transform_skip_enabled_flag: u(1) bit_buffer.WriteBits(0, 1); // cu_qp_delta_enabled_flag: u(1) bit_buffer.WriteBits(cu_qp_delta_enabled_flag, 1); if (cu_qp_delta_enabled_flag) { // diff_cu_qp_delta_depth: ue(v) bit_buffer.WriteExponentialGolomb(kIgnored); } // pps_cb_qp_offset: se(v) bit_buffer.WriteSignedExponentialGolomb(kIgnored); // pps_cr_qp_offset: se(v) bit_buffer.WriteSignedExponentialGolomb(kIgnored); // pps_slice_chroma_qp_offsets_present_flag: u(1) bit_buffer.WriteBits(0, 1); // weighted_pred_flag: u(1) bit_buffer.WriteBits(pps.weighted_pred_flag, 1); // weighted_bipred_flag: u(1) bit_buffer.WriteBits(pps.weighted_bipred_flag, 1); // transquant_bypass_enabled_flag: u(1) bit_buffer.WriteBits(0, 1); // tiles_enabled_flag: u(1) bit_buffer.WriteBits(tiles_enabled_flag, 1); // entropy_coding_sync_enabled_flag: u(1) bit_buffer.WriteBits(1, 1); if (tiles_enabled_flag) { // num_tile_columns_minus1: ue(v) bit_buffer.WriteExponentialGolomb(6); // num_tile_rows_minus1: ue(v) bit_buffer.WriteExponentialGolomb(1); // uniform_spacing_flag: u(1) bit_buffer.WriteBits(0, 1); if (!uniform_spacing_flag) { for (uint32_t i = 0; i < 6; i++) { // column_width_minus1: ue(v) bit_buffer.WriteExponentialGolomb(kIgnored); } for (uint32_t i = 0; i < 1; i++) { // row_height_minus1: ue(v) bit_buffer.WriteExponentialGolomb(kIgnored); } // loop_filter_across_tiles_enabled_flag: u(1) bit_buffer.WriteBits(0, 1); } } // pps_loop_filter_across_slices_enabled_flag: u(1) bit_buffer.WriteBits(1, 1); // deblocking_filter_control_present_flag: u(1) bit_buffer.WriteBits(deblocking_filter_control_present_flag, 1); if (deblocking_filter_control_present_flag) { // deblocking_filter_override_enabled_flag: u(1) bit_buffer.WriteBits(0, 1); // pps_deblocking_filter_disabled_flag: u(1) bit_buffer.WriteBits(pps_deblocking_filter_disabled_flag, 1); if (!pps_deblocking_filter_disabled_flag) { // pps_beta_offset_div2: se(v) bit_buffer.WriteSignedExponentialGolomb(kIgnored); // pps_tc_offset_div2: se(v) bit_buffer.WriteSignedExponentialGolomb(kIgnored); } } // pps_scaling_list_data_present_flag: u(1) bit_buffer.WriteBits(pps_scaling_list_data_present_flag, 1); if (pps_scaling_list_data_present_flag) { for (int size_id = 0; size_id < 4; size_id++) { for (int matrix_id = 0; matrix_id < 6; matrix_id += (size_id == 3) ? 3 : 1) { // scaling_list_pred_mode_flag: u(1) bit_buffer.WriteBits(scaling_list_pred_mode_flag, 1); if (!scaling_list_pred_mode_flag) { // scaling_list_pred_matrix_id_delta: ue(v) bit_buffer.WriteExponentialGolomb(kIgnored); } else { uint32_t coef_num = std::min(64, 1 << (4 + (size_id << 1))); if (size_id > 1) { // scaling_list_dc_coef_minus8: se(v) bit_buffer.WriteSignedExponentialGolomb(kIgnored); } for (uint32_t i = 0; i < coef_num; i++) { // scaling_list_delta_coef: se(v) bit_buffer.WriteSignedExponentialGolomb(kIgnored); } } } } } // lists_modification_present_flag: u(1) bit_buffer.WriteBits(pps.lists_modification_present_flag, 1); // log2_parallel_merge_level_minus2: ue(v) bit_buffer.WriteExponentialGolomb(kIgnored); // slice_segment_header_extension_present_flag: u(1) bit_buffer.WriteBits(0, 1); size_t byte_offset; size_t bit_offset; bit_buffer.GetCurrentOffset(&byte_offset, &bit_offset); if (bit_offset > 0) { bit_buffer.WriteBits(0, 8 - bit_offset); bit_buffer.GetCurrentOffset(&byte_offset, &bit_offset); } H265::WriteRbsp(data, byte_offset, out_buffer); } class H265PpsParserTest : public ::testing::Test { public: H265PpsParserTest() {} ~H265PpsParserTest() override {} void RunTest() { VerifyParsing(generated_pps_, false, false, false, false, false, false, false); // Enable flags to cover more path VerifyParsing(generated_pps_, true, true, false, true, true, true, false); } void VerifyParsing(const H265PpsParser::PpsState& pps, bool cu_qp_delta_enabled_flag, bool tiles_enabled_flag, bool uniform_spacing_flag, bool deblocking_filter_control_present_flag, bool pps_deblocking_filter_disabled_flag, bool pps_scaling_list_data_present_flag, bool scaling_list_pred_mode_flag) { buffer_.Clear(); WritePps(pps, cu_qp_delta_enabled_flag, tiles_enabled_flag, uniform_spacing_flag, deblocking_filter_control_present_flag, pps_deblocking_filter_disabled_flag, pps_scaling_list_data_present_flag, scaling_list_pred_mode_flag, &buffer_); const uint8_t sps_buffer[] = { 0x01, 0x04, 0x08, 0x00, 0x00, 0x03, 0x00, 0x9d, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x5d, 0xb0, 0x02, 0x80, 0x80, 0x2d, 0x16, 0x59, 0x59, 0xa4, 0x93, 0x2b, 0x80, 0x40, 0x00, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x07, 0x82}; H265SpsParser::SpsState parsed_sps = H265SpsParser::ParseSps(sps_buffer, arraysize(sps_buffer)).value(); parsed_pps_ = H265PpsParser::ParsePps(buffer_.data(), buffer_.size(), &parsed_sps); ASSERT_TRUE(parsed_pps_); EXPECT_EQ(pps.dependent_slice_segments_enabled_flag, parsed_pps_->dependent_slice_segments_enabled_flag); EXPECT_EQ(pps.cabac_init_present_flag, parsed_pps_->cabac_init_present_flag); EXPECT_EQ(pps.output_flag_present_flag, parsed_pps_->output_flag_present_flag); EXPECT_EQ(pps.num_extra_slice_header_bits, parsed_pps_->num_extra_slice_header_bits); EXPECT_EQ(pps.num_ref_idx_l0_default_active_minus1, parsed_pps_->num_ref_idx_l0_default_active_minus1); EXPECT_EQ(pps.num_ref_idx_l1_default_active_minus1, parsed_pps_->num_ref_idx_l1_default_active_minus1); EXPECT_EQ(pps.init_qp_minus26, parsed_pps_->init_qp_minus26); EXPECT_EQ(pps.weighted_pred_flag, parsed_pps_->weighted_pred_flag); EXPECT_EQ(pps.weighted_bipred_flag, parsed_pps_->weighted_bipred_flag); EXPECT_EQ(pps.lists_modification_present_flag, parsed_pps_->lists_modification_present_flag); EXPECT_EQ(pps.pps_id, parsed_pps_->pps_id); EXPECT_EQ(pps.sps_id, parsed_pps_->sps_id); } H265PpsParser::PpsState generated_pps_; rtc::Buffer buffer_; absl::optional parsed_pps_; absl::optional parsed_sps_; }; TEST_F(H265PpsParserTest, ZeroPps) { RunTest(); } TEST_F(H265PpsParserTest, MaxPps) { generated_pps_.dependent_slice_segments_enabled_flag = true; generated_pps_.init_qp_minus26 = 25; generated_pps_.num_extra_slice_header_bits = 1; // 1 bit value. generated_pps_.weighted_bipred_flag = true; generated_pps_.weighted_pred_flag = true; generated_pps_.cabac_init_present_flag = true; generated_pps_.pps_id = 2; generated_pps_.sps_id = 1; RunTest(); generated_pps_.init_qp_minus26 = -25; RunTest(); } } // namespace webrtc