h265_sps_parser_unittest.cc (18786B)
1 /* 2 * Copyright (c) 2023 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/h265/h265_sps_parser.h" 12 13 #include <cstddef> 14 #include <cstdint> 15 #include <optional> 16 #include <vector> 17 18 #include "api/array_view.h" 19 #include "common_video/h265/h265_common.h" 20 #include "rtc_base/bit_buffer.h" 21 #include "rtc_base/buffer.h" 22 #include "test/gtest.h" 23 24 namespace webrtc { 25 26 static constexpr size_t kSpsBufferMaxSize = 256; 27 28 // Generates a fake SPS with basically everything empty but the width/height, 29 // max_num_sublayer_minus1 and num_short_term_ref_pic_sets. 30 // Pass in a buffer of at least kSpsBufferMaxSize. 31 // The fake SPS that this generates also always has at least one emulation byte 32 // at offset 2, since the first two bytes are always 0, and has a 0x3 as the 33 // level_idc, to make sure the parser doesn't eat all 0x3 bytes. 34 // num_short_term_ref_pic_sets is set to 11 followed with 11 35 // short_term_ref_pic_set data in this fake sps. 36 void WriteSps(uint16_t width, 37 uint16_t height, 38 int id, 39 uint32_t max_num_sublayer_minus1, 40 bool sub_layer_ordering_info_present_flag, 41 bool long_term_ref_pics_present_flag, 42 Buffer* out_buffer) { 43 uint8_t rbsp[kSpsBufferMaxSize] = {0}; 44 BitBufferWriter writer(rbsp, kSpsBufferMaxSize); 45 // sps_video_parameter_set_id 46 writer.WriteBits(0, 4); 47 // sps_max_sub_layers_minus1 48 writer.WriteBits(max_num_sublayer_minus1, 3); 49 // sps_temporal_id_nesting_flag 50 writer.WriteBits(1, 1); 51 // profile_tier_level(profilePresentFlag=1, maxNumSublayersMinus1=0) 52 // profile-space=0, tier=0, profile-idc=1 53 writer.WriteBits(0, 2); 54 writer.WriteBits(0, 1); 55 writer.WriteBits(1, 5); 56 // general_prfile_compatibility_flag[32] 57 writer.WriteBits(0, 32); 58 // general_progressive_source_flag 59 writer.WriteBits(1, 1); 60 // general_interlace_source_flag 61 writer.WriteBits(0, 1); 62 // general_non_packed_constraint_flag 63 writer.WriteBits(0, 1); 64 // general_frame_only_constraint_flag 65 writer.WriteBits(1, 1); 66 // general_reserved_zero_7bits 67 writer.WriteBits(0, 7); 68 // general_one_picture_only_flag 69 writer.WriteBits(0, 1); 70 // general_reserved_zero_35bits 71 writer.WriteBits(0, 35); 72 // general_inbld_flag 73 writer.WriteBits(0, 1); 74 // general_level_idc 75 writer.WriteBits(93, 8); 76 // if max_sub_layers_minus1 >=1, read the sublayer profile information 77 std::vector<uint32_t> sub_layer_profile_present_flags; 78 std::vector<uint32_t> sub_layer_level_present_flags; 79 for (uint32_t i = 0; i < max_num_sublayer_minus1; i++) { 80 // sublayer_profile_present_flag and sublayer_level_presnet_flag: u(2) 81 writer.WriteBits(1, 1); 82 writer.WriteBits(1, 1); 83 sub_layer_profile_present_flags.push_back(1); 84 sub_layer_level_present_flags.push_back(1); 85 } 86 if (max_num_sublayer_minus1 > 0) { 87 for (uint32_t j = max_num_sublayer_minus1; j < 8; j++) { 88 // reserved 2 bits: u(2) 89 writer.WriteBits(0, 2); 90 } 91 } 92 for (uint32_t k = 0; k < max_num_sublayer_minus1; k++) { 93 if (sub_layer_profile_present_flags[k]) { // 94 // sub_layer profile_space/tier_flag/profile_idc. ignored. u(8) 95 writer.WriteBits(0, 8); 96 // profile_compatability_flag: u(32) 97 writer.WriteBits(0, 32); 98 // sub_layer progressive_source_flag/interlaced_source_flag/ 99 // non_packed_constraint_flag/frame_only_constraint_flag: u(4) 100 writer.WriteBits(0, 4); 101 // following 43-bits are profile_idc specific. We simply read/skip it. 102 // u(43) 103 writer.WriteBits(0, 43); 104 // 1-bit profile_idc specific inbld flag. We simply read/skip it. u(1) 105 writer.WriteBits(0, 1); 106 } 107 if (sub_layer_level_present_flags[k]) { 108 // sub_layer_level_idc: u(8) 109 writer.WriteBits(0, 8); 110 } 111 } 112 113 // seq_parameter_set_id 114 writer.WriteExponentialGolomb(id); 115 // chroma_format_idc 116 writer.WriteExponentialGolomb(2); 117 if (width % 8 != 0 || height % 8 != 0) { 118 int width_delta = 8 - width % 8; 119 int height_delta = 8 - height % 8; 120 if (width_delta != 8) { 121 // pic_width_in_luma_samples 122 writer.WriteExponentialGolomb(width + width_delta); 123 } else { 124 writer.WriteExponentialGolomb(width); 125 } 126 if (height_delta != 8) { 127 // pic_height_in_luma_samples 128 writer.WriteExponentialGolomb(height + height_delta); 129 } else { 130 writer.WriteExponentialGolomb(height); 131 } 132 // conformance_window_flag 133 writer.WriteBits(1, 1); 134 // conf_win_left_offset 135 writer.WriteExponentialGolomb((width % 8) / 2); 136 // conf_win_right_offset 137 writer.WriteExponentialGolomb(0); 138 // conf_win_top_offset 139 writer.WriteExponentialGolomb(height_delta); 140 // conf_win_bottom_offset 141 writer.WriteExponentialGolomb(0); 142 } else { 143 // pic_width_in_luma_samples 144 writer.WriteExponentialGolomb(width); 145 // pic_height_in_luma_samples 146 writer.WriteExponentialGolomb(height); 147 // conformance_window_flag 148 writer.WriteBits(0, 1); 149 } 150 // bit_depth_luma_minus8 151 writer.WriteExponentialGolomb(0); 152 // bit_depth_chroma_minus8 153 writer.WriteExponentialGolomb(0); 154 // log2_max_pic_order_cnt_lsb_minus4 155 writer.WriteExponentialGolomb(4); 156 // sps_sub_layer_ordering_info_present_flag 157 writer.WriteBits(sub_layer_ordering_info_present_flag, 1); 158 for (uint32_t i = (sub_layer_ordering_info_present_flag != 0) 159 ? 0 160 : max_num_sublayer_minus1; 161 i <= max_num_sublayer_minus1; i++) { 162 // sps_max_dec_pic_buffering_minus1: ue(v) 163 writer.WriteExponentialGolomb(4); 164 // sps_max_num_reorder_pics: ue(v) 165 writer.WriteExponentialGolomb(3); 166 // sps_max_latency_increase_plus1: ue(v) 167 writer.WriteExponentialGolomb(0); 168 } 169 // log2_min_luma_coding_block_size_minus3 170 writer.WriteExponentialGolomb(0); 171 // log2_diff_max_min_luma_coding_block_size 172 writer.WriteExponentialGolomb(3); 173 // log2_min_luma_transform_block_size_minus2 174 writer.WriteExponentialGolomb(0); 175 // log2_diff_max_min_luma_transform_block_size 176 writer.WriteExponentialGolomb(3); 177 // max_transform_hierarchy_depth_inter 178 writer.WriteExponentialGolomb(0); 179 // max_transform_hierarchy_depth_intra 180 writer.WriteExponentialGolomb(0); 181 // scaling_list_enabled_flag 182 writer.WriteBits(0, 1); 183 // apm_enabled_flag 184 writer.WriteBits(0, 1); 185 // sample_adaptive_offset_enabled_flag 186 writer.WriteBits(1, 1); 187 // pcm_enabled_flag 188 writer.WriteBits(0, 1); 189 // num_short_term_ref_pic_sets 190 writer.WriteExponentialGolomb(11); 191 // short_term_ref_pic_set[0] 192 // num_negative_pics 193 writer.WriteExponentialGolomb(4); 194 // num_positive_pics 195 writer.WriteExponentialGolomb(0); 196 // delta_poc_s0_minus1 197 writer.WriteExponentialGolomb(7); 198 // used_by_curr_pic_s0_flag 199 writer.WriteBits(1, 1); 200 for (int i = 0; i < 2; i++) { 201 // delta_poc_s0_minus1 202 writer.WriteExponentialGolomb(1); 203 // used_by_curr_pic_s0_flag 204 writer.WriteBits(1, 1); 205 } 206 // delta_poc_s0_minus1 207 writer.WriteExponentialGolomb(3); 208 // used_by_curr_pic_s0_flag 209 writer.WriteBits(1, 1); 210 // short_term_ref_pic_set[1] 211 // inter_ref_pic_set_prediction_flag 212 writer.WriteBits(1, 1); 213 // delta_rps_sign 214 writer.WriteBits(0, 1); 215 // abs_delta_rps_minus1 216 writer.WriteExponentialGolomb(3); 217 for (int i = 0; i < 2; i++) { 218 // used_by_curr_pic_flag 219 writer.WriteBits(1, 1); 220 } 221 for (int i = 0; i < 2; i++) { 222 // used_by_curr_pic_flag 223 writer.WriteBits(0, 1); 224 // use_delta_flag 225 writer.WriteBits(0, 1); 226 } 227 // used_by_curr_pic_flag 228 writer.WriteBits(1, 1); 229 // short_term_ref_pic_set[2] 230 // inter_ref_pic_set_prediction_flag 231 writer.WriteBits(1, 1); 232 // delta_rps_sign 233 writer.WriteBits(0, 1); 234 // abs_delta_rps_minus1 235 writer.WriteExponentialGolomb(1); 236 for (int i = 0; i < 4; i++) { 237 // used_by_curr_pic_flag 238 writer.WriteBits(1, 1); 239 } 240 // short_term_ref_pic_set[3] 241 // inter_ref_pic_set_prediction_flag 242 writer.WriteBits(1, 1); 243 // delta_rps_sign 244 writer.WriteBits(0, 1); 245 // abs_delta_rps_minus1 246 writer.WriteExponentialGolomb(0); 247 // used_by_curr_pic_flag 248 writer.WriteBits(1, 1); 249 // used_by_curr_pic_flag 250 writer.WriteBits(0, 1); 251 // use_delta_flag 252 writer.WriteBits(0, 1); 253 for (int i = 0; i < 3; i++) { 254 // used_by_curr_pic_flag 255 writer.WriteBits(1, 1); 256 } 257 // short_term_ref_pic_set[4] 258 // inter_ref_pic_set_prediction_flag 259 writer.WriteBits(1, 1); 260 // delta_rps_sign 261 writer.WriteBits(1, 1); 262 // abs_delta_rps_minus1 263 writer.WriteExponentialGolomb(1); 264 for (int i = 0; i < 4; i++) { 265 // used_by_curr_pic_flag 266 writer.WriteBits(1, 1); 267 } 268 // used_by_curr_pic_flag 269 writer.WriteBits(0, 1); 270 // use_delta_flag 271 writer.WriteBits(0, 1); 272 // short_term_ref_pic_set[5] 273 // inter_ref_pic_set_prediction_flag 274 writer.WriteBits(1, 1); 275 // delta_rps_sign 276 writer.WriteBits(1, 1); 277 // abs_delta_rps_minus1 278 writer.WriteExponentialGolomb(2); 279 for (int i = 0; i < 4; i++) { 280 // used_by_curr_pic_flag 281 writer.WriteBits(1, 1); 282 } 283 // used_by_curr_pic_flag 284 writer.WriteBits(0, 1); 285 // use_delta_flag 286 writer.WriteBits(0, 1); 287 // short_term_ref_pic_set[6] 288 // inter_ref_pic_set_prediction_flag 289 writer.WriteBits(1, 1); 290 // delta_rps_sign 291 writer.WriteBits(0, 1); 292 // abs_delta_rps_minus1 293 writer.WriteExponentialGolomb(0); 294 // used_by_curr_pic_flag 295 writer.WriteBits(1, 1); 296 // used_by_curr_pic_flag 297 writer.WriteBits(0, 1); 298 // use_delta_flag 299 writer.WriteBits(0, 1); 300 for (int i = 0; i < 3; i++) { 301 // used_by_curr_pic_flag 302 writer.WriteBits(1, 1); 303 } 304 // short_term_ref_pic_set[7] 305 // inter_ref_pic_set_prediction_flag 306 writer.WriteBits(1, 1); 307 // delta_rps_sign 308 writer.WriteBits(1, 1); 309 // abs_delta_rps_minus1 310 writer.WriteExponentialGolomb(1); 311 for (int i = 0; i < 4; i++) { 312 // used_by_curr_pic_flag 313 writer.WriteBits(1, 1); 314 } 315 // used_by_curr_pic_flag 316 writer.WriteBits(0, 1); 317 // use_delta_flag 318 writer.WriteBits(0, 1); 319 // short_term_ref_pic_set[8] 320 // inter_ref_pic_set_prediction_flag 321 writer.WriteBits(0, 1); 322 // num_negative_pics 323 writer.WriteExponentialGolomb(1); 324 // num_positive_pics 325 writer.WriteExponentialGolomb(0); 326 // delta_poc_s0_minus1 327 writer.WriteExponentialGolomb(7); 328 // used_by_curr_pic_s0_flag 329 writer.WriteBits(1, 1); 330 // short_term_ref_pic_set[9] 331 // inter_ref_pic_set_prediction_flag 332 writer.WriteBits(1, 1); 333 // delta_rps_sign 334 writer.WriteBits(0, 1); 335 // abs_delta_rps_minus1 336 writer.WriteExponentialGolomb(3); 337 for (int i = 0; i < 2; i++) { 338 // used_by_curr_pic_flag 339 writer.WriteBits(1, 1); 340 } 341 // short_term_ref_pic_set[10] 342 // inter_ref_pic_set_prediction_flag 343 writer.WriteBits(1, 1); 344 // delta_rps_sign 345 writer.WriteBits(0, 1); 346 // abs_delta_rps_minus1 347 writer.WriteExponentialGolomb(1); 348 for (int i = 0; i < 3; i++) { 349 // used_by_curr_pic_flag 350 writer.WriteBits(1, 1); 351 } 352 // long_term_ref_pics_present_flag 353 writer.WriteBits(long_term_ref_pics_present_flag, 1); 354 if (long_term_ref_pics_present_flag) { 355 // num_long_term_ref_pics_sps 356 writer.WriteExponentialGolomb(1); 357 // lt_ref_pic_poc_lsb_sps 358 writer.WriteExponentialGolomb(1); 359 // used_by_curr_pic_lt_sps_flag 360 writer.WriteBits(1, 8); 361 } 362 // sps_temproal_mvp_enabled_flag 363 writer.WriteBits(1, 1); 364 365 // Get the number of bytes written (including the last partial byte). 366 size_t byte_count, bit_offset; 367 writer.GetCurrentOffset(&byte_count, &bit_offset); 368 if (bit_offset > 0) { 369 byte_count++; 370 } 371 372 out_buffer->Clear(); 373 H265::WriteRbsp(MakeArrayView(rbsp, byte_count), out_buffer); 374 } 375 376 class H265SpsParserTest : public ::testing::Test { 377 public: 378 H265SpsParserTest() {} 379 ~H265SpsParserTest() override {} 380 }; 381 382 TEST_F(H265SpsParserTest, TestSampleSPSHdLandscape) { 383 // SPS for a 1280x720 camera capture from ffmpeg on linux. Contains 384 // emulation bytes but no cropping. This buffer is generated 385 // with following command: 386 // 1) ffmpeg -i /dev/video0 -r 30 -c:v libx265 -s 1280x720 camera.h265 387 // 388 // 2) Open camera.h265 and find the SPS, generally everything between the 389 // second and third start codes (0 0 0 1 or 0 0 1). The first two bytes should 390 // be 0x42 and 0x01, which should be stripped out before being passed to the 391 // parser. 392 const uint8_t buffer[] = {0x01, 0x04, 0x08, 0x00, 0x00, 0x03, 0x00, 0x9d, 393 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x5d, 0xb0, 394 0x02, 0x80, 0x80, 0x2d, 0x16, 0x59, 0x59, 0xa4, 395 0x93, 0x2b, 0x80, 0x40, 0x00, 0x00, 0x03, 0x00, 396 0x40, 0x00, 0x00, 0x07, 0x82}; 397 std::optional<H265SpsParser::SpsState> sps = H265SpsParser::ParseSps(buffer); 398 ASSERT_TRUE(sps.has_value()); 399 EXPECT_EQ(1280u, sps->width); 400 EXPECT_EQ(720u, sps->height); 401 } 402 403 TEST_F(H265SpsParserTest, TestSampleSPSVerticalCropLandscape) { 404 // SPS for a 640x260 camera captureH265SpsParser::ParseSps(buffer.data(), 405 // buffer.size()) from ffmpeg on Linux,. Contains emulation bytes and vertical 406 // cropping (crop from 640x264). The buffer is generated 407 // with following command: 408 // 1) Generate a video, from the camera: 409 // ffmpeg -i /dev/video0 -r 30 -c:v libx265 -s 640x264 camera.h265 410 // 411 // 2) Crop the video to expected size(for example, 640x260 which will crop 412 // from 640x264): 413 // ffmpeg -i camera.h265 -filter:v crop=640:260:200:200 -c:v libx265 414 // cropped.h265 415 // 416 // 3) Open cropped.h265 and find the SPS, generally everything between the 417 // second and third start codes (0 0 0 1 or 0 0 1). The first two bytes should 418 // be 0x42 and 0x01, which should be stripped out before being passed to the 419 // parser. 420 const uint8_t buffer[] = {0x01, 0x04, 0x08, 0x00, 0x00, 0x03, 0x00, 0x9d, 421 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x3f, 0xb0, 422 0x05, 0x02, 0x01, 0x09, 0xf2, 0xe5, 0x95, 0x9a, 423 0x49, 0x32, 0xb8, 0x04, 0x00, 0x00, 0x03, 0x00, 424 0x04, 0x00, 0x00, 0x03, 0x00, 0x78, 0x20}; 425 std::optional<H265SpsParser::SpsState> sps = H265SpsParser::ParseSps(buffer); 426 ASSERT_TRUE(sps.has_value()); 427 EXPECT_EQ(640u, sps->width); 428 EXPECT_EQ(260u, sps->height); 429 } 430 431 TEST_F(H265SpsParserTest, TestSampleSPSHorizontalAndVerticalCrop) { 432 // SPS for a 260x260 camera capture from ffmpeg on Linux. Contains emulation 433 // bytes. Horizontal and veritcal crop (Crop from 264x264). The buffer is 434 // generated with following command: 435 // 1) Generate a video, from the camera: 436 // ffmpeg -i /dev/video0 -r 30 -c:v libx265 -s 264x264 camera.h265 437 // 438 // 2) Crop the video to expected size(for example, 260x260 which will crop 439 // from 264x264): 440 // ffmpeg -i camera.h265 -filter:v crop=260:260:200:200 -c:v libx265 441 // cropped.h265 442 // 443 // 3) Open cropped.h265 and find the SPS, generally everything between the 444 // second and third start codes (0 0 0 1 or 0 0 1). The first two bytes should 445 // be 0x42 and 0x01, which should be stripped out before being passed to the 446 // parser. 447 const uint8_t buffer[] = {0x01, 0x04, 0x08, 0x00, 0x00, 0x03, 0x00, 0x9d, 448 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x3c, 0xb0, 449 0x08, 0x48, 0x04, 0x27, 0x72, 0xe5, 0x95, 0x9a, 450 0x49, 0x32, 0xb8, 0x04, 0x00, 0x00, 0x03, 0x00, 451 0x04, 0x00, 0x00, 0x03, 0x00, 0x78, 0x20}; 452 std::optional<H265SpsParser::SpsState> sps = H265SpsParser::ParseSps(buffer); 453 ASSERT_TRUE(sps.has_value()); 454 EXPECT_EQ(260u, sps->width); 455 EXPECT_EQ(260u, sps->height); 456 } 457 458 TEST_F(H265SpsParserTest, TestSyntheticSPSQvgaLandscape) { 459 Buffer buffer; 460 WriteSps(320u, 180u, 1, 0, 1, 0, &buffer); 461 std::optional<H265SpsParser::SpsState> sps = H265SpsParser::ParseSps(buffer); 462 ASSERT_TRUE(sps.has_value()); 463 EXPECT_EQ(320u, sps->width); 464 EXPECT_EQ(180u, sps->height); 465 EXPECT_EQ(1u, sps->sps_id); 466 } 467 468 TEST_F(H265SpsParserTest, TestSyntheticSPSWeirdResolution) { 469 Buffer buffer; 470 WriteSps(156u, 122u, 2, 0, 1, 0, &buffer); 471 std::optional<H265SpsParser::SpsState> sps = H265SpsParser::ParseSps(buffer); 472 ASSERT_TRUE(sps.has_value()); 473 EXPECT_EQ(156u, sps->width); 474 EXPECT_EQ(122u, sps->height); 475 EXPECT_EQ(2u, sps->sps_id); 476 } 477 478 TEST_F(H265SpsParserTest, TestLog2MaxSubLayersMinus1) { 479 Buffer buffer; 480 WriteSps(320u, 180u, 1, 0, 1, 0, &buffer); 481 std::optional<H265SpsParser::SpsState> sps = H265SpsParser::ParseSps(buffer); 482 ASSERT_TRUE(sps.has_value()); 483 EXPECT_EQ(320u, sps->width); 484 EXPECT_EQ(180u, sps->height); 485 EXPECT_EQ(1u, sps->sps_id); 486 EXPECT_EQ(0u, sps->sps_max_sub_layers_minus1); 487 488 WriteSps(320u, 180u, 1, 6, 1, 0, &buffer); 489 std::optional<H265SpsParser::SpsState> sps1 = H265SpsParser::ParseSps(buffer); 490 ASSERT_TRUE(sps1.has_value()); 491 EXPECT_EQ(320u, sps1->width); 492 EXPECT_EQ(180u, sps1->height); 493 EXPECT_EQ(1u, sps1->sps_id); 494 EXPECT_EQ(6u, sps1->sps_max_sub_layers_minus1); 495 496 WriteSps(320u, 180u, 1, 7, 1, 0, &buffer); 497 std::optional<H265SpsParser::SpsState> result = 498 H265SpsParser::ParseSps(buffer); 499 EXPECT_FALSE(result.has_value()); 500 } 501 502 TEST_F(H265SpsParserTest, TestSubLayerOrderingInfoPresentFlag) { 503 Buffer buffer; 504 WriteSps(320u, 180u, 1, 6, 1, 0, &buffer); 505 std::optional<H265SpsParser::SpsState> sps = H265SpsParser::ParseSps(buffer); 506 ASSERT_TRUE(sps.has_value()); 507 EXPECT_EQ(320u, sps->width); 508 EXPECT_EQ(180u, sps->height); 509 EXPECT_EQ(1u, sps->sps_id); 510 EXPECT_EQ(6u, sps->sps_max_sub_layers_minus1); 511 512 WriteSps(320u, 180u, 1, 6, 1, 0, &buffer); 513 std::optional<H265SpsParser::SpsState> sps1 = H265SpsParser::ParseSps(buffer); 514 ASSERT_TRUE(sps1.has_value()); 515 EXPECT_EQ(320u, sps1->width); 516 EXPECT_EQ(180u, sps1->height); 517 EXPECT_EQ(1u, sps1->sps_id); 518 EXPECT_EQ(6u, sps1->sps_max_sub_layers_minus1); 519 } 520 521 TEST_F(H265SpsParserTest, TestLongTermRefPicsPresentFlag) { 522 Buffer buffer; 523 WriteSps(320u, 180u, 1, 0, 1, 0, &buffer); 524 std::optional<H265SpsParser::SpsState> sps = H265SpsParser::ParseSps(buffer); 525 ASSERT_TRUE(sps.has_value()); 526 EXPECT_EQ(320u, sps->width); 527 EXPECT_EQ(180u, sps->height); 528 EXPECT_EQ(1u, sps->sps_id); 529 EXPECT_EQ(0u, sps->long_term_ref_pics_present_flag); 530 531 WriteSps(320u, 180u, 1, 6, 1, 1, &buffer); 532 std::optional<H265SpsParser::SpsState> sps1 = H265SpsParser::ParseSps(buffer); 533 ASSERT_TRUE(sps1.has_value()); 534 EXPECT_EQ(320u, sps1->width); 535 EXPECT_EQ(180u, sps1->height); 536 EXPECT_EQ(1u, sps1->sps_id); 537 EXPECT_EQ(1u, sps1->long_term_ref_pics_present_flag); 538 } 539 540 } // namespace webrtc