mirror of
https://github.com/aler9/gortsplib
synced 2025-10-06 23:52:46 +08:00
add RTP/MPEG-4 Video decoder and encoder (#266)
This commit is contained in:
@@ -44,8 +44,8 @@ Features:
|
||||
* Utilities
|
||||
* 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, MPEG-2 audio (MP3), MPEG-4 audio (AAC), Opus
|
||||
* 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
|
||||
|
||||
## Table of contents
|
||||
|
||||
|
@@ -6,7 +6,7 @@ import (
|
||||
"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
|
||||
type MPEG2Audio struct{}
|
||||
|
||||
|
@@ -15,7 +15,7 @@ import (
|
||||
// MPEG4Audio is an alias for 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
|
||||
type MPEG4AudioGeneric struct {
|
||||
PayloadTyp uint8
|
||||
|
@@ -9,7 +9,7 @@ import (
|
||||
"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
|
||||
type MPEG4AudioLATM struct {
|
||||
PayloadTyp uint8
|
||||
|
@@ -7,12 +7,14 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v3/pkg/formats/rtpmpeg4video"
|
||||
)
|
||||
|
||||
// MPEG4Video is an alias for 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
|
||||
type MPEG4VideoES struct {
|
||||
PayloadTyp uint8
|
||||
@@ -83,3 +85,19 @@ func (f *MPEG4VideoES) FMTP() map[string]string {
|
||||
func (f *MPEG4VideoES) PTSEqualsDTS(*rtp.Packet) bool {
|
||||
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
|
||||
}
|
||||
|
@@ -17,3 +17,19 @@ func TestMPEG4VideoESAttributes(t *testing.T) {
|
||||
require.Equal(t, 90000, format.ClockRate())
|
||||
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)
|
||||
}
|
||||
|
@@ -52,7 +52,7 @@ func (d *Decoder) Init() {
|
||||
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) {
|
||||
if d.PacketizationMode >= 2 {
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
// been received), the buffer is returned.
|
||||
func (d *Decoder) DecodeUntilMarker(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
||||
|
@@ -3,7 +3,6 @@ package rtph264
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -28,7 +27,6 @@ func mergeBytes(vals ...[]byte) []byte {
|
||||
var cases = []struct {
|
||||
name string
|
||||
nalus [][]byte
|
||||
pts time.Duration
|
||||
pkts []*rtp.Packet
|
||||
}{
|
||||
{
|
||||
@@ -39,7 +37,6 @@ var cases = []struct {
|
||||
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 8),
|
||||
),
|
||||
},
|
||||
25 * time.Millisecond,
|
||||
[]*rtp.Packet{
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -47,7 +44,7 @@ var cases = []struct {
|
||||
Marker: true,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17645,
|
||||
Timestamp: 2289528607,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: mergeBytes(
|
||||
@@ -65,7 +62,6 @@ var cases = []struct {
|
||||
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 512),
|
||||
),
|
||||
},
|
||||
55 * time.Millisecond,
|
||||
[]*rtp.Packet{
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -73,7 +69,7 @@ var cases = []struct {
|
||||
Marker: false,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17645,
|
||||
Timestamp: 2289531307,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: mergeBytes(
|
||||
@@ -88,7 +84,7 @@ var cases = []struct {
|
||||
Marker: false,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17646,
|
||||
Timestamp: 2289531307,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: mergeBytes(
|
||||
@@ -104,7 +100,7 @@ var cases = []struct {
|
||||
Marker: true,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17647,
|
||||
Timestamp: 2289531307,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: mergeBytes(
|
||||
@@ -131,7 +127,6 @@ var cases = []struct {
|
||||
0x00, 0x00, 0x6d, 0x40,
|
||||
},
|
||||
},
|
||||
0,
|
||||
[]*rtp.Packet{
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -177,7 +172,6 @@ var cases = []struct {
|
||||
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 175),
|
||||
),
|
||||
},
|
||||
0,
|
||||
[]*rtp.Packet{
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -227,7 +221,6 @@ var cases = []struct {
|
||||
{0x09, 0xF0},
|
||||
{0x09, 0xF0},
|
||||
},
|
||||
0,
|
||||
[]*rtp.Packet{
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -297,7 +290,7 @@ func TestEncode(t *testing.T) {
|
||||
}
|
||||
e.Init()
|
||||
|
||||
pkts, err := e.Encode(ca.nalus, ca.pts)
|
||||
pkts, err := e.Encode(ca.nalus, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, ca.pkts, pkts)
|
||||
})
|
||||
|
@@ -50,7 +50,7 @@ func (d *Decoder) Init() {
|
||||
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) {
|
||||
if d.MaxDONDiff != 0 {
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
// been received), the buffer is returned.
|
||||
func (d *Decoder) DecodeUntilMarker(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
||||
|
@@ -3,7 +3,6 @@ package rtph265
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -28,13 +27,11 @@ func mergeBytes(vals ...[]byte) []byte {
|
||||
var cases = []struct {
|
||||
name string
|
||||
nalus [][]byte
|
||||
pts time.Duration
|
||||
pkts []*rtp.Packet
|
||||
}{
|
||||
{
|
||||
"single",
|
||||
[][]byte{{0x01, 0x02, 0x03, 0x04, 0x05}},
|
||||
25 * time.Millisecond,
|
||||
[]*rtp.Packet{
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -42,7 +39,7 @@ var cases = []struct {
|
||||
Marker: true,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17645,
|
||||
Timestamp: 2289528607,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: []byte{0x01, 0x02, 0x03, 0x04, 0x05},
|
||||
@@ -56,7 +53,6 @@ var cases = []struct {
|
||||
{0x08, 0x08},
|
||||
{0x09, 0x09},
|
||||
},
|
||||
0,
|
||||
[]*rtp.Packet{
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -79,7 +75,6 @@ var cases = []struct {
|
||||
[][]byte{
|
||||
bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 1024),
|
||||
},
|
||||
55 * time.Millisecond,
|
||||
[]*rtp.Packet{
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -87,7 +82,7 @@ var cases = []struct {
|
||||
Marker: false,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17645,
|
||||
Timestamp: 2289531307,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: mergeBytes(
|
||||
@@ -102,7 +97,7 @@ var cases = []struct {
|
||||
Marker: false,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17646,
|
||||
Timestamp: 2289531307,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: mergeBytes(
|
||||
@@ -116,7 +111,7 @@ var cases = []struct {
|
||||
Marker: true,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17647,
|
||||
Timestamp: 2289531307,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: mergeBytes(
|
||||
@@ -148,7 +143,7 @@ func TestEncode(t *testing.T) {
|
||||
}
|
||||
e.Init()
|
||||
|
||||
pkts, err := e.Encode(ca.nalus, ca.pts)
|
||||
pkts, err := e.Encode(ca.nalus, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, ca.pkts, pkts)
|
||||
})
|
||||
|
@@ -3,7 +3,6 @@ package rtplpcm
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -12,13 +11,11 @@ import (
|
||||
var cases = []struct {
|
||||
name string
|
||||
samples []byte
|
||||
pts time.Duration
|
||||
pkts []*rtp.Packet
|
||||
}{
|
||||
{
|
||||
"single",
|
||||
[]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06},
|
||||
25 * time.Millisecond,
|
||||
[]*rtp.Packet{
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -26,7 +23,7 @@ var cases = []struct {
|
||||
Marker: false,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17645,
|
||||
Timestamp: 2289527557,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06},
|
||||
@@ -36,7 +33,6 @@ var cases = []struct {
|
||||
{
|
||||
"splitted",
|
||||
bytes.Repeat([]byte{0x41, 0x42, 0x43}, 680),
|
||||
25 * time.Millisecond,
|
||||
[]*rtp.Packet{
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -44,7 +40,7 @@ var cases = []struct {
|
||||
Marker: false,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17645,
|
||||
Timestamp: 2289527557,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: bytes.Repeat([]byte{0x41, 0x42, 0x43}, 486),
|
||||
@@ -55,7 +51,7 @@ var cases = []struct {
|
||||
Marker: false,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17646,
|
||||
Timestamp: 2289527800,
|
||||
Timestamp: 2289526600,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: bytes.Repeat([]byte{0x41, 0x42, 0x43}, 194),
|
||||
@@ -87,7 +83,7 @@ func TestEncode(t *testing.T) {
|
||||
}
|
||||
e.Init()
|
||||
|
||||
pkts, err := e.Encode(ca.samples, ca.pts)
|
||||
pkts, err := e.Encode(ca.samples, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, ca.pkts, pkts)
|
||||
})
|
||||
|
@@ -119,7 +119,7 @@ func (d *Decoder) Init() {
|
||||
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) {
|
||||
byts := pkt.Payload
|
||||
|
||||
|
@@ -14,13 +14,12 @@ func TestDecode(t *testing.T) {
|
||||
d.Init()
|
||||
|
||||
for _, pkt := range ca.pkts {
|
||||
image, pts, err := d.Decode(pkt)
|
||||
image, _, err := d.Decode(pkt)
|
||||
if err == ErrMorePacketsNeeded {
|
||||
continue
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, ca.pts, pts)
|
||||
require.Equal(t, ca.image, image)
|
||||
}
|
||||
})
|
||||
|
@@ -2,7 +2,6 @@ package rtpmjpeg
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -11,7 +10,6 @@ import (
|
||||
var cases = []struct {
|
||||
name string
|
||||
image []byte
|
||||
pts time.Duration
|
||||
pkts []*rtp.Packet
|
||||
}{
|
||||
{
|
||||
@@ -276,7 +274,6 @@ var cases = []struct {
|
||||
0x59, 0x54, 0xda, 0xb6, 0xb3, 0x2e, 0xb3, 0x7f,
|
||||
0xe7, 0x7f, 0xaa, 0xff, 0xff, 0xd9,
|
||||
},
|
||||
0,
|
||||
[]*rtp.Packet{
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -527,7 +524,7 @@ func TestEncode(t *testing.T) {
|
||||
}
|
||||
e.Init()
|
||||
|
||||
pkts, err := e.Encode(ca.image, ca.pts)
|
||||
pkts, err := e.Encode(ca.image, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, ca.pkts, pkts)
|
||||
})
|
||||
|
@@ -30,7 +30,7 @@ func joinFragments(fragments [][]byte, size int) []byte {
|
||||
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
|
||||
type Decoder struct {
|
||||
timeDecoder *rtptime.Decoder
|
||||
@@ -45,7 +45,7 @@ func (d *Decoder) Init() {
|
||||
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) {
|
||||
if len(pkt.Payload) < 5 {
|
||||
d.fragments = d.fragments[:0] // discard pending fragmented packets
|
||||
|
@@ -28,7 +28,7 @@ func lenAggregated(frames [][]byte, frame []byte) int {
|
||||
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
|
||||
type Encoder struct {
|
||||
// SSRC of packets (optional).
|
||||
@@ -73,7 +73,7 @@ func (e *Encoder) Init() {
|
||||
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) {
|
||||
var rets []*rtp.Packet
|
||||
var batch [][]byte
|
||||
|
@@ -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
|
||||
|
@@ -24,7 +24,7 @@ func joinFragments(fragments [][]byte, size int) []byte {
|
||||
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
|
||||
type Decoder struct {
|
||||
// sample rate of input packets.
|
||||
@@ -51,7 +51,7 @@ func (d *Decoder) Init() {
|
||||
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.
|
||||
// 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) {
|
||||
|
@@ -81,7 +81,7 @@ func (e *Encoder) Init() {
|
||||
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) {
|
||||
var rets []*rtp.Packet
|
||||
var batch [][]byte
|
||||
|
@@ -3,7 +3,6 @@ package rtpmpeg4audio
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -31,7 +30,6 @@ var cases = []struct {
|
||||
indexLength int
|
||||
indexDeltaLength int
|
||||
aus [][]byte
|
||||
pts time.Duration
|
||||
pkts []*rtp.Packet
|
||||
}{
|
||||
{
|
||||
@@ -87,7 +85,6 @@ var cases = []struct {
|
||||
0xaf, 0x7,
|
||||
},
|
||||
},
|
||||
20 * time.Millisecond,
|
||||
[]*rtp.Packet{
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -95,7 +92,7 @@ var cases = []struct {
|
||||
Marker: true,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17645,
|
||||
Timestamp: 2289527317,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: []byte{
|
||||
@@ -158,7 +155,6 @@ var cases = []struct {
|
||||
{0x04, 0x05, 0x06, 0x07},
|
||||
{0x08, 0x09, 0x0A, 0x0B},
|
||||
},
|
||||
0,
|
||||
[]*rtp.Packet{
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -177,7 +173,7 @@ var cases = []struct {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
{ //nolint:dupl
|
||||
"fragmented",
|
||||
13,
|
||||
3,
|
||||
@@ -185,7 +181,6 @@ var cases = []struct {
|
||||
[][]byte{
|
||||
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 512),
|
||||
},
|
||||
0,
|
||||
[]*rtp.Packet{ //nolint:dupl
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -242,7 +237,6 @@ var cases = []struct {
|
||||
{0x08, 0x09, 0x0A, 0x0B},
|
||||
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 256),
|
||||
},
|
||||
0,
|
||||
[]*rtp.Packet{
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -297,7 +291,6 @@ var cases = []struct {
|
||||
[][]byte{
|
||||
{0x01, 0x02, 0x03, 0x04},
|
||||
},
|
||||
20 * time.Millisecond,
|
||||
[]*rtp.Packet{
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -305,7 +298,7 @@ var cases = []struct {
|
||||
Marker: true,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17645,
|
||||
Timestamp: 2289527317,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: []byte{
|
||||
@@ -323,7 +316,6 @@ var cases = []struct {
|
||||
[][]byte{
|
||||
{0x01, 0x02, 0x03, 0x04},
|
||||
},
|
||||
20 * time.Millisecond,
|
||||
[]*rtp.Packet{
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -331,7 +323,7 @@ var cases = []struct {
|
||||
Marker: true,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17645,
|
||||
Timestamp: 2289527317,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: []byte{
|
||||
@@ -350,7 +342,6 @@ var cases = []struct {
|
||||
{0x01, 0x02, 0x03, 0x04},
|
||||
{0x05, 0x06, 0x07, 0x08},
|
||||
},
|
||||
20 * time.Millisecond,
|
||||
[]*rtp.Packet{
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -358,7 +349,7 @@ var cases = []struct {
|
||||
Marker: true,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17645,
|
||||
Timestamp: 2289527317,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: []byte{
|
||||
@@ -377,7 +368,6 @@ var cases = []struct {
|
||||
[][]byte{
|
||||
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 512),
|
||||
},
|
||||
20 * time.Millisecond,
|
||||
[]*rtp.Packet{
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -385,7 +375,7 @@ var cases = []struct {
|
||||
Marker: false,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17645,
|
||||
Timestamp: 2289527317,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: mergeBytes(
|
||||
@@ -400,7 +390,7 @@ var cases = []struct {
|
||||
Marker: false,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17646,
|
||||
Timestamp: 2289527317,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: mergeBytes(
|
||||
@@ -415,7 +405,7 @@ var cases = []struct {
|
||||
Marker: true,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17647,
|
||||
Timestamp: 2289527317,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: mergeBytes(
|
||||
@@ -425,7 +415,7 @@ var cases = []struct {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
{ //nolint:dupl
|
||||
"fragmented, custom sized, padded",
|
||||
13,
|
||||
0,
|
||||
@@ -433,7 +423,6 @@ var cases = []struct {
|
||||
[][]byte{
|
||||
bytes.Repeat([]byte{0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, 512),
|
||||
},
|
||||
20 * time.Millisecond,
|
||||
[]*rtp.Packet{ //nolint:dupl
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -441,7 +430,7 @@ var cases = []struct {
|
||||
Marker: false,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17645,
|
||||
Timestamp: 2289527317,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: mergeBytes(
|
||||
@@ -455,7 +444,7 @@ var cases = []struct {
|
||||
Marker: false,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17646,
|
||||
Timestamp: 2289527317,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: mergeBytes(
|
||||
@@ -469,7 +458,7 @@ var cases = []struct {
|
||||
Marker: true,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17647,
|
||||
Timestamp: 2289527317,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: mergeBytes(
|
||||
@@ -505,7 +494,7 @@ func TestEncode(t *testing.T) {
|
||||
}
|
||||
e.Init()
|
||||
|
||||
pkts, err := e.Encode(ca.aus, ca.pts)
|
||||
pkts, err := e.Encode(ca.aus, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, ca.pkts, pkts)
|
||||
})
|
||||
|
@@ -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
|
||||
|
72
pkg/formats/rtpmpeg4video/decoder.go
Normal file
72
pkg/formats/rtpmpeg4video/decoder.go
Normal 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
|
||||
}
|
62
pkg/formats/rtpmpeg4video/decoder_test.go
Normal file
62
pkg/formats/rtpmpeg4video/decoder_test.go
Normal 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,
|
||||
})
|
||||
})
|
||||
}
|
111
pkg/formats/rtpmpeg4video/encoder.go
Normal file
111
pkg/formats/rtpmpeg4video/encoder.go
Normal 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
|
||||
}
|
101
pkg/formats/rtpmpeg4video/encoder_test.go
Normal file
101
pkg/formats/rtpmpeg4video/encoder_test.go
Normal 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)
|
||||
}
|
2
pkg/formats/rtpmpeg4video/rtpmpeg4video.go
Normal file
2
pkg/formats/rtpmpeg4video/rtpmpeg4video.go
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package rtpmpeg4video contains a RTP/MPEG-4 Video decoder and encoder.
|
||||
package rtpmpeg4video
|
@@ -2,7 +2,6 @@ package rtpsimpleaudio
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -11,20 +10,18 @@ import (
|
||||
var cases = []struct {
|
||||
name string
|
||||
frame []byte
|
||||
pts time.Duration
|
||||
pkt *rtp.Packet
|
||||
}{
|
||||
{
|
||||
"single",
|
||||
[]byte{0x01, 0x02, 0x03, 0x04},
|
||||
25 * time.Millisecond,
|
||||
&rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: 2,
|
||||
Marker: false,
|
||||
PayloadType: 0,
|
||||
SequenceNumber: 17645,
|
||||
Timestamp: 2289526557,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: []byte{0x01, 0x02, 0x03, 0x04},
|
||||
@@ -53,7 +50,7 @@ func TestEncode(t *testing.T) {
|
||||
}
|
||||
e.Init()
|
||||
|
||||
pkt, err := e.Encode(ca.frame, ca.pts)
|
||||
pkt, err := e.Encode(ca.frame, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, ca.pkt, pkt)
|
||||
})
|
||||
|
@@ -43,7 +43,7 @@ func (d *Decoder) Init() {
|
||||
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) {
|
||||
var vpkt codecs.VP8Packet
|
||||
_, err := vpkt.Unmarshal(pkt.Payload)
|
||||
|
@@ -3,7 +3,6 @@ package rtpvp8
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -28,13 +27,11 @@ func mergeBytes(vals ...[]byte) []byte {
|
||||
var cases = []struct {
|
||||
name string
|
||||
frame []byte
|
||||
pts time.Duration
|
||||
pkts []*rtp.Packet
|
||||
}{
|
||||
{
|
||||
"single",
|
||||
[]byte{0x01, 0x02, 0x03, 0x04},
|
||||
25 * time.Millisecond,
|
||||
[]*rtp.Packet{
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -42,7 +39,7 @@ var cases = []struct {
|
||||
Marker: true,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17645,
|
||||
Timestamp: 2289528607,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: []byte{0x10, 0x01, 0x02, 0x03, 0x04},
|
||||
@@ -52,7 +49,6 @@ var cases = []struct {
|
||||
{
|
||||
"fragmented",
|
||||
bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 4096/4),
|
||||
55 * time.Millisecond,
|
||||
[]*rtp.Packet{
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -60,7 +56,7 @@ var cases = []struct {
|
||||
Marker: false,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17645,
|
||||
Timestamp: 2289531307,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
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,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17646,
|
||||
Timestamp: 2289531307,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
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,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17647,
|
||||
Timestamp: 2289531307,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
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()
|
||||
|
||||
pkts, err := e.Encode(ca.frame, ca.pts)
|
||||
pkts, err := e.Encode(ca.frame, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, ca.pkts, pkts)
|
||||
})
|
||||
|
@@ -43,7 +43,7 @@ func (d *Decoder) Init() {
|
||||
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) {
|
||||
var vpkt codecs.VP9Packet
|
||||
_, err := vpkt.Unmarshal(pkt.Payload)
|
||||
|
@@ -3,7 +3,6 @@ package rtpvp9
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -28,13 +27,11 @@ func mergeBytes(vals ...[]byte) []byte {
|
||||
var cases = []struct {
|
||||
name string
|
||||
frame []byte
|
||||
pts time.Duration
|
||||
pkts []*rtp.Packet
|
||||
}{
|
||||
{
|
||||
"single",
|
||||
[]byte{0x01, 0x02, 0x03, 0x04},
|
||||
25 * time.Millisecond,
|
||||
[]*rtp.Packet{
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -42,7 +39,7 @@ var cases = []struct {
|
||||
Marker: true,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17645,
|
||||
Timestamp: 2289528607,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: []byte{0x9c, 0xb5, 0xaf, 0x01, 0x02, 0x03, 0x04},
|
||||
@@ -52,7 +49,6 @@ var cases = []struct {
|
||||
{
|
||||
"fragmented",
|
||||
bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 4096/4),
|
||||
55 * time.Millisecond,
|
||||
[]*rtp.Packet{
|
||||
{
|
||||
Header: rtp.Header{
|
||||
@@ -60,7 +56,7 @@ var cases = []struct {
|
||||
Marker: false,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17645,
|
||||
Timestamp: 2289531307,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
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,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17646,
|
||||
Timestamp: 2289531307,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
Payload: mergeBytes([]byte{0x90, 0xb5, 0xaf, 0x02, 0x03, 0x04},
|
||||
@@ -83,7 +79,7 @@ var cases = []struct {
|
||||
Marker: true,
|
||||
PayloadType: 96,
|
||||
SequenceNumber: 17647,
|
||||
Timestamp: 2289531307,
|
||||
Timestamp: 2289526357,
|
||||
SSRC: 0x9dbb7812,
|
||||
},
|
||||
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()
|
||||
|
||||
pkts, err := e.Encode(ca.frame, ca.pts)
|
||||
pkts, err := e.Encode(ca.frame, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, ca.pkts, pkts)
|
||||
})
|
||||
|
Reference in New Issue
Block a user