sps_vui_rewriter_unittest.cc (15665B)
1 /* 2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "common_video/h264/sps_vui_rewriter.h" 12 13 #include <cstddef> 14 #include <cstdint> 15 #include <optional> 16 #include <tuple> 17 18 #include "api/array_view.h" 19 #include "api/video/color_space.h" 20 #include "common_video/h264/h264_common.h" 21 #include "common_video/h264/sps_parser.h" 22 #include "rtc_base/bit_buffer.h" 23 #include "rtc_base/buffer.h" 24 #include "rtc_base/logging.h" 25 #include "test/gmock.h" 26 #include "test/gtest.h" 27 28 namespace webrtc { 29 30 namespace { 31 enum SpsMode { 32 kNoRewriteRequired_VuiOptimal, 33 kRewriteRequired_NoVui, 34 kRewriteRequired_NoBitstreamRestriction, 35 kRewriteRequired_VuiSuboptimal, 36 }; 37 38 constexpr size_t kSpsBufferMaxSize = 256; 39 constexpr size_t kWidth = 640; 40 constexpr size_t kHeight = 480; 41 42 constexpr uint8_t kStartSequence[] = {0x00, 0x00, 0x00, 0x01}; 43 constexpr uint8_t kAud[] = {H264::NaluType::kAud, 0x09, 0x10}; 44 constexpr uint8_t kSpsNaluType[] = {H264::NaluType::kSps}; 45 constexpr uint8_t kIdr1[] = {H264::NaluType::kIdr, 0xFF, 0x00, 0x00, 0x04}; 46 constexpr uint8_t kIdr2[] = {H264::NaluType::kIdr, 0xFF, 0x00, 0x11}; 47 48 struct VuiHeader { 49 uint32_t vui_parameters_present_flag = 0; 50 uint32_t bitstream_restriction_flag = 0; 51 uint32_t max_num_reorder_frames = 0; 52 uint32_t max_dec_frame_buffering = 0; 53 uint32_t video_signal_type_present_flag = 0; 54 uint32_t video_full_range_flag = 0; 55 uint32_t colour_description_present_flag = 0; 56 uint8_t colour_primaries = 0; 57 uint8_t transfer_characteristics = 0; 58 uint8_t matrix_coefficients = 0; 59 }; 60 61 const VuiHeader kVuiNotPresent; 62 63 const VuiHeader kVuiNoBitstreamRestriction = { 64 .vui_parameters_present_flag = 1, 65 }; 66 67 const VuiHeader kVuiNoFrameBuffering = { 68 .vui_parameters_present_flag = 1, 69 .bitstream_restriction_flag = 1, 70 .max_dec_frame_buffering = 1, 71 }; 72 73 const VuiHeader kVuiFrameBuffering = { 74 .vui_parameters_present_flag = 1, 75 .bitstream_restriction_flag = 1, 76 .max_num_reorder_frames = 3, 77 .max_dec_frame_buffering = 3, 78 }; 79 80 const VuiHeader kVuiNoVideoSignalType = { 81 .vui_parameters_present_flag = 1, 82 .bitstream_restriction_flag = 1, 83 .max_dec_frame_buffering = 1, 84 }; 85 86 const VuiHeader kVuiLimitedRangeNoColourDescription = { 87 .vui_parameters_present_flag = 1, 88 .bitstream_restriction_flag = 1, 89 .max_dec_frame_buffering = 1, 90 .video_signal_type_present_flag = 1, 91 }; 92 93 const VuiHeader kVuiFullRangeNoColourDescription = { 94 .vui_parameters_present_flag = 1, 95 .bitstream_restriction_flag = 1, 96 .max_dec_frame_buffering = 1, 97 .video_signal_type_present_flag = 1, 98 .video_full_range_flag = 1, 99 }; 100 101 const VuiHeader kVuiLimitedRangeBt709Color = { 102 .vui_parameters_present_flag = 1, 103 .bitstream_restriction_flag = 1, 104 .max_dec_frame_buffering = 1, 105 .video_signal_type_present_flag = 1, 106 .colour_description_present_flag = 1, 107 .colour_primaries = 1, 108 .transfer_characteristics = 1, 109 .matrix_coefficients = 1, 110 }; 111 112 const ColorSpace kColorSpaceH264Default(ColorSpace::PrimaryID::kUnspecified, 113 ColorSpace::TransferID::kUnspecified, 114 ColorSpace::MatrixID::kUnspecified, 115 ColorSpace::RangeID::kLimited); 116 117 const ColorSpace kColorSpacePrimariesBt709(ColorSpace::PrimaryID::kBT709, 118 ColorSpace::TransferID::kUnspecified, 119 ColorSpace::MatrixID::kUnspecified, 120 ColorSpace::RangeID::kLimited); 121 122 const ColorSpace kColorSpaceTransferBt709(ColorSpace::PrimaryID::kUnspecified, 123 ColorSpace::TransferID::kBT709, 124 ColorSpace::MatrixID::kUnspecified, 125 ColorSpace::RangeID::kLimited); 126 127 const ColorSpace kColorSpaceMatrixBt709(ColorSpace::PrimaryID::kUnspecified, 128 ColorSpace::TransferID::kUnspecified, 129 ColorSpace::MatrixID::kBT709, 130 ColorSpace::RangeID::kLimited); 131 132 const ColorSpace kColorSpaceFullRange(ColorSpace::PrimaryID::kBT709, 133 ColorSpace::TransferID::kUnspecified, 134 ColorSpace::MatrixID::kUnspecified, 135 ColorSpace::RangeID::kFull); 136 137 const ColorSpace kColorSpaceBt709LimitedRange(ColorSpace::PrimaryID::kBT709, 138 ColorSpace::TransferID::kBT709, 139 ColorSpace::MatrixID::kBT709, 140 ColorSpace::RangeID::kLimited); 141 } // namespace 142 143 // Generates a fake SPS with basically everything empty and with characteristics 144 // based off SpsMode. 145 // Pass in a buffer of at least kSpsBufferMaxSize. 146 // The fake SPS that this generates also always has at least one emulation byte 147 // at offset 2, since the first two bytes are always 0, and has a 0x3 as the 148 // level_idc, to make sure the parser doesn't eat all 0x3 bytes. 149 void GenerateFakeSps(const VuiHeader& vui, Buffer* out_buffer) { 150 uint8_t rbsp[kSpsBufferMaxSize] = {0}; 151 BitBufferWriter writer(rbsp, kSpsBufferMaxSize); 152 // Profile byte. 153 writer.WriteUInt8(0); 154 // Constraint sets and reserved zero bits. 155 writer.WriteUInt8(0); 156 // level_idc. 157 writer.WriteUInt8(3); 158 // seq_paramter_set_id. 159 writer.WriteExponentialGolomb(0); 160 // Profile is not special, so we skip all the chroma format settings. 161 162 // Now some bit magic. 163 // log2_max_frame_num_minus4: ue(v). 0 is fine. 164 writer.WriteExponentialGolomb(0); 165 // pic_order_cnt_type: ue(v). 166 writer.WriteExponentialGolomb(0); 167 // log2_max_pic_order_cnt_lsb_minus4: ue(v). 0 is fine. 168 writer.WriteExponentialGolomb(0); 169 170 // max_num_ref_frames: ue(v). Use 1, to make optimal/suboptimal more obvious. 171 writer.WriteExponentialGolomb(1); 172 // gaps_in_frame_num_value_allowed_flag: u(1). 173 writer.WriteBits(0, 1); 174 // Next are width/height. First, calculate the mbs/map_units versions. 175 uint16_t width_in_mbs_minus1 = (kWidth + 15) / 16 - 1; 176 177 // For the height, we're going to define frame_mbs_only_flag, so we need to 178 // divide by 2. See the parser for the full calculation. 179 uint16_t height_in_map_units_minus1 = ((kHeight + 15) / 16 - 1) / 2; 180 // Write each as ue(v). 181 writer.WriteExponentialGolomb(width_in_mbs_minus1); 182 writer.WriteExponentialGolomb(height_in_map_units_minus1); 183 // frame_mbs_only_flag: u(1). Needs to be false. 184 writer.WriteBits(0, 1); 185 // mb_adaptive_frame_field_flag: u(1). 186 writer.WriteBits(0, 1); 187 // direct_8x8_inferene_flag: u(1). 188 writer.WriteBits(0, 1); 189 // frame_cropping_flag: u(1). 1, so we can supply crop. 190 writer.WriteBits(1, 1); 191 // Now we write the left/right/top/bottom crop. For simplicity, we'll put all 192 // the crop at the left/top. 193 // We picked a 4:2:0 format, so the crops are 1/2 the pixel crop values. 194 // Left/right. 195 writer.WriteExponentialGolomb(((16 - (kWidth % 16)) % 16) / 2); 196 writer.WriteExponentialGolomb(0); 197 // Top/bottom. 198 writer.WriteExponentialGolomb(((16 - (kHeight % 16)) % 16) / 2); 199 writer.WriteExponentialGolomb(0); 200 201 // Finally! The VUI. 202 // vui_parameters_present_flag: u(1) 203 writer.WriteBits(vui.vui_parameters_present_flag, 1); 204 if (vui.vui_parameters_present_flag) { 205 // aspect_ratio_info_present_flag, overscan_info_present_flag. Both u(1). 206 writer.WriteBits(0, 2); 207 208 writer.WriteBits(vui.video_signal_type_present_flag, 1); 209 if (vui.video_signal_type_present_flag) { 210 // video_format: u(3). 5 = Unspecified 211 writer.WriteBits(5, 3); 212 writer.WriteBits(vui.video_full_range_flag, 1); 213 writer.WriteBits(vui.colour_description_present_flag, 1); 214 if (vui.colour_description_present_flag) { 215 writer.WriteUInt8(vui.colour_primaries); 216 writer.WriteUInt8(vui.transfer_characteristics); 217 writer.WriteUInt8(vui.matrix_coefficients); 218 } 219 } 220 221 // chroma_loc_info_present_flag, timing_info_present_flag, 222 // nal_hrd_parameters_present_flag, vcl_hrd_parameters_present_flag, 223 // pic_struct_present_flag, All u(1) 224 writer.WriteBits(0, 5); 225 226 writer.WriteBits(vui.bitstream_restriction_flag, 1); 227 if (vui.bitstream_restriction_flag) { 228 // Write some defaults. Shouldn't matter for parsing, though. 229 // motion_vectors_over_pic_boundaries_flag: u(1) 230 writer.WriteBits(1, 1); 231 // max_bytes_per_pic_denom: ue(v) 232 writer.WriteExponentialGolomb(2); 233 // max_bits_per_mb_denom: ue(v) 234 writer.WriteExponentialGolomb(1); 235 // log2_max_mv_length_horizontal: ue(v) 236 // log2_max_mv_length_vertical: ue(v) 237 writer.WriteExponentialGolomb(16); 238 writer.WriteExponentialGolomb(16); 239 240 // Next are the limits we care about. 241 writer.WriteExponentialGolomb(vui.max_num_reorder_frames); 242 writer.WriteExponentialGolomb(vui.max_dec_frame_buffering); 243 } 244 } 245 246 // Get the number of bytes written (including the last partial byte). 247 size_t byte_count, bit_offset; 248 writer.GetCurrentOffset(&byte_count, &bit_offset); 249 if (bit_offset > 0) { 250 byte_count++; 251 } 252 253 H264::WriteRbsp(MakeArrayView(rbsp, byte_count), out_buffer); 254 } 255 256 void TestSps(const VuiHeader& vui, 257 const ColorSpace* color_space, 258 SpsVuiRewriter::ParseResult expected_parse_result) { 259 LogMessage::LogToDebug(LS_VERBOSE); 260 Buffer original_sps; 261 GenerateFakeSps(vui, &original_sps); 262 263 std::optional<SpsParser::SpsState> sps; 264 Buffer rewritten_sps; 265 SpsVuiRewriter::ParseResult result = SpsVuiRewriter::ParseAndRewriteSps( 266 original_sps, &sps, color_space, &rewritten_sps, 267 SpsVuiRewriter::Direction::kIncoming); 268 EXPECT_EQ(expected_parse_result, result); 269 ASSERT_TRUE(sps); 270 EXPECT_EQ(sps->width, kWidth); 271 EXPECT_EQ(sps->height, kHeight); 272 if (vui.vui_parameters_present_flag) { 273 EXPECT_EQ(sps->vui_params_present, 1u); 274 } 275 276 if (result == SpsVuiRewriter::ParseResult::kVuiRewritten) { 277 // Ensure that added/rewritten SPS is parsable. 278 Buffer tmp; 279 result = SpsVuiRewriter::ParseAndRewriteSps( 280 rewritten_sps, &sps, nullptr, &tmp, 281 SpsVuiRewriter::Direction::kIncoming); 282 EXPECT_EQ(SpsVuiRewriter::ParseResult::kVuiOk, result); 283 ASSERT_TRUE(sps); 284 EXPECT_EQ(sps->width, kWidth); 285 EXPECT_EQ(sps->height, kHeight); 286 EXPECT_EQ(sps->vui_params_present, 1u); 287 } 288 } 289 290 class SpsVuiRewriterTest : public ::testing::Test, 291 public ::testing::WithParamInterface< 292 ::testing::tuple<VuiHeader, 293 const ColorSpace*, 294 SpsVuiRewriter::ParseResult>> { 295 }; 296 297 TEST_P(SpsVuiRewriterTest, RewriteVui) { 298 VuiHeader vui = ::testing::get<0>(GetParam()); 299 const ColorSpace* color_space = ::testing::get<1>(GetParam()); 300 SpsVuiRewriter::ParseResult expected_parse_result = 301 ::testing::get<2>(GetParam()); 302 TestSps(vui, color_space, expected_parse_result); 303 } 304 305 INSTANTIATE_TEST_SUITE_P( 306 All, 307 SpsVuiRewriterTest, 308 ::testing::Values( 309 std::make_tuple(kVuiNoFrameBuffering, 310 nullptr, 311 SpsVuiRewriter::ParseResult::kVuiOk), 312 std::make_tuple(kVuiNoVideoSignalType, 313 &kColorSpaceH264Default, 314 SpsVuiRewriter::ParseResult::kVuiOk), 315 std::make_tuple(kVuiLimitedRangeBt709Color, 316 &kColorSpaceBt709LimitedRange, 317 SpsVuiRewriter::ParseResult::kVuiOk), 318 std::make_tuple(kVuiNotPresent, 319 nullptr, 320 SpsVuiRewriter::ParseResult::kVuiRewritten), 321 std::make_tuple(kVuiNoBitstreamRestriction, 322 nullptr, 323 SpsVuiRewriter::ParseResult::kVuiRewritten), 324 std::make_tuple(kVuiFrameBuffering, 325 nullptr, 326 SpsVuiRewriter::ParseResult::kVuiRewritten), 327 std::make_tuple(kVuiLimitedRangeNoColourDescription, 328 &kColorSpaceFullRange, 329 SpsVuiRewriter::ParseResult::kVuiRewritten), 330 std::make_tuple(kVuiNoVideoSignalType, 331 &kColorSpacePrimariesBt709, 332 SpsVuiRewriter::ParseResult::kVuiRewritten), 333 std::make_tuple(kVuiNoVideoSignalType, 334 &kColorSpaceTransferBt709, 335 SpsVuiRewriter::ParseResult::kVuiRewritten), 336 std::make_tuple(kVuiNoVideoSignalType, 337 &kColorSpaceMatrixBt709, 338 SpsVuiRewriter::ParseResult::kVuiRewritten), 339 std::make_tuple(kVuiFullRangeNoColourDescription, 340 &kColorSpaceH264Default, 341 SpsVuiRewriter::ParseResult::kVuiRewritten), 342 std::make_tuple(kVuiLimitedRangeBt709Color, 343 &kColorSpaceH264Default, 344 SpsVuiRewriter::ParseResult::kVuiRewritten))); 345 346 TEST(SpsVuiRewriterOutgoingVuiTest, ParseOutgoingBitstreamOptimalVui) { 347 LogMessage::LogToDebug(LS_VERBOSE); 348 349 Buffer optimal_sps; 350 GenerateFakeSps(kVuiNoFrameBuffering, &optimal_sps); 351 352 Buffer buffer; 353 buffer.AppendData(kStartSequence); 354 buffer.AppendData(optimal_sps); 355 buffer.AppendData(kStartSequence); 356 buffer.AppendData(kIdr1); 357 358 EXPECT_THAT(SpsVuiRewriter::ParseOutgoingBitstreamAndRewrite(buffer, nullptr), 359 ::testing::ElementsAreArray(buffer)); 360 } 361 362 TEST(SpsVuiRewriterOutgoingVuiTest, ParseOutgoingBitstreamNoVui) { 363 LogMessage::LogToDebug(LS_VERBOSE); 364 365 Buffer sps; 366 GenerateFakeSps(kVuiNotPresent, &sps); 367 368 Buffer buffer; 369 buffer.AppendData(kStartSequence); 370 buffer.AppendData(kIdr1); 371 buffer.AppendData(kStartSequence); 372 buffer.AppendData(kSpsNaluType); 373 buffer.AppendData(sps); 374 buffer.AppendData(kStartSequence); 375 buffer.AppendData(kIdr2); 376 377 Buffer optimal_sps; 378 GenerateFakeSps(kVuiNoFrameBuffering, &optimal_sps); 379 380 Buffer expected_buffer; 381 expected_buffer.AppendData(kStartSequence); 382 expected_buffer.AppendData(kIdr1); 383 expected_buffer.AppendData(kStartSequence); 384 expected_buffer.AppendData(kSpsNaluType); 385 expected_buffer.AppendData(optimal_sps); 386 expected_buffer.AppendData(kStartSequence); 387 expected_buffer.AppendData(kIdr2); 388 389 EXPECT_THAT(SpsVuiRewriter::ParseOutgoingBitstreamAndRewrite(buffer, nullptr), 390 ::testing::ElementsAreArray(expected_buffer)); 391 } 392 393 TEST(SpsVuiRewriterOutgoingAudTest, ParseOutgoingBitstreamWithAud) { 394 LogMessage::LogToDebug(LS_VERBOSE); 395 396 Buffer optimal_sps; 397 GenerateFakeSps(kVuiNoFrameBuffering, &optimal_sps); 398 399 Buffer buffer; 400 buffer.AppendData(kStartSequence); 401 buffer.AppendData(kAud); 402 buffer.AppendData(kStartSequence); 403 buffer.AppendData(optimal_sps); 404 buffer.AppendData(kStartSequence); 405 buffer.AppendData(kIdr1); 406 407 Buffer expected_buffer; 408 expected_buffer.AppendData(kStartSequence); 409 expected_buffer.AppendData(optimal_sps); 410 expected_buffer.AppendData(kStartSequence); 411 expected_buffer.AppendData(kIdr1); 412 413 EXPECT_THAT(SpsVuiRewriter::ParseOutgoingBitstreamAndRewrite(buffer, nullptr), 414 ::testing::ElementsAreArray(expected_buffer)); 415 } 416 } // namespace webrtc