mirror of
https://github.com/aler9/gortsplib
synced 2025-10-06 07:37:07 +08:00
add RTP/MPEG-2 audio decoder and encoder (#257)
This commit is contained in:
@@ -45,7 +45,7 @@ Features:
|
||||
* Parse RTSP elements
|
||||
* Encode/decode format-specific frames into/from RTP packets. The following formats are supported:
|
||||
* Video: H264, H265, M-JPEG, VP8, VP9
|
||||
* Audio: G711 (PCMA, PCMU), G722, LPCM, MPEG4 Audio (AAC), Opus
|
||||
* Audio: G711 (PCMA, PCMU), G722, LPCM, MPEG-2 Audio (MP3), MPEG-4 Audio (AAC), Opus
|
||||
|
||||
## Table of contents
|
||||
|
||||
|
2
go.mod
2
go.mod
@@ -4,7 +4,7 @@ go 1.18
|
||||
|
||||
require (
|
||||
github.com/asticode/go-astits v1.11.0
|
||||
github.com/bluenviron/mediacommon v0.3.1
|
||||
github.com/bluenviron/mediacommon v0.4.1
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/pion/rtcp v1.2.10
|
||||
github.com/pion/rtp v0.0.0-20230107162714-c3ea6851e25b
|
||||
|
4
go.sum
4
go.sum
@@ -2,8 +2,8 @@ github.com/asticode/go-astikit v0.30.0 h1:DkBkRQRIxYcknlaU7W7ksNfn4gMFsB0tqMJflx
|
||||
github.com/asticode/go-astikit v0.30.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0=
|
||||
github.com/asticode/go-astits v1.11.0 h1:GTHUXht0ZXAJXsVbsLIcyfHr1Bchi4QQwMARw2ZWAng=
|
||||
github.com/asticode/go-astits v1.11.0/go.mod h1:QSHmknZ51pf6KJdHKZHJTLlMegIrhega3LPWz3ND/iI=
|
||||
github.com/bluenviron/mediacommon v0.3.1 h1:C4okNqyN1Mg5CVGcGKk2tEk9Uj2hHZusHV7nqdjn1Lk=
|
||||
github.com/bluenviron/mediacommon v0.3.1/go.mod h1:t0dqPsWUTchyvib0MhixIwXEgvDX4V9G+I0GzWLQRb8=
|
||||
github.com/bluenviron/mediacommon v0.4.1 h1:oiqvqwnZ0NbB+mCZjuyBtgY7cF88rZdhb/PTSNG9M+Q=
|
||||
github.com/bluenviron/mediacommon v0.4.1/go.mod h1:t0dqPsWUTchyvib0MhixIwXEgvDX4V9G+I0GzWLQRb8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@@ -1,4 +1,4 @@
|
||||
package formats
|
||||
package formats //nolint:dupl
|
||||
|
||||
import (
|
||||
"github.com/pion/rtp"
|
||||
|
@@ -1,7 +1,9 @@
|
||||
package formats
|
||||
package formats //nolint:dupl
|
||||
|
||||
import (
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v3/pkg/formats/rtpmpeg2audio"
|
||||
)
|
||||
|
||||
// MPEG2Audio is a RTP format that uses a MPEG-1 or MPEG-2 audio codec.
|
||||
@@ -44,3 +46,17 @@ func (f *MPEG2Audio) FMTP() map[string]string {
|
||||
func (f *MPEG2Audio) PTSEqualsDTS(*rtp.Packet) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// CreateDecoder creates a decoder able to decode the content of the format.
|
||||
func (f *MPEG2Audio) CreateDecoder() *rtpmpeg2audio.Decoder {
|
||||
d := &rtpmpeg2audio.Decoder{}
|
||||
d.Init()
|
||||
return d
|
||||
}
|
||||
|
||||
// CreateEncoder creates an encoder able to encode the content of the format.
|
||||
func (f *MPEG2Audio) CreateEncoder() *rtpmpeg2audio.Encoder {
|
||||
e := &rtpmpeg2audio.Encoder{}
|
||||
e.Init()
|
||||
return e
|
||||
}
|
||||
|
@@ -13,3 +13,43 @@ func TestMPEG2AudioAttributes(t *testing.T) {
|
||||
require.Equal(t, 90000, format.ClockRate())
|
||||
require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
|
||||
}
|
||||
|
||||
func TestMPEG2AudioDecEncoder(t *testing.T) {
|
||||
format := &MPEG2Audio{}
|
||||
|
||||
enc := format.CreateEncoder()
|
||||
pkts, err := enc.Encode([][]byte{{
|
||||
0xff, 0xfb, 0x14, 0x64, 0x00, 0x0f, 0xf0, 0x00,
|
||||
0x00, 0x69, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
|
||||
0x0d, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
|
||||
0xa4, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x34,
|
||||
0x80, 0x00, 0x00, 0x04, 0x4c, 0x41, 0x4d, 0x45,
|
||||
0x33, 0x2e, 0x31, 0x30, 0x30, 0x55, 0x55, 0x55,
|
||||
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
||||
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
||||
0x55, 0xc0, 0x65, 0xf4, 0xa0, 0x31, 0x8f, 0xce,
|
||||
0x8d, 0x46, 0xfc, 0x8c, 0x73, 0xb9, 0x34, 0x3e,
|
||||
0xb5, 0x03, 0x39, 0xc0, 0x04, 0x01, 0x98, 0x44,
|
||||
0x38, 0xe0, 0x98, 0x10, 0x9b, 0xa8, 0x0f, 0xa8,
|
||||
}}, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
||||
|
||||
dec := format.CreateDecoder()
|
||||
byts, _, err := dec.Decode(pkts[0])
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, [][]byte{{
|
||||
0xff, 0xfb, 0x14, 0x64, 0x00, 0x0f, 0xf0, 0x00,
|
||||
0x00, 0x69, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
|
||||
0x0d, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
|
||||
0xa4, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x34,
|
||||
0x80, 0x00, 0x00, 0x04, 0x4c, 0x41, 0x4d, 0x45,
|
||||
0x33, 0x2e, 0x31, 0x30, 0x30, 0x55, 0x55, 0x55,
|
||||
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
||||
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
||||
0x55, 0xc0, 0x65, 0xf4, 0xa0, 0x31, 0x8f, 0xce,
|
||||
0x8d, 0x46, 0xfc, 0x8c, 0x73, 0xb9, 0x34, 0x3e,
|
||||
0xb5, 0x03, 0x39, 0xc0, 0x04, 0x01, 0x98, 0x44,
|
||||
0x38, 0xe0, 0x98, 0x10, 0x9b, 0xa8, 0x0f, 0xa8,
|
||||
}}, byts)
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ var ErrNonStartingPacketAndNoPrevious = errors.New(
|
||||
"received a non-starting fragment without any previous starting fragment")
|
||||
|
||||
// Decoder is a RTP/H264 decoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc6184
|
||||
type Decoder struct {
|
||||
// indicates the packetization mode.
|
||||
PacketizationMode int
|
||||
|
@@ -204,7 +204,7 @@ func TestDecodeUntilMarker(t *testing.T) {
|
||||
require.Equal(t, [][]byte{{0x01, 0x02}, {0x01, 0x02}}, nalus)
|
||||
}
|
||||
|
||||
func FuzzDecoderUnmarshal(f *testing.F) {
|
||||
func FuzzDecoder(f *testing.F) {
|
||||
d := &Decoder{}
|
||||
d.Init()
|
||||
|
||||
|
@@ -22,6 +22,7 @@ func randUint32() uint32 {
|
||||
}
|
||||
|
||||
// Encoder is a RTP/H264 encoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc6184
|
||||
type Encoder struct {
|
||||
// payload type of packets.
|
||||
PayloadType uint8
|
||||
|
@@ -22,6 +22,7 @@ var ErrNonStartingPacketAndNoPrevious = errors.New(
|
||||
"received a non-starting fragment without any previous starting fragment")
|
||||
|
||||
// Decoder is a RTP/H265 decoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc7798
|
||||
type Decoder struct {
|
||||
// indicates that NALUs have an additional field that specifies the decoding order.
|
||||
MaxDONDiff int
|
||||
|
@@ -56,7 +56,7 @@ func TestDecode(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzDecoderUnmarshal(f *testing.F) {
|
||||
func FuzzDecoder(f *testing.F) {
|
||||
d := &Decoder{}
|
||||
d.Init()
|
||||
|
||||
|
@@ -21,6 +21,7 @@ func randUint32() uint32 {
|
||||
}
|
||||
|
||||
// Encoder is a RTP/H265 encoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc7798
|
||||
type Encoder struct {
|
||||
// payload type of packets.
|
||||
PayloadType uint8
|
||||
|
@@ -10,6 +10,7 @@ import (
|
||||
)
|
||||
|
||||
// Decoder is a RTP/LPCM decoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc3190
|
||||
type Decoder struct {
|
||||
BitDepth int
|
||||
SampleRate int
|
||||
|
@@ -54,7 +54,7 @@ func TestDecode(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzDecoderUnmarshal(f *testing.F) {
|
||||
func FuzzDecoder(f *testing.F) {
|
||||
d := &Decoder{
|
||||
BitDepth: 24,
|
||||
SampleRate: 48000,
|
||||
|
@@ -21,6 +21,7 @@ func randUint32() uint32 {
|
||||
}
|
||||
|
||||
// Encoder is a RTP/LPCM encoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc3190
|
||||
type Encoder struct {
|
||||
// payload type of packets.
|
||||
PayloadType uint8
|
||||
|
@@ -95,6 +95,7 @@ var chmAcSymbols = []byte{ //nolint:dupl
|
||||
}
|
||||
|
||||
// Decoder is a RTP/M-JPEG decoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc2435
|
||||
type Decoder struct {
|
||||
timeDecoder *rtptime.Decoder
|
||||
firstPacketReceived bool
|
||||
|
@@ -30,7 +30,7 @@ func TestDecode(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzDecoderUnmarshal(f *testing.F) {
|
||||
func FuzzDecoder(f *testing.F) {
|
||||
d := &Decoder{}
|
||||
d.Init()
|
||||
|
||||
|
@@ -24,6 +24,7 @@ func randUint32() uint32 {
|
||||
}
|
||||
|
||||
// Encoder is a RTP/M-JPEG encoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc2435
|
||||
type Encoder struct {
|
||||
// SSRC of packets (optional).
|
||||
// It defaults to a random value.
|
||||
|
46
pkg/formats/rtpmpeg2audio/decoder.go
Normal file
46
pkg/formats/rtpmpeg2audio/decoder.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package rtpmpeg2audio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg2audio"
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v3/pkg/rtptime"
|
||||
)
|
||||
|
||||
// Decoder is a RTP/MPEG2-audio decoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc2250
|
||||
type Decoder struct {
|
||||
timeDecoder *rtptime.Decoder
|
||||
}
|
||||
|
||||
// Init initializes the decoder.
|
||||
func (d *Decoder) Init() {
|
||||
d.timeDecoder = rtptime.NewDecoder(90000)
|
||||
}
|
||||
|
||||
// Decode decodes frames from a RTP/MPEG2-audio packet.
|
||||
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
||||
if len(pkt.Payload) < 5 {
|
||||
return nil, 0, fmt.Errorf("payload is too short")
|
||||
}
|
||||
|
||||
mbz := uint16(pkt.Payload[0])<<8 | uint16(pkt.Payload[1])
|
||||
if mbz != 0 {
|
||||
return nil, 0, fmt.Errorf("invalid MBZ: %v", mbz)
|
||||
}
|
||||
|
||||
offset := uint16(pkt.Payload[2])<<8 | uint16(pkt.Payload[3])
|
||||
if offset != 0 {
|
||||
return nil, 0, fmt.Errorf("fragmented units are not supported")
|
||||
}
|
||||
|
||||
frames, err := mpeg2audio.SplitFrames(pkt.Payload[4:])
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return frames, d.timeDecoder.Decode(pkt.Timestamp), nil
|
||||
}
|
39
pkg/formats/rtpmpeg2audio/decoder_test.go
Normal file
39
pkg/formats/rtpmpeg2audio/decoder_test.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package rtpmpeg2audio
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
for _, ca := range cases {
|
||||
t.Run(ca.name, func(t *testing.T) {
|
||||
d := &Decoder{}
|
||||
d.Init()
|
||||
|
||||
frames, _, err := d.Decode(ca.pkt)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, ca.frames, frames)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzDecoder(f *testing.F) {
|
||||
d := &Decoder{}
|
||||
d.Init()
|
||||
|
||||
f.Fuzz(func(t *testing.T, b []byte) {
|
||||
d.Decode(&rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: 2,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17645,
|
||||
Timestamp: 2289527317,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: b,
|
||||
})
|
||||
})
|
||||
}
|
151
pkg/formats/rtpmpeg2audio/encoder.go
Normal file
151
pkg/formats/rtpmpeg2audio/encoder.go
Normal file
@@ -0,0 +1,151 @@
|
||||
package rtpmpeg2audio
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg2audio"
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v3/pkg/rtptime"
|
||||
)
|
||||
|
||||
const (
|
||||
rtpVersion = 2
|
||||
)
|
||||
|
||||
func randUint32() uint32 {
|
||||
var b [4]byte
|
||||
rand.Read(b[:])
|
||||
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3])
|
||||
}
|
||||
|
||||
func lenAggregated(frames [][]byte, frame []byte) int {
|
||||
l := 4 + len(frame)
|
||||
for _, fr := range frames {
|
||||
l += len(fr)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// Encoder is a RTP/MPEG2-audio encoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc2250
|
||||
type Encoder struct {
|
||||
// SSRC of packets (optional).
|
||||
// It defaults to a random value.
|
||||
SSRC *uint32
|
||||
|
||||
// initial sequence number of packets (optional).
|
||||
// It defaults to a random value.
|
||||
InitialSequenceNumber *uint16
|
||||
|
||||
// initial timestamp of packets (optional).
|
||||
// It defaults to a random value.
|
||||
InitialTimestamp *uint32
|
||||
|
||||
// maximum size of packet payloads (optional).
|
||||
// It defaults to 1460.
|
||||
PayloadMaxSize int
|
||||
|
||||
sequenceNumber uint16
|
||||
timeEncoder *rtptime.Encoder
|
||||
}
|
||||
|
||||
// Init initializes the encoder.
|
||||
func (e *Encoder) Init() {
|
||||
if e.SSRC == nil {
|
||||
v := randUint32()
|
||||
e.SSRC = &v
|
||||
}
|
||||
if e.InitialSequenceNumber == nil {
|
||||
v := uint16(randUint32())
|
||||
e.InitialSequenceNumber = &v
|
||||
}
|
||||
if e.InitialTimestamp == nil {
|
||||
v := randUint32()
|
||||
e.InitialTimestamp = &v
|
||||
}
|
||||
if e.PayloadMaxSize == 0 {
|
||||
e.PayloadMaxSize = 1460 // 1500 (UDP MTU) - 20 (IP header) - 8 (UDP header) - 12 (RTP header)
|
||||
}
|
||||
|
||||
e.sequenceNumber = *e.InitialSequenceNumber
|
||||
e.timeEncoder = rtptime.NewEncoder(90000, *e.InitialTimestamp)
|
||||
}
|
||||
|
||||
// Encode encodes frames into RTP/MPEG2-audio packets.
|
||||
func (e *Encoder) Encode(frames [][]byte, pts time.Duration) ([]*rtp.Packet, error) {
|
||||
var rets []*rtp.Packet
|
||||
var batch [][]byte
|
||||
|
||||
for _, frame := range frames {
|
||||
if len(frame) > e.PayloadMaxSize {
|
||||
return nil, fmt.Errorf("frame is too big")
|
||||
}
|
||||
|
||||
if lenAggregated(batch, frame) <= e.PayloadMaxSize {
|
||||
batch = append(batch, frame)
|
||||
} else {
|
||||
// write last batch
|
||||
if batch != nil {
|
||||
pkt, err := e.writeBatch(batch, pts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rets = append(rets, pkt)
|
||||
|
||||
for _, frame := range batch {
|
||||
var h mpeg2audio.FrameHeader
|
||||
err := h.Unmarshal(frame)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pts += time.Duration(h.SampleCount()) * time.Second / time.Duration(h.SampleRate)
|
||||
}
|
||||
}
|
||||
|
||||
// initialize new batch
|
||||
batch = [][]byte{frame}
|
||||
}
|
||||
}
|
||||
|
||||
// write last batch
|
||||
pkt, err := e.writeBatch(batch, pts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rets = append(rets, pkt)
|
||||
|
||||
return rets, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) writeBatch(frames [][]byte, pts time.Duration) (*rtp.Packet, error) {
|
||||
l := 4
|
||||
for _, frame := range frames {
|
||||
l += len(frame)
|
||||
}
|
||||
|
||||
payload := make([]byte, l)
|
||||
n := 4
|
||||
for _, frame := range frames {
|
||||
n += copy(payload[n:], frame)
|
||||
}
|
||||
|
||||
pkt := &rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: rtpVersion,
|
||||
PayloadType: 14,
|
||||
SequenceNumber: e.sequenceNumber,
|
||||
Timestamp: e.timeEncoder.Encode(pts),
|
||||
SSRC: *e.SSRC,
|
||||
Marker: true,
|
||||
},
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
e.sequenceNumber++
|
||||
|
||||
return pkt, nil
|
||||
}
|
163
pkg/formats/rtpmpeg2audio/encoder_test.go
Normal file
163
pkg/formats/rtpmpeg2audio/encoder_test.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package rtpmpeg2audio
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var cases = []struct {
|
||||
name string
|
||||
frames [][]byte
|
||||
pkt *rtp.Packet
|
||||
}{
|
||||
{
|
||||
"single",
|
||||
[][]byte{
|
||||
{
|
||||
0xff, 0xfb, 0x14, 0x64, 0x00, 0x0f, 0xf0, 0x00,
|
||||
0x00, 0x69, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
|
||||
0x0d, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
|
||||
0xa4, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x34,
|
||||
0x80, 0x00, 0x00, 0x04, 0x4c, 0x41, 0x4d, 0x45,
|
||||
0x33, 0x2e, 0x31, 0x30, 0x30, 0x55, 0x55, 0x55,
|
||||
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
||||
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
||||
0x55, 0xc0, 0x65, 0xf4, 0xa0, 0x31, 0x8f, 0xce,
|
||||
0x8d, 0x46, 0xfc, 0x8c, 0x73, 0xb9, 0x34, 0x3e,
|
||||
0xb5, 0x03, 0x39, 0xc0, 0x04, 0x01, 0x98, 0x44,
|
||||
0x38, 0xe0, 0x98, 0x10, 0x9b, 0xa8, 0x0f, 0xa8,
|
||||
},
|
||||
},
|
||||
&rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: 2,
|
||||
Marker: true,
|
||||
PayloadType: 14,
|
||||
SequenceNumber: 17645,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: []byte{
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xfb, 0x14, 0x64, 0x00, 0x0f, 0xf0, 0x00,
|
||||
0x00, 0x69, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
|
||||
0x0d, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
|
||||
0xa4, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x34,
|
||||
0x80, 0x00, 0x00, 0x04, 0x4c, 0x41, 0x4d, 0x45,
|
||||
0x33, 0x2e, 0x31, 0x30, 0x30, 0x55, 0x55, 0x55,
|
||||
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
||||
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
||||
0x55, 0xc0, 0x65, 0xf4, 0xa0, 0x31, 0x8f, 0xce,
|
||||
0x8d, 0x46, 0xfc, 0x8c, 0x73, 0xb9, 0x34, 0x3e,
|
||||
0xb5, 0x03, 0x39, 0xc0, 0x04, 0x01, 0x98, 0x44,
|
||||
0x38, 0xe0, 0x98, 0x10, 0x9b, 0xa8, 0x0f, 0xa8,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"aggregated",
|
||||
[][]byte{
|
||||
{
|
||||
0xff, 0xfb, 0x14, 0x64, 0x00, 0x0f, 0xf0, 0x00,
|
||||
0x00, 0x69, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
|
||||
0x0d, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
|
||||
0xa4, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x34,
|
||||
0x80, 0x00, 0x00, 0x04, 0x4c, 0x41, 0x4d, 0x45,
|
||||
0x33, 0x2e, 0x31, 0x30, 0x30, 0x55, 0x55, 0x55,
|
||||
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
||||
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
||||
0x55, 0xc0, 0x65, 0xf4, 0xa0, 0x31, 0x8f, 0xce,
|
||||
0x8d, 0x46, 0xfc, 0x8c, 0x73, 0xb9, 0x34, 0x3e,
|
||||
0xb5, 0x03, 0x39, 0xc0, 0x04, 0x01, 0x98, 0x44,
|
||||
0x38, 0xe0, 0x98, 0x10, 0x9b, 0xa8, 0x0f, 0xa8,
|
||||
},
|
||||
{
|
||||
0xff, 0xfb, 0x14, 0x64, 0x1e, 0x0f, 0xf0, 0x00,
|
||||
0x00, 0x69, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
|
||||
0x0d, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
|
||||
0xa4, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x34,
|
||||
0x80, 0x00, 0x00, 0x04, 0xe6, 0x50, 0x10, 0x01,
|
||||
0xca, 0x13, 0x94, 0x27, 0x4a, 0x4a, 0x64, 0xce,
|
||||
0x07, 0xc2, 0x2f, 0x59, 0xc0, 0x19, 0x04, 0x05,
|
||||
0xdf, 0xe7, 0xce, 0x65, 0x24, 0xed, 0xa4, 0xe3,
|
||||
0xff, 0xc9, 0x00, 0x00, 0x05, 0x5f, 0x4a, 0x04,
|
||||
0x0e, 0xc4, 0x24, 0xfd, 0x5e, 0x4a, 0x35, 0x72,
|
||||
0x21, 0x27, 0x31, 0x08, 0x47, 0x18, 0x00, 0x06,
|
||||
0xc4, 0x02, 0x72, 0x81, 0x89, 0xc3, 0xe4, 0x0a,
|
||||
},
|
||||
},
|
||||
&rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: 2,
|
||||
Marker: true,
|
||||
PayloadType: 14,
|
||||
SequenceNumber: 17645,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: []byte{
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xfb, 0x14, 0x64, 0x00, 0x0f, 0xf0, 0x00,
|
||||
0x00, 0x69, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
|
||||
0x0d, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
|
||||
0xa4, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x34,
|
||||
0x80, 0x00, 0x00, 0x04, 0x4c, 0x41, 0x4d, 0x45,
|
||||
0x33, 0x2e, 0x31, 0x30, 0x30, 0x55, 0x55, 0x55,
|
||||
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
||||
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
||||
0x55, 0xc0, 0x65, 0xf4, 0xa0, 0x31, 0x8f, 0xce,
|
||||
0x8d, 0x46, 0xfc, 0x8c, 0x73, 0xb9, 0x34, 0x3e,
|
||||
0xb5, 0x03, 0x39, 0xc0, 0x04, 0x01, 0x98, 0x44,
|
||||
0x38, 0xe0, 0x98, 0x10, 0x9b, 0xa8, 0x0f, 0xa8,
|
||||
0xff, 0xfb, 0x14, 0x64, 0x1e, 0x0f, 0xf0, 0x00,
|
||||
0x00, 0x69, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
|
||||
0x0d, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
|
||||
0xa4, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x34,
|
||||
0x80, 0x00, 0x00, 0x04, 0xe6, 0x50, 0x10, 0x01,
|
||||
0xca, 0x13, 0x94, 0x27, 0x4a, 0x4a, 0x64, 0xce,
|
||||
0x07, 0xc2, 0x2f, 0x59, 0xc0, 0x19, 0x04, 0x05,
|
||||
0xdf, 0xe7, 0xce, 0x65, 0x24, 0xed, 0xa4, 0xe3,
|
||||
0xff, 0xc9, 0x00, 0x00, 0x05, 0x5f, 0x4a, 0x04,
|
||||
0x0e, 0xc4, 0x24, 0xfd, 0x5e, 0x4a, 0x35, 0x72,
|
||||
0x21, 0x27, 0x31, 0x08, 0x47, 0x18, 0x00, 0x06,
|
||||
0xc4, 0x02, 0x72, 0x81, 0x89, 0xc3, 0xe4, 0x0a,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestEncode(t *testing.T) {
|
||||
for _, ca := range cases {
|
||||
t.Run(ca.name, func(t *testing.T) {
|
||||
e := &Encoder{
|
||||
SSRC: func() *uint32 {
|
||||
v := uint32(0x9dbb7812)
|
||||
return &v
|
||||
}(),
|
||||
InitialSequenceNumber: func() *uint16 {
|
||||
v := uint16(0x44ed)
|
||||
return &v
|
||||
}(),
|
||||
InitialTimestamp: func() *uint32 {
|
||||
v := uint32(0x88776655)
|
||||
return &v
|
||||
}(),
|
||||
}
|
||||
e.Init()
|
||||
|
||||
pkts, err := e.Encode(ca.frames, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, ca.pkt, pkts[0])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeRandomInitialState(t *testing.T) {
|
||||
e := &Encoder{}
|
||||
e.Init()
|
||||
require.NotEqual(t, nil, e.SSRC)
|
||||
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
||||
require.NotEqual(t, nil, e.InitialTimestamp)
|
||||
}
|
2
pkg/formats/rtpmpeg2audio/rtpmpeg2audio.go
Normal file
2
pkg/formats/rtpmpeg2audio/rtpmpeg2audio.go
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package rtpmpeg2audio contains a RTP/MPEG2-audio decoder and encoder.
|
||||
package rtpmpeg2audio
|
2
pkg/formats/rtpmpeg2audio/testdata/fuzz/FuzzDecoder/582528ddfad69eb5
vendored
Normal file
2
pkg/formats/rtpmpeg2audio/testdata/fuzz/FuzzDecoder/582528ddfad69eb5
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("0")
|
2
pkg/formats/rtpmpeg2audio/testdata/fuzz/FuzzDecoder/b456f27416f443a7
vendored
Normal file
2
pkg/formats/rtpmpeg2audio/testdata/fuzz/FuzzDecoder/b456f27416f443a7
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x00\x00\x00\x000")
|
2
pkg/formats/rtpmpeg2audio/testdata/fuzz/FuzzDecoder/d92bed38676468bc
vendored
Normal file
2
pkg/formats/rtpmpeg2audio/testdata/fuzz/FuzzDecoder/d92bed38676468bc
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x00\x00000")
|
2
pkg/formats/rtpmpeg2audio/testdata/fuzz/FuzzDecoder/f34630c44c11bb13
vendored
Normal file
2
pkg/formats/rtpmpeg2audio/testdata/fuzz/FuzzDecoder/f34630c44c11bb13
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("00000")
|
@@ -16,6 +16,7 @@ import (
|
||||
var ErrMorePacketsNeeded = errors.New("need more packets")
|
||||
|
||||
// Decoder is a RTP/MPEG4-audio decoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc3640
|
||||
type Decoder struct {
|
||||
// sample rate of input packets.
|
||||
SampleRate int
|
||||
@@ -73,7 +74,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
||||
var aus [][]byte
|
||||
|
||||
if len(d.fragments) == 0 {
|
||||
if pkt.Header.Marker {
|
||||
if pkt.Marker {
|
||||
// AUs
|
||||
aus = make([][]byte, len(dataLens))
|
||||
for i, dataLen := range dataLens {
|
||||
@@ -117,7 +118,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
||||
|
||||
d.fragments = append(d.fragments, payload[:dataLens[0]])
|
||||
|
||||
if !pkt.Header.Marker {
|
||||
if !pkt.Marker {
|
||||
return nil, 0, ErrMorePacketsNeeded
|
||||
}
|
||||
|
||||
|
@@ -109,7 +109,7 @@ func TestDecodeADTS(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzDecoderUnmarshal(f *testing.F) {
|
||||
func FuzzDecoder(f *testing.F) {
|
||||
d := &Decoder{
|
||||
SampleRate: 16000,
|
||||
SizeLength: 13,
|
||||
|
@@ -22,6 +22,7 @@ func randUint32() uint32 {
|
||||
}
|
||||
|
||||
// Encoder is a RTP/MPEG4-audio encoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc3640
|
||||
type Encoder struct {
|
||||
// payload type of packets.
|
||||
PayloadType uint8
|
||||
|
@@ -22,6 +22,7 @@ var ErrNonStartingPacketAndNoPrevious = errors.New(
|
||||
"received a non-starting fragment without any previous starting fragment")
|
||||
|
||||
// Decoder is a RTP/VP8 decoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc7741
|
||||
type Decoder struct {
|
||||
timeDecoder *rtptime.Decoder
|
||||
firstPacketReceived bool
|
||||
|
@@ -52,7 +52,7 @@ func TestDecode(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzDecoderUnmarshal(f *testing.F) {
|
||||
func FuzzDecoder(f *testing.F) {
|
||||
d := &Decoder{}
|
||||
d.Init()
|
||||
|
||||
|
@@ -22,6 +22,7 @@ func randUint32() uint32 {
|
||||
}
|
||||
|
||||
// Encoder is a RTP/VP8 encoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/rfc7741
|
||||
type Encoder struct {
|
||||
// payload type of packets.
|
||||
PayloadType uint8
|
||||
|
@@ -22,6 +22,7 @@ var ErrNonStartingPacketAndNoPrevious = errors.New(
|
||||
"received a non-starting fragment without any previous starting fragment")
|
||||
|
||||
// Decoder is a RTP/VP9 decoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/draft-ietf-payload-vp9-16
|
||||
type Decoder struct {
|
||||
timeDecoder *rtptime.Decoder
|
||||
firstPacketReceived bool
|
||||
|
@@ -52,7 +52,7 @@ func TestDecode(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzDecoderUnmarshal(f *testing.F) {
|
||||
func FuzzDecoder(f *testing.F) {
|
||||
d := &Decoder{}
|
||||
d.Init()
|
||||
|
||||
|
@@ -22,6 +22,7 @@ func randUint32() uint32 {
|
||||
}
|
||||
|
||||
// Encoder is a RTP/VP9 encoder.
|
||||
// Specification: https://datatracker.ietf.org/doc/html/draft-ietf-payload-vp9-16
|
||||
type Encoder struct {
|
||||
// payload type of packets.
|
||||
PayloadType uint8
|
||||
|
Reference in New Issue
Block a user