mirror of
https://github.com/aler9/gortsplib
synced 2025-10-08 00:20:05 +08:00
rtph264: support streams that encode NALUs into Annex-B
(https://github.com/aler9/rtsp-simple-server/issues/1029)
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package rtph264
|
package rtph264
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
@@ -28,6 +29,8 @@ type Decoder struct {
|
|||||||
fragmentedMode bool
|
fragmentedMode bool
|
||||||
fragmentedParts [][]byte
|
fragmentedParts [][]byte
|
||||||
fragmentedSize int
|
fragmentedSize int
|
||||||
|
firstNALUParsed bool
|
||||||
|
annexBMode bool
|
||||||
|
|
||||||
// for DecodeUntilMarker()
|
// for DecodeUntilMarker()
|
||||||
naluBuffer [][]byte
|
naluBuffer [][]byte
|
||||||
@@ -46,6 +49,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
typ := naluType(pkt.Payload[0] & 0x1F)
|
typ := naluType(pkt.Payload[0] & 0x1F)
|
||||||
|
|
||||||
switch typ {
|
switch typ {
|
||||||
case naluTypeSTAPA:
|
case naluTypeSTAPA:
|
||||||
var nalus [][]byte
|
var nalus [][]byte
|
||||||
@@ -77,6 +81,13 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
d.firstPacketReceived = true
|
d.firstPacketReceived = true
|
||||||
|
|
||||||
|
var err error
|
||||||
|
nalus, err = d.finalize(nalus)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
return nalus, d.timeDecoder.Decode(pkt.Timestamp), nil
|
return nalus, d.timeDecoder.Decode(pkt.Timestamp), nil
|
||||||
|
|
||||||
case naluTypeFUA: // first packet of a fragmented NALU
|
case naluTypeFUA: // first packet of a fragmented NALU
|
||||||
@@ -112,8 +123,17 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
|||||||
return nil, 0, fmt.Errorf("packet type not supported (%v)", typ)
|
return nil, 0, fmt.Errorf("packet type not supported (%v)", typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nalus := [][]byte{pkt.Payload}
|
||||||
|
|
||||||
d.firstPacketReceived = true
|
d.firstPacketReceived = true
|
||||||
return [][]byte{pkt.Payload}, d.timeDecoder.Decode(pkt.Timestamp), nil
|
|
||||||
|
var err error
|
||||||
|
nalus, err = d.finalize(nalus)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nalus, d.timeDecoder.Decode(pkt.Timestamp), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// we are decoding a fragmented NALU
|
// we are decoding a fragmented NALU
|
||||||
@@ -157,10 +177,18 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
|||||||
for _, p := range d.fragmentedParts {
|
for _, p := range d.fragmentedParts {
|
||||||
n += copy(ret[n:], p)
|
n += copy(ret[n:], p)
|
||||||
}
|
}
|
||||||
|
nalus := [][]byte{ret}
|
||||||
|
|
||||||
d.fragmentedParts = d.fragmentedParts[:0]
|
d.fragmentedParts = d.fragmentedParts[:0]
|
||||||
d.fragmentedMode = false
|
d.fragmentedMode = false
|
||||||
return [][]byte{ret}, d.timeDecoder.Decode(pkt.Timestamp), nil
|
|
||||||
|
var err error
|
||||||
|
nalus, err = d.finalize(nalus)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nalus, d.timeDecoder.Decode(pkt.Timestamp), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeUntilMarker decodes NALUs from a RTP/H264 packet and puts them in a buffer.
|
// DecodeUntilMarker decodes NALUs from a RTP/H264 packet and puts them in a buffer.
|
||||||
@@ -188,3 +216,39 @@ func (d *Decoder) DecodeUntilMarker(pkt *rtp.Packet) ([][]byte, time.Duration, e
|
|||||||
|
|
||||||
return ret, pts, nil
|
return ret, pts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) finalize(nalus [][]byte) ([][]byte, error) {
|
||||||
|
// some cameras / servers wrap NALUs into Annex-B
|
||||||
|
if !d.firstNALUParsed {
|
||||||
|
d.firstNALUParsed = true
|
||||||
|
|
||||||
|
if len(nalus) == 1 {
|
||||||
|
nalu := nalus[0]
|
||||||
|
|
||||||
|
i := bytes.Index(nalu, []byte{0x00, 0x00, 0x00, 0x01})
|
||||||
|
if i >= 0 {
|
||||||
|
d.annexBMode = true
|
||||||
|
|
||||||
|
if !bytes.HasPrefix(nalu, []byte{0x00, 0x00, 0x00, 0x01}) {
|
||||||
|
nalu = append([]byte{0x00, 0x00, 0x00, 0x01}, nalu...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return h264.AnnexBUnmarshal(nalu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if d.annexBMode {
|
||||||
|
if len(nalus) != 1 {
|
||||||
|
return nil, fmt.Errorf("multiple NALUs in Annex-B mode are not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
nalu := nalus[0]
|
||||||
|
|
||||||
|
if !bytes.HasPrefix(nalu, []byte{0x00, 0x00, 0x00, 0x01}) {
|
||||||
|
nalu = append([]byte{0x00, 0x00, 0x00, 0x01}, nalu...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return h264.AnnexBUnmarshal(nalu)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nalus, nil
|
||||||
|
}
|
||||||
|
@@ -418,6 +418,35 @@ func TestDecodeSTAPAWithPadding(t *testing.T) {
|
|||||||
}, nalus)
|
}, nalus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDecodeAnnexB(t *testing.T) {
|
||||||
|
d := &Decoder{}
|
||||||
|
d.Init()
|
||||||
|
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
nalus, _, err := d.Decode(&rtp.Packet{
|
||||||
|
Header: rtp.Header{
|
||||||
|
Version: 2,
|
||||||
|
Marker: true,
|
||||||
|
PayloadType: 96,
|
||||||
|
SequenceNumber: 17647,
|
||||||
|
Timestamp: 2289531307,
|
||||||
|
SSRC: 0x9dbb7812,
|
||||||
|
},
|
||||||
|
Payload: mergeBytes(
|
||||||
|
[]byte{0x00, 0x00, 0x00, 0x01},
|
||||||
|
[]byte{0x01, 0x02, 0x03, 0x04},
|
||||||
|
[]byte{0x00, 0x00, 0x00, 0x01},
|
||||||
|
[]byte{0x01, 0x02, 0x03, 0x04},
|
||||||
|
),
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, [][]byte{
|
||||||
|
{0x01, 0x02, 0x03, 0x04},
|
||||||
|
{0x01, 0x02, 0x03, 0x04},
|
||||||
|
}, nalus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDecodeErrors(t *testing.T) {
|
func TestDecodeErrors(t *testing.T) {
|
||||||
for _, ca := range []struct {
|
for _, ca := range []struct {
|
||||||
name string
|
name string
|
||||||
|
Reference in New Issue
Block a user