Use single packet limit when all fragments end up in one H.264 packet
Update RtpPacketizerH264::PacketizeStapA to use
single_packet_reduction_len when all fragments end up in one H.264
packet.
Previous code was using first_packet_reduction_len +
last_packet_reduction_len for this case, which can cause an occasional
RTC_CHECK crash in RtpPacketizerH264::NextAggregatePacket due to
exceeding the available payload capacity of an RTP packet.
Bug: webrtc:15477
Change-Id: Iba1564a6a29618bef22f19d82aba938420994b23
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/319645
Reviewed-by: Philip Eliasson <philipel@webrtc.org>
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#40737}
diff --git a/modules/rtp_rtcp/source/rtp_format_h264.cc b/modules/rtp_rtcp/source/rtp_format_h264.cc
index cc8d1bf..d066baf 100644
--- a/modules/rtp_rtcp/source/rtp_format_h264.cc
+++ b/modules/rtp_rtcp/source/rtp_format_h264.cc
@@ -153,28 +153,25 @@
size_t RtpPacketizerH264::PacketizeStapA(size_t fragment_index) {
// Aggregate fragments into one packet (STAP-A).
size_t payload_size_left = limits_.max_payload_len;
- if (input_fragments_.size() == 1)
- payload_size_left -= limits_.single_packet_reduction_len;
- else if (fragment_index == 0)
- payload_size_left -= limits_.first_packet_reduction_len;
int aggregated_fragments = 0;
size_t fragment_headers_length = 0;
rtc::ArrayView<const uint8_t> fragment = input_fragments_[fragment_index];
RTC_CHECK_GE(payload_size_left, fragment.size());
++num_packets_left_;
+ const bool has_first_fragment = fragment_index == 0;
auto payload_size_needed = [&] {
size_t fragment_size = fragment.size() + fragment_headers_length;
- if (input_fragments_.size() == 1) {
- // Single fragment, single packet, payload_size_left already adjusted
- // with limits_.single_packet_reduction_len.
+ bool has_last_fragment = fragment_index == input_fragments_.size() - 1;
+ if (has_first_fragment && has_last_fragment) {
+ return fragment_size + limits_.single_packet_reduction_len;
+ } else if (has_first_fragment) {
+ return fragment_size + limits_.first_packet_reduction_len;
+ } else if (has_last_fragment) {
+ return fragment_size + limits_.last_packet_reduction_len;
+ } else {
return fragment_size;
}
- if (fragment_index == input_fragments_.size() - 1) {
- // Last fragment, so STAP-A might be the last packet.
- return fragment_size + limits_.last_packet_reduction_len;
- }
- return fragment_size;
};
while (payload_size_left >= payload_size_needed()) {
diff --git a/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc b/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc
index 80d8801..18311c6 100644
--- a/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_format_h264_unittest.cc
@@ -285,14 +285,50 @@
0, 2, nalus[2][0], nalus[2][1]));
}
+TEST(RtpPacketizerH264Test, StapARespectsSinglePacketReduction) {
+ RtpPacketizer::PayloadSizeLimits limits;
+ limits.max_payload_len = 1000;
+ // It is possible for single_packet_reduction_len to be greater than
+ // first_packet_reduction_len + last_packet_reduction_len. Check that the
+ // right limit is used when first and last fragment go to one packet.
+ limits.first_packet_reduction_len = 4;
+ limits.last_packet_reduction_len = 0;
+ limits.single_packet_reduction_len = 8;
+ // 3 fragments of sizes 2 + 2 + 981, plus 7 bytes of headers, is expected to
+ // be packetized to single packet of size 992.
+ rtc::Buffer first_nalus[] = {GenerateNalUnit(/*size=*/2),
+ GenerateNalUnit(/*size=*/2),
+ GenerateNalUnit(/*size=*/981)};
+ rtc::Buffer first_frame = CreateFrame(first_nalus);
+
+ RtpPacketizerH264 first_packetizer(first_frame, limits,
+ H264PacketizationMode::NonInterleaved);
+ std::vector<RtpPacketToSend> packets = FetchAllPackets(&first_packetizer);
+
+ // Expect that everything fits in a single packet.
+ ASSERT_THAT(packets, SizeIs(1));
+ EXPECT_EQ(packets[0].payload_size(), 992u);
+
+ // Increasing the last fragment size by one exceeds
+ // single_packet_reduction_len and produces two packets.
+ rtc::Buffer second_nalus[] = {GenerateNalUnit(/*size=*/2),
+ GenerateNalUnit(/*size=*/2),
+ GenerateNalUnit(/*size=*/982)};
+ rtc::Buffer second_frame = CreateFrame(second_nalus);
+ RtpPacketizerH264 second_packetizer(second_frame, limits,
+ H264PacketizationMode::NonInterleaved);
+ packets = FetchAllPackets(&second_packetizer);
+ ASSERT_THAT(packets, SizeIs(2));
+}
+
TEST(RtpPacketizerH264Test, StapARespectsLastPacketReduction) {
RtpPacketizer::PayloadSizeLimits limits;
limits.max_payload_len = 1000;
limits.last_packet_reduction_len = 100;
+ const size_t kFirstFragmentSize = 1000;
const size_t kLastFragmentSize =
- limits.max_payload_len - limits.last_packet_reduction_len;
- rtc::Buffer nalus[] = {GenerateNalUnit(/*size=*/2),
- GenerateNalUnit(/*size=*/2),
+ limits.max_payload_len - limits.last_packet_reduction_len + 1;
+ rtc::Buffer nalus[] = {GenerateNalUnit(/*size=*/kFirstFragmentSize),
GenerateNalUnit(/*size=*/kLastFragmentSize)};
rtc::Buffer frame = CreateFrame(nalus);
@@ -300,14 +336,13 @@
H264PacketizationMode::NonInterleaved);
std::vector<RtpPacketToSend> 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, 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(nalus[2]));
+ ASSERT_THAT(packets, SizeIs(3));
+ // Expect 1st packet contains first fragment.
+ EXPECT_THAT(packets[0].payload()[0], kSlice);
+ // Expect 2nd and 3rd packets to be FU-A since last_packet_reduction_len
+ // was exceeded by one byte.
+ EXPECT_THAT(packets[1].payload()[0], kFuA);
+ EXPECT_THAT(packets[2].payload()[0], kFuA);
}
TEST(RtpPacketizerH264Test, TooSmallForStapAHeaders) {