rtp_packetizer_h265_unittest.cc (29480B)
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 "modules/rtp_rtcp/source/rtp_packetizer_h265.h" 12 13 #include <cstddef> 14 #include <cstdint> 15 #include <cstring> 16 #include <initializer_list> 17 #include <vector> 18 19 #include "absl/algorithm/container.h" 20 #include "api/array_view.h" 21 #include "common_video/h265/h265_common.h" 22 #include "modules/rtp_rtcp/source/rtp_format.h" 23 #include "modules/rtp_rtcp/source/rtp_packet_h265_common.h" 24 #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" 25 #include "rtc_base/buffer.h" 26 #include "rtc_base/checks.h" 27 #include "test/gmock.h" 28 #include "test/gtest.h" 29 30 namespace webrtc { 31 namespace { 32 33 using ::testing::Each; 34 using ::testing::ElementsAre; 35 using ::testing::ElementsAreArray; 36 using ::testing::Eq; 37 using ::testing::SizeIs; 38 39 constexpr RtpPacketToSend::ExtensionManager* kNoExtensions = nullptr; 40 constexpr size_t kMaxPayloadSizeBytes = 1200; 41 constexpr size_t kH265LengthFieldSizeBytes = 2; 42 constexpr RtpPacketizer::PayloadSizeLimits kNoLimits; 43 44 constexpr size_t kFuHeaderSizeBytes = 45 kH265FuHeaderSizeBytes + kH265PayloadHeaderSizeBytes; 46 47 struct NalUnitHeader { 48 uint8_t forbidden_zero_bit = 0; 49 uint8_t nal_unit_type = 0; 50 uint8_t nuh_layer_id = 0; 51 uint8_t nuh_temporal_id_plus1 = 0; 52 }; 53 54 // Creates Buffer that looks like nal unit of given header and size. 55 Buffer GenerateNalUnit(NalUnitHeader header, size_t size) { 56 RTC_CHECK_GT(size, 0); 57 Buffer buffer(size); 58 buffer[0] = (header.nal_unit_type << 1) | (header.nuh_layer_id >> 5); 59 buffer[1] = (header.nuh_layer_id << 3) | header.nuh_temporal_id_plus1; 60 for (size_t i = 2; i < size; ++i) { 61 buffer[i] = static_cast<uint8_t>(i); 62 } 63 // Last byte shouldn't be 0, or it may be counted as part of next 4-byte start 64 // sequence. 65 buffer[size - 1] |= 0x10; 66 return buffer; 67 } 68 69 // Create frame consisting of nalus of given size. 70 Buffer CreateFrame(std::initializer_list<size_t> nalu_sizes) { 71 static constexpr int kStartCodeSize = 3; 72 Buffer frame(absl::c_accumulate(nalu_sizes, size_t{0}) + 73 kStartCodeSize * nalu_sizes.size()); 74 size_t offset = 0; 75 for (size_t nalu_size : nalu_sizes) { 76 EXPECT_GE(nalu_size, 1u); 77 // Insert nalu start code 78 frame[offset] = 0; 79 frame[offset + 1] = 0; 80 frame[offset + 2] = 1; 81 // Set some valid header. 82 frame[offset + 3] = 2; 83 // Fill payload avoiding accidental start codes 84 if (nalu_size > 1) { 85 memset(frame.data() + offset + 4, 0x3f, nalu_size - 1); 86 } 87 offset += (kStartCodeSize + nalu_size); 88 } 89 return frame; 90 } 91 92 // Create frame consisting of given nalus. 93 Buffer CreateFrame(ArrayView<const Buffer> nalus) { 94 static constexpr int kStartCodeSize = 3; 95 int frame_size = 0; 96 for (const Buffer& nalu : nalus) { 97 frame_size += (kStartCodeSize + nalu.size()); 98 } 99 Buffer frame(frame_size); 100 size_t offset = 0; 101 for (const Buffer& nalu : nalus) { 102 // Insert nalu start code 103 frame[offset] = 0; 104 frame[offset + 1] = 0; 105 frame[offset + 2] = 1; 106 // Copy the nalu unit. 107 memcpy(frame.data() + offset + 3, nalu.data(), nalu.size()); 108 offset += (kStartCodeSize + nalu.size()); 109 } 110 return frame; 111 } 112 113 std::vector<RtpPacketToSend> FetchAllPackets(RtpPacketizerH265* packetizer) { 114 std::vector<RtpPacketToSend> result; 115 size_t num_packets = packetizer->NumPackets(); 116 result.reserve(num_packets); 117 RtpPacketToSend packet(kNoExtensions); 118 while (packetizer->NextPacket(&packet)) { 119 result.push_back(packet); 120 } 121 EXPECT_THAT(result, SizeIs(num_packets)); 122 return result; 123 } 124 125 // Single nalu tests. 126 TEST(RtpPacketizerH265Test, SingleNalu) { 127 const uint8_t frame[] = {0, 0, 1, H265::kIdrWRadl, 0xFF}; 128 129 RtpPacketizerH265 packetizer(frame, kNoLimits); 130 std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer); 131 132 ASSERT_THAT(packets, SizeIs(1)); 133 EXPECT_THAT(packets[0].payload(), ElementsAre(H265::kIdrWRadl, 0xFF)); 134 } 135 136 TEST(RtpPacketizerH265Test, SingleNaluTwoPackets) { 137 RtpPacketizer::PayloadSizeLimits limits; 138 limits.max_payload_len = kMaxPayloadSizeBytes; 139 Buffer nalus[] = {GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 140 .nuh_layer_id = 32, 141 .nuh_temporal_id_plus1 = 2}, 142 kMaxPayloadSizeBytes), 143 GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 144 .nuh_layer_id = 32, 145 .nuh_temporal_id_plus1 = 2}, 146 100)}; 147 Buffer frame = CreateFrame(nalus); 148 149 RtpPacketizerH265 packetizer(frame, limits); 150 std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer); 151 152 ASSERT_THAT(packets, SizeIs(2)); 153 EXPECT_THAT(packets[0].payload(), ElementsAreArray(nalus[0])); 154 EXPECT_THAT(packets[1].payload(), ElementsAreArray(nalus[1])); 155 } 156 157 TEST(RtpPacketizerH265Test, 158 SingleNaluFirstPacketReductionAppliesOnlyToFirstFragment) { 159 RtpPacketizer::PayloadSizeLimits limits; 160 limits.max_payload_len = 200; 161 limits.first_packet_reduction_len = 5; 162 Buffer nalus[] = {GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 163 .nuh_layer_id = 32, 164 .nuh_temporal_id_plus1 = 2}, 165 /*size=*/195), 166 GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 167 .nuh_layer_id = 32, 168 .nuh_temporal_id_plus1 = 2}, 169 /*size=*/200), 170 GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 171 .nuh_layer_id = 32, 172 .nuh_temporal_id_plus1 = 2}, 173 /*size=*/200)}; 174 Buffer frame = CreateFrame(nalus); 175 176 RtpPacketizerH265 packetizer(frame, limits); 177 std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer); 178 179 ASSERT_THAT(packets, SizeIs(3)); 180 EXPECT_THAT(packets[0].payload(), ElementsAreArray(nalus[0])); 181 EXPECT_THAT(packets[1].payload(), ElementsAreArray(nalus[1])); 182 EXPECT_THAT(packets[2].payload(), ElementsAreArray(nalus[2])); 183 } 184 185 TEST(RtpPacketizerH265Test, 186 SingleNaluLastPacketReductionAppliesOnlyToLastFragment) { 187 RtpPacketizer::PayloadSizeLimits limits; 188 limits.max_payload_len = 200; 189 limits.last_packet_reduction_len = 5; 190 Buffer nalus[] = {GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 191 .nuh_layer_id = 32, 192 .nuh_temporal_id_plus1 = 2}, 193 /*size=*/200), 194 GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 195 .nuh_layer_id = 32, 196 .nuh_temporal_id_plus1 = 2}, 197 /*size=*/200), 198 GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 199 .nuh_layer_id = 32, 200 .nuh_temporal_id_plus1 = 2}, 201 /*size=*/195)}; 202 Buffer frame = CreateFrame(nalus); 203 204 RtpPacketizerH265 packetizer(frame, limits); 205 std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer); 206 207 ASSERT_THAT(packets, SizeIs(3)); 208 EXPECT_THAT(packets[0].payload(), ElementsAreArray(nalus[0])); 209 EXPECT_THAT(packets[1].payload(), ElementsAreArray(nalus[1])); 210 EXPECT_THAT(packets[2].payload(), ElementsAreArray(nalus[2])); 211 } 212 213 TEST(RtpPacketizerH265Test, SingleNaluUsesSinglePacketReductionLen) { 214 RtpPacketizer::PayloadSizeLimits limits; 215 limits.max_payload_len = 200; 216 limits.first_packet_reduction_len = 20; 217 limits.single_packet_reduction_len = 40; 218 limits.last_packet_reduction_len = 30; 219 Buffer frame = CreateFrame({160}); 220 221 RtpPacketizerH265 packetizer(frame, limits); 222 223 EXPECT_THAT(FetchAllPackets(&packetizer), SizeIs(1)); 224 } 225 226 TEST(RtpPacketizerH265Test, FragmentsNalUnitWhenDoesntFitIntoSinglePacket) { 227 RtpPacketizer::PayloadSizeLimits limits; 228 limits.max_payload_len = 200; 229 limits.first_packet_reduction_len = 10; 230 limits.single_packet_reduction_len = 40; 231 limits.last_packet_reduction_len = 10; 232 Buffer frame = CreateFrame({170}); 233 234 RtpPacketizerH265 packetizer(frame, limits); 235 236 EXPECT_THAT(FetchAllPackets(&packetizer), SizeIs(2)); 237 } 238 239 // Aggregation tests. 240 TEST(RtpPacketizerH265Test, ApRespectsNoPacketReduction) { 241 Buffer nalus[] = {GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 242 .nuh_layer_id = 32, 243 .nuh_temporal_id_plus1 = 2}, 244 /*size=*/3), 245 GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 246 .nuh_layer_id = 32, 247 .nuh_temporal_id_plus1 = 2}, 248 /*size=*/3), 249 GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 250 .nuh_layer_id = 32, 251 .nuh_temporal_id_plus1 = 2}, 252 /*size=*/0x123)}; 253 Buffer frame = CreateFrame(nalus); 254 255 RtpPacketizerH265 packetizer(frame, kNoLimits); 256 std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer); 257 258 ASSERT_THAT(packets, SizeIs(1)); 259 auto payload = packets[0].payload(); 260 int type = H265::ParseNaluType(payload[0]); 261 EXPECT_EQ(payload.size(), kH265NalHeaderSizeBytes + 262 3 * kH265LengthFieldSizeBytes + 3 + 3 + 0x123); 263 264 EXPECT_EQ(type, H265::NaluType::kAp); 265 payload = payload.subview(kH265NalHeaderSizeBytes); 266 // 1st fragment. 267 EXPECT_THAT(payload.subview(0, kH265LengthFieldSizeBytes), 268 ElementsAre(0, 3)); // Size. 269 EXPECT_THAT(payload.subview(kH265LengthFieldSizeBytes, 3), 270 ElementsAreArray(nalus[0])); 271 payload = payload.subview(kH265LengthFieldSizeBytes + 3); 272 // 2nd fragment. 273 EXPECT_THAT(payload.subview(0, kH265LengthFieldSizeBytes), 274 ElementsAre(0, 3)); // Size. 275 EXPECT_THAT(payload.subview(kH265LengthFieldSizeBytes, 3), 276 ElementsAreArray(nalus[1])); 277 payload = payload.subview(kH265LengthFieldSizeBytes + 3); 278 // 3rd fragment. 279 EXPECT_THAT(payload.subview(0, kH265LengthFieldSizeBytes), 280 ElementsAre(0x1, 0x23)); // Size. 281 EXPECT_THAT(payload.subview(kH265LengthFieldSizeBytes), 282 ElementsAreArray(nalus[2])); 283 } 284 285 TEST(RtpPacketizerH265Test, ApRespectsLayerIdAndTemporalId) { 286 // Generate 3 NALUs: NALU 1 with nuh_layer_id 2 and nuh_temporal_id_plus1 6, 287 // NALU 2 with nuh_layer_id 0 and nuh_temporal_id_plus1 1, 288 // NALU 3 with nuh_layer_id 32 and nuh_temporal_id_plus1 2, 289 // So in the AP packet header, nuh_layer_id should be 0 which is the lowest 290 // nuh_layer_id value of 3 NALUs and nuh_temporal_id_plus1 should be 1 which 291 // is the lowest nuh_temporal_id_plus1 value of 3 NALUs 292 Buffer nalus[] = {GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 293 .nuh_layer_id = 2, 294 .nuh_temporal_id_plus1 = 6}, 295 /*size=*/3), 296 GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 297 .nuh_layer_id = 0, 298 .nuh_temporal_id_plus1 = 1}, 299 /*size=*/3), 300 GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 301 .nuh_layer_id = 32, 302 .nuh_temporal_id_plus1 = 2}, 303 /*size=*/0x123)}; 304 Buffer frame = CreateFrame(nalus); 305 306 RtpPacketizerH265 packetizer(frame, kNoLimits); 307 std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer); 308 309 ASSERT_THAT(packets, SizeIs(1)); 310 auto payload = packets[0].payload(); 311 uint8_t type = H265::ParseNaluType(payload[0]); 312 uint8_t layer_id = ((payload[0] & kH265LayerIDHMask) << 5) | 313 ((payload[1] & kH265LayerIDLMask) >> 3); 314 uint8_t temporal_id = payload[1] & kH265TIDMask; 315 EXPECT_EQ(payload.size(), kH265NalHeaderSizeBytes + 316 3 * kH265LengthFieldSizeBytes + 3 + 3 + 0x123); 317 318 EXPECT_EQ(type, H265::NaluType::kAp); 319 EXPECT_EQ(layer_id, 0); 320 EXPECT_EQ(temporal_id, 1); 321 payload = payload.subview(kH265NalHeaderSizeBytes); 322 // 1st fragment. 323 EXPECT_THAT(payload.subview(0, kH265LengthFieldSizeBytes), ElementsAre(0, 3)); 324 EXPECT_THAT(payload.subview(kH265LengthFieldSizeBytes, 3), 325 ElementsAreArray(nalus[0])); 326 payload = payload.subview(kH265LengthFieldSizeBytes + 3); 327 // 2nd fragment. 328 EXPECT_THAT(payload.subview(0, kH265LengthFieldSizeBytes), ElementsAre(0, 3)); 329 EXPECT_THAT(payload.subview(kH265LengthFieldSizeBytes, 3), 330 ElementsAreArray(nalus[1])); 331 payload = payload.subview(kH265LengthFieldSizeBytes + 3); 332 // 3rd fragment. 333 EXPECT_THAT(payload.subview(0, kH265LengthFieldSizeBytes), 334 ElementsAre(0x1, 0x23)); 335 EXPECT_THAT(payload.subview(kH265LengthFieldSizeBytes), 336 ElementsAreArray(nalus[2])); 337 } 338 339 TEST(RtpPacketizerH265Test, ApRespectsSinglePacketReduction) { 340 RtpPacketizer::PayloadSizeLimits limits; 341 limits.max_payload_len = 200; 342 limits.first_packet_reduction_len = 5; 343 limits.single_packet_reduction_len = 40; 344 limits.last_packet_reduction_len = 5; 345 Buffer frame = CreateFrame({60, 60, 60}); 346 347 RtpPacketizerH265 packetizer(frame, limits); 348 349 EXPECT_THAT(FetchAllPackets(&packetizer), SizeIs(2)); 350 } 351 352 TEST(RtpPacketizerH265Test, ApRespectsFirstPacketReduction) { 353 RtpPacketizer::PayloadSizeLimits limits; 354 limits.max_payload_len = 1000; 355 limits.first_packet_reduction_len = 100; 356 const size_t kFirstFragmentSize = 357 limits.max_payload_len - limits.first_packet_reduction_len; 358 Buffer nalus[] = {GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 359 .nuh_layer_id = 32, 360 .nuh_temporal_id_plus1 = 2}, 361 /*size=*/kFirstFragmentSize), 362 GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 363 .nuh_layer_id = 32, 364 .nuh_temporal_id_plus1 = 2}, 365 /*size=*/3), 366 GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 367 .nuh_layer_id = 32, 368 .nuh_temporal_id_plus1 = 2}, 369 /*size=*/3)}; 370 Buffer frame = CreateFrame(nalus); 371 372 RtpPacketizerH265 packetizer(frame, limits); 373 std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer); 374 375 ASSERT_THAT(packets, SizeIs(2)); 376 // Expect 1st packet is single nalu. 377 EXPECT_THAT(packets[0].payload(), ElementsAreArray(nalus[0])); 378 // Expect 2nd packet is aggregate of last two fragments. 379 // The size of H265 nal_unit_header is 2 bytes, according to 7.3.1.2 380 // in H265 spec. Aggregation packet type is 48, nuh_layer_id is 32 and 381 // nuh_temporal_id_plus1 is 2, so the nal_unit_header should be "01100001 382 // 00000010", which is 97 and 2. 383 EXPECT_THAT(packets[1].payload(), 384 ElementsAre(97, 2, // 385 0, 3, nalus[1][0], nalus[1][1], nalus[1][2], // 386 0, 3, nalus[2][0], nalus[2][1], nalus[2][2])); 387 } 388 389 TEST(RtpPacketizerH265Test, ApRespectsLastPacketReduction) { 390 RtpPacketizer::PayloadSizeLimits limits; 391 limits.max_payload_len = 1000; 392 limits.last_packet_reduction_len = 100; 393 const size_t kLastFragmentSize = 394 limits.max_payload_len - limits.last_packet_reduction_len; 395 // First nalu forces packetizer to consider last packet reduction len instead 396 // of single packet reduction len when aggregating last 3 nalus. 397 Buffer nalus[] = {GenerateNalUnit({.nal_unit_type = H265::NaluType::kSps, 398 .nuh_layer_id = 32, 399 .nuh_temporal_id_plus1 = 2}, 400 /*size=*/999), 401 GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 402 .nuh_layer_id = 32, 403 .nuh_temporal_id_plus1 = 2}, 404 /*size=*/3), 405 GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 406 .nuh_layer_id = 32, 407 .nuh_temporal_id_plus1 = 2}, 408 /*size=*/3), 409 GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 410 .nuh_layer_id = 32, 411 .nuh_temporal_id_plus1 = 2}, 412 /*size=*/kLastFragmentSize)}; 413 Buffer frame = CreateFrame(nalus); 414 415 RtpPacketizerH265 packetizer(frame, limits); 416 std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer); 417 418 ASSERT_THAT(packets, SizeIs(3)); 419 // Expect 1st packet is single nalu with the 1st fragment. 420 EXPECT_THAT(packets[0].payload(), ElementsAreArray(nalus[0])); 421 // Expect 2nd packet is aggregate of 2nd and 3rd fragments. 422 EXPECT_THAT(packets[1].payload(), 423 ElementsAre(97, 2, // 424 0, 3, nalus[1][0], nalus[1][1], nalus[1][2], // 425 0, 3, nalus[2][0], nalus[2][1], nalus[2][2])); 426 // Expect 3rd packet is single nalu with the last fragment. 427 EXPECT_THAT(packets[2].payload(), ElementsAreArray(nalus[3])); 428 } 429 430 TEST(RtpPacketizerH265Test, TooSmallForApHeaders) { 431 RtpPacketizer::PayloadSizeLimits limits; 432 limits.max_payload_len = 1000; 433 const size_t kLastFragmentSize = 434 limits.max_payload_len - 3 * kH265LengthFieldSizeBytes - 4; 435 Buffer nalus[] = {GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 436 .nuh_layer_id = 32, 437 .nuh_temporal_id_plus1 = 2}, 438 /*size=*/3), 439 GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 440 .nuh_layer_id = 32, 441 .nuh_temporal_id_plus1 = 2}, 442 /*size=*/3), 443 GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 444 .nuh_layer_id = 32, 445 .nuh_temporal_id_plus1 = 2}, 446 /*size=*/kLastFragmentSize)}; 447 Buffer frame = CreateFrame(nalus); 448 449 RtpPacketizerH265 packetizer(frame, limits); 450 std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer); 451 452 ASSERT_THAT(packets, SizeIs(2)); 453 // Expect 1st packet is aggregate of 1st two fragments. 454 EXPECT_THAT(packets[0].payload(), 455 ElementsAre(97, 2, // 456 0, 3, nalus[0][0], nalus[0][1], nalus[0][2], // 457 0, 3, nalus[1][0], nalus[1][1], nalus[1][2])); 458 // Expect 2nd packet is single nalu. 459 EXPECT_THAT(packets[1].payload(), ElementsAreArray(nalus[2])); 460 } 461 462 TEST(RtpPacketizerH265Test, LastFragmentFitsInSingleButNotLastPacket) { 463 RtpPacketizer::PayloadSizeLimits limits; 464 limits.max_payload_len = 1178; 465 limits.first_packet_reduction_len = 0; 466 limits.last_packet_reduction_len = 20; 467 limits.single_packet_reduction_len = 20; 468 // Actual sizes, which triggered this bug. 469 Buffer frame = CreateFrame({20, 8, 18, 1161}); 470 471 RtpPacketizerH265 packetizer(frame, limits); 472 std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer); 473 474 // Last packet has to be of correct size. 475 // Incorrect implementation might miss this constraint and not split the last 476 // fragment in two packets. 477 EXPECT_LE(static_cast<int>(packets.back().payload_size()), 478 limits.max_payload_len - limits.last_packet_reduction_len); 479 } 480 481 // Splits frame with payload size `frame_payload_size` without fragmentation, 482 // Returns sizes of the payloads excluding FU headers. 483 std::vector<int> TestFu(size_t frame_payload_size, 484 const RtpPacketizer::PayloadSizeLimits& limits) { 485 Buffer nalu[] = { 486 GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 487 .nuh_layer_id = 32, 488 .nuh_temporal_id_plus1 = 2}, 489 kH265NalHeaderSizeBytes + frame_payload_size)}; 490 Buffer frame = CreateFrame(nalu); 491 492 RtpPacketizerH265 packetizer(frame, limits); 493 std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer); 494 495 EXPECT_GE(packets.size(), 2u); // Single packet indicates it is not FU. 496 std::vector<uint16_t> fu_header; 497 std::vector<int> payload_sizes; 498 499 for (const RtpPacketToSend& packet : packets) { 500 auto payload = packet.payload(); 501 EXPECT_GT(payload.size(), kFuHeaderSizeBytes); 502 // FU header is after the 2-bytes size PayloadHdr according to 4.4.3 in spec 503 fu_header.push_back(payload[2]); 504 payload_sizes.push_back(payload.size() - kFuHeaderSizeBytes); 505 } 506 507 EXPECT_TRUE(fu_header.front() & kH265SBitMask); 508 EXPECT_TRUE(fu_header.back() & kH265EBitMask); 509 // Clear S and E bits before testing all are duplicating same original header. 510 fu_header.front() &= ~kH265SBitMask; 511 fu_header.back() &= ~kH265EBitMask; 512 uint8_t nalu_type = (nalu[0][0] & kH265TypeMask) >> 1; 513 EXPECT_THAT(fu_header, Each(Eq(nalu_type))); 514 515 return payload_sizes; 516 } 517 518 // Fragmentation tests. 519 TEST(RtpPacketizerH265Test, FuOddSize) { 520 RtpPacketizer::PayloadSizeLimits limits; 521 limits.max_payload_len = 1200; 522 EXPECT_THAT(TestFu(1200, limits), ElementsAre(600, 600)); 523 } 524 525 TEST(RtpPacketizerH265Test, FuWithFirstPacketReduction) { 526 RtpPacketizer::PayloadSizeLimits limits; 527 limits.max_payload_len = 1200; 528 limits.first_packet_reduction_len = 4; 529 limits.single_packet_reduction_len = 4; 530 EXPECT_THAT(TestFu(1198, limits), ElementsAre(597, 601)); 531 } 532 533 TEST(RtpPacketizerH265Test, FuWithLastPacketReduction) { 534 RtpPacketizer::PayloadSizeLimits limits; 535 limits.max_payload_len = 1200; 536 limits.last_packet_reduction_len = 4; 537 limits.single_packet_reduction_len = 4; 538 EXPECT_THAT(TestFu(1198, limits), ElementsAre(601, 597)); 539 } 540 541 TEST(RtpPacketizerH265Test, FuWithSinglePacketReduction) { 542 RtpPacketizer::PayloadSizeLimits limits; 543 limits.max_payload_len = 1199; 544 limits.single_packet_reduction_len = 200; 545 EXPECT_THAT(TestFu(1000, limits), ElementsAre(500, 500)); 546 } 547 548 TEST(RtpPacketizerH265Test, FuEvenSize) { 549 RtpPacketizer::PayloadSizeLimits limits; 550 limits.max_payload_len = 1200; 551 EXPECT_THAT(TestFu(1201, limits), ElementsAre(600, 601)); 552 } 553 554 TEST(RtpPacketizerH265Test, FuRounding) { 555 RtpPacketizer::PayloadSizeLimits limits; 556 limits.max_payload_len = 1448; 557 EXPECT_THAT(TestFu(10123, limits), 558 ElementsAre(1265, 1265, 1265, 1265, 1265, 1266, 1266, 1266)); 559 } 560 561 TEST(RtpPacketizerH265Test, FuBig) { 562 RtpPacketizer::PayloadSizeLimits limits; 563 limits.max_payload_len = 1200; 564 // Generate 10 full sized packets, leave room for FU headers. 565 EXPECT_THAT( 566 TestFu(10 * (1200 - kFuHeaderSizeBytes), limits), 567 ElementsAre(1197, 1197, 1197, 1197, 1197, 1197, 1197, 1197, 1197, 1197)); 568 } 569 570 // Invalid input tests. 571 TEST(RtpPacketizerH265Test, TooShortNalUnitHeader) { 572 const uint8_t kFrame[] = {0, 0, 0, 1, 15}; 573 RtpPacketizer::PayloadSizeLimits limits; 574 RtpPacketizerH265 packetizer(kFrame, limits); 575 EXPECT_EQ(packetizer.NumPackets(), 0u); 576 } 577 578 struct PacketInfo { 579 bool first_fragment = false; 580 bool last_fragment = false; 581 bool aggregated = false; 582 int nalu_index = 0; 583 int nalu_number = 0; 584 int payload_size = 0; 585 int start_offset = 0; 586 }; 587 588 struct MixedApFuTestParams { 589 std::vector<int> nalus; 590 int expect_packetsSize = 0; 591 std::vector<PacketInfo> expected_packets; 592 }; 593 594 class RtpPacketizerH265ParametrizedTest 595 : public ::testing::TestWithParam<MixedApFuTestParams> {}; 596 597 // Fragmentation + aggregation mixed testing. 598 TEST_P(RtpPacketizerH265ParametrizedTest, MixedApFu) { 599 RtpPacketizer::PayloadSizeLimits limits; 600 const MixedApFuTestParams params = GetParam(); 601 limits.max_payload_len = 100; 602 std::vector<Buffer> nalus; 603 nalus.reserve(params.nalus.size()); 604 605 // Generate nalus according to size specified in paramters 606 for (size_t index = 0; index < params.nalus.size(); index++) { 607 nalus.push_back(GenerateNalUnit({.nal_unit_type = H265::NaluType::kIdrNLp, 608 .nuh_layer_id = 32, 609 .nuh_temporal_id_plus1 = 2}, 610 params.nalus[index])); 611 } 612 Buffer frame = CreateFrame(nalus); 613 614 RtpPacketizerH265 packetizer(frame, limits); 615 std::vector<RtpPacketToSend> packets = FetchAllPackets(&packetizer); 616 617 ASSERT_THAT(packets, SizeIs(params.expect_packetsSize)); 618 for (int i = 0; i < params.expect_packetsSize; i++) { 619 PacketInfo expected_packet = params.expected_packets[i]; 620 if (expected_packet.aggregated) { 621 int type = H265::ParseNaluType(packets[i].payload()[0]); 622 EXPECT_THAT(type, H265::NaluType::kAp); 623 auto payload = packets[i].payload().subview(kH265NalHeaderSizeBytes); 624 int offset = 0; 625 // Generated AP packet header and payload align 626 for (int j = expected_packet.nalu_index; j < expected_packet.nalu_number; 627 j++) { 628 EXPECT_THAT(payload.subview(0, kH265LengthFieldSizeBytes), 629 ElementsAre(0, nalus[j].size())); 630 EXPECT_THAT(payload.subview(offset + kH265LengthFieldSizeBytes, 631 nalus[j].size()), 632 ElementsAreArray(nalus[j])); 633 offset += kH265LengthFieldSizeBytes + nalus[j].size(); 634 } 635 } else { 636 uint8_t fu_header = 0; 637 fu_header |= (expected_packet.first_fragment ? kH265SBitMask : 0); 638 fu_header |= (expected_packet.last_fragment ? kH265EBitMask : 0); 639 fu_header |= H265::NaluType::kIdrNLp; 640 EXPECT_THAT(packets[i].payload().subview(0, kFuHeaderSizeBytes), 641 ElementsAre(99, 2, fu_header)); 642 EXPECT_THAT(packets[i].payload().subview(kFuHeaderSizeBytes), 643 ElementsAreArray(nalus[expected_packet.nalu_index].data() + 644 kH265NalHeaderSizeBytes + 645 expected_packet.start_offset, 646 expected_packet.payload_size)); 647 } 648 } 649 } 650 651 INSTANTIATE_TEST_SUITE_P( 652 RtpPacketizerH265Test, 653 RtpPacketizerH265ParametrizedTest, 654 testing::Values( 655 // FU + AP + FU. 656 // GenerateNalUnit will include 2 bytes nalu header, for FU packet split 657 // calculation, this 2-byte nalu header length should be excluded. 658 MixedApFuTestParams{.nalus = {140, 20, 20, 160}, 659 .expect_packetsSize = 5, 660 .expected_packets = {{.first_fragment = true, 661 .nalu_index = 0, 662 .payload_size = 69, 663 .start_offset = 0}, 664 {.last_fragment = true, 665 .nalu_index = 0, 666 .payload_size = 69, 667 .start_offset = 69}, 668 {.aggregated = true, 669 .nalu_index = 1, 670 .nalu_number = 2}, 671 {.first_fragment = true, 672 .nalu_index = 3, 673 .payload_size = 79, 674 .start_offset = 0}, 675 {.last_fragment = true, 676 .nalu_index = 3, 677 .payload_size = 79, 678 .start_offset = 79}}}, 679 // AP + FU + AP 680 MixedApFuTestParams{ 681 .nalus = {20, 20, 160, 30, 30}, 682 .expect_packetsSize = 4, 683 .expected_packets = { 684 {.aggregated = true, .nalu_index = 0, .nalu_number = 2}, 685 {.first_fragment = true, 686 .nalu_index = 2, 687 .payload_size = 79, 688 .start_offset = 0}, 689 {.last_fragment = true, 690 .nalu_index = 2, 691 .payload_size = 79, 692 .start_offset = 79}, 693 {.aggregated = true, .nalu_index = 3, .nalu_number = 2}}})); 694 695 } // namespace 696 } // namespace webrtc