add RTP/MPEG-2 audio decoder and encoder (#257)

This commit is contained in:
Alessandro Ros
2023-04-25 17:49:23 +02:00
committed by GitHub
parent 2dc8838a6d
commit 84fcc018e0
74 changed files with 494 additions and 15 deletions

View File

@@ -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
View File

@@ -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
View File

@@ -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=

View File

@@ -1,4 +1,4 @@
package formats
package formats //nolint:dupl
import (
"github.com/pion/rtp"

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -56,7 +56,7 @@ func TestDecode(t *testing.T) {
}
}
func FuzzDecoderUnmarshal(f *testing.F) {
func FuzzDecoder(f *testing.F) {
d := &Decoder{}
d.Init()

View File

@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

@@ -30,7 +30,7 @@ func TestDecode(t *testing.T) {
}
}
func FuzzDecoderUnmarshal(f *testing.F) {
func FuzzDecoder(f *testing.F) {
d := &Decoder{}
d.Init()

View File

@@ -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.

View 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
}

View 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,
})
})
}

View 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
}

View 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)
}

View File

@@ -0,0 +1,2 @@
// Package rtpmpeg2audio contains a RTP/MPEG2-audio decoder and encoder.
package rtpmpeg2audio

View File

@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("0")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("\x00\x00\x00\x000")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("\x00\x00000")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("00000")

View File

@@ -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
}

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

@@ -52,7 +52,7 @@ func TestDecode(t *testing.T) {
}
}
func FuzzDecoderUnmarshal(f *testing.F) {
func FuzzDecoder(f *testing.F) {
d := &Decoder{}
d.Init()

View File

@@ -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

View File

@@ -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

View File

@@ -52,7 +52,7 @@ func TestDecode(t *testing.T) {
}
}
func FuzzDecoderUnmarshal(f *testing.F) {
func FuzzDecoder(f *testing.F) {
d := &Decoder{}
d.Init()

View File

@@ -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