mirror of
https://github.com/aler9/gortsplib
synced 2025-10-05 15:16:51 +08:00
rtph264: accept non-compliant single FU-A frames (#649)
Some IP cameras (e.g. CostarHD) have been observed to emit single fragment RTP FU-A packets, with start and end bit both set, for sufficiently small H.264 P-frames. Rather than emit an error, workaround such non-compliant frames.
This commit is contained in:
@@ -90,10 +90,6 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, error) {
|
|||||||
if start == 1 {
|
if start == 1 {
|
||||||
d.resetFragments()
|
d.resetFragments()
|
||||||
|
|
||||||
if end != 0 {
|
|
||||||
return nil, fmt.Errorf("invalid FU-A packet (can't contain both a start and end bit)")
|
|
||||||
}
|
|
||||||
|
|
||||||
nri := (pkt.Payload[0] >> 5) & 0x03
|
nri := (pkt.Payload[0] >> 5) & 0x03
|
||||||
typ := pkt.Payload[1] & 0x1F
|
typ := pkt.Payload[1] & 0x1F
|
||||||
d.fragmentsSize = len(pkt.Payload[1:])
|
d.fragmentsSize = len(pkt.Payload[1:])
|
||||||
@@ -101,6 +97,20 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, error) {
|
|||||||
d.fragmentNextSeqNum = pkt.SequenceNumber + 1
|
d.fragmentNextSeqNum = pkt.SequenceNumber + 1
|
||||||
d.firstPacketReceived = true
|
d.firstPacketReceived = true
|
||||||
|
|
||||||
|
// RFC 6184 clearly states:
|
||||||
|
//
|
||||||
|
// A fragmented NAL unit MUST NOT be transmitted in one FU; that is, the
|
||||||
|
// Start bit and End bit MUST NOT both be set to one in the same FU
|
||||||
|
// header.
|
||||||
|
//
|
||||||
|
// However, some vendors camera (e.g. CostarHD) have been observed to nevertheless
|
||||||
|
// emit one fragmented NAL unit for sufficiently small P-frames.
|
||||||
|
if end != 0 {
|
||||||
|
nalus = [][]byte{joinFragments(d.fragments, d.fragmentsSize)}
|
||||||
|
d.resetFragments()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
return nil, ErrMorePacketsNeeded
|
return nil, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -79,6 +79,38 @@ func TestDecodeCorruptedFragment(t *testing.T) {
|
|||||||
require.Equal(t, [][]byte{{0x01, 0x00}}, nalus)
|
require.Equal(t, [][]byte{{0x01, 0x00}}, nalus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDecodeNoncompliantFragment(t *testing.T) {
|
||||||
|
d := &Decoder{}
|
||||||
|
err := d.Init()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
nalus, err := d.Decode(&rtp.Packet{
|
||||||
|
Header: rtp.Header{
|
||||||
|
Version: 2,
|
||||||
|
Marker: true,
|
||||||
|
PayloadType: 96,
|
||||||
|
SequenceNumber: 18853,
|
||||||
|
Timestamp: 1731630255,
|
||||||
|
SSRC: 0x466b0000,
|
||||||
|
},
|
||||||
|
|
||||||
|
// FU-A with both start and end bit intentionally set
|
||||||
|
// While not compliant with RFC 6184, IP cameras from some vendors
|
||||||
|
// (e.g. CostarHD) have been observed to produce such FU-A payloads for
|
||||||
|
// sufficiently small P-frames.
|
||||||
|
Payload: mergeBytes(
|
||||||
|
[]byte{
|
||||||
|
0x3c, // FU indicator
|
||||||
|
0xc1, // FU header (start and end bit both intentionally set)
|
||||||
|
0xe7, 0x00, // DON
|
||||||
|
0xca, 0xfe, // Payload
|
||||||
|
},
|
||||||
|
),
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, [][]byte{{0x21, 0xe7, 0x00, 0xca, 0xfe}}, nalus)
|
||||||
|
}
|
||||||
|
|
||||||
func TestDecodeSTAPAWithPadding(t *testing.T) {
|
func TestDecodeSTAPAWithPadding(t *testing.T) {
|
||||||
d := &Decoder{}
|
d := &Decoder{}
|
||||||
err := d.Init()
|
err := d.Init()
|
||||||
|
Reference in New Issue
Block a user