add RTP/MPEG-4 Video decoder and encoder (#266)

This commit is contained in:
Alessandro Ros
2023-05-01 21:19:23 +02:00
committed by GitHub
parent 00f66555e2
commit 6937b85704
31 changed files with 445 additions and 105 deletions

View File

@@ -44,8 +44,8 @@ Features:
* Utilities * Utilities
* Parse RTSP elements * Parse RTSP elements
* Encode/decode format-specific frames into/from RTP packets. The following formats are supported: * Encode/decode format-specific frames into/from RTP packets. The following formats are supported:
* Video: H264, H265, M-JPEG, VP8, VP9 * Video: H264, H265, M-JPEG, VP8, VP9, MPEG-4 Video (H263, DivX)
* Audio: G711 (PCMA, PCMU), G722, LPCM, MPEG-2 audio (MP3), MPEG-4 audio (AAC), Opus * Audio: G711 (PCMA, PCMU), G722, LPCM, MPEG-2 Audio (MP3), MPEG-4 Audio (AAC), Opus
## Table of contents ## Table of contents

View File

@@ -6,7 +6,7 @@ import (
"github.com/bluenviron/gortsplib/v3/pkg/formats/rtpmpeg2audio" "github.com/bluenviron/gortsplib/v3/pkg/formats/rtpmpeg2audio"
) )
// MPEG2Audio is a RTP format that uses a MPEG-1 or MPEG-2 audio codec. // MPEG2Audio is a RTP format that uses a MPEG-1 or MPEG-2 Audio codec.
// Specification: https://datatracker.ietf.org/doc/html/rfc2250 // Specification: https://datatracker.ietf.org/doc/html/rfc2250
type MPEG2Audio struct{} type MPEG2Audio struct{}

View File

@@ -15,7 +15,7 @@ import (
// MPEG4Audio is an alias for MPEG4AudioGeneric. // MPEG4Audio is an alias for MPEG4AudioGeneric.
type MPEG4Audio = MPEG4AudioGeneric type MPEG4Audio = MPEG4AudioGeneric
// MPEG4AudioGeneric is a RTP format that uses a MPEG-4 audio codec. // MPEG4AudioGeneric is a RTP format that uses a MPEG-4 Audio codec.
// Specification: https://datatracker.ietf.org/doc/html/rfc3640 // Specification: https://datatracker.ietf.org/doc/html/rfc3640
type MPEG4AudioGeneric struct { type MPEG4AudioGeneric struct {
PayloadTyp uint8 PayloadTyp uint8

View File

@@ -9,7 +9,7 @@ import (
"github.com/pion/rtp" "github.com/pion/rtp"
) )
// MPEG4AudioLATM is a RTP format that uses a MPEG-4 audio codec. // MPEG4AudioLATM is a RTP format that uses a MPEG-4 Audio codec.
// Specification: https://datatracker.ietf.org/doc/html/rfc6416#section-7.3 // Specification: https://datatracker.ietf.org/doc/html/rfc6416#section-7.3
type MPEG4AudioLATM struct { type MPEG4AudioLATM struct {
PayloadTyp uint8 PayloadTyp uint8

View File

@@ -7,12 +7,14 @@ import (
"strings" "strings"
"github.com/pion/rtp" "github.com/pion/rtp"
"github.com/bluenviron/gortsplib/v3/pkg/formats/rtpmpeg4video"
) )
// MPEG4Video is an alias for MPEG4VideoES. // MPEG4Video is an alias for MPEG4VideoES.
type MPEG4Video = MPEG4VideoES type MPEG4Video = MPEG4VideoES
// MPEG4VideoES is a RTP format that uses the video codec defined in MPEG-4 part 2. // MPEG4VideoES is a RTP format that uses a MPEG-4 Video codec.
// Specification: https://datatracker.ietf.org/doc/html/rfc6416#section-7.1 // Specification: https://datatracker.ietf.org/doc/html/rfc6416#section-7.1
type MPEG4VideoES struct { type MPEG4VideoES struct {
PayloadTyp uint8 PayloadTyp uint8
@@ -83,3 +85,19 @@ func (f *MPEG4VideoES) FMTP() map[string]string {
func (f *MPEG4VideoES) PTSEqualsDTS(*rtp.Packet) bool { func (f *MPEG4VideoES) PTSEqualsDTS(*rtp.Packet) bool {
return true return true
} }
// CreateDecoder creates a decoder able to decode the content of the format.
func (f *MPEG4VideoES) CreateDecoder() *rtpmpeg4video.Decoder {
d := &rtpmpeg4video.Decoder{}
d.Init()
return d
}
// CreateEncoder creates an encoder able to encode the content of the format.
func (f *MPEG4VideoES) CreateEncoder() *rtpmpeg4video.Encoder {
e := &rtpmpeg4video.Encoder{
PayloadType: f.PayloadTyp,
}
e.Init()
return e
}

View File

@@ -17,3 +17,19 @@ func TestMPEG4VideoESAttributes(t *testing.T) {
require.Equal(t, 90000, format.ClockRate()) require.Equal(t, 90000, format.ClockRate())
require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
} }
func TestMPEG4VideoESDecEncoder(t *testing.T) {
format := &MPEG4VideoES{
PayloadTyp: 96,
}
enc := format.CreateEncoder()
pkts, err := enc.Encode([]byte{0x01, 0x02, 0x03, 0x04}, 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{0x01, 0x02, 0x03, 0x04}, byts)
}

View File

@@ -52,7 +52,7 @@ func (d *Decoder) Init() {
d.timeDecoder = rtptime.NewDecoder(rtpClockRate) d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
} }
// Decode decodes NALUs from a RTP/H264 packet. // Decode decodes NALUs from a RTP packet.
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) { func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
if d.PacketizationMode >= 2 { if d.PacketizationMode >= 2 {
return nil, 0, fmt.Errorf("PacketizationMode >= 2 is not supported") return nil, 0, fmt.Errorf("PacketizationMode >= 2 is not supported")
@@ -167,7 +167,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
return nalus, d.timeDecoder.Decode(pkt.Timestamp), nil 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 packet and puts them in a buffer.
// When a packet has the marker flag (meaning that all the NALUs with the same PTS have // When a packet has the marker flag (meaning that all the NALUs with the same PTS have
// been received), the buffer is returned. // been received), the buffer is returned.
func (d *Decoder) DecodeUntilMarker(pkt *rtp.Packet) ([][]byte, time.Duration, error) { func (d *Decoder) DecodeUntilMarker(pkt *rtp.Packet) ([][]byte, time.Duration, error) {

View File

@@ -3,7 +3,6 @@ package rtph264
import ( import (
"bytes" "bytes"
"testing" "testing"
"time"
"github.com/pion/rtp" "github.com/pion/rtp"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -28,7 +27,6 @@ func mergeBytes(vals ...[]byte) []byte {
var cases = []struct { var cases = []struct {
name string name string
nalus [][]byte nalus [][]byte
pts time.Duration
pkts []*rtp.Packet pkts []*rtp.Packet
}{ }{
{ {
@@ -39,7 +37,6 @@ var cases = []struct {
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 8), bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 8),
), ),
}, },
25 * time.Millisecond,
[]*rtp.Packet{ []*rtp.Packet{
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -47,7 +44,7 @@ var cases = []struct {
Marker: true, Marker: true,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17645, SequenceNumber: 17645,
Timestamp: 2289528607, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: mergeBytes( Payload: mergeBytes(
@@ -65,7 +62,6 @@ var cases = []struct {
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 512), bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 512),
), ),
}, },
55 * time.Millisecond,
[]*rtp.Packet{ []*rtp.Packet{
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -73,7 +69,7 @@ var cases = []struct {
Marker: false, Marker: false,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17645, SequenceNumber: 17645,
Timestamp: 2289531307, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: mergeBytes( Payload: mergeBytes(
@@ -88,7 +84,7 @@ var cases = []struct {
Marker: false, Marker: false,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17646, SequenceNumber: 17646,
Timestamp: 2289531307, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: mergeBytes( Payload: mergeBytes(
@@ -104,7 +100,7 @@ var cases = []struct {
Marker: true, Marker: true,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17647, SequenceNumber: 17647,
Timestamp: 2289531307, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: mergeBytes( Payload: mergeBytes(
@@ -131,7 +127,6 @@ var cases = []struct {
0x00, 0x00, 0x6d, 0x40, 0x00, 0x00, 0x6d, 0x40,
}, },
}, },
0,
[]*rtp.Packet{ []*rtp.Packet{
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -177,7 +172,6 @@ var cases = []struct {
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 175), bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 175),
), ),
}, },
0,
[]*rtp.Packet{ []*rtp.Packet{
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -227,7 +221,6 @@ var cases = []struct {
{0x09, 0xF0}, {0x09, 0xF0},
{0x09, 0xF0}, {0x09, 0xF0},
}, },
0,
[]*rtp.Packet{ []*rtp.Packet{
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -297,7 +290,7 @@ func TestEncode(t *testing.T) {
} }
e.Init() e.Init()
pkts, err := e.Encode(ca.nalus, ca.pts) pkts, err := e.Encode(ca.nalus, 0)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, ca.pkts, pkts) require.Equal(t, ca.pkts, pkts)
}) })

View File

@@ -50,7 +50,7 @@ func (d *Decoder) Init() {
d.timeDecoder = rtptime.NewDecoder(rtpClockRate) d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
} }
// Decode decodes NALUs from a RTP/H265 packet. // Decode decodes NALUs from a RTP packet.
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) { func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
if d.MaxDONDiff != 0 { if d.MaxDONDiff != 0 {
return nil, 0, fmt.Errorf("MaxDONDiff != 0 is not supported (yet)") return nil, 0, fmt.Errorf("MaxDONDiff != 0 is not supported (yet)")
@@ -159,7 +159,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
return nalus, d.timeDecoder.Decode(pkt.Timestamp), nil return nalus, d.timeDecoder.Decode(pkt.Timestamp), nil
} }
// DecodeUntilMarker decodes NALUs from a RTP/H265 packet and puts them in a buffer. // DecodeUntilMarker decodes NALUs from a RTP packet and puts them in a buffer.
// When a packet has the marker flag (meaning that all the NALUs with the same PTS have // When a packet has the marker flag (meaning that all the NALUs with the same PTS have
// been received), the buffer is returned. // been received), the buffer is returned.
func (d *Decoder) DecodeUntilMarker(pkt *rtp.Packet) ([][]byte, time.Duration, error) { func (d *Decoder) DecodeUntilMarker(pkt *rtp.Packet) ([][]byte, time.Duration, error) {

View File

@@ -3,7 +3,6 @@ package rtph265
import ( import (
"bytes" "bytes"
"testing" "testing"
"time"
"github.com/pion/rtp" "github.com/pion/rtp"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -28,13 +27,11 @@ func mergeBytes(vals ...[]byte) []byte {
var cases = []struct { var cases = []struct {
name string name string
nalus [][]byte nalus [][]byte
pts time.Duration
pkts []*rtp.Packet pkts []*rtp.Packet
}{ }{
{ {
"single", "single",
[][]byte{{0x01, 0x02, 0x03, 0x04, 0x05}}, [][]byte{{0x01, 0x02, 0x03, 0x04, 0x05}},
25 * time.Millisecond,
[]*rtp.Packet{ []*rtp.Packet{
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -42,7 +39,7 @@ var cases = []struct {
Marker: true, Marker: true,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17645, SequenceNumber: 17645,
Timestamp: 2289528607, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: []byte{0x01, 0x02, 0x03, 0x04, 0x05}, Payload: []byte{0x01, 0x02, 0x03, 0x04, 0x05},
@@ -56,7 +53,6 @@ var cases = []struct {
{0x08, 0x08}, {0x08, 0x08},
{0x09, 0x09}, {0x09, 0x09},
}, },
0,
[]*rtp.Packet{ []*rtp.Packet{
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -79,7 +75,6 @@ var cases = []struct {
[][]byte{ [][]byte{
bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 1024), bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 1024),
}, },
55 * time.Millisecond,
[]*rtp.Packet{ []*rtp.Packet{
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -87,7 +82,7 @@ var cases = []struct {
Marker: false, Marker: false,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17645, SequenceNumber: 17645,
Timestamp: 2289531307, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: mergeBytes( Payload: mergeBytes(
@@ -102,7 +97,7 @@ var cases = []struct {
Marker: false, Marker: false,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17646, SequenceNumber: 17646,
Timestamp: 2289531307, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: mergeBytes( Payload: mergeBytes(
@@ -116,7 +111,7 @@ var cases = []struct {
Marker: true, Marker: true,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17647, SequenceNumber: 17647,
Timestamp: 2289531307, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: mergeBytes( Payload: mergeBytes(
@@ -148,7 +143,7 @@ func TestEncode(t *testing.T) {
} }
e.Init() e.Init()
pkts, err := e.Encode(ca.nalus, ca.pts) pkts, err := e.Encode(ca.nalus, 0)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, ca.pkts, pkts) require.Equal(t, ca.pkts, pkts)
}) })

View File

@@ -3,7 +3,6 @@ package rtplpcm
import ( import (
"bytes" "bytes"
"testing" "testing"
"time"
"github.com/pion/rtp" "github.com/pion/rtp"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -12,13 +11,11 @@ import (
var cases = []struct { var cases = []struct {
name string name string
samples []byte samples []byte
pts time.Duration
pkts []*rtp.Packet pkts []*rtp.Packet
}{ }{
{ {
"single", "single",
[]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06},
25 * time.Millisecond,
[]*rtp.Packet{ []*rtp.Packet{
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -26,7 +23,7 @@ var cases = []struct {
Marker: false, Marker: false,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17645, SequenceNumber: 17645,
Timestamp: 2289527557, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}, Payload: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06},
@@ -36,7 +33,6 @@ var cases = []struct {
{ {
"splitted", "splitted",
bytes.Repeat([]byte{0x41, 0x42, 0x43}, 680), bytes.Repeat([]byte{0x41, 0x42, 0x43}, 680),
25 * time.Millisecond,
[]*rtp.Packet{ []*rtp.Packet{
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -44,7 +40,7 @@ var cases = []struct {
Marker: false, Marker: false,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17645, SequenceNumber: 17645,
Timestamp: 2289527557, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: bytes.Repeat([]byte{0x41, 0x42, 0x43}, 486), Payload: bytes.Repeat([]byte{0x41, 0x42, 0x43}, 486),
@@ -55,7 +51,7 @@ var cases = []struct {
Marker: false, Marker: false,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17646, SequenceNumber: 17646,
Timestamp: 2289527800, Timestamp: 2289526600,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: bytes.Repeat([]byte{0x41, 0x42, 0x43}, 194), Payload: bytes.Repeat([]byte{0x41, 0x42, 0x43}, 194),
@@ -87,7 +83,7 @@ func TestEncode(t *testing.T) {
} }
e.Init() e.Init()
pkts, err := e.Encode(ca.samples, ca.pts) pkts, err := e.Encode(ca.samples, 0)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, ca.pkts, pkts) require.Equal(t, ca.pkts, pkts)
}) })

View File

@@ -119,7 +119,7 @@ func (d *Decoder) Init() {
d.timeDecoder = rtptime.NewDecoder(rtpClockRate) d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
} }
// Decode decodes an image from a RTP/M-JPEG packet. // Decode decodes an image from a RTP packet.
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) { func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
byts := pkt.Payload byts := pkt.Payload

View File

@@ -14,13 +14,12 @@ func TestDecode(t *testing.T) {
d.Init() d.Init()
for _, pkt := range ca.pkts { for _, pkt := range ca.pkts {
image, pts, err := d.Decode(pkt) image, _, err := d.Decode(pkt)
if err == ErrMorePacketsNeeded { if err == ErrMorePacketsNeeded {
continue continue
} }
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, ca.pts, pts)
require.Equal(t, ca.image, image) require.Equal(t, ca.image, image)
} }
}) })

View File

@@ -2,7 +2,6 @@ package rtpmjpeg
import ( import (
"testing" "testing"
"time"
"github.com/pion/rtp" "github.com/pion/rtp"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -11,7 +10,6 @@ import (
var cases = []struct { var cases = []struct {
name string name string
image []byte image []byte
pts time.Duration
pkts []*rtp.Packet pkts []*rtp.Packet
}{ }{
{ {
@@ -276,7 +274,6 @@ var cases = []struct {
0x59, 0x54, 0xda, 0xb6, 0xb3, 0x2e, 0xb3, 0x7f, 0x59, 0x54, 0xda, 0xb6, 0xb3, 0x2e, 0xb3, 0x7f,
0xe7, 0x7f, 0xaa, 0xff, 0xff, 0xd9, 0xe7, 0x7f, 0xaa, 0xff, 0xff, 0xd9,
}, },
0,
[]*rtp.Packet{ []*rtp.Packet{
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -527,7 +524,7 @@ func TestEncode(t *testing.T) {
} }
e.Init() e.Init()
pkts, err := e.Encode(ca.image, ca.pts) pkts, err := e.Encode(ca.image, 0)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, ca.pkts, pkts) require.Equal(t, ca.pkts, pkts)
}) })

View File

@@ -30,7 +30,7 @@ func joinFragments(fragments [][]byte, size int) []byte {
return ret return ret
} }
// Decoder is a RTP/MPEG2-audio decoder. // Decoder is a RTP/MPEG-2 Audio decoder.
// Specification: https://datatracker.ietf.org/doc/html/rfc2250 // Specification: https://datatracker.ietf.org/doc/html/rfc2250
type Decoder struct { type Decoder struct {
timeDecoder *rtptime.Decoder timeDecoder *rtptime.Decoder
@@ -45,7 +45,7 @@ func (d *Decoder) Init() {
d.timeDecoder = rtptime.NewDecoder(90000) d.timeDecoder = rtptime.NewDecoder(90000)
} }
// Decode decodes frames from a RTP/MPEG2-audio packet. // Decode decodes frames from a RTP packet.
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) { func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
if len(pkt.Payload) < 5 { if len(pkt.Payload) < 5 {
d.fragments = d.fragments[:0] // discard pending fragmented packets d.fragments = d.fragments[:0] // discard pending fragmented packets

View File

@@ -28,7 +28,7 @@ func lenAggregated(frames [][]byte, frame []byte) int {
return l return l
} }
// Encoder is a RTP/MPEG2-audio encoder. // Encoder is a RTP/MPEG-2 Audio encoder.
// Specification: https://datatracker.ietf.org/doc/html/rfc2250 // Specification: https://datatracker.ietf.org/doc/html/rfc2250
type Encoder struct { type Encoder struct {
// SSRC of packets (optional). // SSRC of packets (optional).
@@ -73,7 +73,7 @@ func (e *Encoder) Init() {
e.timeEncoder = rtptime.NewEncoder(90000, *e.InitialTimestamp) e.timeEncoder = rtptime.NewEncoder(90000, *e.InitialTimestamp)
} }
// Encode encodes frames into RTP/MPEG2-audio packets. // Encode encodes frames into RTP packets.
func (e *Encoder) Encode(frames [][]byte, pts time.Duration) ([]*rtp.Packet, error) { func (e *Encoder) Encode(frames [][]byte, pts time.Duration) ([]*rtp.Packet, error) {
var rets []*rtp.Packet var rets []*rtp.Packet
var batch [][]byte var batch [][]byte

View File

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

View File

@@ -24,7 +24,7 @@ func joinFragments(fragments [][]byte, size int) []byte {
return ret return ret
} }
// Decoder is a RTP/MPEG4-audio decoder. // Decoder is a RTP/MPEG-4 Audio decoder.
// Specification: https://datatracker.ietf.org/doc/html/rfc3640 // Specification: https://datatracker.ietf.org/doc/html/rfc3640
type Decoder struct { type Decoder struct {
// sample rate of input packets. // sample rate of input packets.
@@ -51,7 +51,7 @@ func (d *Decoder) Init() {
d.timeDecoder = rtptime.NewDecoder(d.SampleRate) d.timeDecoder = rtptime.NewDecoder(d.SampleRate)
} }
// Decode decodes AUs from a RTP/MPEG4-audio packet. // Decode decodes AUs from a RTP packet.
// It returns the AUs and the PTS of the first AU. // It returns the AUs and the PTS of the first AU.
// The PTS of subsequent AUs can be calculated by adding time.Second*mpeg4audio.SamplesPerAccessUnit/clockRate. // The PTS of subsequent AUs can be calculated by adding time.Second*mpeg4audio.SamplesPerAccessUnit/clockRate.
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) { func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {

View File

@@ -81,7 +81,7 @@ func (e *Encoder) Init() {
e.timeEncoder = rtptime.NewEncoder(e.SampleRate, *e.InitialTimestamp) e.timeEncoder = rtptime.NewEncoder(e.SampleRate, *e.InitialTimestamp)
} }
// Encode encodes AUs into RTP/MPEG4-audio packets. // Encode encodes AUs into RTP packets.
func (e *Encoder) Encode(aus [][]byte, pts time.Duration) ([]*rtp.Packet, error) { func (e *Encoder) Encode(aus [][]byte, pts time.Duration) ([]*rtp.Packet, error) {
var rets []*rtp.Packet var rets []*rtp.Packet
var batch [][]byte var batch [][]byte

View File

@@ -3,7 +3,6 @@ package rtpmpeg4audio
import ( import (
"bytes" "bytes"
"testing" "testing"
"time"
"github.com/pion/rtp" "github.com/pion/rtp"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -31,7 +30,6 @@ var cases = []struct {
indexLength int indexLength int
indexDeltaLength int indexDeltaLength int
aus [][]byte aus [][]byte
pts time.Duration
pkts []*rtp.Packet pkts []*rtp.Packet
}{ }{
{ {
@@ -87,7 +85,6 @@ var cases = []struct {
0xaf, 0x7, 0xaf, 0x7,
}, },
}, },
20 * time.Millisecond,
[]*rtp.Packet{ []*rtp.Packet{
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -95,7 +92,7 @@ var cases = []struct {
Marker: true, Marker: true,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17645, SequenceNumber: 17645,
Timestamp: 2289527317, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: []byte{ Payload: []byte{
@@ -158,7 +155,6 @@ var cases = []struct {
{0x04, 0x05, 0x06, 0x07}, {0x04, 0x05, 0x06, 0x07},
{0x08, 0x09, 0x0A, 0x0B}, {0x08, 0x09, 0x0A, 0x0B},
}, },
0,
[]*rtp.Packet{ []*rtp.Packet{
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -177,7 +173,7 @@ var cases = []struct {
}, },
}, },
}, },
{ { //nolint:dupl
"fragmented", "fragmented",
13, 13,
3, 3,
@@ -185,7 +181,6 @@ var cases = []struct {
[][]byte{ [][]byte{
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 512), bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 512),
}, },
0,
[]*rtp.Packet{ //nolint:dupl []*rtp.Packet{ //nolint:dupl
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -242,7 +237,6 @@ var cases = []struct {
{0x08, 0x09, 0x0A, 0x0B}, {0x08, 0x09, 0x0A, 0x0B},
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 256), bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 256),
}, },
0,
[]*rtp.Packet{ []*rtp.Packet{
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -297,7 +291,6 @@ var cases = []struct {
[][]byte{ [][]byte{
{0x01, 0x02, 0x03, 0x04}, {0x01, 0x02, 0x03, 0x04},
}, },
20 * time.Millisecond,
[]*rtp.Packet{ []*rtp.Packet{
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -305,7 +298,7 @@ var cases = []struct {
Marker: true, Marker: true,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17645, SequenceNumber: 17645,
Timestamp: 2289527317, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: []byte{ Payload: []byte{
@@ -323,7 +316,6 @@ var cases = []struct {
[][]byte{ [][]byte{
{0x01, 0x02, 0x03, 0x04}, {0x01, 0x02, 0x03, 0x04},
}, },
20 * time.Millisecond,
[]*rtp.Packet{ []*rtp.Packet{
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -331,7 +323,7 @@ var cases = []struct {
Marker: true, Marker: true,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17645, SequenceNumber: 17645,
Timestamp: 2289527317, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: []byte{ Payload: []byte{
@@ -350,7 +342,6 @@ var cases = []struct {
{0x01, 0x02, 0x03, 0x04}, {0x01, 0x02, 0x03, 0x04},
{0x05, 0x06, 0x07, 0x08}, {0x05, 0x06, 0x07, 0x08},
}, },
20 * time.Millisecond,
[]*rtp.Packet{ []*rtp.Packet{
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -358,7 +349,7 @@ var cases = []struct {
Marker: true, Marker: true,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17645, SequenceNumber: 17645,
Timestamp: 2289527317, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: []byte{ Payload: []byte{
@@ -377,7 +368,6 @@ var cases = []struct {
[][]byte{ [][]byte{
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 512), bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 512),
}, },
20 * time.Millisecond,
[]*rtp.Packet{ []*rtp.Packet{
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -385,7 +375,7 @@ var cases = []struct {
Marker: false, Marker: false,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17645, SequenceNumber: 17645,
Timestamp: 2289527317, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: mergeBytes( Payload: mergeBytes(
@@ -400,7 +390,7 @@ var cases = []struct {
Marker: false, Marker: false,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17646, SequenceNumber: 17646,
Timestamp: 2289527317, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: mergeBytes( Payload: mergeBytes(
@@ -415,7 +405,7 @@ var cases = []struct {
Marker: true, Marker: true,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17647, SequenceNumber: 17647,
Timestamp: 2289527317, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: mergeBytes( Payload: mergeBytes(
@@ -425,7 +415,7 @@ var cases = []struct {
}, },
}, },
}, },
{ { //nolint:dupl
"fragmented, custom sized, padded", "fragmented, custom sized, padded",
13, 13,
0, 0,
@@ -433,7 +423,6 @@ var cases = []struct {
[][]byte{ [][]byte{
bytes.Repeat([]byte{0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, 512), bytes.Repeat([]byte{0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, 512),
}, },
20 * time.Millisecond,
[]*rtp.Packet{ //nolint:dupl []*rtp.Packet{ //nolint:dupl
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -441,7 +430,7 @@ var cases = []struct {
Marker: false, Marker: false,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17645, SequenceNumber: 17645,
Timestamp: 2289527317, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: mergeBytes( Payload: mergeBytes(
@@ -455,7 +444,7 @@ var cases = []struct {
Marker: false, Marker: false,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17646, SequenceNumber: 17646,
Timestamp: 2289527317, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: mergeBytes( Payload: mergeBytes(
@@ -469,7 +458,7 @@ var cases = []struct {
Marker: true, Marker: true,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17647, SequenceNumber: 17647,
Timestamp: 2289527317, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: mergeBytes( Payload: mergeBytes(
@@ -505,7 +494,7 @@ func TestEncode(t *testing.T) {
} }
e.Init() e.Init()
pkts, err := e.Encode(ca.aus, ca.pts) pkts, err := e.Encode(ca.aus, 0)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, ca.pkts, pkts) require.Equal(t, ca.pkts, pkts)
}) })

View File

@@ -1,2 +1,2 @@
// Package rtpmpeg4audio contains a RTP/MPEG4-audio decoder and encoder. // Package rtpmpeg4audio contains a RTP/MPEG-4 Audio decoder and encoder.
package rtpmpeg4audio package rtpmpeg4audio

View File

@@ -0,0 +1,72 @@
package rtpmpeg4video
import (
"errors"
"fmt"
"time"
"github.com/pion/rtp"
"github.com/bluenviron/gortsplib/v3/pkg/rtptime"
)
// ErrMorePacketsNeeded is returned when more packets are needed.
var ErrMorePacketsNeeded = errors.New("need more packets")
const (
maxFrameSize = 1 * 1024 * 1024
)
func joinFragments(fragments [][]byte, size int) []byte {
ret := make([]byte, size)
n := 0
for _, p := range fragments {
n += copy(ret[n:], p)
}
return ret
}
// Decoder is a RTP/MPEG-4 Video decoder.
// Specification: https://datatracker.ietf.org/doc/html/rfc6416
type Decoder struct {
timeDecoder *rtptime.Decoder
fragments [][]byte
fragmentedSize int
}
// Init initializes the decoder.
func (d *Decoder) Init() {
d.timeDecoder = rtptime.NewDecoder(90000)
}
// Decode decodes a frame from a RTP packet.
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
var frame []byte
if len(d.fragments) == 0 {
if pkt.Marker {
frame = pkt.Payload
} else {
d.fragmentedSize = len(pkt.Payload)
d.fragments = append(d.fragments, pkt.Payload)
return nil, 0, ErrMorePacketsNeeded
}
} else {
d.fragmentedSize += len(pkt.Payload)
if d.fragmentedSize > maxFrameSize {
d.fragments = d.fragments[:0] // discard pending fragmented packets
return nil, 0, fmt.Errorf("frame size (%d) is too big (maximum is %d)", d.fragmentedSize, maxFrameSize)
}
d.fragments = append(d.fragments, pkt.Payload)
if !pkt.Marker {
return nil, 0, ErrMorePacketsNeeded
}
frame = joinFragments(d.fragments, d.fragmentedSize)
d.fragments = d.fragments[:0]
}
return frame, d.timeDecoder.Decode(pkt.Timestamp), nil
}

View File

@@ -0,0 +1,62 @@
package rtpmpeg4video
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()
var frame []byte
var err error
for _, pkt := range ca.pkts {
frame, _, err = d.Decode(pkt)
if err == ErrMorePacketsNeeded {
continue
}
require.NoError(t, err)
}
require.Equal(t, ca.frame, frame)
})
}
}
func FuzzDecoder(f *testing.F) {
f.Fuzz(func(t *testing.T, a []byte, am bool, b []byte, bm bool) {
d := &Decoder{}
d.Init()
d.Decode(&rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: am,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289527317,
SSRC: 0x9dbb7812,
},
Payload: a,
})
d.Decode(&rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: bm,
PayloadType: 96,
SequenceNumber: 17646,
Timestamp: 2289527317,
SSRC: 0x9dbb7812,
},
Payload: b,
})
})
}

View File

@@ -0,0 +1,111 @@
package rtpmpeg4video
import (
"crypto/rand"
"time"
"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])
}
// Encoder is a RTP/MPEG-4 Video encoder.
// Specification: https://datatracker.ietf.org/doc/html/rfc6416
type Encoder struct {
// payload type of packets.
PayloadType uint8
// 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 a frame into RTP packets.
func (e *Encoder) Encode(frame []byte, pts time.Duration) ([]*rtp.Packet, error) {
availPerPacket := e.PayloadMaxSize
le := len(frame)
packetCount := le / availPerPacket
lastPacketSize := le % availPerPacket
if lastPacketSize > 0 {
packetCount++
}
pos := 0
ret := make([]*rtp.Packet, packetCount)
encPTS := e.timeEncoder.Encode(pts)
for i := range ret {
var le int
if i != (packetCount - 1) {
le = availPerPacket
} else {
le = lastPacketSize
}
payload := make([]byte, le)
pos += copy(payload, frame[pos:])
ret[i] = &rtp.Packet{
Header: rtp.Header{
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: encPTS,
SSRC: *e.SSRC,
Marker: (i == len(ret)-1),
},
Payload: payload,
}
e.sequenceNumber++
}
return ret, nil
}

View File

@@ -0,0 +1,101 @@
package rtpmpeg4video
import (
"bytes"
"testing"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
)
var cases = []struct {
name string
frame []byte
pkts []*rtp.Packet
}{
{
"single",
[]byte{0x01, 0x02, 0x03, 0x04},
[]*rtp.Packet{
{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
0x01, 0x02, 0x03, 0x04,
},
},
},
},
{
"fragmented",
bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 150/4),
[]*rtp.Packet{
{
Header: rtp.Header{
Version: 2,
Marker: false,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 100/4),
},
{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 17646,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 50/4),
},
},
},
}
func TestEncode(t *testing.T) {
for _, ca := range cases {
t.Run(ca.name, func(t *testing.T) {
e := &Encoder{
PayloadType: 96,
SSRC: func() *uint32 {
v := uint32(0x9dbb7812)
return &v
}(),
InitialSequenceNumber: func() *uint16 {
v := uint16(0x44ed)
return &v
}(),
InitialTimestamp: func() *uint32 {
v := uint32(0x88776655)
return &v
}(),
PayloadMaxSize: 100,
}
e.Init()
pkts, err := e.Encode(ca.frame, 0)
require.NoError(t, err)
require.Equal(t, ca.pkts, pkts)
})
}
}
func TestEncodeRandomInitialState(t *testing.T) {
e := &Encoder{
PayloadType: 96,
}
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 rtpmpeg4video contains a RTP/MPEG-4 Video decoder and encoder.
package rtpmpeg4video

View File

@@ -2,7 +2,6 @@ package rtpsimpleaudio
import ( import (
"testing" "testing"
"time"
"github.com/pion/rtp" "github.com/pion/rtp"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -11,20 +10,18 @@ import (
var cases = []struct { var cases = []struct {
name string name string
frame []byte frame []byte
pts time.Duration
pkt *rtp.Packet pkt *rtp.Packet
}{ }{
{ {
"single", "single",
[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04},
25 * time.Millisecond,
&rtp.Packet{ &rtp.Packet{
Header: rtp.Header{ Header: rtp.Header{
Version: 2, Version: 2,
Marker: false, Marker: false,
PayloadType: 0, PayloadType: 0,
SequenceNumber: 17645, SequenceNumber: 17645,
Timestamp: 2289526557, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: []byte{0x01, 0x02, 0x03, 0x04}, Payload: []byte{0x01, 0x02, 0x03, 0x04},
@@ -53,7 +50,7 @@ func TestEncode(t *testing.T) {
} }
e.Init() e.Init()
pkt, err := e.Encode(ca.frame, ca.pts) pkt, err := e.Encode(ca.frame, 0)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, ca.pkt, pkt) require.Equal(t, ca.pkt, pkt)
}) })

View File

@@ -43,7 +43,7 @@ func (d *Decoder) Init() {
d.timeDecoder = rtptime.NewDecoder(rtpClockRate) d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
} }
// Decode decodes a VP8 frame from a RTP/VP8 packet. // Decode decodes a VP8 frame from a RTP packet.
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) { func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
var vpkt codecs.VP8Packet var vpkt codecs.VP8Packet
_, err := vpkt.Unmarshal(pkt.Payload) _, err := vpkt.Unmarshal(pkt.Payload)

View File

@@ -3,7 +3,6 @@ package rtpvp8
import ( import (
"bytes" "bytes"
"testing" "testing"
"time"
"github.com/pion/rtp" "github.com/pion/rtp"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -28,13 +27,11 @@ func mergeBytes(vals ...[]byte) []byte {
var cases = []struct { var cases = []struct {
name string name string
frame []byte frame []byte
pts time.Duration
pkts []*rtp.Packet pkts []*rtp.Packet
}{ }{
{ {
"single", "single",
[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04},
25 * time.Millisecond,
[]*rtp.Packet{ []*rtp.Packet{
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -42,7 +39,7 @@ var cases = []struct {
Marker: true, Marker: true,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17645, SequenceNumber: 17645,
Timestamp: 2289528607, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: []byte{0x10, 0x01, 0x02, 0x03, 0x04}, Payload: []byte{0x10, 0x01, 0x02, 0x03, 0x04},
@@ -52,7 +49,6 @@ var cases = []struct {
{ {
"fragmented", "fragmented",
bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 4096/4), bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 4096/4),
55 * time.Millisecond,
[]*rtp.Packet{ []*rtp.Packet{
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -60,7 +56,7 @@ var cases = []struct {
Marker: false, Marker: false,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17645, SequenceNumber: 17645,
Timestamp: 2289531307, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: mergeBytes([]byte{0x10}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 364), []byte{0x01, 0x02, 0x03}), Payload: mergeBytes([]byte{0x10}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 364), []byte{0x01, 0x02, 0x03}),
@@ -71,7 +67,7 @@ var cases = []struct {
Marker: false, Marker: false,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17646, SequenceNumber: 17646,
Timestamp: 2289531307, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: mergeBytes([]byte{0x00, 0x04}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 364), []byte{0x01, 0x02}), Payload: mergeBytes([]byte{0x00, 0x04}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 364), []byte{0x01, 0x02}),
@@ -82,7 +78,7 @@ var cases = []struct {
Marker: true, Marker: true,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17647, SequenceNumber: 17647,
Timestamp: 2289531307, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: mergeBytes([]byte{0x00, 0x03, 0x04}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 294)), Payload: mergeBytes([]byte{0x00, 0x03, 0x04}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 294)),
@@ -111,7 +107,7 @@ func TestEncode(t *testing.T) {
} }
e.Init() e.Init()
pkts, err := e.Encode(ca.frame, ca.pts) pkts, err := e.Encode(ca.frame, 0)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, ca.pkts, pkts) require.Equal(t, ca.pkts, pkts)
}) })

View File

@@ -43,7 +43,7 @@ func (d *Decoder) Init() {
d.timeDecoder = rtptime.NewDecoder(rtpClockRate) d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
} }
// Decode decodes a VP9 frame from a RTP/VP9 packet. // Decode decodes a VP9 frame from a RTP packet.
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) { func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
var vpkt codecs.VP9Packet var vpkt codecs.VP9Packet
_, err := vpkt.Unmarshal(pkt.Payload) _, err := vpkt.Unmarshal(pkt.Payload)

View File

@@ -3,7 +3,6 @@ package rtpvp9
import ( import (
"bytes" "bytes"
"testing" "testing"
"time"
"github.com/pion/rtp" "github.com/pion/rtp"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -28,13 +27,11 @@ func mergeBytes(vals ...[]byte) []byte {
var cases = []struct { var cases = []struct {
name string name string
frame []byte frame []byte
pts time.Duration
pkts []*rtp.Packet pkts []*rtp.Packet
}{ }{
{ {
"single", "single",
[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04},
25 * time.Millisecond,
[]*rtp.Packet{ []*rtp.Packet{
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -42,7 +39,7 @@ var cases = []struct {
Marker: true, Marker: true,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17645, SequenceNumber: 17645,
Timestamp: 2289528607, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: []byte{0x9c, 0xb5, 0xaf, 0x01, 0x02, 0x03, 0x04}, Payload: []byte{0x9c, 0xb5, 0xaf, 0x01, 0x02, 0x03, 0x04},
@@ -52,7 +49,6 @@ var cases = []struct {
{ {
"fragmented", "fragmented",
bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 4096/4), bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 4096/4),
55 * time.Millisecond,
[]*rtp.Packet{ []*rtp.Packet{
{ {
Header: rtp.Header{ Header: rtp.Header{
@@ -60,7 +56,7 @@ var cases = []struct {
Marker: false, Marker: false,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17645, SequenceNumber: 17645,
Timestamp: 2289531307, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: mergeBytes([]byte{0x98, 0xb5, 0xaf}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 364), []byte{0x01}), Payload: mergeBytes([]byte{0x98, 0xb5, 0xaf}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 364), []byte{0x01}),
@@ -71,7 +67,7 @@ var cases = []struct {
Marker: false, Marker: false,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17646, SequenceNumber: 17646,
Timestamp: 2289531307, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: mergeBytes([]byte{0x90, 0xb5, 0xaf, 0x02, 0x03, 0x04}, Payload: mergeBytes([]byte{0x90, 0xb5, 0xaf, 0x02, 0x03, 0x04},
@@ -83,7 +79,7 @@ var cases = []struct {
Marker: true, Marker: true,
PayloadType: 96, PayloadType: 96,
SequenceNumber: 17647, SequenceNumber: 17647,
Timestamp: 2289531307, Timestamp: 2289526357,
SSRC: 0x9dbb7812, SSRC: 0x9dbb7812,
}, },
Payload: mergeBytes([]byte{0x94, 0xb5, 0xaf, 0x03, 0x04}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 295)), Payload: mergeBytes([]byte{0x94, 0xb5, 0xaf, 0x03, 0x04}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 295)),
@@ -116,7 +112,7 @@ func TestEncode(t *testing.T) {
} }
e.Init() e.Init()
pkts, err := e.Encode(ca.frame, ca.pts) pkts, err := e.Encode(ca.frame, 0)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, ca.pkts, pkts) require.Equal(t, ca.pkts, pkts)
}) })