From 820021d24600b8628b8644c43fddf67fa37ccf84 Mon Sep 17 00:00:00 2001 From: Danil Chapovalov Date: Fri, 10 Jul 2020 10:21:49 +0200 Subject: [PATCH] Ignore fragmentation header when packetizing H264 instead reparse nalu boundaries from the bitstream. H264 is the last use of the RTPFragmentationHeader and this would allow to avoid passing and precalculating this legacy structure. Bug: webrtc:6471 Change-Id: Ia6e8bf0836fd5c022423d836894cde81f136d1f1 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/178911 Reviewed-by: Philip Eliasson Reviewed-by: Niels Moller Commit-Queue: Danil Chapovalov Cr-Commit-Position: refs/heads/master@{#31746} --- modules/rtp_rtcp/source/rtp_format.cc | 7 +- modules/rtp_rtcp/source/rtp_format_h264.cc | 14 +- modules/rtp_rtcp/source/rtp_format_h264.h | 4 +- .../source/rtp_format_h264_unittest.cc | 303 +++++++++--------- test/fake_encoder.cc | 47 +-- 5 files changed, 186 insertions(+), 189 deletions(-) diff --git a/modules/rtp_rtcp/source/rtp_format.cc b/modules/rtp_rtcp/source/rtp_format.cc index 28f63f1109..f6f4a48f04 100644 --- a/modules/rtp_rtcp/source/rtp_format.cc +++ b/modules/rtp_rtcp/source/rtp_format.cc @@ -31,7 +31,7 @@ std::unique_ptr RtpPacketizer::Create( PayloadSizeLimits limits, // Codec-specific details. const RTPVideoHeader& rtp_video_header, - const RTPFragmentationHeader* fragmentation) { + const RTPFragmentationHeader* /*fragmentation*/) { if (!type) { // Use raw packetizer. return std::make_unique(payload, limits); @@ -39,11 +39,10 @@ std::unique_ptr RtpPacketizer::Create( switch (*type) { case kVideoCodecH264: { - RTC_CHECK(fragmentation); const auto& h264 = absl::get(rtp_video_header.video_type_header); - return std::make_unique( - payload, limits, h264.packetization_mode, *fragmentation); + return std::make_unique(payload, limits, + h264.packetization_mode); } case kVideoCodecVP8: { const auto& vp8 = diff --git a/modules/rtp_rtcp/source/rtp_format_h264.cc b/modules/rtp_rtcp/source/rtp_format_h264.cc index 6f19e38629..6c3966cb93 100644 --- a/modules/rtp_rtcp/source/rtp_format_h264.cc +++ b/modules/rtp_rtcp/source/rtp_format_h264.cc @@ -25,7 +25,6 @@ #include "common_video/h264/pps_parser.h" #include "common_video/h264/sps_parser.h" #include "common_video/h264/sps_vui_rewriter.h" -#include "modules/include/module_common_types.h" #include "modules/rtp_rtcp/source/byte_io.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "rtc_base/checks.h" @@ -46,19 +45,18 @@ enum FuDefs : uint8_t { kSBit = 0x80, kEBit = 0x40, kRBit = 0x20 }; } // namespace -RtpPacketizerH264::RtpPacketizerH264( - rtc::ArrayView payload, - PayloadSizeLimits limits, - H264PacketizationMode packetization_mode, - const RTPFragmentationHeader& fragmentation) +RtpPacketizerH264::RtpPacketizerH264(rtc::ArrayView payload, + PayloadSizeLimits limits, + H264PacketizationMode packetization_mode) : limits_(limits), num_packets_left_(0) { // Guard against uninitialized memory in packetization_mode. RTC_CHECK(packetization_mode == H264PacketizationMode::NonInterleaved || packetization_mode == H264PacketizationMode::SingleNalUnit); - for (size_t i = 0; i < fragmentation.fragmentationVectorSize; ++i) { + for (const auto& nalu : + H264::FindNaluIndices(payload.data(), payload.size())) { input_fragments_.push_back( - payload.subview(fragmentation.Offset(i), fragmentation.Length(i))); + payload.subview(nalu.payload_start_offset, nalu.payload_size)); } if (!GeneratePackets(packetization_mode)) { diff --git a/modules/rtp_rtcp/source/rtp_format_h264.h b/modules/rtp_rtcp/source/rtp_format_h264.h index 4661dc2163..7c10dd5754 100644 --- a/modules/rtp_rtcp/source/rtp_format_h264.h +++ b/modules/rtp_rtcp/source/rtp_format_h264.h @@ -19,7 +19,6 @@ #include #include "api/array_view.h" -#include "modules/include/module_common_types.h" #include "modules/rtp_rtcp/source/rtp_format.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" #include "modules/video_coding/codecs/h264/include/h264_globals.h" @@ -34,8 +33,7 @@ class RtpPacketizerH264 : public RtpPacketizer { // The payload_data must be exactly one encoded H264 frame. RtpPacketizerH264(rtc::ArrayView payload, PayloadSizeLimits limits, - H264PacketizationMode packetization_mode, - const RTPFragmentationHeader& fragmentation); + H264PacketizationMode packetization_mode); ~RtpPacketizerH264() override; diff --git a/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc b/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc index bf9771ab9f..9f660b7a74 100644 --- a/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc @@ -13,9 +13,9 @@ #include #include +#include "absl/algorithm/container.h" #include "api/array_view.h" #include "common_video/h264/h264_common.h" -#include "modules/include/module_common_types.h" #include "modules/rtp_rtcp/mocks/mock_rtp_rtcp.h" #include "modules/rtp_rtcp/source/byte_io.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" @@ -56,45 +56,61 @@ enum NalDefs { kFBit = 0x80, kNriMask = 0x60, kTypeMask = 0x1F }; // Bit masks for FU (A and B) headers. enum FuDefs { kSBit = 0x80, kEBit = 0x40, kRBit = 0x20 }; -RTPFragmentationHeader CreateFragmentation(rtc::ArrayView sizes) { - RTPFragmentationHeader fragmentation; - 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; -} - -// 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); +// Creates Buffer that looks like nal unit of given size. +rtc::Buffer GenerateNalUnit(size_t size) { + RTC_CHECK_GT(size, 0); + rtc::Buffer buffer(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); + buffer[0] = kSlice; + for (size_t i = 1; i < size; ++i) { + buffer[i] = static_cast(i); + } + // Last byte shouldn't be 0, or it may be counted as part of next 4-byte start + // sequence. + buffer[size - 1] |= 0x10; + return buffer; +} + +// Create frame consisting of nalus of given size. +rtc::Buffer CreateFrame(std::initializer_list nalu_sizes) { + static constexpr int kStartCodeSize = 3; + rtc::Buffer frame(absl::c_accumulate(nalu_sizes, 0) + + kStartCodeSize * nalu_sizes.size()); + size_t offset = 0; + for (size_t nalu_size : nalu_sizes) { + EXPECT_GE(nalu_size, 1u); + // Insert nalu start code + frame[offset] = 0; + frame[offset + 1] = 0; + frame[offset + 2] = 1; + // Set some valid header. + frame[offset + 3] = 1; + // Fill payload avoiding accidental start codes + if (nalu_size > 1) { + memset(frame.data() + offset + 4, 0x3f, nalu_size - 1); + } + offset += (kStartCodeSize + nalu_size); + } 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; +// Create frame consisting of given nalus. +rtc::Buffer CreateFrame(rtc::ArrayView nalus) { + static constexpr int kStartCodeSize = 3; + int frame_size = 0; + for (const rtc::Buffer& nalu : nalus) { + frame_size += (kStartCodeSize + nalu.size()); + } + rtc::Buffer frame(frame_size); + size_t offset = 0; + for (const rtc::Buffer& nalu : nalus) { + // Insert nalu start code + frame[offset] = 0; + frame[offset + 1] = 0; + frame[offset + 2] = 1; + // Copy the nalu unit. + memcpy(frame.data() + offset + 3, nalu.data(), nalu.size()); + offset += (kStartCodeSize + nalu.size()); } return frame; } @@ -117,31 +133,28 @@ class RtpPacketizerH264ModeTest : public ::testing::TestWithParam {}; TEST_P(RtpPacketizerH264ModeTest, SingleNalu) { - const uint8_t frame[2] = {kIdr, 0xFF}; + const uint8_t frame[] = {0, 0, 1, kIdr, 0xFF}; - RtpPacketizerH264 packetizer(frame, kNoLimits, GetParam(), - NoFragmentation(frame)); + RtpPacketizerH264 packetizer(frame, kNoLimits, GetParam()); std::vector packets = FetchAllPackets(&packetizer); ASSERT_THAT(packets, SizeIs(1)); - EXPECT_THAT(packets[0].payload(), ElementsAreArray(frame)); + EXPECT_THAT(packets[0].payload(), ElementsAre(kIdr, 0xFF)); } TEST_P(RtpPacketizerH264ModeTest, SingleNaluTwoPackets) { RtpPacketizer::PayloadSizeLimits limits; limits.max_payload_len = kMaxPayloadSize; - const size_t fragment_sizes[] = {kMaxPayloadSize, 100}; - RTPFragmentationHeader fragmentation = CreateFragmentation(fragment_sizes); - rtc::Buffer frame = CreateFrame(fragmentation); + rtc::Buffer nalus[] = {GenerateNalUnit(kMaxPayloadSize), + GenerateNalUnit(100)}; + rtc::Buffer frame = CreateFrame(nalus); - RtpPacketizerH264 packetizer(frame, limits, GetParam(), fragmentation); + RtpPacketizerH264 packetizer(frame, limits, GetParam()); std::vector packets = FetchAllPackets(&packetizer); ASSERT_THAT(packets, SizeIs(2)); - EXPECT_THAT(packets[0].payload(), - ElementsAreArray(frame.data(), kMaxPayloadSize)); - EXPECT_THAT(packets[1].payload(), - ElementsAreArray(frame.data() + kMaxPayloadSize, 100)); + EXPECT_THAT(packets[0].payload(), ElementsAreArray(nalus[0])); + EXPECT_THAT(packets[1].payload(), ElementsAreArray(nalus[1])); } TEST_P(RtpPacketizerH264ModeTest, @@ -149,21 +162,18 @@ TEST_P(RtpPacketizerH264ModeTest, RtpPacketizer::PayloadSizeLimits limits; limits.max_payload_len = 200; limits.first_packet_reduction_len = 5; - const size_t fragments[] = {195, 200, 200}; + rtc::Buffer nalus[] = {GenerateNalUnit(/*size=*/195), + GenerateNalUnit(/*size=*/200), + GenerateNalUnit(/*size=*/200)}; + rtc::Buffer frame = CreateFrame(nalus); - RTPFragmentationHeader fragmentation = CreateFragmentation(fragments); - rtc::Buffer frame = CreateFrame(fragmentation); - - RtpPacketizerH264 packetizer(frame, limits, GetParam(), fragmentation); + RtpPacketizerH264 packetizer(frame, limits, GetParam()); 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, 195)); - next_fragment += 195; - EXPECT_THAT(packets[1].payload(), ElementsAreArray(next_fragment, 200)); - next_fragment += 200; - EXPECT_THAT(packets[2].payload(), ElementsAreArray(next_fragment, 200)); + EXPECT_THAT(packets[0].payload(), ElementsAreArray(nalus[0])); + EXPECT_THAT(packets[1].payload(), ElementsAreArray(nalus[1])); + EXPECT_THAT(packets[2].payload(), ElementsAreArray(nalus[2])); } TEST_P(RtpPacketizerH264ModeTest, @@ -171,21 +181,18 @@ TEST_P(RtpPacketizerH264ModeTest, RtpPacketizer::PayloadSizeLimits limits; limits.max_payload_len = 200; limits.last_packet_reduction_len = 5; - const size_t fragments[] = {200, 200, 195}; + rtc::Buffer nalus[] = {GenerateNalUnit(/*size=*/200), + GenerateNalUnit(/*size=*/200), + GenerateNalUnit(/*size=*/195)}; + rtc::Buffer frame = CreateFrame(nalus); - RTPFragmentationHeader fragmentation = CreateFragmentation(fragments); - rtc::Buffer frame = CreateFrame(fragmentation); - - RtpPacketizerH264 packetizer(frame, limits, GetParam(), fragmentation); + RtpPacketizerH264 packetizer(frame, limits, GetParam()); 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)); + EXPECT_THAT(packets[0].payload(), ElementsAreArray(nalus[0])); + EXPECT_THAT(packets[1].payload(), ElementsAreArray(nalus[1])); + EXPECT_THAT(packets[2].payload(), ElementsAreArray(nalus[2])); } TEST_P(RtpPacketizerH264ModeTest, @@ -194,10 +201,9 @@ TEST_P(RtpPacketizerH264ModeTest, limits.max_payload_len = 200; limits.first_packet_reduction_len = 20; limits.last_packet_reduction_len = 30; - rtc::Buffer frame = CreateFrame(150); + rtc::Buffer frame = CreateFrame({150}); - RtpPacketizerH264 packetizer(frame, limits, GetParam(), - NoFragmentation(frame)); + RtpPacketizerH264 packetizer(frame, limits, GetParam()); std::vector packets = FetchAllPackets(&packetizer); EXPECT_THAT(packets, SizeIs(1)); @@ -211,19 +217,19 @@ INSTANTIATE_TEST_SUITE_P( // Aggregation tests. TEST(RtpPacketizerH264Test, StapA) { - size_t fragments[] = {2, 2, 0x123}; + rtc::Buffer nalus[] = {GenerateNalUnit(/*size=*/2), + GenerateNalUnit(/*size=*/2), + GenerateNalUnit(/*size=*/0x123)}; + rtc::Buffer frame = CreateFrame(nalus); - RTPFragmentationHeader fragmentation = CreateFragmentation(fragments); - rtc::Buffer frame = CreateFrame(fragmentation); - - RtpPacketizerH264 packetizer( - frame, kNoLimits, H264PacketizationMode::NonInterleaved, fragmentation); + RtpPacketizerH264 packetizer(frame, kNoLimits, + H264PacketizationMode::NonInterleaved); std::vector packets = FetchAllPackets(&packetizer); ASSERT_THAT(packets, SizeIs(1)); auto payload = packets[0].payload(); EXPECT_EQ(payload.size(), - kNalHeaderSize + 3 * kLengthFieldLength + frame.size()); + kNalHeaderSize + 3 * kLengthFieldLength + 2 + 2 + 0x123); EXPECT_EQ(payload[0], kStapA); payload = payload.subview(kNalHeaderSize); @@ -231,29 +237,26 @@ TEST(RtpPacketizerH264Test, StapA) { EXPECT_THAT(payload.subview(0, kLengthFieldLength), ElementsAre(0, 2)); // Size. EXPECT_THAT(payload.subview(kLengthFieldLength, 2), - ElementsAreArray(frame.data(), 2)); + ElementsAreArray(nalus[0])); 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)); + ElementsAreArray(nalus[1])); 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)); + EXPECT_THAT(payload.subview(kLengthFieldLength), ElementsAreArray(nalus[2])); } 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); + rtc::Buffer frame = CreateFrame({2, 2, 0x123}); - RtpPacketizerH264 packetizer( - frame, kNoLimits, H264PacketizationMode::SingleNalUnit, fragmentation); + RtpPacketizerH264 packetizer(frame, kNoLimits, + H264PacketizationMode::SingleNalUnit); std::vector packets = FetchAllPackets(&packetizer); // The three fragments should be returned as three packets. @@ -269,23 +272,23 @@ TEST(RtpPacketizerH264Test, StapARespectsFirstPacketReduction) { limits.first_packet_reduction_len = 100; const size_t kFirstFragmentSize = limits.max_payload_len - limits.first_packet_reduction_len; - size_t fragments[] = {kFirstFragmentSize, 2, 2}; - RTPFragmentationHeader fragmentation = CreateFragmentation(fragments); - rtc::Buffer frame = CreateFrame(fragmentation); + rtc::Buffer nalus[] = {GenerateNalUnit(/*size=*/kFirstFragmentSize), + GenerateNalUnit(/*size=*/2), + GenerateNalUnit(/*size=*/2)}; + rtc::Buffer frame = CreateFrame(nalus); - RtpPacketizerH264 packetizer( - frame, limits, H264PacketizationMode::NonInterleaved, fragmentation); + RtpPacketizerH264 packetizer(frame, limits, + H264PacketizationMode::NonInterleaved); std::vector packets = FetchAllPackets(&packetizer); ASSERT_THAT(packets, SizeIs(2)); // Expect 1st packet is single nalu. - EXPECT_THAT(packets[0].payload(), - ElementsAreArray(frame.data(), kFirstFragmentSize)); + EXPECT_THAT(packets[0].payload(), ElementsAreArray(nalus[0])); // Expect 2nd packet is aggregate of last two fragments. - const uint8_t* tail = frame.data() + kFirstFragmentSize; - EXPECT_THAT(packets[1].payload(), ElementsAre(kStapA, // - 0, 2, tail[0], tail[1], // - 0, 2, tail[2], tail[3])); + EXPECT_THAT(packets[1].payload(), + ElementsAre(kStapA, // + 0, 2, nalus[1][0], nalus[1][1], // + 0, 2, nalus[2][0], nalus[2][1])); } TEST(RtpPacketizerH264Test, StapARespectsLastPacketReduction) { @@ -294,22 +297,23 @@ TEST(RtpPacketizerH264Test, StapARespectsLastPacketReduction) { 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); + rtc::Buffer nalus[] = {GenerateNalUnit(/*size=*/2), + GenerateNalUnit(/*size=*/2), + GenerateNalUnit(/*size=*/kLastFragmentSize)}; + rtc::Buffer frame = CreateFrame(nalus); - RtpPacketizerH264 packetizer( - frame, limits, H264PacketizationMode::NonInterleaved, fragmentation); + RtpPacketizerH264 packetizer(frame, limits, + H264PacketizationMode::NonInterleaved); std::vector packets = FetchAllPackets(&packetizer); 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_THAT(packets[0].payload(), + ElementsAre(kStapA, // + 0, 2, nalus[0][0], nalus[0][1], // + 0, 2, nalus[1][0], nalus[1][1])); // Expect 2nd packet is single nalu. - EXPECT_THAT(packets[1].payload(), - ElementsAreArray(frame.data() + 4, kLastFragmentSize)); + EXPECT_THAT(packets[1].payload(), ElementsAreArray(nalus[2])); } TEST(RtpPacketizerH264Test, TooSmallForStapAHeaders) { @@ -317,22 +321,23 @@ TEST(RtpPacketizerH264Test, TooSmallForStapAHeaders) { 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); + rtc::Buffer nalus[] = {GenerateNalUnit(/*size=*/2), + GenerateNalUnit(/*size=*/2), + GenerateNalUnit(/*size=*/kLastFragmentSize)}; + rtc::Buffer frame = CreateFrame(nalus); - RtpPacketizerH264 packetizer( - frame, limits, H264PacketizationMode::NonInterleaved, fragmentation); + RtpPacketizerH264 packetizer(frame, limits, + H264PacketizationMode::NonInterleaved); std::vector packets = FetchAllPackets(&packetizer); 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_THAT(packets[0].payload(), + ElementsAre(kStapA, // + 0, 2, nalus[0][0], nalus[0][1], // + 0, 2, nalus[1][0], nalus[1][1])); // Expect 2nd packet is single nalu. - EXPECT_THAT(packets[1].payload(), - ElementsAreArray(frame.data() + 4, kLastFragmentSize)); + EXPECT_THAT(packets[1].payload(), ElementsAreArray(nalus[2])); } // Fragmentation + aggregation. @@ -342,28 +347,29 @@ TEST(RtpPacketizerH264Test, MixedStapAFUA) { 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); + rtc::Buffer nalus[] = {GenerateNalUnit(kFuaNaluSize), + GenerateNalUnit(kStapANaluSize), + GenerateNalUnit(kStapANaluSize)}; + rtc::Buffer frame = CreateFrame(nalus); - RtpPacketizerH264 packetizer( - frame, limits, H264PacketizationMode::NonInterleaved, fragmentation); + RtpPacketizerH264 packetizer(frame, limits, + H264PacketizationMode::NonInterleaved); 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; + ElementsAre(kFuA, FuDefs::kSBit | nalus[0][0])); + EXPECT_THAT( + packets[0].payload().subview(kFuAHeaderSize), + ElementsAreArray(nalus[0].data() + kNalHeaderSize, 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; + ElementsAre(kFuA, FuDefs::kEBit | nalus[0][0])); + EXPECT_THAT( + packets[1].payload().subview(kFuAHeaderSize), + ElementsAreArray(nalus[0].data() + kNalHeaderSize + kFuaPayloadSize, + kFuaPayloadSize)); // Then expect one STAP-A packet with two nal units. EXPECT_THAT(packets[2].payload()[0], kStapA); @@ -371,13 +377,11 @@ TEST(RtpPacketizerH264Test, MixedStapAFUA) { EXPECT_THAT(payload.subview(0, kLengthFieldLength), ElementsAre(0, kStapANaluSize)); EXPECT_THAT(payload.subview(kLengthFieldLength, kStapANaluSize), - ElementsAreArray(next_fragment, kStapANaluSize)); + ElementsAreArray(nalus[1])); 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)); + EXPECT_THAT(payload.subview(kLengthFieldLength), ElementsAreArray(nalus[2])); } TEST(RtpPacketizerH264Test, LastFragmentFitsInSingleButNotLastPacket) { @@ -387,12 +391,10 @@ TEST(RtpPacketizerH264Test, LastFragmentFitsInSingleButNotLastPacket) { limits.last_packet_reduction_len = 20; limits.single_packet_reduction_len = 20; // Actual sizes, which triggered this bug. - size_t fragments[] = {20, 8, 18, 1161}; - RTPFragmentationHeader fragmentation = CreateFragmentation(fragments); - rtc::Buffer frame = CreateFrame(fragmentation); + rtc::Buffer frame = CreateFrame({20, 8, 18, 1161}); - RtpPacketizerH264 packetizer( - frame, limits, H264PacketizationMode::NonInterleaved, fragmentation); + RtpPacketizerH264 packetizer(frame, limits, + H264PacketizationMode::NonInterleaved); std::vector packets = FetchAllPackets(&packetizer); // Last packet has to be of correct size. @@ -406,11 +408,11 @@ TEST(RtpPacketizerH264Test, LastFragmentFitsInSingleButNotLastPacket) { // 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); + rtc::Buffer nalu[] = {GenerateNalUnit(kNalHeaderSize + frame_payload_size)}; + rtc::Buffer frame = CreateFrame(nalu); RtpPacketizerH264 packetizer(frame, limits, - H264PacketizationMode::NonInterleaved, - NoFragmentation(frame)); + H264PacketizationMode::NonInterleaved); std::vector packets = FetchAllPackets(&packetizer); EXPECT_GE(packets.size(), 2u); // Single packet indicates it is not FuA. @@ -429,7 +431,7 @@ std::vector TestFua(size_t frame_payload_size, // 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]))); + EXPECT_THAT(fua_header, Each(Eq((kFuA << 8) | nalu[0][0]))); return payload_sizes; } @@ -488,11 +490,10 @@ TEST(RtpPacketizerH264Test, FUABig) { TEST(RtpPacketizerH264Test, RejectsOverlongDataInPacketizationMode0) { RtpPacketizer::PayloadSizeLimits limits; - rtc::Buffer frame = CreateFrame(kMaxPayloadSize + 1); - RTPFragmentationHeader fragmentation = NoFragmentation(frame); + rtc::Buffer frame = CreateFrame({kMaxPayloadSize + 1}); - RtpPacketizerH264 packetizer( - frame, limits, H264PacketizationMode::SingleNalUnit, fragmentation); + RtpPacketizerH264 packetizer(frame, limits, + H264PacketizationMode::SingleNalUnit); std::vector packets = FetchAllPackets(&packetizer); EXPECT_THAT(packets, IsEmpty()); diff --git a/test/fake_encoder.cc b/test/fake_encoder.cc index 84a4afd0d2..219dafcf16 100644 --- a/test/fake_encoder.cc +++ b/test/fake_encoder.cc @@ -290,6 +290,7 @@ FakeH264Encoder::FakeH264Encoder(Clock* clock) std::unique_ptr FakeH264Encoder::EncodeHook( EncodedImage* encoded_image, CodecSpecificInfo* codec_specific) { + static constexpr std::array kStartCode = {0, 0, 1}; const size_t kSpsSize = 8; const size_t kPpsSize = 11; const int kIdrFrequency = 10; @@ -299,46 +300,46 @@ std::unique_ptr FakeH264Encoder::EncodeHook( current_idr_counter = idr_counter_; ++idr_counter_; } + for (size_t i = 0; i < encoded_image->size(); ++i) { + encoded_image->data()[i] = static_cast(i); + } + auto fragmentation = std::make_unique(); if (current_idr_counter % kIdrFrequency == 0 && - encoded_image->size() > kSpsSize + kPpsSize + 1) { + encoded_image->size() > kSpsSize + kPpsSize + 1 + 3 * kStartCode.size()) { const size_t kNumSlices = 3; fragmentation->VerifyAndAllocateFragmentationHeader(kNumSlices); - fragmentation->fragmentationOffset[0] = 0; + fragmentation->fragmentationOffset[0] = kStartCode.size(); fragmentation->fragmentationLength[0] = kSpsSize; - fragmentation->fragmentationOffset[1] = kSpsSize; + fragmentation->fragmentationOffset[1] = 2 * kStartCode.size() + kSpsSize; fragmentation->fragmentationLength[1] = kPpsSize; - fragmentation->fragmentationOffset[2] = kSpsSize + kPpsSize; + fragmentation->fragmentationOffset[2] = + 3 * kStartCode.size() + kSpsSize + kPpsSize; fragmentation->fragmentationLength[2] = - encoded_image->size() - (kSpsSize + kPpsSize); + encoded_image->size() - (3 * kStartCode.size() + kSpsSize + kPpsSize); const size_t kSpsNalHeader = 0x67; const size_t kPpsNalHeader = 0x68; const size_t kIdrNalHeader = 0x65; - encoded_image->data()[fragmentation->fragmentationOffset[0]] = - kSpsNalHeader; - encoded_image->data()[fragmentation->fragmentationOffset[1]] = - kPpsNalHeader; - encoded_image->data()[fragmentation->fragmentationOffset[2]] = - kIdrNalHeader; + memcpy(encoded_image->data(), kStartCode.data(), kStartCode.size()); + encoded_image->data()[fragmentation->Offset(0)] = kSpsNalHeader; + memcpy(encoded_image->data() + fragmentation->Offset(1) - kStartCode.size(), + kStartCode.data(), kStartCode.size()); + encoded_image->data()[fragmentation->Offset(1)] = kPpsNalHeader; + memcpy(encoded_image->data() + fragmentation->Offset(2) - kStartCode.size(), + kStartCode.data(), kStartCode.size()); + encoded_image->data()[fragmentation->Offset(2)] = kIdrNalHeader; } else { const size_t kNumSlices = 1; fragmentation->VerifyAndAllocateFragmentationHeader(kNumSlices); - fragmentation->fragmentationOffset[0] = 0; - fragmentation->fragmentationLength[0] = encoded_image->size(); + fragmentation->fragmentationOffset[0] = kStartCode.size(); + fragmentation->fragmentationLength[0] = + encoded_image->size() - kStartCode.size(); + memcpy(encoded_image->data(), kStartCode.data(), kStartCode.size()); const size_t kNalHeader = 0x41; encoded_image->data()[fragmentation->fragmentationOffset[0]] = kNalHeader; } - uint8_t value = 0; - int fragment_counter = 0; - for (size_t i = 0; i < encoded_image->size(); ++i) { - if (fragment_counter == fragmentation->fragmentationVectorSize || - i != fragmentation->fragmentationOffset[fragment_counter]) { - encoded_image->data()[i] = value++; - } else { - ++fragment_counter; - } - } + codec_specific->codecType = kVideoCodecH264; codec_specific->codecSpecific.H264.packetization_mode = H264PacketizationMode::NonInterleaved;