Skip to content

Commit a8e468d

Browse files
authored
fix: improve RTP depacketizer robustness for H264 and H265 (#2032)
- Improved H264/H265 depacketizers to properly cache decoding parameter sets (SPS/PPS/VPS) when they are delivered via FU-A/FU fragmentation, which is uncommon but valid per RFC 6184/7798 - Fixed minor bugs in H264 and H265 depacketizers - Removed the redundant `start_payload` flag in H264. First fragment detection now relies solely on the FU-A S-bit (RFC 6184) - Fixed an incorrect DONL size check in H265 that silently dropped valid FU packets when `sprop-max-don-diff=0`
1 parent c29783e commit a8e468d

File tree

4 files changed

+86
-19
lines changed

4 files changed

+86
-19
lines changed

src/projects/modules/rtp_rtcp/rtp_depacketizer_h264.cpp

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ std::shared_ptr<ov::Data> RtpDepacketizerH264::ParseAndAssembleFrame(std::vector
1515
reserve_size += 16; // spare
1616
}
1717

18+
// Reset FU-A DPS reassembly buffer at frame boundary
19+
_fua_dps_buffer = nullptr;
20+
1821
auto bitstream = std::make_shared<ov::Data>(reserve_size);
19-
bool start_payload = true;
2022
for(const auto &payload : payload_list)
2123
{
2224
if (payload->GetLength() < NAL_HEADER_SIZE)
@@ -30,7 +32,7 @@ std::shared_ptr<ov::Data> RtpDepacketizerH264::ParseAndAssembleFrame(std::vector
3032
// Fragmented NAL units
3133
if(nal_type == NaluType::kFuA)
3234
{
33-
result = ParseFuaAndConvertAnnexB(payload, start_payload);
35+
result = ParseFuaAndConvertAnnexB(payload);
3436
if(result == nullptr)
3537
{
3638
return nullptr;
@@ -58,32 +60,61 @@ std::shared_ptr<ov::Data> RtpDepacketizerH264::ParseAndAssembleFrame(std::vector
5860

5961
bitstream->Append(result);
6062
}
61-
62-
start_payload = false;
6363
}
6464

6565
return bitstream;
6666
}
6767

68-
std::shared_ptr<ov::Data> RtpDepacketizerH264::ParseFuaAndConvertAnnexB(const std::shared_ptr<ov::Data> &payload, bool start)
68+
std::shared_ptr<ov::Data> RtpDepacketizerH264::ParseFuaAndConvertAnnexB(const std::shared_ptr<ov::Data> &payload)
6969
{
7070
auto bitstream = std::make_shared<ov::Data>(payload->GetLength() + 16);
7171

72-
if(payload->GetLength() < FUA_HEADER_SIZE)
72+
if (payload->GetLength() < FUA_HEADER_SIZE)
7373
{
7474
// Invalid Data
7575
return nullptr;
7676
}
7777

7878
auto buffer = payload->GetDataAs<uint8_t>();
7979
auto fnri = buffer[0] & (NAL_FBIT | NAL_NRI_MASK);
80-
auto original_nal_type = buffer[1] & NAL_TYPE_MASK;
80+
uint8_t original_nal_type = buffer[1] & NAL_TYPE_MASK;
8181
bool first_fragment = (buffer[1] & FUA_SBIT) > 0;
82+
bool last_fragment = (buffer[1] & FUA_EBIT) > 0;
83+
uint8_t original_nal_header = fnri | original_nal_type;
8284

83-
if(first_fragment == true || start == true)
85+
// Cache SPS/PPS carried over FU-A (non-standard but possible encoders)
86+
if(first_fragment)
87+
{
88+
_fua_dps_buffer = nullptr;
89+
90+
if (IsDecodingParmeterSets(original_nal_type))
91+
{
92+
_fua_dps_nal_type = original_nal_type;
93+
_fua_dps_buffer = std::make_shared<ov::Data>();
94+
_fua_dps_buffer->Append(&original_nal_header, NAL_HEADER_SIZE);
95+
_fua_dps_buffer->Append(payload->Subdata(FUA_HEADER_SIZE));
96+
97+
// Single-fragment FU-A (S+E both set): flush immediately
98+
if(last_fragment)
99+
{
100+
AddDecodingParameterSet(_fua_dps_nal_type, _fua_dps_buffer);
101+
_fua_dps_buffer = nullptr;
102+
}
103+
}
104+
}
105+
else if (_fua_dps_buffer != nullptr)
106+
{
107+
_fua_dps_buffer->Append(payload->Subdata(FUA_HEADER_SIZE));
108+
if (last_fragment)
109+
{
110+
AddDecodingParameterSet(_fua_dps_nal_type, _fua_dps_buffer);
111+
_fua_dps_buffer = nullptr;
112+
}
113+
}
114+
115+
if (first_fragment == true)
84116
{
85117
uint8_t start_prefix_and_nal_header[ANNEXB_START_PREFIX_LENGTH + NAL_HEADER_SIZE];
86-
uint8_t original_nal_header = fnri | original_nal_type;
87118

88119
start_prefix_and_nal_header[0] = 0;
89120
start_prefix_and_nal_header[1] = 0;
@@ -93,7 +124,7 @@ std::shared_ptr<ov::Data> RtpDepacketizerH264::ParseFuaAndConvertAnnexB(const st
93124

94125
bitstream->Append(start_prefix_and_nal_header, ANNEXB_START_PREFIX_LENGTH + NAL_HEADER_SIZE);
95126
}
96-
127+
97128
bitstream->Append(payload->Subdata(FUA_HEADER_SIZE));
98129

99130
return bitstream;

src/projects/modules/rtp_rtcp/rtp_depacketizer_h264.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,14 @@ class RtpDepacketizerH264 : public RtpDepacketizingManager
2424
std::shared_ptr<ov::Data> GetDecodingParameterSetsToAnnexB() override;
2525

2626
private:
27-
std::shared_ptr<ov::Data> ParseFuaAndConvertAnnexB(const std::shared_ptr<ov::Data> &payload, bool start=false);
27+
std::shared_ptr<ov::Data> ParseFuaAndConvertAnnexB(const std::shared_ptr<ov::Data> &payload);
2828
std::shared_ptr<ov::Data> ParseStapAAndConvertToAnnexB(const std::shared_ptr<ov::Data> &payload);
2929
std::shared_ptr<ov::Data> ConvertSingleNaluToAnnexB(const std::shared_ptr<ov::Data> &payload);
3030

3131
bool IsDecodingParmeterSets(uint8_t nal_unit_type);
3232

33+
// For FU-A SPS/PPS reassembly
34+
std::shared_ptr<ov::Data> _fua_dps_buffer;
35+
uint8_t _fua_dps_nal_type = 0;
36+
3337
};

src/projects/modules/rtp_rtcp/rtp_depacketizer_h265.cpp

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ std::shared_ptr<ov::Data> RtpDepacketizerH265::ParseAndAssembleFrame(std::vector
2525
reserve_size += 16; // spare
2626
}
2727

28+
// Reset FU DPS reassembly buffer at frame boundary
29+
_fu_dps_buffer = nullptr;
30+
2831
auto bitstream = std::make_shared<ov::Data>(reserve_size);
2932
for (const auto &payload : payload_list)
3033
{
@@ -149,7 +152,7 @@ std::shared_ptr<ov::Data> RtpDepacketizerH265::ParseFUsAndConvertAnnexB(const st
149152
// FU header
150153
uint8_t fu_hdr = buffer[offset];
151154
uint8_t fu_s = FUH_SBIT(fu_hdr);
152-
[[maybe_unused]] uint8_t fu_e = FUH_EBIT(fu_hdr);
155+
uint8_t fu_e = FUH_EBIT(fu_hdr);
153156
uint8_t fu_type = FUH_TYPE(fu_hdr);
154157
offset += FU_HEADER_SIZE;
155158

@@ -160,12 +163,10 @@ std::shared_ptr<ov::Data> RtpDepacketizerH265::ParseFUsAndConvertAnnexB(const st
160163
return nullptr;
161164
}
162165

163-
// If the S field is 1, the FU header must include the DONL(Conditional) field.
164-
if (fu_s == 1 && payload->GetLength() < PAYLOAD_HEADER_SIZE + FU_HEADER_SIZE + DONL_FIELD_SIZE)
165-
{
166-
// Invalid Data
167-
return nullptr;
168-
}
166+
// DONL is conditionally present only when S=1 and sprop-max-don-diff > 0 (SDP negotiated).
167+
// sprop-max-don-diff > 0 is not used in WebRTC (browsers do not support it) and is
168+
// virtually never seen in real RTSP sources either. OvenMediaEngine assumes
169+
// sprop-max-don-diff=0 (the default), so DONL is treated as absent.
169170

170171
// If the S field is 1, the NAL header must be attached.
171172
if (fu_s == 1)
@@ -179,8 +180,35 @@ std::shared_ptr<ov::Data> RtpDepacketizerH265::ParseFUsAndConvertAnnexB(const st
179180
bitstream->Append(nal_header, PAYLOAD_HEADER_SIZE);
180181
}
181182

183+
// Cache VPS/SPS/PPS carried over FU (non-standard but possible encoders)
184+
const size_t fu_payload_length = payload->GetLength() - PAYLOAD_HEADER_SIZE - FU_HEADER_SIZE;
185+
if (fu_s == 1)
186+
{
187+
_fu_dps_buffer = nullptr;
188+
189+
if (IsDecodingParmeterSets(fu_type))
190+
{
191+
_fu_dps_nal_type = fu_type;
192+
_fu_dps_buffer = std::make_shared<ov::Data>();
193+
194+
uint8_t nal_header[2];
195+
ByteWriter<uint16_t>::WriteBigEndian((uint8_t *)&nal_header[0], NUH_HEADER(fu_type, nuh_layer_id, nuh_temporal_id));
196+
_fu_dps_buffer->Append(nal_header, PAYLOAD_HEADER_SIZE);
197+
_fu_dps_buffer->Append(&buffer[offset], fu_payload_length);
198+
}
199+
}
200+
else if (_fu_dps_buffer != nullptr)
201+
{
202+
_fu_dps_buffer->Append(&buffer[offset], fu_payload_length);
203+
if (fu_e == 1)
204+
{
205+
AddDecodingParameterSet(_fu_dps_nal_type, _fu_dps_buffer);
206+
_fu_dps_buffer = nullptr;
207+
}
208+
}
209+
182210
// Set FU Payload
183-
bitstream->Append(&buffer[offset], payload->GetLength() - PAYLOAD_HEADER_SIZE - FU_HEADER_SIZE);
211+
bitstream->Append(&buffer[offset], fu_payload_length);
184212

185213
return bitstream;
186214
}

src/projects/modules/rtp_rtcp/rtp_depacketizer_h265.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,8 @@ class RtpDepacketizerH265 : public RtpDepacketizingManager
4444
void AppendBitstream(std::shared_ptr<ov::Data>&bitstream, uint8_t nal_type, uint8_t nal_header[2], const void *payload, size_t payload_length);
4545
bool IsDecodingParmeterSets(uint8_t nal_unit_type);
4646

47+
// For FU DPS reassembly (VPS/SPS/PPS carried over FU units)
48+
std::shared_ptr<ov::Data> _fu_dps_buffer;
49+
uint8_t _fu_dps_nal_type = 0;
50+
4751
};

0 commit comments

Comments
 (0)