26 changed files with 690 additions and 912 deletions
@ -1,13 +0,0 @@
@@ -1,13 +0,0 @@
|
||||
package core |
||||
|
||||
import ( |
||||
"time" |
||||
|
||||
"github.com/pion/rtp" |
||||
) |
||||
|
||||
// data is the data unit routed across the server.
|
||||
type data interface { |
||||
getRTPPackets() []*rtp.Packet |
||||
getNTP() time.Time |
||||
} |
||||
@ -1,34 +0,0 @@
@@ -1,34 +0,0 @@
|
||||
package core |
||||
|
||||
import ( |
||||
"github.com/aler9/gortsplib/v2/pkg/format" |
||||
) |
||||
|
||||
type formatProcessor interface { |
||||
process(data, bool) error |
||||
} |
||||
|
||||
func newFormatProcessor(forma format.Format, generateRTPPackets bool) (formatProcessor, error) { |
||||
switch forma := forma.(type) { |
||||
case *format.H264: |
||||
return newFormatProcessorH264(forma, generateRTPPackets) |
||||
|
||||
case *format.H265: |
||||
return newFormatProcessorH265(forma, generateRTPPackets) |
||||
|
||||
case *format.VP8: |
||||
return newFormatProcessorVP8(forma, generateRTPPackets) |
||||
|
||||
case *format.VP9: |
||||
return newFormatProcessorVP9(forma, generateRTPPackets) |
||||
|
||||
case *format.MPEG4Audio: |
||||
return newFormatProcessorMPEG4Audio(forma, generateRTPPackets) |
||||
|
||||
case *format.Opus: |
||||
return newFormatProcessorOpus(forma, generateRTPPackets) |
||||
|
||||
default: |
||||
return newFormatProcessorGeneric(forma, generateRTPPackets) |
||||
} |
||||
} |
||||
@ -1,91 +0,0 @@
@@ -1,91 +0,0 @@
|
||||
package core |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
"github.com/aler9/gortsplib/v2" |
||||
"github.com/aler9/gortsplib/v2/pkg/format" |
||||
"github.com/aler9/gortsplib/v2/pkg/media" |
||||
"github.com/aler9/gortsplib/v2/pkg/url" |
||||
"github.com/pion/rtp" |
||||
"github.com/stretchr/testify/require" |
||||
) |
||||
|
||||
func TestFormatProcessorRemovePadding(t *testing.T) { |
||||
p, ok := newInstance("rtmpDisable: yes\n" + |
||||
"hlsDisable: yes\n" + |
||||
"webrtcDisable: yes\n" + |
||||
"paths:\n" + |
||||
" all:\n") |
||||
require.Equal(t, true, ok) |
||||
defer p.Close() |
||||
|
||||
forma := &format.Generic{ |
||||
PayloadTyp: 96, |
||||
RTPMap: "private/90000", |
||||
} |
||||
forma.Init() |
||||
medi := &media.Media{ |
||||
Type: media.TypeApplication, |
||||
Formats: []format.Format{forma}, |
||||
} |
||||
source := gortsplib.Client{} |
||||
|
||||
err := source.StartRecording( |
||||
"rtsp://localhost:8554/stream", |
||||
media.Medias{medi}) |
||||
require.NoError(t, err) |
||||
defer source.Close() |
||||
|
||||
c := gortsplib.Client{} |
||||
|
||||
u, err := url.Parse("rtsp://127.0.0.1:8554/stream") |
||||
require.NoError(t, err) |
||||
|
||||
err = c.Start(u.Scheme, u.Host) |
||||
require.NoError(t, err) |
||||
defer c.Close() |
||||
|
||||
medias, baseURL, _, err := c.Describe(u) |
||||
require.NoError(t, err) |
||||
|
||||
err = c.SetupAll(medias, baseURL) |
||||
require.NoError(t, err) |
||||
|
||||
packetRecv := make(chan struct{}) |
||||
|
||||
c.OnPacketRTP(medias[0], medias[0].Formats[0], func(pkt *rtp.Packet) { |
||||
require.Equal(t, &rtp.Packet{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: true, |
||||
PayloadType: 96, |
||||
SequenceNumber: 123, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
CSRC: []uint32{}, |
||||
}, |
||||
Payload: []byte{0x01, 0x02, 0x03, 0x04}, |
||||
}, pkt) |
||||
close(packetRecv) |
||||
}) |
||||
|
||||
_, err = c.Play(nil) |
||||
require.NoError(t, err) |
||||
|
||||
source.WritePacketRTP(medi, &rtp.Packet{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: true, |
||||
PayloadType: 96, |
||||
SequenceNumber: 123, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
Padding: true, |
||||
}, |
||||
Payload: []byte{0x01, 0x02, 0x03, 0x04}, |
||||
PaddingSize: 20, |
||||
}) |
||||
|
||||
<-packetRecv |
||||
} |
||||
@ -1,450 +0,0 @@
@@ -1,450 +0,0 @@
|
||||
package core |
||||
|
||||
import ( |
||||
"bytes" |
||||
"net" |
||||
"testing" |
||||
|
||||
"github.com/aler9/gortsplib/v2" |
||||
"github.com/aler9/gortsplib/v2/pkg/base" |
||||
"github.com/aler9/gortsplib/v2/pkg/codecs/h265" |
||||
"github.com/aler9/gortsplib/v2/pkg/conn" |
||||
"github.com/aler9/gortsplib/v2/pkg/format" |
||||
"github.com/aler9/gortsplib/v2/pkg/headers" |
||||
"github.com/aler9/gortsplib/v2/pkg/media" |
||||
"github.com/aler9/gortsplib/v2/pkg/url" |
||||
"github.com/pion/rtp" |
||||
"github.com/stretchr/testify/require" |
||||
) |
||||
|
||||
func TestFormatProcessorDynamicParams(t *testing.T) { |
||||
checkTrack := func(t *testing.T, forma format.Format) { |
||||
c := gortsplib.Client{} |
||||
|
||||
u, err := url.Parse("rtsp://127.0.0.1:8554/stream") |
||||
require.NoError(t, err) |
||||
|
||||
err = c.Start(u.Scheme, u.Host) |
||||
require.NoError(t, err) |
||||
defer c.Close() |
||||
|
||||
medias, _, _, err := c.Describe(u) |
||||
require.NoError(t, err) |
||||
|
||||
forma1 := medias[0].Formats[0] |
||||
require.Equal(t, forma, forma1) |
||||
} |
||||
|
||||
for _, ca := range []string{"h264", "h265"} { |
||||
t.Run(ca, func(t *testing.T) { |
||||
p, ok := newInstance("rtmpDisable: yes\n" + |
||||
"hlsDisable: yes\n" + |
||||
"webrtcDisable: yes\n" + |
||||
"paths:\n" + |
||||
" all:\n") |
||||
require.Equal(t, true, ok) |
||||
defer p.Close() |
||||
|
||||
formah264 := &format.H264{ |
||||
PayloadTyp: 96, |
||||
PacketizationMode: 1, |
||||
} |
||||
|
||||
formah265 := &format.H265{ |
||||
PayloadTyp: 96, |
||||
} |
||||
|
||||
var forma format.Format |
||||
if ca == "h264" { |
||||
forma = formah264 |
||||
} else { |
||||
forma = formah265 |
||||
} |
||||
|
||||
medi := &media.Media{ |
||||
Type: media.TypeVideo, |
||||
Formats: []format.Format{forma}, |
||||
} |
||||
|
||||
source := gortsplib.Client{} |
||||
|
||||
err := source.StartRecording( |
||||
"rtsp://localhost:8554/stream", |
||||
media.Medias{medi}) |
||||
require.NoError(t, err) |
||||
defer source.Close() |
||||
|
||||
if ca == "h264" { |
||||
enc := formah264.CreateEncoder() |
||||
|
||||
pkts, err := enc.Encode([][]byte{{7, 1, 2, 3}}, 0) // SPS
|
||||
require.NoError(t, err) |
||||
source.WritePacketRTP(medi, pkts[0]) |
||||
|
||||
pkts, err = enc.Encode([][]byte{{8}}, 0) // PPS
|
||||
require.NoError(t, err) |
||||
source.WritePacketRTP(medi, pkts[0]) |
||||
|
||||
checkTrack(t, &format.H264{ |
||||
PayloadTyp: 96, |
||||
PacketizationMode: 1, |
||||
SPS: []byte{7, 1, 2, 3}, |
||||
PPS: []byte{8}, |
||||
}) |
||||
|
||||
pkts, err = enc.Encode([][]byte{{7, 4, 5, 6}}, 0) // SPS
|
||||
require.NoError(t, err) |
||||
source.WritePacketRTP(medi, pkts[0]) |
||||
|
||||
pkts, err = enc.Encode([][]byte{{8, 1}}, 0) // PPS
|
||||
require.NoError(t, err) |
||||
source.WritePacketRTP(medi, pkts[0]) |
||||
|
||||
checkTrack(t, &format.H264{ |
||||
PayloadTyp: 96, |
||||
PacketizationMode: 1, |
||||
SPS: []byte{7, 4, 5, 6}, |
||||
PPS: []byte{8, 1}, |
||||
}) |
||||
} else { |
||||
enc := formah265.CreateEncoder() |
||||
|
||||
pkts, err := enc.Encode([][]byte{{byte(h265.NALUType_VPS_NUT) << 1, 1, 2, 3}}, 0) |
||||
require.NoError(t, err) |
||||
source.WritePacketRTP(medi, pkts[0]) |
||||
|
||||
pkts, err = enc.Encode([][]byte{{byte(h265.NALUType_SPS_NUT) << 1, 4, 5, 6}}, 0) |
||||
require.NoError(t, err) |
||||
source.WritePacketRTP(medi, pkts[0]) |
||||
|
||||
pkts, err = enc.Encode([][]byte{{byte(h265.NALUType_PPS_NUT) << 1, 7, 8, 9}}, 0) |
||||
require.NoError(t, err) |
||||
source.WritePacketRTP(medi, pkts[0]) |
||||
|
||||
checkTrack(t, &format.H265{ |
||||
PayloadTyp: 96, |
||||
VPS: []byte{byte(h265.NALUType_VPS_NUT) << 1, 1, 2, 3}, |
||||
SPS: []byte{byte(h265.NALUType_SPS_NUT) << 1, 4, 5, 6}, |
||||
PPS: []byte{byte(h265.NALUType_PPS_NUT) << 1, 7, 8, 9}, |
||||
}) |
||||
|
||||
pkts, err = enc.Encode([][]byte{{byte(h265.NALUType_VPS_NUT) << 1, 10, 11, 12}}, 0) |
||||
require.NoError(t, err) |
||||
source.WritePacketRTP(medi, pkts[0]) |
||||
|
||||
pkts, err = enc.Encode([][]byte{{byte(h265.NALUType_SPS_NUT) << 1, 13, 14, 15}}, 0) |
||||
require.NoError(t, err) |
||||
source.WritePacketRTP(medi, pkts[0]) |
||||
|
||||
pkts, err = enc.Encode([][]byte{{byte(h265.NALUType_PPS_NUT) << 1, 16, 17, 18}}, 0) |
||||
require.NoError(t, err) |
||||
source.WritePacketRTP(medi, pkts[0]) |
||||
|
||||
checkTrack(t, &format.H265{ |
||||
PayloadTyp: 96, |
||||
VPS: []byte{byte(h265.NALUType_VPS_NUT) << 1, 10, 11, 12}, |
||||
SPS: []byte{byte(h265.NALUType_SPS_NUT) << 1, 13, 14, 15}, |
||||
PPS: []byte{byte(h265.NALUType_PPS_NUT) << 1, 16, 17, 18}, |
||||
}) |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
func TestFormatProcessorOversizedPackets(t *testing.T) { |
||||
for _, ca := range []string{"h264", "h265"} { |
||||
t.Run(ca, func(t *testing.T) { |
||||
p, ok := newInstance("rtmpDisable: yes\n" + |
||||
"hlsDisable: yes\n" + |
||||
"webrtcDisable: yes\n" + |
||||
"paths:\n" + |
||||
" all:\n") |
||||
require.Equal(t, true, ok) |
||||
defer p.Close() |
||||
|
||||
nconn, err := net.Dial("tcp", "localhost:8554") |
||||
require.NoError(t, err) |
||||
defer nconn.Close() |
||||
conn := conn.NewConn(nconn) |
||||
|
||||
var forma format.Format |
||||
if ca == "h264" { |
||||
forma = &format.H264{ |
||||
PayloadTyp: 96, |
||||
SPS: []byte{0x01, 0x02, 0x03, 0x04}, |
||||
PPS: []byte{0x01, 0x02, 0x03, 0x04}, |
||||
PacketizationMode: 1, |
||||
} |
||||
} else { |
||||
forma = &format.H265{ |
||||
PayloadTyp: 96, |
||||
VPS: []byte{byte(h265.NALUType_VPS_NUT) << 1, 10, 11, 12}, |
||||
SPS: []byte{byte(h265.NALUType_SPS_NUT) << 1, 13, 14, 15}, |
||||
PPS: []byte{byte(h265.NALUType_PPS_NUT) << 1, 16, 17, 18}, |
||||
} |
||||
} |
||||
|
||||
medi := &media.Media{ |
||||
Type: media.TypeVideo, |
||||
Formats: []format.Format{forma}, |
||||
} |
||||
medias := media.Medias{medi} |
||||
medias.SetControls() |
||||
|
||||
res, err := writeReqReadRes(conn, base.Request{ |
||||
Method: base.Announce, |
||||
URL: mustParseURL("rtsp://localhost:8554/stream"), |
||||
Header: base.Header{ |
||||
"CSeq": base.HeaderValue{"1"}, |
||||
"Content-Type": base.HeaderValue{"application/sdp"}, |
||||
}, |
||||
Body: mustMarshalSDP(medias.Marshal(false)), |
||||
}) |
||||
require.NoError(t, err) |
||||
require.Equal(t, base.StatusOK, res.StatusCode) |
||||
|
||||
var sx headers.Session |
||||
|
||||
inTH := &headers.Transport{ |
||||
Delivery: func() *headers.TransportDelivery { |
||||
v := headers.TransportDeliveryUnicast |
||||
return &v |
||||
}(), |
||||
Mode: func() *headers.TransportMode { |
||||
v := headers.TransportModeRecord |
||||
return &v |
||||
}(), |
||||
Protocol: headers.TransportProtocolTCP, |
||||
InterleavedIDs: &[2]int{0, 1}, |
||||
} |
||||
|
||||
res, err = writeReqReadRes(conn, base.Request{ |
||||
Method: base.Setup, |
||||
URL: mustParseURL("rtsp://localhost:8554/stream/" + medias[0].Control), |
||||
Header: base.Header{ |
||||
"CSeq": base.HeaderValue{"2"}, |
||||
"Transport": inTH.Marshal(), |
||||
}, |
||||
}) |
||||
require.NoError(t, err) |
||||
require.Equal(t, base.StatusOK, res.StatusCode) |
||||
|
||||
err = sx.Unmarshal(res.Header["Session"]) |
||||
require.NoError(t, err) |
||||
|
||||
res, err = writeReqReadRes(conn, base.Request{ |
||||
Method: base.Record, |
||||
URL: mustParseURL("rtsp://localhost:8554/stream"), |
||||
Header: base.Header{ |
||||
"CSeq": base.HeaderValue{"3"}, |
||||
"Session": base.HeaderValue{sx.Session}, |
||||
}, |
||||
}) |
||||
require.NoError(t, err) |
||||
require.Equal(t, base.StatusOK, res.StatusCode) |
||||
|
||||
c := gortsplib.Client{} |
||||
|
||||
u, err := url.Parse("rtsp://127.0.0.1:8554/stream") |
||||
require.NoError(t, err) |
||||
|
||||
err = c.Start(u.Scheme, u.Host) |
||||
require.NoError(t, err) |
||||
defer c.Close() |
||||
|
||||
medias, baseURL, _, err := c.Describe(u) |
||||
require.NoError(t, err) |
||||
|
||||
err = c.SetupAll(medias, baseURL) |
||||
require.NoError(t, err) |
||||
|
||||
packetRecv := make(chan struct{}) |
||||
i := 0 |
||||
|
||||
var expected []*rtp.Packet |
||||
|
||||
if ca == "h264" { |
||||
expected = []*rtp.Packet{ |
||||
{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: true, |
||||
PayloadType: 96, |
||||
SequenceNumber: 123, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
CSRC: []uint32{}, |
||||
}, |
||||
Payload: []byte{0x01, 0x02, 0x03, 0x04}, |
||||
}, |
||||
{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: false, |
||||
PayloadType: 96, |
||||
SequenceNumber: 124, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
CSRC: []uint32{}, |
||||
}, |
||||
Payload: append( |
||||
append([]byte{0x1c, 0x80}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 364)...), |
||||
[]byte{0x01, 0x02}..., |
||||
), |
||||
}, |
||||
{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: true, |
||||
PayloadType: 96, |
||||
SequenceNumber: 125, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
CSRC: []uint32{}, |
||||
}, |
||||
Payload: append( |
||||
[]byte{0x1c, 0x40, 0x03, 0x04}, |
||||
bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 136)..., |
||||
), |
||||
}, |
||||
} |
||||
} else { |
||||
expected = []*rtp.Packet{ |
||||
{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: true, |
||||
PayloadType: 96, |
||||
SequenceNumber: 123, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
CSRC: []uint32{}, |
||||
}, |
||||
Payload: []byte{0x01, 0x02, 0x03, 0x04}, |
||||
}, |
||||
{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: false, |
||||
PayloadType: 96, |
||||
SequenceNumber: 124, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
CSRC: []uint32{}, |
||||
}, |
||||
Payload: append( |
||||
append([]byte{0x63, 0x02, 0x80, 0x03, 0x04}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 363)...), |
||||
[]byte{0x01, 0x02, 0x03}..., |
||||
), |
||||
}, |
||||
{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: true, |
||||
PayloadType: 96, |
||||
SequenceNumber: 125, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
CSRC: []uint32{}, |
||||
}, |
||||
Payload: append( |
||||
[]byte{0x63, 0x02, 0x40, 0x04}, |
||||
bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 135)..., |
||||
), |
||||
}, |
||||
} |
||||
} |
||||
|
||||
c.OnPacketRTP(medias[0], medias[0].Formats[0], func(pkt *rtp.Packet) { |
||||
require.Equal(t, expected[i], pkt) |
||||
i++ |
||||
if i >= len(expected) { |
||||
close(packetRecv) |
||||
} |
||||
}) |
||||
|
||||
_, err = c.Play(nil) |
||||
require.NoError(t, err) |
||||
|
||||
var tosend []*rtp.Packet |
||||
if ca == "h264" { |
||||
tosend = []*rtp.Packet{ |
||||
{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: true, |
||||
PayloadType: 96, |
||||
SequenceNumber: 123, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
Padding: true, |
||||
}, |
||||
Payload: []byte{0x01, 0x02, 0x03, 0x04}, |
||||
}, |
||||
{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: false, |
||||
PayloadType: 96, |
||||
SequenceNumber: 124, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
Padding: true, |
||||
}, |
||||
Payload: append([]byte{0x1c, 0b10000000}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 2000/4)...), |
||||
}, |
||||
{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: true, |
||||
PayloadType: 96, |
||||
SequenceNumber: 125, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
Padding: true, |
||||
}, |
||||
Payload: []byte{0x1c, 0b01000000, 0x01, 0x02, 0x03, 0x04}, |
||||
}, |
||||
} |
||||
} else { |
||||
tosend = []*rtp.Packet{ |
||||
{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: true, |
||||
PayloadType: 96, |
||||
SequenceNumber: 123, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
Padding: true, |
||||
}, |
||||
Payload: []byte{0x01, 0x02, 0x03, 0x04}, |
||||
}, |
||||
{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: true, |
||||
PayloadType: 96, |
||||
SequenceNumber: 124, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
Padding: true, |
||||
}, |
||||
Payload: bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 2000/4), |
||||
}, |
||||
} |
||||
} |
||||
|
||||
for _, pkt := range tosend { |
||||
byts, _ := pkt.Marshal() |
||||
err = conn.WriteInterleavedFrame(&base.InterleavedFrame{ |
||||
Channel: 0, |
||||
Payload: byts, |
||||
}, make([]byte, 2048)) |
||||
require.NoError(t, err) |
||||
} |
||||
|
||||
<-packetRecv |
||||
}) |
||||
} |
||||
} |
||||
@ -0,0 +1,51 @@
@@ -0,0 +1,51 @@
|
||||
package formatprocessor |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
"github.com/aler9/gortsplib/v2/pkg/format" |
||||
"github.com/pion/rtp" |
||||
"github.com/stretchr/testify/require" |
||||
) |
||||
|
||||
func TestGenericRemovePadding(t *testing.T) { |
||||
forma := &format.Generic{ |
||||
PayloadTyp: 96, |
||||
RTPMap: "private/90000", |
||||
} |
||||
forma.Init() |
||||
|
||||
p, err := New(forma, false) |
||||
require.NoError(t, err) |
||||
|
||||
pkt := &rtp.Packet{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: true, |
||||
PayloadType: 96, |
||||
SequenceNumber: 123, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
Padding: true, |
||||
}, |
||||
Payload: []byte{0x01, 0x02, 0x03, 0x04}, |
||||
PaddingSize: 20, |
||||
} |
||||
|
||||
err = p.Process(&DataGeneric{ |
||||
RTPPackets: []*rtp.Packet{pkt}, |
||||
}, false) |
||||
require.NoError(t, err) |
||||
|
||||
require.Equal(t, &rtp.Packet{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: true, |
||||
PayloadType: 96, |
||||
SequenceNumber: 123, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
}, |
||||
Payload: []byte{0x01, 0x02, 0x03, 0x04}, |
||||
}, pkt) |
||||
} |
||||
@ -0,0 +1,140 @@
@@ -0,0 +1,140 @@
|
||||
package formatprocessor |
||||
|
||||
import ( |
||||
"bytes" |
||||
"testing" |
||||
|
||||
"github.com/aler9/gortsplib/v2/pkg/format" |
||||
"github.com/pion/rtp" |
||||
"github.com/stretchr/testify/require" |
||||
) |
||||
|
||||
func TestH264DynamicParams(t *testing.T) { |
||||
forma := &format.H264{ |
||||
PayloadTyp: 96, |
||||
PacketizationMode: 1, |
||||
} |
||||
|
||||
p, err := New(forma, false) |
||||
require.NoError(t, err) |
||||
|
||||
enc := forma.CreateEncoder() |
||||
|
||||
pkts, err := enc.Encode([][]byte{{7, 1, 2, 3}}, 0) // SPS
|
||||
require.NoError(t, err) |
||||
p.Process(&DataH264{RTPPackets: []*rtp.Packet{pkts[0]}}, false) |
||||
|
||||
pkts, err = enc.Encode([][]byte{{8}}, 0) // PPS
|
||||
require.NoError(t, err) |
||||
p.Process(&DataH264{RTPPackets: []*rtp.Packet{pkts[0]}}, false) |
||||
|
||||
pkts, err = enc.Encode([][]byte{{7, 4, 5, 6}}, 0) // SPS
|
||||
require.NoError(t, err) |
||||
p.Process(&DataH264{RTPPackets: []*rtp.Packet{pkts[0]}}, false) |
||||
|
||||
pkts, err = enc.Encode([][]byte{{8, 1}}, 0) // PPS
|
||||
require.NoError(t, err) |
||||
p.Process(&DataH264{RTPPackets: []*rtp.Packet{pkts[0]}}, false) |
||||
|
||||
require.Equal(t, []byte{7, 4, 5, 6}, forma.SPS) |
||||
require.Equal(t, []byte{8, 1}, forma.PPS) |
||||
} |
||||
|
||||
func TestH264OversizedPackets(t *testing.T) { |
||||
forma := &format.H264{ |
||||
PayloadTyp: 96, |
||||
SPS: []byte{0x01, 0x02, 0x03, 0x04}, |
||||
PPS: []byte{0x01, 0x02, 0x03, 0x04}, |
||||
PacketizationMode: 1, |
||||
} |
||||
|
||||
p, err := New(forma, false) |
||||
require.NoError(t, err) |
||||
|
||||
var out []*rtp.Packet |
||||
|
||||
for _, pkt := range []*rtp.Packet{ |
||||
{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: true, |
||||
PayloadType: 96, |
||||
SequenceNumber: 123, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
Padding: true, |
||||
}, |
||||
Payload: []byte{0x01, 0x02, 0x03, 0x04}, |
||||
}, |
||||
{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: false, |
||||
PayloadType: 96, |
||||
SequenceNumber: 124, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
Padding: true, |
||||
}, |
||||
Payload: append([]byte{0x1c, 0b10000000}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 2000/4)...), |
||||
}, |
||||
{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: true, |
||||
PayloadType: 96, |
||||
SequenceNumber: 125, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
Padding: true, |
||||
}, |
||||
Payload: []byte{0x1c, 0b01000000, 0x01, 0x02, 0x03, 0x04}, |
||||
}, |
||||
} { |
||||
data := &DataH264{RTPPackets: []*rtp.Packet{pkt}} |
||||
p.Process(data, false) |
||||
out = append(out, data.RTPPackets...) |
||||
} |
||||
|
||||
require.Equal(t, []*rtp.Packet{ |
||||
{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: true, |
||||
PayloadType: 96, |
||||
SequenceNumber: 123, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
}, |
||||
Payload: []byte{0x01, 0x02, 0x03, 0x04}, |
||||
}, |
||||
{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: false, |
||||
PayloadType: 96, |
||||
SequenceNumber: 124, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
}, |
||||
Payload: append( |
||||
append([]byte{0x1c, 0x80}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 364)...), |
||||
[]byte{0x01, 0x02}..., |
||||
), |
||||
}, |
||||
{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: true, |
||||
PayloadType: 96, |
||||
SequenceNumber: 125, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
}, |
||||
Payload: append( |
||||
[]byte{0x1c, 0x40, 0x03, 0x04}, |
||||
bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 136)..., |
||||
), |
||||
}, |
||||
}, out) |
||||
} |
||||
@ -0,0 +1,125 @@
@@ -0,0 +1,125 @@
|
||||
package formatprocessor |
||||
|
||||
import ( |
||||
"bytes" |
||||
"testing" |
||||
|
||||
"github.com/aler9/gortsplib/v2/pkg/codecs/h265" |
||||
"github.com/aler9/gortsplib/v2/pkg/format" |
||||
"github.com/pion/rtp" |
||||
"github.com/stretchr/testify/require" |
||||
) |
||||
|
||||
func TestH265DynamicParams(t *testing.T) { |
||||
forma := &format.H265{ |
||||
PayloadTyp: 96, |
||||
} |
||||
|
||||
p, err := New(forma, false) |
||||
require.NoError(t, err) |
||||
|
||||
enc := forma.CreateEncoder() |
||||
|
||||
pkts, err := enc.Encode([][]byte{{byte(h265.NALUType_VPS_NUT) << 1, 1, 2, 3}}, 0) |
||||
require.NoError(t, err) |
||||
p.Process(&DataH265{RTPPackets: []*rtp.Packet{pkts[0]}}, false) |
||||
|
||||
pkts, err = enc.Encode([][]byte{{byte(h265.NALUType_SPS_NUT) << 1, 4, 5, 6}}, 0) |
||||
require.NoError(t, err) |
||||
p.Process(&DataH265{RTPPackets: []*rtp.Packet{pkts[0]}}, false) |
||||
|
||||
pkts, err = enc.Encode([][]byte{{byte(h265.NALUType_PPS_NUT) << 1, 7, 8, 9}}, 0) |
||||
require.NoError(t, err) |
||||
p.Process(&DataH265{RTPPackets: []*rtp.Packet{pkts[0]}}, false) |
||||
|
||||
require.Equal(t, []byte{byte(h265.NALUType_VPS_NUT) << 1, 1, 2, 3}, forma.VPS) |
||||
require.Equal(t, []byte{byte(h265.NALUType_SPS_NUT) << 1, 4, 5, 6}, forma.SPS) |
||||
require.Equal(t, []byte{byte(h265.NALUType_PPS_NUT) << 1, 7, 8, 9}, forma.PPS) |
||||
} |
||||
|
||||
func TestH265OversizedPackets(t *testing.T) { |
||||
forma := &format.H265{ |
||||
PayloadTyp: 96, |
||||
VPS: []byte{byte(h265.NALUType_VPS_NUT) << 1, 10, 11, 12}, |
||||
SPS: []byte{byte(h265.NALUType_SPS_NUT) << 1, 13, 14, 15}, |
||||
PPS: []byte{byte(h265.NALUType_PPS_NUT) << 1, 16, 17, 18}, |
||||
} |
||||
|
||||
p, err := New(forma, false) |
||||
require.NoError(t, err) |
||||
|
||||
var out []*rtp.Packet |
||||
|
||||
for _, pkt := range []*rtp.Packet{ |
||||
{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: true, |
||||
PayloadType: 96, |
||||
SequenceNumber: 123, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
Padding: true, |
||||
}, |
||||
Payload: []byte{0x01, 0x02, 0x03, 0x04}, |
||||
}, |
||||
{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: true, |
||||
PayloadType: 96, |
||||
SequenceNumber: 124, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
Padding: true, |
||||
}, |
||||
Payload: bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 2000/4), |
||||
}, |
||||
} { |
||||
data := &DataH265{RTPPackets: []*rtp.Packet{pkt}} |
||||
p.Process(data, false) |
||||
out = append(out, data.RTPPackets...) |
||||
} |
||||
|
||||
require.Equal(t, []*rtp.Packet{ |
||||
{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: true, |
||||
PayloadType: 96, |
||||
SequenceNumber: 123, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
}, |
||||
Payload: []byte{0x01, 0x02, 0x03, 0x04}, |
||||
}, |
||||
{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: false, |
||||
PayloadType: 96, |
||||
SequenceNumber: 124, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
}, |
||||
Payload: append( |
||||
append([]byte{0x63, 0x02, 0x80, 0x03, 0x04}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 363)...), |
||||
[]byte{0x01, 0x02, 0x03}..., |
||||
), |
||||
}, |
||||
{ |
||||
Header: rtp.Header{ |
||||
Version: 2, |
||||
Marker: true, |
||||
PayloadType: 96, |
||||
SequenceNumber: 125, |
||||
Timestamp: 45343, |
||||
SSRC: 563423, |
||||
}, |
||||
Payload: append( |
||||
[]byte{0x63, 0x02, 0x40, 0x04}, |
||||
bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 135)..., |
||||
), |
||||
}, |
||||
}, out) |
||||
} |
||||
@ -0,0 +1,48 @@
@@ -0,0 +1,48 @@
|
||||
// Package formatprocessor contains code to cleanup and normalize streams.
|
||||
package formatprocessor |
||||
|
||||
import ( |
||||
"time" |
||||
|
||||
"github.com/pion/rtp" |
||||
|
||||
"github.com/aler9/gortsplib/v2/pkg/format" |
||||
) |
||||
|
||||
// Data is the elementary data unit routed across the server.
|
||||
type Data interface { |
||||
GetRTPPackets() []*rtp.Packet |
||||
GetNTP() time.Time |
||||
} |
||||
|
||||
// Processor allows to cleanup and normalize streams.
|
||||
type Processor interface { |
||||
// cleanups and normalizes a data unit.
|
||||
Process(Data, bool) error |
||||
} |
||||
|
||||
// New allocates a Processor.
|
||||
func New(forma format.Format, generateRTPPackets bool) (Processor, error) { |
||||
switch forma := forma.(type) { |
||||
case *format.H264: |
||||
return newH264(forma, generateRTPPackets) |
||||
|
||||
case *format.H265: |
||||
return newH265(forma, generateRTPPackets) |
||||
|
||||
case *format.VP8: |
||||
return newVP8(forma, generateRTPPackets) |
||||
|
||||
case *format.VP9: |
||||
return newVP9(forma, generateRTPPackets) |
||||
|
||||
case *format.MPEG4Audio: |
||||
return newMPEG4Audio(forma, generateRTPPackets) |
||||
|
||||
case *format.Opus: |
||||
return newOpus(forma, generateRTPPackets) |
||||
|
||||
default: |
||||
return newGeneric(forma, generateRTPPackets) |
||||
} |
||||
} |
||||
Loading…
Reference in new issue