diff --git a/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc b/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc index c90dbba9c1..fabaa931a8 100644 --- a/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc @@ -24,13 +24,12 @@ namespace webrtc { namespace { +using ::testing::Each; +using ::testing::ElementsAre; using ::testing::ElementsAreArray; - -struct H264ParsedPayload : public RtpDepacketizer::ParsedPayload { - RTPVideoHeaderH264& h264() { - return absl::get(video.video_type_header); - } -}; +using ::testing::Eq; +using ::testing::IsEmpty; +using ::testing::SizeIs; constexpr RtpPacketToSend::ExtensionManager* kNoExtensions = nullptr; constexpr size_t kMaxPayloadSize = 1200; @@ -56,173 +55,114 @@ enum NalDefs { kFBit = 0x80, kNriMask = 0x60, kTypeMask = 0x1F }; // Bit masks for FU (A and B) headers. enum FuDefs { kSBit = 0x80, kEBit = 0x40, kRBit = 0x20 }; -void CreateThreeFragments(RTPFragmentationHeader* fragmentation, - size_t frameSize, - size_t payloadOffset) { - fragmentation->VerifyAndAllocateFragmentationHeader(3); - fragmentation->fragmentationOffset[0] = 0; - fragmentation->fragmentationLength[0] = 2; - fragmentation->fragmentationOffset[1] = 2; - fragmentation->fragmentationLength[1] = 2; - fragmentation->fragmentationOffset[2] = 4; - fragmentation->fragmentationLength[2] = - kNalHeaderSize + frameSize - payloadOffset; -} - -void VerifyFua(size_t fua_index, - const uint8_t* expected_payload, - int offset, - rtc::ArrayView packet, - rtc::ArrayView expected_sizes) { - ASSERT_EQ(expected_sizes[fua_index] + kFuAHeaderSize, packet.size()) - << "FUA index: " << fua_index; - const uint8_t kFuIndicator = 0x1C; // F=0, NRI=0, Type=28. - EXPECT_EQ(kFuIndicator, packet[0]) << "FUA index: " << fua_index; - bool should_be_last_fua = (fua_index == expected_sizes.size() - 1); - uint8_t fu_header = 0; - if (fua_index == 0) - fu_header = 0x85; // S=1, E=0, R=0, Type=5. - else if (should_be_last_fua) - fu_header = 0x45; // S=0, E=1, R=0, Type=5. - else - fu_header = 0x05; // S=0, E=0, R=0, Type=5. - EXPECT_EQ(fu_header, packet[1]) << "FUA index: " << fua_index; - std::vector expected_packet_payload( - &expected_payload[offset], - &expected_payload[offset + expected_sizes[fua_index]]); - EXPECT_THAT(expected_packet_payload, - ElementsAreArray(&packet[2], expected_sizes[fua_index])) - << "FUA index: " << fua_index; -} - -void TestFua(size_t frame_size, - size_t max_payload_size, - size_t last_packet_reduction, - rtc::ArrayView expected_sizes) { - std::vector frame(frame_size); - frame[0] = 0x05; // F=0, NRI=0, Type=5. - for (size_t i = 0; i < frame_size - kNalHeaderSize; ++i) { - frame[i + kNalHeaderSize] = i; - } - RtpPacketizer::PayloadSizeLimits limits; - limits.max_payload_len = max_payload_size; - limits.last_packet_reduction_len = last_packet_reduction; +RTPFragmentationHeader CreateFragmentation(rtc::ArrayView sizes) { RTPFragmentationHeader fragmentation; - fragmentation.VerifyAndAllocateFragmentationHeader(1); - fragmentation.fragmentationOffset[0] = 0; - fragmentation.fragmentationLength[0] = frame_size; + fragmentation.VerifyAndAllocateFragmentationHeader(sizes.size()); + size_t offset = 0; + for (size_t i = 0; i < sizes.size(); ++i) { + fragmentation.fragmentationOffset[i] = offset; + fragmentation.fragmentationLength[i] = sizes[i]; + offset += sizes[i]; + } + return fragmentation; +} - RtpPacketizerH264 packetizer( - frame, limits, H264PacketizationMode::NonInterleaved, fragmentation); - EXPECT_EQ(packetizer.NumPackets(), expected_sizes.size()); +// Create fragmentation with single fragment of same size as |frame| +RTPFragmentationHeader NoFragmentation(rtc::ArrayView frame) { + size_t frame_size[] = {frame.size()}; + return CreateFragmentation(frame_size); +} +// Create frame of given size. +rtc::Buffer CreateFrame(size_t frame_size) { + rtc::Buffer frame(frame_size); + // Set some valid header. + frame[0] = 0x01; + // Generate payload to detect when shifted payload was put into a packet. + for (size_t i = 1; i < frame_size; ++i) + frame[i] = static_cast(i); + return frame; +} + +// Create frame with size deduced from fragmentation. +rtc::Buffer CreateFrame(const RTPFragmentationHeader& fragmentation) { + size_t last_frame_index = fragmentation.fragmentationVectorSize - 1; + size_t frame_size = fragmentation.fragmentationOffset[last_frame_index] + + fragmentation.fragmentationLength[last_frame_index]; + rtc::Buffer frame = CreateFrame(frame_size); + // Set some headers. + // Tests can expect those are valid but shouln't rely on actual values. + for (size_t i = 0; i <= last_frame_index; ++i) { + frame[fragmentation.fragmentationOffset[i]] = i + 1; + } + return frame; +} + +std::vector FetchAllPackets(RtpPacketizerH264* packetizer) { + std::vector result; + size_t num_packets = packetizer->NumPackets(); + result.reserve(num_packets); RtpPacketToSend packet(kNoExtensions); - ASSERT_LE(max_payload_size, packet.FreeCapacity()); - size_t offset = kNalHeaderSize; - for (size_t i = 0; i < expected_sizes.size(); ++i) { - ASSERT_TRUE(packetizer.NextPacket(&packet)); - VerifyFua(i, frame.data(), offset, packet.payload(), expected_sizes); - offset += expected_sizes[i]; + while (packetizer->NextPacket(&packet)) { + result.push_back(packet); } - - EXPECT_FALSE(packetizer.NextPacket(&packet)); + EXPECT_THAT(result, SizeIs(num_packets)); + return result; } -size_t GetExpectedNaluOffset(const RTPFragmentationHeader& fragmentation, - size_t start_index, - size_t nalu_index) { - assert(nalu_index < fragmentation.fragmentationVectorSize); - size_t expected_nalu_offset = kNalHeaderSize; // STAP-A header. - for (size_t i = start_index; i < nalu_index; ++i) { - expected_nalu_offset += - kLengthFieldLength + fragmentation.fragmentationLength[i]; - } - return expected_nalu_offset; -} - -void VerifyStapAPayload(const RTPFragmentationHeader& fragmentation, - size_t first_stapa_index, - size_t nalu_index, - rtc::ArrayView frame, - rtc::ArrayView packet) { - size_t expected_payload_offset = - GetExpectedNaluOffset(fragmentation, first_stapa_index, nalu_index) + - kLengthFieldLength; - size_t offset = fragmentation.fragmentationOffset[nalu_index]; - const uint8_t* expected_payload = &frame[offset]; - size_t expected_payload_length = - fragmentation.fragmentationLength[nalu_index]; - ASSERT_LE(offset + expected_payload_length, frame.size()); - ASSERT_LE(expected_payload_offset + expected_payload_length, packet.size()); - std::vector expected_payload_vector( - expected_payload, &expected_payload[expected_payload_length]); - EXPECT_THAT(expected_payload_vector, - ElementsAreArray(&packet[expected_payload_offset], - expected_payload_length)); -} - -void VerifySingleNaluPayload(const RTPFragmentationHeader& fragmentation, - size_t nalu_index, - rtc::ArrayView frame, - rtc::ArrayView packet) { - auto fragment = frame.subview(fragmentation.fragmentationOffset[nalu_index], - fragmentation.fragmentationLength[nalu_index]); - EXPECT_THAT(packet, ElementsAreArray(fragment.begin(), fragment.end())); -} -} // namespace - // Tests that should work with both packetization mode 0 and // packetization mode 1. class RtpPacketizerH264ModeTest : public ::testing::TestWithParam {}; -TEST_P(RtpPacketizerH264ModeTest, TestSingleNalu) { - const uint8_t frame[2] = {0x05, 0xFF}; // F=0, NRI=0, Type=5. - RTPFragmentationHeader fragmentation; - fragmentation.VerifyAndAllocateFragmentationHeader(1); - fragmentation.fragmentationOffset[0] = 0; - fragmentation.fragmentationLength[0] = sizeof(frame); +TEST_P(RtpPacketizerH264ModeTest, SingleNalu) { + const uint8_t frame[2] = {kIdr, 0xFF}; - RtpPacketizerH264 packetizer(frame, kNoLimits, GetParam(), fragmentation); - ASSERT_EQ(packetizer.NumPackets(), 1u); + RtpPacketizerH264 packetizer(frame, kNoLimits, GetParam(), + NoFragmentation(frame)); + std::vector packets = FetchAllPackets(&packetizer); - RtpPacketToSend packet(kNoExtensions); - ASSERT_TRUE(packetizer.NextPacket(&packet)); - EXPECT_EQ(2u, packet.payload_size()); - VerifySingleNaluPayload(fragmentation, 0, frame, packet.payload()); - EXPECT_FALSE(packetizer.NextPacket(&packet)); + ASSERT_THAT(packets, SizeIs(1)); + EXPECT_THAT(packets[0].payload(), ElementsAreArray(frame)); } -TEST_P(RtpPacketizerH264ModeTest, TestSingleNaluTwoPackets) { +TEST_P(RtpPacketizerH264ModeTest, SingleNaluTwoPackets) { RtpPacketizer::PayloadSizeLimits limits; limits.max_payload_len = kMaxPayloadSize; - const size_t kFrameSize = kMaxPayloadSize + 100; - uint8_t frame[kFrameSize]; - for (size_t i = 0; i < kFrameSize; ++i) - frame[i] = i; - RTPFragmentationHeader fragmentation; - fragmentation.VerifyAndAllocateFragmentationHeader(2); - fragmentation.fragmentationOffset[0] = 0; - fragmentation.fragmentationLength[0] = kMaxPayloadSize; - fragmentation.fragmentationOffset[1] = kMaxPayloadSize; - fragmentation.fragmentationLength[1] = 100; - // Set NAL headers. - frame[fragmentation.fragmentationOffset[0]] = 0x01; - frame[fragmentation.fragmentationOffset[1]] = 0x01; + const size_t fragment_sizes[] = {kMaxPayloadSize, 100}; + RTPFragmentationHeader fragmentation = CreateFragmentation(fragment_sizes); + rtc::Buffer frame = CreateFrame(fragmentation); RtpPacketizerH264 packetizer(frame, limits, GetParam(), fragmentation); - ASSERT_EQ(packetizer.NumPackets(), 2u); + std::vector packets = FetchAllPackets(&packetizer); - RtpPacketToSend packet(kNoExtensions); - ASSERT_TRUE(packetizer.NextPacket(&packet)); - ASSERT_EQ(fragmentation.fragmentationOffset[1], packet.payload_size()); - VerifySingleNaluPayload(fragmentation, 0, frame, packet.payload()); + ASSERT_THAT(packets, SizeIs(2)); + EXPECT_THAT(packets[0].payload(), + ElementsAreArray(frame.data(), kMaxPayloadSize)); + EXPECT_THAT(packets[1].payload(), + ElementsAreArray(frame.data() + kMaxPayloadSize, 100)); +} - ASSERT_TRUE(packetizer.NextPacket(&packet)); - ASSERT_EQ(fragmentation.fragmentationLength[1], packet.payload_size()); - VerifySingleNaluPayload(fragmentation, 1, frame, packet.payload()); +TEST_P(RtpPacketizerH264ModeTest, + SingleNaluLastPacketReductionAppliesOnlyToLastFragment) { + RtpPacketizer::PayloadSizeLimits limits; + limits.max_payload_len = 200; + limits.last_packet_reduction_len = 5; + const size_t fragments[] = {200, 200, 195}; - EXPECT_FALSE(packetizer.NextPacket(&packet)); + RTPFragmentationHeader fragmentation = CreateFragmentation(fragments); + rtc::Buffer frame = CreateFrame(fragmentation); + + RtpPacketizerH264 packetizer(frame, limits, GetParam(), fragmentation); + std::vector packets = FetchAllPackets(&packetizer); + + ASSERT_THAT(packets, SizeIs(3)); + const uint8_t* next_fragment = frame.data(); + EXPECT_THAT(packets[0].payload(), ElementsAreArray(next_fragment, 200)); + next_fragment += 200; + EXPECT_THAT(packets[1].payload(), ElementsAreArray(next_fragment, 200)); + next_fragment += 200; + EXPECT_THAT(packets[2].payload(), ElementsAreArray(next_fragment, 195)); } INSTANTIATE_TEST_CASE_P( @@ -231,252 +171,232 @@ INSTANTIATE_TEST_CASE_P( ::testing::Values(H264PacketizationMode::SingleNalUnit, H264PacketizationMode::NonInterleaved)); -TEST(RtpPacketizerH264Test, TestStapA) { - RtpPacketizer::PayloadSizeLimits limits; - limits.max_payload_len = kMaxPayloadSize; - const size_t kFrameSize = - kMaxPayloadSize - 3 * kLengthFieldLength - kNalHeaderSize; - uint8_t frame[kFrameSize] = {0x07, 0xFF, // F=0, NRI=0, Type=7 (SPS). - 0x08, 0xFF, // F=0, NRI=0, Type=8 (PPS). - 0x05}; // F=0, NRI=0, Type=5 (IDR). - const size_t kPayloadOffset = 5; - for (size_t i = 0; i < kFrameSize - kPayloadOffset; ++i) - frame[i + kPayloadOffset] = i; - RTPFragmentationHeader fragmentation; - CreateThreeFragments(&fragmentation, kFrameSize, kPayloadOffset); +// Aggregation tests. +TEST(RtpPacketizerH264Test, StapA) { + size_t fragments[] = {2, 2, 0x123}; + + RTPFragmentationHeader fragmentation = CreateFragmentation(fragments); + rtc::Buffer frame = CreateFrame(fragmentation); RtpPacketizerH264 packetizer( - frame, limits, H264PacketizationMode::NonInterleaved, fragmentation); - ASSERT_EQ(packetizer.NumPackets(), 1u); + frame, kNoLimits, H264PacketizationMode::NonInterleaved, fragmentation); + std::vector packets = FetchAllPackets(&packetizer); - RtpPacketToSend packet(kNoExtensions); - ASSERT_LE(kMaxPayloadSize, packet.FreeCapacity()); - ASSERT_TRUE(packetizer.NextPacket(&packet)); - size_t expected_packet_size = - kNalHeaderSize + 3 * kLengthFieldLength + kFrameSize; - ASSERT_EQ(expected_packet_size, packet.payload_size()); + ASSERT_THAT(packets, SizeIs(1)); + auto payload = packets[0].payload(); + EXPECT_EQ(payload.size(), + kNalHeaderSize + 3 * kLengthFieldLength + frame.size()); - for (size_t i = 0; i < fragmentation.fragmentationVectorSize; ++i) - VerifyStapAPayload(fragmentation, 0, i, frame, packet.payload()); - - EXPECT_FALSE(packetizer.NextPacket(&packet)); + EXPECT_EQ(payload[0], kStapA); + payload = payload.subview(kNalHeaderSize); + // 1st fragment. + EXPECT_THAT(payload.subview(0, kLengthFieldLength), + ElementsAre(0, 2)); // Size. + EXPECT_THAT(payload.subview(kLengthFieldLength, 2), + ElementsAreArray(frame.data(), 2)); + payload = payload.subview(kLengthFieldLength + 2); + // 2nd fragment. + EXPECT_THAT(payload.subview(0, kLengthFieldLength), + ElementsAre(0, 2)); // Size. + EXPECT_THAT(payload.subview(kLengthFieldLength, 2), + ElementsAreArray(frame.data() + 2, 2)); + payload = payload.subview(kLengthFieldLength + 2); + // 3rd fragment. + EXPECT_THAT(payload.subview(0, kLengthFieldLength), + ElementsAre(0x1, 0x23)); // Size. + EXPECT_THAT(payload.subview(kLengthFieldLength), + ElementsAreArray(frame.data() + 4, 0x123)); } -TEST(RtpPacketizerH264Test, TestStapARespectsPacketReduction) { - RtpPacketizer::PayloadSizeLimits limits; - limits.max_payload_len = kMaxPayloadSize; - limits.last_packet_reduction_len = 100; - const size_t kFrameSize = kMaxPayloadSize - 1 - 100; - uint8_t frame[kFrameSize] = {0x07, 0xFF, // F=0, NRI=0, Type=7. - 0x08, 0xFF, // F=0, NRI=0, Type=8. - 0x05}; // F=0, NRI=0, Type=5. - const size_t kPayloadOffset = 5; - for (size_t i = 0; i < kFrameSize - kPayloadOffset; ++i) - frame[i + kPayloadOffset] = i; - RTPFragmentationHeader fragmentation; - fragmentation.VerifyAndAllocateFragmentationHeader(3); - fragmentation.fragmentationOffset[0] = 0; - fragmentation.fragmentationLength[0] = 2; - fragmentation.fragmentationOffset[1] = 2; - fragmentation.fragmentationLength[1] = 2; - fragmentation.fragmentationOffset[2] = 4; - fragmentation.fragmentationLength[2] = - kNalHeaderSize + kFrameSize - kPayloadOffset; +TEST(RtpPacketizerH264Test, SingleNalUnitModeHasNoStapA) { + // This is the same setup as for the StapA test. + size_t fragments[] = {2, 2, 0x123}; + RTPFragmentationHeader fragmentation = CreateFragmentation(fragments); + rtc::Buffer frame = CreateFrame(fragmentation); RtpPacketizerH264 packetizer( - frame, limits, H264PacketizationMode::NonInterleaved, fragmentation); - ASSERT_EQ(packetizer.NumPackets(), 2u); + frame, kNoLimits, H264PacketizationMode::SingleNalUnit, fragmentation); + std::vector packets = FetchAllPackets(&packetizer); - RtpPacketToSend packet(kNoExtensions); - ASSERT_LE(kMaxPayloadSize, packet.FreeCapacity()); - ASSERT_TRUE(packetizer.NextPacket(&packet)); - size_t expected_packet_size = kNalHeaderSize; - for (size_t i = 0; i < 2; ++i) { - expected_packet_size += - kLengthFieldLength + fragmentation.fragmentationLength[i]; - } - ASSERT_EQ(expected_packet_size, packet.payload_size()); - for (size_t i = 0; i < 2; ++i) - VerifyStapAPayload(fragmentation, 0, i, frame, packet.payload()); - - ASSERT_TRUE(packetizer.NextPacket(&packet)); - expected_packet_size = fragmentation.fragmentationLength[2]; - ASSERT_EQ(expected_packet_size, packet.payload_size()); - VerifySingleNaluPayload(fragmentation, 2, frame, packet.payload()); - - EXPECT_FALSE(packetizer.NextPacket(&packet)); -} - -TEST(RtpPacketizerH264Test, TestSingleNalUnitModeHasNoStapA) { - // This is the same setup as for the TestStapA test. - RtpPacketizer::PayloadSizeLimits limits; - limits.max_payload_len = kMaxPayloadSize; - const size_t kFrameSize = - kMaxPayloadSize - 3 * kLengthFieldLength - kNalHeaderSize; - uint8_t frame[kFrameSize] = {0x07, 0xFF, // F=0, NRI=0, Type=7 (SPS). - 0x08, 0xFF, // F=0, NRI=0, Type=8 (PPS). - 0x05}; // F=0, NRI=0, Type=5 (IDR). - const size_t kPayloadOffset = 5; - for (size_t i = 0; i < kFrameSize - kPayloadOffset; ++i) - frame[i + kPayloadOffset] = i; - RTPFragmentationHeader fragmentation; - CreateThreeFragments(&fragmentation, kFrameSize, kPayloadOffset); - - RtpPacketizerH264 packetizer( - frame, limits, H264PacketizationMode::SingleNalUnit, fragmentation); - EXPECT_EQ(packetizer.NumPackets(), 3u); - - RtpPacketToSend packet(kNoExtensions); // The three fragments should be returned as three packets. - ASSERT_TRUE(packetizer.NextPacket(&packet)); - ASSERT_TRUE(packetizer.NextPacket(&packet)); - ASSERT_TRUE(packetizer.NextPacket(&packet)); - EXPECT_FALSE(packetizer.NextPacket(&packet)); + ASSERT_THAT(packets, SizeIs(3)); + EXPECT_EQ(packets[0].payload_size(), 2u); + EXPECT_EQ(packets[1].payload_size(), 2u); + EXPECT_EQ(packets[2].payload_size(), 0x123u); } -TEST(RtpPacketizerH264Test, TestTooSmallForStapAHeaders) { +TEST(RtpPacketizerH264Test, StapARespectsLastPacketReduction) { RtpPacketizer::PayloadSizeLimits limits; - limits.max_payload_len = kMaxPayloadSize; - const size_t kFrameSize = kMaxPayloadSize - 1; - uint8_t frame[kFrameSize] = {0x07, 0xFF, // F=0, NRI=0, Type=7. - 0x08, 0xFF, // F=0, NRI=0, Type=8. - 0x05}; // F=0, NRI=0, Type=5. - const size_t kPayloadOffset = 5; - for (size_t i = 0; i < kFrameSize - kPayloadOffset; ++i) - frame[i + kPayloadOffset] = i; - RTPFragmentationHeader fragmentation; - fragmentation.VerifyAndAllocateFragmentationHeader(3); - fragmentation.fragmentationOffset[0] = 0; - fragmentation.fragmentationLength[0] = 2; - fragmentation.fragmentationOffset[1] = 2; - fragmentation.fragmentationLength[1] = 2; - fragmentation.fragmentationOffset[2] = 4; - fragmentation.fragmentationLength[2] = - kNalHeaderSize + kFrameSize - kPayloadOffset; + limits.max_payload_len = 1000; + limits.last_packet_reduction_len = 100; + const size_t kLastFragmentSize = + limits.max_payload_len - limits.last_packet_reduction_len; + size_t fragments[] = {2, 2, kLastFragmentSize}; + RTPFragmentationHeader fragmentation = CreateFragmentation(fragments); + rtc::Buffer frame = CreateFrame(fragmentation); + RtpPacketizerH264 packetizer( frame, limits, H264PacketizationMode::NonInterleaved, fragmentation); - ASSERT_EQ(packetizer.NumPackets(), 2u); + std::vector packets = FetchAllPackets(&packetizer); - RtpPacketToSend packet(kNoExtensions); - ASSERT_LE(kMaxPayloadSize, packet.FreeCapacity()); - ASSERT_TRUE(packetizer.NextPacket(&packet)); - size_t expected_packet_size = kNalHeaderSize; - for (size_t i = 0; i < 2; ++i) { - expected_packet_size += - kLengthFieldLength + fragmentation.fragmentationLength[i]; - } - ASSERT_EQ(expected_packet_size, packet.payload_size()); - for (size_t i = 0; i < 2; ++i) - VerifyStapAPayload(fragmentation, 0, i, frame, packet.payload()); - - ASSERT_TRUE(packetizer.NextPacket(&packet)); - expected_packet_size = fragmentation.fragmentationLength[2]; - ASSERT_EQ(expected_packet_size, packet.payload_size()); - VerifySingleNaluPayload(fragmentation, 2, frame, packet.payload()); - - EXPECT_FALSE(packetizer.NextPacket(&packet)); + ASSERT_THAT(packets, SizeIs(2)); + // Expect 1st packet is aggregate of 1st two fragments. + EXPECT_THAT(packets[0].payload(), ElementsAre(kStapA, // + 0, 2, frame[0], frame[1], // + 0, 2, frame[2], frame[3])); + // Expect 2nd packet is single nalu. + EXPECT_THAT(packets[1].payload(), + ElementsAreArray(frame.data() + 4, kLastFragmentSize)); } -TEST(RtpPacketizerH264Test, TestMixedStapA_FUA) { +TEST(RtpPacketizerH264Test, TooSmallForStapAHeaders) { RtpPacketizer::PayloadSizeLimits limits; - limits.max_payload_len = kMaxPayloadSize; - const size_t kFuaNaluSize = 2 * (kMaxPayloadSize - 100); - const size_t kStapANaluSize = 100; - RTPFragmentationHeader fragmentation; - fragmentation.VerifyAndAllocateFragmentationHeader(3); - fragmentation.fragmentationOffset[0] = 0; - fragmentation.fragmentationLength[0] = kFuaNaluSize; - fragmentation.fragmentationOffset[1] = kFuaNaluSize; - fragmentation.fragmentationLength[1] = kStapANaluSize; - fragmentation.fragmentationOffset[2] = kFuaNaluSize + kStapANaluSize; - fragmentation.fragmentationLength[2] = kStapANaluSize; - const size_t kFrameSize = kFuaNaluSize + 2 * kStapANaluSize; - uint8_t frame[kFrameSize]; - size_t nalu_offset = 0; - for (size_t i = 0; i < fragmentation.fragmentationVectorSize; ++i) { - nalu_offset = fragmentation.fragmentationOffset[i]; - frame[nalu_offset] = 0x05; // F=0, NRI=0, Type=5. - for (size_t j = 1; j < fragmentation.fragmentationLength[i]; ++j) { - frame[nalu_offset + j] = i + j; - } - } + limits.max_payload_len = 1000; + const size_t kLastFragmentSize = + limits.max_payload_len - 3 * kLengthFieldLength - 4; + size_t fragments[] = {2, 2, kLastFragmentSize}; + RTPFragmentationHeader fragmentation = CreateFragmentation(fragments); + rtc::Buffer frame = CreateFrame(fragmentation); + RtpPacketizerH264 packetizer( frame, limits, H264PacketizationMode::NonInterleaved, fragmentation); - ASSERT_EQ(packetizer.NumPackets(), 3u); + std::vector packets = FetchAllPackets(&packetizer); - // First expecting two FU-A packets. - std::vector fua_sizes; - fua_sizes.push_back(1099); - fua_sizes.push_back(1100); - RtpPacketToSend packet(kNoExtensions); - ASSERT_LE(kMaxPayloadSize, packet.FreeCapacity()); - int fua_offset = kNalHeaderSize; - for (size_t i = 0; i < 2; ++i) { - ASSERT_TRUE(packetizer.NextPacket(&packet)); - VerifyFua(i, frame, fua_offset, packet.payload(), fua_sizes); - fua_offset += fua_sizes[i]; - } - // Then expecting one STAP-A packet with two nal units. - ASSERT_TRUE(packetizer.NextPacket(&packet)); - size_t expected_packet_size = - kNalHeaderSize + 2 * kLengthFieldLength + 2 * kStapANaluSize; - ASSERT_EQ(expected_packet_size, packet.payload_size()); - for (size_t i = 1; i < fragmentation.fragmentationVectorSize; ++i) - VerifyStapAPayload(fragmentation, 1, i, frame, packet.payload()); - - EXPECT_FALSE(packetizer.NextPacket(&packet)); + ASSERT_THAT(packets, SizeIs(2)); + // Expect 1st packet is aggregate of 1st two fragments. + EXPECT_THAT(packets[0].payload(), ElementsAre(kStapA, // + 0, 2, frame[0], frame[1], // + 0, 2, frame[2], frame[3])); + // Expect 2nd packet is single nalu. + EXPECT_THAT(packets[1].payload(), + ElementsAreArray(frame.data() + 4, kLastFragmentSize)); } -TEST(RtpPacketizerH264Test, TestFUAOddSize) { - const size_t kExpectedPayloadSizes[2] = {600, 600}; - TestFua(kMaxPayloadSize + 1, kMaxPayloadSize, 0, kExpectedPayloadSizes); -} - -TEST(RtpPacketizerH264Test, TestFUAWithLastPacketReduction) { - const size_t kExpectedPayloadSizes[2] = {601, 597}; - TestFua(kMaxPayloadSize - 1, kMaxPayloadSize, 4, kExpectedPayloadSizes); -} - -TEST(RtpPacketizerH264Test, TestFUAEvenSize) { - const size_t kExpectedPayloadSizes[2] = {600, 601}; - TestFua(kMaxPayloadSize + 2, kMaxPayloadSize, 0, kExpectedPayloadSizes); -} - -TEST(RtpPacketizerH264Test, TestFUARounding) { - const size_t kExpectedPayloadSizes[8] = {1265, 1265, 1265, 1265, - 1265, 1266, 1266, 1266}; - TestFua(10124, 1448, 0, kExpectedPayloadSizes); -} - -TEST(RtpPacketizerH264Test, TestFUABig) { - const size_t kExpectedPayloadSizes[10] = {1198, 1198, 1198, 1198, 1198, - 1198, 1198, 1198, 1198, 1198}; - // Generate 10 full sized packets, leave room for FU-A headers minus the NALU - // header. - TestFua(10 * (kMaxPayloadSize - kFuAHeaderSize) + kNalHeaderSize, - kMaxPayloadSize, 0, kExpectedPayloadSizes); -} - -TEST(RtpPacketizerH264Test, SendOverlongDataInPacketizationMode0) { +// Fragmentation + aggregation. +TEST(RtpPacketizerH264Test, MixedStapAFUA) { RtpPacketizer::PayloadSizeLimits limits; - limits.max_payload_len = kMaxPayloadSize; - const size_t kFrameSize = kMaxPayloadSize + 1; - uint8_t frame[kFrameSize] = {0}; - for (size_t i = 0; i < kFrameSize; ++i) - frame[i] = i; - RTPFragmentationHeader fragmentation; - fragmentation.VerifyAndAllocateFragmentationHeader(1); - fragmentation.fragmentationOffset[0] = 0; - fragmentation.fragmentationLength[0] = kFrameSize; - // Set NAL headers. - frame[fragmentation.fragmentationOffset[0]] = 0x01; + limits.max_payload_len = 100; + const size_t kFuaPayloadSize = 70; + const size_t kFuaNaluSize = kNalHeaderSize + 2 * kFuaPayloadSize; + const size_t kStapANaluSize = 20; + size_t fragments[] = {kFuaNaluSize, kStapANaluSize, kStapANaluSize}; + RTPFragmentationHeader fragmentation = CreateFragmentation(fragments); + rtc::Buffer frame = CreateFrame(fragmentation); + + RtpPacketizerH264 packetizer( + frame, limits, H264PacketizationMode::NonInterleaved, fragmentation); + std::vector packets = FetchAllPackets(&packetizer); + + ASSERT_THAT(packets, SizeIs(3)); + const uint8_t* next_fragment = frame.data() + kNalHeaderSize; + // First expect two FU-A packets. + EXPECT_THAT(packets[0].payload().subview(0, kFuAHeaderSize), + ElementsAre(kFuA, FuDefs::kSBit | frame[0])); + EXPECT_THAT(packets[0].payload().subview(kFuAHeaderSize), + ElementsAreArray(next_fragment, kFuaPayloadSize)); + next_fragment += kFuaPayloadSize; + + EXPECT_THAT(packets[1].payload().subview(0, kFuAHeaderSize), + ElementsAre(kFuA, FuDefs::kEBit | frame[0])); + EXPECT_THAT(packets[1].payload().subview(kFuAHeaderSize), + ElementsAreArray(next_fragment, kFuaPayloadSize)); + next_fragment += kFuaPayloadSize; + + // Then expect one STAP-A packet with two nal units. + EXPECT_THAT(packets[2].payload()[0], kStapA); + auto payload = packets[2].payload().subview(kNalHeaderSize); + EXPECT_THAT(payload.subview(0, kLengthFieldLength), + ElementsAre(0, kStapANaluSize)); + EXPECT_THAT(payload.subview(kLengthFieldLength, kStapANaluSize), + ElementsAreArray(next_fragment, kStapANaluSize)); + payload = payload.subview(kLengthFieldLength + kStapANaluSize); + next_fragment += kStapANaluSize; + EXPECT_THAT(payload.subview(0, kLengthFieldLength), + ElementsAre(0, kStapANaluSize)); + EXPECT_THAT(payload.subview(kLengthFieldLength), + ElementsAreArray(next_fragment, kStapANaluSize)); +} + +// Splits frame with payload size |frame_payload_size| without fragmentation, +// Returns sizes of the payloads excluding fua headers. +std::vector TestFua(size_t frame_payload_size, + const RtpPacketizer::PayloadSizeLimits& limits) { + rtc::Buffer frame = CreateFrame(kNalHeaderSize + frame_payload_size); + + RtpPacketizerH264 packetizer(frame, limits, + H264PacketizationMode::NonInterleaved, + NoFragmentation(frame)); + std::vector packets = FetchAllPackets(&packetizer); + + RTC_CHECK_GE(packets.size(), 2); // Single packet indicates it is not FuA. + std::vector fua_header; + std::vector payload_sizes; + + for (const RtpPacketToSend& packet : packets) { + auto payload = packet.payload(); + EXPECT_GT(payload.size(), kFuAHeaderSize); + fua_header.push_back((payload[0] << 8) | payload[1]); + payload_sizes.push_back(payload.size() - kFuAHeaderSize); + } + + EXPECT_TRUE(fua_header.front() & FuDefs::kSBit); + EXPECT_TRUE(fua_header.back() & FuDefs::kEBit); + // Clear S and E bits before testing all are duplicating same original header. + fua_header.front() &= ~FuDefs::kSBit; + fua_header.back() &= ~FuDefs::kEBit; + EXPECT_THAT(fua_header, Each(Eq((kFuA << 8) | frame[0]))); + + return payload_sizes; +} + +// Fragmentation tests. +TEST(RtpPacketizerH264Test, FUAOddSize) { + RtpPacketizer::PayloadSizeLimits limits; + limits.max_payload_len = 1200; + EXPECT_THAT(TestFua(1200, limits), ElementsAre(600, 600)); +} + +TEST(RtpPacketizerH264Test, FUAWithLastPacketReduction) { + RtpPacketizer::PayloadSizeLimits limits; + limits.max_payload_len = 1200; + limits.last_packet_reduction_len = 4; + EXPECT_THAT(TestFua(1198, limits), ElementsAre(601, 597)); +} + +TEST(RtpPacketizerH264Test, FUAEvenSize) { + RtpPacketizer::PayloadSizeLimits limits; + limits.max_payload_len = 1200; + EXPECT_THAT(TestFua(1201, limits), ElementsAre(600, 601)); +} + +TEST(RtpPacketizerH264Test, FUARounding) { + RtpPacketizer::PayloadSizeLimits limits; + limits.max_payload_len = 1448; + EXPECT_THAT(TestFua(10123, limits), + ElementsAre(1265, 1265, 1265, 1265, 1265, 1266, 1266, 1266)); +} + +TEST(RtpPacketizerH264Test, FUABig) { + RtpPacketizer::PayloadSizeLimits limits; + limits.max_payload_len = 1200; + // Generate 10 full sized packets, leave room for FU-A headers. + EXPECT_THAT( + TestFua(10 * (1200 - kFuAHeaderSize), limits), + ElementsAre(1198, 1198, 1198, 1198, 1198, 1198, 1198, 1198, 1198, 1198)); +} + +TEST(RtpPacketizerH264Test, RejectsOverlongDataInPacketizationMode0) { + RtpPacketizer::PayloadSizeLimits limits; + rtc::Buffer frame = CreateFrame(kMaxPayloadSize + 1); + RTPFragmentationHeader fragmentation = NoFragmentation(frame); RtpPacketizerH264 packetizer( frame, limits, H264PacketizationMode::SingleNalUnit, fragmentation); - EXPECT_EQ(packetizer.NumPackets(), 0u); + std::vector packets = FetchAllPackets(&packetizer); + + EXPECT_THAT(packets, IsEmpty()); } -namespace { const uint8_t kStartSequence[] = {0x00, 0x00, 0x00, 0x01}; const uint8_t kOriginalSps[] = {kSps, 0x00, 0x00, 0x03, 0x03, 0xF4, 0x05, 0x03, 0xC7, 0xC0}; @@ -484,7 +404,6 @@ const uint8_t kRewrittenSps[] = {kSps, 0x00, 0x00, 0x03, 0x03, 0xF4, 0x05, 0x03, 0xC7, 0xE0, 0x1B, 0x41, 0x10, 0x8D, 0x00}; const uint8_t kIdrOne[] = {kIdr, 0xFF, 0x00, 0x00, 0x04}; const uint8_t kIdrTwo[] = {kIdr, 0xFF, 0x00, 0x11}; -} // namespace class RtpPacketizerH264TestSpsRewriting : public ::testing::Test { public: @@ -520,20 +439,16 @@ TEST_F(RtpPacketizerH264TestSpsRewriting, FuASps) { RtpPacketizerH264 packetizer(in_buffer_, limits, H264PacketizationMode::NonInterleaved, fragmentation_header_); + std::vector packets = FetchAllPackets(&packetizer); - RtpPacketToSend packet(kNoExtensions); - ASSERT_LE(sizeof(kOriginalSps) + kHeaderOverhead, packet.FreeCapacity()); - - EXPECT_TRUE(packetizer.NextPacket(&packet)); size_t offset = H264::kNaluTypeSize; - size_t length = packet.payload_size() - kFuAHeaderSize; - EXPECT_THAT(packet.payload().subview(kFuAHeaderSize), + size_t length = packets[0].payload_size() - kFuAHeaderSize; + EXPECT_THAT(packets[0].payload().subview(kFuAHeaderSize), ElementsAreArray(&kRewrittenSps[offset], length)); offset += length; - EXPECT_TRUE(packetizer.NextPacket(&packet)); - length = packet.payload_size() - kFuAHeaderSize; - EXPECT_THAT(packet.payload().subview(kFuAHeaderSize), + length = packets[1].payload_size() - kFuAHeaderSize; + EXPECT_THAT(packets[1].payload().subview(kFuAHeaderSize), ElementsAreArray(&kRewrittenSps[offset], length)); offset += length; @@ -553,17 +468,22 @@ TEST_F(RtpPacketizerH264TestSpsRewriting, StapASps) { RtpPacketizerH264 packetizer(in_buffer_, limits, H264PacketizationMode::NonInterleaved, fragmentation_header_); + std::vector packets = FetchAllPackets(&packetizer); - RtpPacketToSend packet(kNoExtensions); - ASSERT_LE(kExpectedTotalSize + kHeaderOverhead, packet.FreeCapacity()); - - EXPECT_TRUE(packetizer.NextPacket(&packet)); - EXPECT_EQ(kExpectedTotalSize, packet.payload_size()); - EXPECT_THAT(packet.payload().subview(H264::kNaluTypeSize + kLengthFieldLength, - sizeof(kRewrittenSps)), - ElementsAreArray(kRewrittenSps)); + ASSERT_THAT(packets, SizeIs(1)); + EXPECT_EQ(packets[0].payload_size(), kExpectedTotalSize); + EXPECT_THAT( + packets[0].payload().subview(H264::kNaluTypeSize + kLengthFieldLength, + sizeof(kRewrittenSps)), + ElementsAreArray(kRewrittenSps)); } +struct H264ParsedPayload : public RtpDepacketizer::ParsedPayload { + RTPVideoHeaderH264& h264() { + return absl::get(video.video_type_header); + } +}; + class RtpDepacketizerH264Test : public ::testing::Test { protected: RtpDepacketizerH264Test() @@ -925,4 +845,5 @@ TEST_F(RtpDepacketizerH264Test, TestSeiPacket) { EXPECT_EQ(-1, h264.nalus[0].pps_id); } +} // namespace } // namespace webrtc