26 changed files with 690 additions and 912 deletions
@ -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 @@ |
|||||||
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 @@ |
|||||||
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 @@ |
|||||||
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
// 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