move format processors into dedicated folder
This commit is contained in:
@@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
+26
-25
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/aler9/rtsp-simple-server/internal/conf"
|
||||
"github.com/aler9/rtsp-simple-server/internal/formatprocessor"
|
||||
"github.com/aler9/rtsp-simple-server/internal/hls"
|
||||
"github.com/aler9/rtsp-simple-server/internal/logger"
|
||||
)
|
||||
@@ -321,21 +322,21 @@ func (m *hlsMuxer) setupVideoMedia(stream *stream) (*media.Media, format.Format)
|
||||
videoStartPTSFilled := false
|
||||
var videoStartPTS time.Duration
|
||||
|
||||
stream.readerAdd(m, videoMedia, videoFormatH265, func(dat data) {
|
||||
stream.readerAdd(m, videoMedia, videoFormatH265, func(dat formatprocessor.Data) {
|
||||
m.ringBuffer.Push(func() error {
|
||||
tdata := dat.(*dataH265)
|
||||
tdata := dat.(*formatprocessor.DataH265)
|
||||
|
||||
if tdata.au == nil {
|
||||
if tdata.AU == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !videoStartPTSFilled {
|
||||
videoStartPTSFilled = true
|
||||
videoStartPTS = tdata.pts
|
||||
videoStartPTS = tdata.PTS
|
||||
}
|
||||
pts := tdata.pts - videoStartPTS
|
||||
pts := tdata.PTS - videoStartPTS
|
||||
|
||||
err := m.muxer.WriteH26x(tdata.ntp, pts, tdata.au)
|
||||
err := m.muxer.WriteH26x(tdata.NTP, pts, tdata.AU)
|
||||
if err != nil {
|
||||
return fmt.Errorf("muxer error: %v", err)
|
||||
}
|
||||
@@ -354,21 +355,21 @@ func (m *hlsMuxer) setupVideoMedia(stream *stream) (*media.Media, format.Format)
|
||||
videoStartPTSFilled := false
|
||||
var videoStartPTS time.Duration
|
||||
|
||||
stream.readerAdd(m, videoMedia, videoFormatH264, func(dat data) {
|
||||
stream.readerAdd(m, videoMedia, videoFormatH264, func(dat formatprocessor.Data) {
|
||||
m.ringBuffer.Push(func() error {
|
||||
tdata := dat.(*dataH264)
|
||||
tdata := dat.(*formatprocessor.DataH264)
|
||||
|
||||
if tdata.au == nil {
|
||||
if tdata.AU == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !videoStartPTSFilled {
|
||||
videoStartPTSFilled = true
|
||||
videoStartPTS = tdata.pts
|
||||
videoStartPTS = tdata.PTS
|
||||
}
|
||||
pts := tdata.pts - videoStartPTS
|
||||
pts := tdata.PTS - videoStartPTS
|
||||
|
||||
err := m.muxer.WriteH26x(tdata.ntp, pts, tdata.au)
|
||||
err := m.muxer.WriteH26x(tdata.NTP, pts, tdata.AU)
|
||||
if err != nil {
|
||||
return fmt.Errorf("muxer error: %v", err)
|
||||
}
|
||||
@@ -391,23 +392,23 @@ func (m *hlsMuxer) setupAudioMedia(stream *stream) (*media.Media, format.Format)
|
||||
audioStartPTSFilled := false
|
||||
var audioStartPTS time.Duration
|
||||
|
||||
stream.readerAdd(m, audioMedia, audioFormatMPEG4Audio, func(dat data) {
|
||||
stream.readerAdd(m, audioMedia, audioFormatMPEG4Audio, func(dat formatprocessor.Data) {
|
||||
m.ringBuffer.Push(func() error {
|
||||
tdata := dat.(*dataMPEG4Audio)
|
||||
tdata := dat.(*formatprocessor.DataMPEG4Audio)
|
||||
|
||||
if tdata.aus == nil {
|
||||
if tdata.AUs == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !audioStartPTSFilled {
|
||||
audioStartPTSFilled = true
|
||||
audioStartPTS = tdata.pts
|
||||
audioStartPTS = tdata.PTS
|
||||
}
|
||||
pts := tdata.pts - audioStartPTS
|
||||
pts := tdata.PTS - audioStartPTS
|
||||
|
||||
for i, au := range tdata.aus {
|
||||
for i, au := range tdata.AUs {
|
||||
err := m.muxer.WriteAudio(
|
||||
tdata.ntp,
|
||||
tdata.NTP,
|
||||
pts+time.Duration(i)*mpeg4audio.SamplesPerAccessUnit*
|
||||
time.Second/time.Duration(audioFormatMPEG4Audio.ClockRate()),
|
||||
au)
|
||||
@@ -430,20 +431,20 @@ func (m *hlsMuxer) setupAudioMedia(stream *stream) (*media.Media, format.Format)
|
||||
audioStartPTSFilled := false
|
||||
var audioStartPTS time.Duration
|
||||
|
||||
stream.readerAdd(m, audioMedia, audioFormatOpus, func(dat data) {
|
||||
stream.readerAdd(m, audioMedia, audioFormatOpus, func(dat formatprocessor.Data) {
|
||||
m.ringBuffer.Push(func() error {
|
||||
tdata := dat.(*dataOpus)
|
||||
tdata := dat.(*formatprocessor.DataOpus)
|
||||
|
||||
if !audioStartPTSFilled {
|
||||
audioStartPTSFilled = true
|
||||
audioStartPTS = tdata.pts
|
||||
audioStartPTS = tdata.PTS
|
||||
}
|
||||
pts := tdata.pts - audioStartPTS
|
||||
pts := tdata.PTS - audioStartPTS
|
||||
|
||||
err := m.muxer.WriteAudio(
|
||||
tdata.ntp,
|
||||
tdata.NTP,
|
||||
pts,
|
||||
tdata.frame)
|
||||
tdata.Frame)
|
||||
if err != nil {
|
||||
return fmt.Errorf("muxer error: %v", err)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/aler9/gortsplib/v2/pkg/format"
|
||||
"github.com/aler9/gortsplib/v2/pkg/media"
|
||||
|
||||
"github.com/aler9/rtsp-simple-server/internal/formatprocessor"
|
||||
"github.com/aler9/rtsp-simple-server/internal/hls"
|
||||
"github.com/aler9/rtsp-simple-server/internal/logger"
|
||||
)
|
||||
@@ -85,10 +86,10 @@ func (s *hlsSource) run(ctx context.Context) error {
|
||||
}
|
||||
|
||||
onVideoData := func(pts time.Duration, au [][]byte) {
|
||||
err := stream.writeData(videoMedia, videoMedia.Formats[0], &dataH264{
|
||||
pts: pts,
|
||||
au: au,
|
||||
ntp: time.Now(),
|
||||
err := stream.writeData(videoMedia, videoMedia.Formats[0], &formatprocessor.DataH264{
|
||||
PTS: pts,
|
||||
AU: au,
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
s.Log(logger.Warn, "%v", err)
|
||||
@@ -96,10 +97,10 @@ func (s *hlsSource) run(ctx context.Context) error {
|
||||
}
|
||||
|
||||
onAudioData := func(pts time.Duration, au []byte) {
|
||||
err := stream.writeData(audioMedia, audioMedia.Formats[0], &dataMPEG4Audio{
|
||||
pts: pts,
|
||||
aus: [][]byte{au},
|
||||
ntp: time.Now(),
|
||||
err := stream.writeData(audioMedia, audioMedia.Formats[0], &formatprocessor.DataMPEG4Audio{
|
||||
PTS: pts,
|
||||
AUs: [][]byte{au},
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
s.Log(logger.Warn, "%v", err)
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/aler9/gortsplib/v2/pkg/format"
|
||||
"github.com/aler9/gortsplib/v2/pkg/media"
|
||||
|
||||
"github.com/aler9/rtsp-simple-server/internal/formatprocessor"
|
||||
"github.com/aler9/rtsp-simple-server/internal/logger"
|
||||
"github.com/aler9/rtsp-simple-server/internal/rpicamera"
|
||||
)
|
||||
@@ -62,10 +63,10 @@ func (s *rpiCameraSource) run(ctx context.Context) error {
|
||||
stream = res.stream
|
||||
}
|
||||
|
||||
err := stream.writeData(medi, medi.Formats[0], &dataH264{
|
||||
pts: dts,
|
||||
au: au,
|
||||
ntp: time.Now(),
|
||||
err := stream.writeData(medi, medi.Formats[0], &formatprocessor.DataH264{
|
||||
PTS: dts,
|
||||
AU: au,
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
s.Log(logger.Warn, "%v", err)
|
||||
|
||||
+32
-31
@@ -20,6 +20,7 @@ import (
|
||||
|
||||
"github.com/aler9/rtsp-simple-server/internal/conf"
|
||||
"github.com/aler9/rtsp-simple-server/internal/externalcmd"
|
||||
"github.com/aler9/rtsp-simple-server/internal/formatprocessor"
|
||||
"github.com/aler9/rtsp-simple-server/internal/logger"
|
||||
"github.com/aler9/rtsp-simple-server/internal/rtmp"
|
||||
"github.com/aler9/rtsp-simple-server/internal/rtmp/h264conf"
|
||||
@@ -277,24 +278,24 @@ func (c *rtmpConn) runRead(ctx context.Context, u *url.URL) error {
|
||||
var videoStartPTS time.Duration
|
||||
var videoDTSExtractor *h264.DTSExtractor
|
||||
|
||||
res.stream.readerAdd(c, videoMedia, videoFormat, func(dat data) {
|
||||
res.stream.readerAdd(c, videoMedia, videoFormat, func(dat formatprocessor.Data) {
|
||||
ringBuffer.Push(func() error {
|
||||
tdata := dat.(*dataH264)
|
||||
tdata := dat.(*formatprocessor.DataH264)
|
||||
|
||||
if tdata.au == nil {
|
||||
if tdata.AU == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !videoStartPTSFilled {
|
||||
videoStartPTSFilled = true
|
||||
videoStartPTS = tdata.pts
|
||||
videoStartPTS = tdata.PTS
|
||||
}
|
||||
pts := tdata.pts - videoStartPTS
|
||||
pts := tdata.PTS - videoStartPTS
|
||||
|
||||
idrPresent := false
|
||||
nonIDRPresent := false
|
||||
|
||||
for _, nalu := range tdata.au {
|
||||
for _, nalu := range tdata.AU {
|
||||
typ := h264.NALUType(nalu[0] & 0x1F)
|
||||
switch typ {
|
||||
case h264.NALUTypeIDR:
|
||||
@@ -317,7 +318,7 @@ func (c *rtmpConn) runRead(ctx context.Context, u *url.URL) error {
|
||||
videoDTSExtractor = h264.NewDTSExtractor()
|
||||
|
||||
var err error
|
||||
dts, err = videoDTSExtractor.Extract(tdata.au, pts)
|
||||
dts, err = videoDTSExtractor.Extract(tdata.AU, pts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -331,7 +332,7 @@ func (c *rtmpConn) runRead(ctx context.Context, u *url.URL) error {
|
||||
}
|
||||
|
||||
var err error
|
||||
dts, err = videoDTSExtractor.Extract(tdata.au, pts)
|
||||
dts, err = videoDTSExtractor.Extract(tdata.AU, pts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -340,7 +341,7 @@ func (c *rtmpConn) runRead(ctx context.Context, u *url.URL) error {
|
||||
pts -= videoStartDTS
|
||||
}
|
||||
|
||||
avcc, err := h264.AVCCMarshal(tdata.au)
|
||||
avcc, err := h264.AVCCMarshal(tdata.AU)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -370,19 +371,19 @@ func (c *rtmpConn) runRead(ctx context.Context, u *url.URL) error {
|
||||
audioStartPTSFilled := false
|
||||
var audioStartPTS time.Duration
|
||||
|
||||
res.stream.readerAdd(c, audioMedia, audioFormat, func(dat data) {
|
||||
res.stream.readerAdd(c, audioMedia, audioFormat, func(dat formatprocessor.Data) {
|
||||
ringBuffer.Push(func() error {
|
||||
tdata := dat.(*dataMPEG4Audio)
|
||||
tdata := dat.(*formatprocessor.DataMPEG4Audio)
|
||||
|
||||
if tdata.aus == nil {
|
||||
if tdata.AUs == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !audioStartPTSFilled {
|
||||
audioStartPTSFilled = true
|
||||
audioStartPTS = tdata.pts
|
||||
audioStartPTS = tdata.PTS
|
||||
}
|
||||
pts := tdata.pts - audioStartPTS
|
||||
pts := tdata.PTS - audioStartPTS
|
||||
|
||||
if videoFormat != nil {
|
||||
if !videoFirstIDRFound {
|
||||
@@ -395,7 +396,7 @@ func (c *rtmpConn) runRead(ctx context.Context, u *url.URL) error {
|
||||
}
|
||||
}
|
||||
|
||||
for i, au := range tdata.aus {
|
||||
for i, au := range tdata.AUs {
|
||||
c.nconn.SetWriteDeadline(time.Now().Add(time.Duration(c.writeTimeout)))
|
||||
err := c.conn.WriteMessage(&message.MsgAudio{
|
||||
ChunkStreamID: message.MsgAudioChunkStreamID,
|
||||
@@ -539,10 +540,10 @@ func (c *rtmpConn) runPublish(ctx context.Context, u *url.URL) error {
|
||||
|
||||
if _, ok := videoFormat.(*format.H264); ok {
|
||||
onVideoData = func(pts time.Duration, au [][]byte) {
|
||||
err = rres.stream.writeData(videoMedia, videoFormat, &dataH264{
|
||||
pts: pts,
|
||||
au: au,
|
||||
ntp: time.Now(),
|
||||
err = rres.stream.writeData(videoMedia, videoFormat, &formatprocessor.DataH264{
|
||||
PTS: pts,
|
||||
AU: au,
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
c.log(logger.Warn, "%v", err)
|
||||
@@ -550,10 +551,10 @@ func (c *rtmpConn) runPublish(ctx context.Context, u *url.URL) error {
|
||||
}
|
||||
} else {
|
||||
onVideoData = func(pts time.Duration, au [][]byte) {
|
||||
err = rres.stream.writeData(videoMedia, videoFormat, &dataH265{
|
||||
pts: pts,
|
||||
au: au,
|
||||
ntp: time.Now(),
|
||||
err = rres.stream.writeData(videoMedia, videoFormat, &formatprocessor.DataH265{
|
||||
PTS: pts,
|
||||
AU: au,
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
c.log(logger.Warn, "%v", err)
|
||||
@@ -582,10 +583,10 @@ func (c *rtmpConn) runPublish(ctx context.Context, u *url.URL) error {
|
||||
conf.PPS,
|
||||
}
|
||||
|
||||
err := rres.stream.writeData(videoMedia, videoFormat, &dataH264{
|
||||
pts: tmsg.DTS + tmsg.PTSDelta,
|
||||
au: au,
|
||||
ntp: time.Now(),
|
||||
err := rres.stream.writeData(videoMedia, videoFormat, &formatprocessor.DataH264{
|
||||
PTS: tmsg.DTS + tmsg.PTSDelta,
|
||||
AU: au,
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
c.log(logger.Warn, "%v", err)
|
||||
@@ -629,10 +630,10 @@ func (c *rtmpConn) runPublish(ctx context.Context, u *url.URL) error {
|
||||
return fmt.Errorf("received an audio packet, but track is not set up")
|
||||
}
|
||||
|
||||
err := rres.stream.writeData(audioMedia, audioFormat, &dataMPEG4Audio{
|
||||
pts: tmsg.DTS,
|
||||
aus: [][]byte{tmsg.Payload},
|
||||
ntp: time.Now(),
|
||||
err := rres.stream.writeData(audioMedia, audioFormat, &formatprocessor.DataMPEG4Audio{
|
||||
PTS: tmsg.DTS,
|
||||
AUs: [][]byte{tmsg.Payload},
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
c.log(logger.Warn, "%v", err)
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/notedit/rtmp/format/flv/flvio"
|
||||
|
||||
"github.com/aler9/rtsp-simple-server/internal/conf"
|
||||
"github.com/aler9/rtsp-simple-server/internal/formatprocessor"
|
||||
"github.com/aler9/rtsp-simple-server/internal/logger"
|
||||
"github.com/aler9/rtsp-simple-server/internal/rtmp"
|
||||
"github.com/aler9/rtsp-simple-server/internal/rtmp/message"
|
||||
@@ -181,10 +182,10 @@ func (s *rtmpSource) run(ctx context.Context) error {
|
||||
return fmt.Errorf("unable to decode AVCC: %v", err)
|
||||
}
|
||||
|
||||
err = res.stream.writeData(videoMedia, videoFormat, &dataH264{
|
||||
pts: tmsg.DTS + tmsg.PTSDelta,
|
||||
au: au,
|
||||
ntp: time.Now(),
|
||||
err = res.stream.writeData(videoMedia, videoFormat, &formatprocessor.DataH264{
|
||||
PTS: tmsg.DTS + tmsg.PTSDelta,
|
||||
AU: au,
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
s.Log(logger.Warn, "%v", err)
|
||||
@@ -197,10 +198,10 @@ func (s *rtmpSource) run(ctx context.Context) error {
|
||||
return fmt.Errorf("received an AAC packet, but track is not set up")
|
||||
}
|
||||
|
||||
err := res.stream.writeData(audioMedia, audioFormat, &dataMPEG4Audio{
|
||||
pts: tmsg.DTS,
|
||||
aus: [][]byte{tmsg.Payload},
|
||||
ntp: time.Now(),
|
||||
err := res.stream.writeData(audioMedia, audioFormat, &formatprocessor.DataMPEG4Audio{
|
||||
PTS: tmsg.DTS,
|
||||
AUs: [][]byte{tmsg.Payload},
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
s.Log(logger.Warn, "%v", err)
|
||||
|
||||
@@ -4,43 +4,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/aler9/gortsplib/v2"
|
||||
"github.com/aler9/gortsplib/v2/pkg/base"
|
||||
"github.com/aler9/gortsplib/v2/pkg/conn"
|
||||
"github.com/aler9/gortsplib/v2/pkg/media"
|
||||
"github.com/aler9/gortsplib/v2/pkg/sdp"
|
||||
"github.com/aler9/gortsplib/v2/pkg/url"
|
||||
"github.com/pion/rtp"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func mustParseURL(s string) *url.URL {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
func mustMarshalSDP(sdp *sdp.SessionDescription) []byte {
|
||||
byts, err := sdp.Marshal()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return byts
|
||||
}
|
||||
|
||||
func writeReqReadRes(
|
||||
conn *conn.Conn,
|
||||
req base.Request,
|
||||
) (*base.Response, error) {
|
||||
err := conn.WriteRequest(&req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn.ReadResponse()
|
||||
}
|
||||
|
||||
func TestRTSPServerAuth(t *testing.T) {
|
||||
for _, ca := range []string{
|
||||
"internal",
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
|
||||
"github.com/aler9/rtsp-simple-server/internal/conf"
|
||||
"github.com/aler9/rtsp-simple-server/internal/externalcmd"
|
||||
"github.com/aler9/rtsp-simple-server/internal/formatprocessor"
|
||||
"github.com/aler9/rtsp-simple-server/internal/logger"
|
||||
)
|
||||
|
||||
@@ -310,9 +311,9 @@ func (s *rtspSession) onRecord(ctx *gortsplib.ServerHandlerOnRecordCtx) (*base.R
|
||||
switch forma.(type) {
|
||||
case *format.H264:
|
||||
ctx.Session.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||
err := s.stream.writeData(cmedia, cformat, &dataH264{
|
||||
rtpPackets: []*rtp.Packet{pkt},
|
||||
ntp: time.Now(),
|
||||
err := s.stream.writeData(cmedia, cformat, &formatprocessor.DataH264{
|
||||
RTPPackets: []*rtp.Packet{pkt},
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
s.log(logger.Warn, "%v", err)
|
||||
@@ -321,9 +322,9 @@ func (s *rtspSession) onRecord(ctx *gortsplib.ServerHandlerOnRecordCtx) (*base.R
|
||||
|
||||
case *format.H265:
|
||||
ctx.Session.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||
err := s.stream.writeData(cmedia, cformat, &dataH265{
|
||||
rtpPackets: []*rtp.Packet{pkt},
|
||||
ntp: time.Now(),
|
||||
err := s.stream.writeData(cmedia, cformat, &formatprocessor.DataH265{
|
||||
RTPPackets: []*rtp.Packet{pkt},
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
s.log(logger.Warn, "%v", err)
|
||||
@@ -332,9 +333,9 @@ func (s *rtspSession) onRecord(ctx *gortsplib.ServerHandlerOnRecordCtx) (*base.R
|
||||
|
||||
case *format.VP8:
|
||||
ctx.Session.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||
err := s.stream.writeData(cmedia, cformat, &dataVP8{
|
||||
rtpPackets: []*rtp.Packet{pkt},
|
||||
ntp: time.Now(),
|
||||
err := s.stream.writeData(cmedia, cformat, &formatprocessor.DataVP8{
|
||||
RTPPackets: []*rtp.Packet{pkt},
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
s.log(logger.Warn, "%v", err)
|
||||
@@ -343,9 +344,9 @@ func (s *rtspSession) onRecord(ctx *gortsplib.ServerHandlerOnRecordCtx) (*base.R
|
||||
|
||||
case *format.VP9:
|
||||
ctx.Session.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||
err := s.stream.writeData(cmedia, cformat, &dataVP9{
|
||||
rtpPackets: []*rtp.Packet{pkt},
|
||||
ntp: time.Now(),
|
||||
err := s.stream.writeData(cmedia, cformat, &formatprocessor.DataVP9{
|
||||
RTPPackets: []*rtp.Packet{pkt},
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
s.log(logger.Warn, "%v", err)
|
||||
@@ -354,9 +355,9 @@ func (s *rtspSession) onRecord(ctx *gortsplib.ServerHandlerOnRecordCtx) (*base.R
|
||||
|
||||
case *format.MPEG4Audio:
|
||||
ctx.Session.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||
err := s.stream.writeData(cmedia, cformat, &dataMPEG4Audio{
|
||||
rtpPackets: []*rtp.Packet{pkt},
|
||||
ntp: time.Now(),
|
||||
err := s.stream.writeData(cmedia, cformat, &formatprocessor.DataMPEG4Audio{
|
||||
RTPPackets: []*rtp.Packet{pkt},
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
s.log(logger.Warn, "%v", err)
|
||||
@@ -365,9 +366,9 @@ func (s *rtspSession) onRecord(ctx *gortsplib.ServerHandlerOnRecordCtx) (*base.R
|
||||
|
||||
case *format.Opus:
|
||||
ctx.Session.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||
err := s.stream.writeData(cmedia, cformat, &dataOpus{
|
||||
rtpPackets: []*rtp.Packet{pkt},
|
||||
ntp: time.Now(),
|
||||
err := s.stream.writeData(cmedia, cformat, &formatprocessor.DataOpus{
|
||||
RTPPackets: []*rtp.Packet{pkt},
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
s.log(logger.Warn, "%v", err)
|
||||
@@ -376,9 +377,9 @@ func (s *rtspSession) onRecord(ctx *gortsplib.ServerHandlerOnRecordCtx) (*base.R
|
||||
|
||||
default:
|
||||
ctx.Session.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||
err := s.stream.writeData(cmedia, cformat, &dataGeneric{
|
||||
rtpPackets: []*rtp.Packet{pkt},
|
||||
ntp: time.Now(),
|
||||
err := s.stream.writeData(cmedia, cformat, &formatprocessor.DataGeneric{
|
||||
RTPPackets: []*rtp.Packet{pkt},
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
s.log(logger.Warn, "%v", err)
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
|
||||
"github.com/aler9/gortsplib/v2/pkg/url"
|
||||
"github.com/aler9/rtsp-simple-server/internal/conf"
|
||||
"github.com/aler9/rtsp-simple-server/internal/formatprocessor"
|
||||
"github.com/aler9/rtsp-simple-server/internal/logger"
|
||||
)
|
||||
|
||||
@@ -150,9 +151,9 @@ func (s *rtspSource) run(ctx context.Context) error {
|
||||
switch forma.(type) {
|
||||
case *format.H264:
|
||||
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||
err := res.stream.writeData(cmedia, cformat, &dataH264{
|
||||
rtpPackets: []*rtp.Packet{pkt},
|
||||
ntp: time.Now(),
|
||||
err := res.stream.writeData(cmedia, cformat, &formatprocessor.DataH264{
|
||||
RTPPackets: []*rtp.Packet{pkt},
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
s.Log(logger.Warn, "%v", err)
|
||||
@@ -161,9 +162,9 @@ func (s *rtspSource) run(ctx context.Context) error {
|
||||
|
||||
case *format.H265:
|
||||
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||
err := res.stream.writeData(cmedia, cformat, &dataH265{
|
||||
rtpPackets: []*rtp.Packet{pkt},
|
||||
ntp: time.Now(),
|
||||
err := res.stream.writeData(cmedia, cformat, &formatprocessor.DataH265{
|
||||
RTPPackets: []*rtp.Packet{pkt},
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
s.Log(logger.Warn, "%v", err)
|
||||
@@ -172,9 +173,9 @@ func (s *rtspSource) run(ctx context.Context) error {
|
||||
|
||||
case *format.VP8:
|
||||
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||
err := res.stream.writeData(cmedia, cformat, &dataVP8{
|
||||
rtpPackets: []*rtp.Packet{pkt},
|
||||
ntp: time.Now(),
|
||||
err := res.stream.writeData(cmedia, cformat, &formatprocessor.DataVP8{
|
||||
RTPPackets: []*rtp.Packet{pkt},
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
s.Log(logger.Warn, "%v", err)
|
||||
@@ -183,9 +184,9 @@ func (s *rtspSource) run(ctx context.Context) error {
|
||||
|
||||
case *format.VP9:
|
||||
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||
err := res.stream.writeData(cmedia, cformat, &dataVP9{
|
||||
rtpPackets: []*rtp.Packet{pkt},
|
||||
ntp: time.Now(),
|
||||
err := res.stream.writeData(cmedia, cformat, &formatprocessor.DataVP9{
|
||||
RTPPackets: []*rtp.Packet{pkt},
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
s.Log(logger.Warn, "%v", err)
|
||||
@@ -194,9 +195,9 @@ func (s *rtspSource) run(ctx context.Context) error {
|
||||
|
||||
case *format.MPEG4Audio:
|
||||
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||
err := res.stream.writeData(cmedia, cformat, &dataMPEG4Audio{
|
||||
rtpPackets: []*rtp.Packet{pkt},
|
||||
ntp: time.Now(),
|
||||
err := res.stream.writeData(cmedia, cformat, &formatprocessor.DataMPEG4Audio{
|
||||
RTPPackets: []*rtp.Packet{pkt},
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
s.Log(logger.Warn, "%v", err)
|
||||
@@ -205,9 +206,9 @@ func (s *rtspSource) run(ctx context.Context) error {
|
||||
|
||||
case *format.Opus:
|
||||
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||
err := res.stream.writeData(cmedia, cformat, &dataOpus{
|
||||
rtpPackets: []*rtp.Packet{pkt},
|
||||
ntp: time.Now(),
|
||||
err := res.stream.writeData(cmedia, cformat, &formatprocessor.DataOpus{
|
||||
RTPPackets: []*rtp.Packet{pkt},
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
s.Log(logger.Warn, "%v", err)
|
||||
@@ -216,9 +217,9 @@ func (s *rtspSource) run(ctx context.Context) error {
|
||||
|
||||
default:
|
||||
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||
err := res.stream.writeData(cmedia, cformat, &dataGeneric{
|
||||
rtpPackets: []*rtp.Packet{pkt},
|
||||
ntp: time.Now(),
|
||||
err := res.stream.writeData(cmedia, cformat, &formatprocessor.DataGeneric{
|
||||
RTPPackets: []*rtp.Packet{pkt},
|
||||
NTP: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
s.Log(logger.Warn, "%v", err)
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"github.com/aler9/gortsplib/v2"
|
||||
"github.com/aler9/gortsplib/v2/pkg/format"
|
||||
"github.com/aler9/gortsplib/v2/pkg/media"
|
||||
|
||||
"github.com/aler9/rtsp-simple-server/internal/formatprocessor"
|
||||
)
|
||||
|
||||
type stream struct {
|
||||
@@ -43,7 +45,7 @@ func (s *stream) medias() media.Medias {
|
||||
return s.rtspStream.Medias()
|
||||
}
|
||||
|
||||
func (s *stream) readerAdd(r reader, medi *media.Media, forma format.Format, cb func(data)) {
|
||||
func (s *stream) readerAdd(r reader, medi *media.Media, forma format.Format, cb func(formatprocessor.Data)) {
|
||||
sm := s.smedias[medi]
|
||||
sf := sm.formats[forma]
|
||||
sf.readerAdd(r, cb)
|
||||
@@ -57,7 +59,7 @@ func (s *stream) readerRemove(r reader) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stream) writeData(medi *media.Media, forma format.Format, data data) error {
|
||||
func (s *stream) writeData(medi *media.Media, forma format.Format, data formatprocessor.Data) error {
|
||||
sm := s.smedias[medi]
|
||||
sf := sm.formats[forma]
|
||||
return sf.writeData(s, medi, data)
|
||||
|
||||
@@ -6,29 +6,31 @@ import (
|
||||
|
||||
"github.com/aler9/gortsplib/v2/pkg/format"
|
||||
"github.com/aler9/gortsplib/v2/pkg/media"
|
||||
|
||||
"github.com/aler9/rtsp-simple-server/internal/formatprocessor"
|
||||
)
|
||||
|
||||
type streamFormat struct {
|
||||
proc formatProcessor
|
||||
proc formatprocessor.Processor
|
||||
mutex sync.RWMutex
|
||||
nonRTSPReaders map[reader]func(data)
|
||||
nonRTSPReaders map[reader]func(formatprocessor.Data)
|
||||
}
|
||||
|
||||
func newStreamFormat(forma format.Format, generateRTPPackets bool) (*streamFormat, error) {
|
||||
proc, err := newFormatProcessor(forma, generateRTPPackets)
|
||||
proc, err := formatprocessor.New(forma, generateRTPPackets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sf := &streamFormat{
|
||||
proc: proc,
|
||||
nonRTSPReaders: make(map[reader]func(data)),
|
||||
nonRTSPReaders: make(map[reader]func(formatprocessor.Data)),
|
||||
}
|
||||
|
||||
return sf, nil
|
||||
}
|
||||
|
||||
func (sf *streamFormat) readerAdd(r reader, cb func(data)) {
|
||||
func (sf *streamFormat) readerAdd(r reader, cb func(formatprocessor.Data)) {
|
||||
sf.mutex.Lock()
|
||||
defer sf.mutex.Unlock()
|
||||
sf.nonRTSPReaders[r] = cb
|
||||
@@ -40,21 +42,21 @@ func (sf *streamFormat) readerRemove(r reader) {
|
||||
delete(sf.nonRTSPReaders, r)
|
||||
}
|
||||
|
||||
func (sf *streamFormat) writeData(s *stream, medi *media.Media, data data) error {
|
||||
func (sf *streamFormat) writeData(s *stream, medi *media.Media, data formatprocessor.Data) error {
|
||||
sf.mutex.RLock()
|
||||
defer sf.mutex.RUnlock()
|
||||
|
||||
hasNonRTSPReaders := len(sf.nonRTSPReaders) > 0
|
||||
|
||||
err := sf.proc.process(data, hasNonRTSPReaders)
|
||||
err := sf.proc.Process(data, hasNonRTSPReaders)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// forward RTP packets to RTSP readers
|
||||
for _, pkt := range data.getRTPPackets() {
|
||||
for _, pkt := range data.GetRTPPackets() {
|
||||
atomic.AddUint64(s.bytesReceived, uint64(pkt.MarshalSize()))
|
||||
s.rtspStream.WritePacketRTPWithNTP(medi, pkt, data.getNTP())
|
||||
s.rtspStream.WritePacketRTPWithNTP(medi, pkt, data.GetNTP())
|
||||
}
|
||||
|
||||
// forward decoded frames to non-RTSP readers
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/pion/webrtc/v3"
|
||||
|
||||
"github.com/aler9/rtsp-simple-server/internal/conf"
|
||||
"github.com/aler9/rtsp-simple-server/internal/formatprocessor"
|
||||
"github.com/aler9/rtsp-simple-server/internal/logger"
|
||||
)
|
||||
|
||||
@@ -96,7 +97,7 @@ type webRTCTrack struct {
|
||||
media *media.Media
|
||||
format format.Format
|
||||
webRTCTrack *webrtc.TrackLocalStaticRTP
|
||||
cb func(data, context.Context, chan error)
|
||||
cb func(formatprocessor.Data, context.Context, chan error)
|
||||
}
|
||||
|
||||
func gatherMedias(tracks []*webRTCTrack) media.Medias {
|
||||
@@ -441,7 +442,7 @@ outer:
|
||||
|
||||
for _, track := range tracks {
|
||||
ctrack := track
|
||||
res.stream.readerAdd(c, track.media, track.format, func(dat data) {
|
||||
res.stream.readerAdd(c, track.media, track.format, func(dat formatprocessor.Data) {
|
||||
ringBuffer.Push(func() {
|
||||
ctrack.cb(dat, ctx, writeError)
|
||||
})
|
||||
@@ -503,14 +504,14 @@ func (c *webRTCConn) allocateTracks(medias media.Medias) ([]*webRTCTrack, error)
|
||||
media: vp9Media,
|
||||
format: vp9Format,
|
||||
webRTCTrack: webRTCTrak,
|
||||
cb: func(dat data, ctx context.Context, writeError chan error) {
|
||||
tdata := dat.(*dataVP9)
|
||||
cb: func(dat formatprocessor.Data, ctx context.Context, writeError chan error) {
|
||||
tdata := dat.(*formatprocessor.DataVP9)
|
||||
|
||||
if tdata.frame == nil {
|
||||
if tdata.Frame == nil {
|
||||
return
|
||||
}
|
||||
|
||||
packets, err := encoder.Encode(tdata.frame, tdata.pts)
|
||||
packets, err := encoder.Encode(tdata.Frame, tdata.PTS)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -550,14 +551,14 @@ func (c *webRTCConn) allocateTracks(medias media.Medias) ([]*webRTCTrack, error)
|
||||
media: vp8Media,
|
||||
format: vp8Format,
|
||||
webRTCTrack: webRTCTrak,
|
||||
cb: func(dat data, ctx context.Context, writeError chan error) {
|
||||
tdata := dat.(*dataVP8)
|
||||
cb: func(dat formatprocessor.Data, ctx context.Context, writeError chan error) {
|
||||
tdata := dat.(*formatprocessor.DataVP8)
|
||||
|
||||
if tdata.frame == nil {
|
||||
if tdata.Frame == nil {
|
||||
return
|
||||
}
|
||||
|
||||
packets, err := encoder.Encode(tdata.frame, tdata.pts)
|
||||
packets, err := encoder.Encode(tdata.Frame, tdata.PTS)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -600,32 +601,32 @@ func (c *webRTCConn) allocateTracks(medias media.Medias) ([]*webRTCTrack, error)
|
||||
media: h264Media,
|
||||
format: h264Format,
|
||||
webRTCTrack: webRTCTrak,
|
||||
cb: func(dat data, ctx context.Context, writeError chan error) {
|
||||
tdata := dat.(*dataH264)
|
||||
cb: func(dat formatprocessor.Data, ctx context.Context, writeError chan error) {
|
||||
tdata := dat.(*formatprocessor.DataH264)
|
||||
|
||||
if tdata.au == nil {
|
||||
if tdata.AU == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !firstNALUReceived {
|
||||
if !h264.IDRPresent(tdata.au) {
|
||||
if !h264.IDRPresent(tdata.AU) {
|
||||
return
|
||||
}
|
||||
|
||||
firstNALUReceived = true
|
||||
lastPTS = tdata.pts
|
||||
lastPTS = tdata.PTS
|
||||
} else {
|
||||
if tdata.pts < lastPTS {
|
||||
if tdata.PTS < lastPTS {
|
||||
select {
|
||||
case writeError <- fmt.Errorf("WebRTC doesn't support H264 streams with B-frames"):
|
||||
case <-ctx.Done():
|
||||
}
|
||||
return
|
||||
}
|
||||
lastPTS = tdata.pts
|
||||
lastPTS = tdata.PTS
|
||||
}
|
||||
|
||||
packets, err := encoder.Encode(tdata.au, tdata.pts)
|
||||
packets, err := encoder.Encode(tdata.AU, tdata.PTS)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -658,8 +659,8 @@ func (c *webRTCConn) allocateTracks(medias media.Medias) ([]*webRTCTrack, error)
|
||||
media: opusMedia,
|
||||
format: opusFormat,
|
||||
webRTCTrack: webRTCTrak,
|
||||
cb: func(dat data, ctx context.Context, writeError chan error) {
|
||||
for _, pkt := range dat.getRTPPackets() {
|
||||
cb: func(dat formatprocessor.Data, ctx context.Context, writeError chan error) {
|
||||
for _, pkt := range dat.GetRTPPackets() {
|
||||
webRTCTrak.WriteRTP(pkt)
|
||||
}
|
||||
},
|
||||
@@ -688,8 +689,8 @@ func (c *webRTCConn) allocateTracks(medias media.Medias) ([]*webRTCTrack, error)
|
||||
media: g722Media,
|
||||
format: g722Format,
|
||||
webRTCTrack: webRTCTrak,
|
||||
cb: func(dat data, ctx context.Context, writeError chan error) {
|
||||
for _, pkt := range dat.getRTPPackets() {
|
||||
cb: func(dat formatprocessor.Data, ctx context.Context, writeError chan error) {
|
||||
for _, pkt := range dat.GetRTPPackets() {
|
||||
webRTCTrak.WriteRTP(pkt)
|
||||
}
|
||||
},
|
||||
@@ -726,8 +727,8 @@ func (c *webRTCConn) allocateTracks(medias media.Medias) ([]*webRTCTrack, error)
|
||||
media: g711Media,
|
||||
format: g711Format,
|
||||
webRTCTrack: webRTCTrak,
|
||||
cb: func(dat data, ctx context.Context, writeError chan error) {
|
||||
for _, pkt := range dat.getRTPPackets() {
|
||||
cb: func(dat formatprocessor.Data, ctx context.Context, writeError chan error) {
|
||||
for _, pkt := range dat.GetRTPPackets() {
|
||||
webRTCTrak.WriteRTP(pkt)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package formatprocessor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -13,22 +13,25 @@ const (
|
||||
maxPacketSize = 1472
|
||||
)
|
||||
|
||||
type dataGeneric struct {
|
||||
rtpPackets []*rtp.Packet
|
||||
ntp time.Time
|
||||
// DataGeneric is a generic data unit.
|
||||
type DataGeneric struct {
|
||||
RTPPackets []*rtp.Packet
|
||||
NTP time.Time
|
||||
}
|
||||
|
||||
func (d *dataGeneric) getRTPPackets() []*rtp.Packet {
|
||||
return d.rtpPackets
|
||||
// GetRTPPackets implements Data.
|
||||
func (d *DataGeneric) GetRTPPackets() []*rtp.Packet {
|
||||
return d.RTPPackets
|
||||
}
|
||||
|
||||
func (d *dataGeneric) getNTP() time.Time {
|
||||
return d.ntp
|
||||
// GetNTP implements Data.
|
||||
func (d *DataGeneric) GetNTP() time.Time {
|
||||
return d.NTP
|
||||
}
|
||||
|
||||
type formatProcessorGeneric struct{}
|
||||
|
||||
func newFormatProcessorGeneric(forma format.Format, generateRTPPackets bool) (*formatProcessorGeneric, error) {
|
||||
func newGeneric(forma format.Format, generateRTPPackets bool) (*formatProcessorGeneric, error) {
|
||||
if generateRTPPackets {
|
||||
return nil, fmt.Errorf("we don't know how to generate RTP packets of format %+v", forma)
|
||||
}
|
||||
@@ -36,10 +39,10 @@ func newFormatProcessorGeneric(forma format.Format, generateRTPPackets bool) (*f
|
||||
return &formatProcessorGeneric{}, nil
|
||||
}
|
||||
|
||||
func (t *formatProcessorGeneric) process(dat data, hasNonRTSPReaders bool) error {
|
||||
tdata := dat.(*dataGeneric)
|
||||
func (t *formatProcessorGeneric) Process(dat Data, hasNonRTSPReaders bool) error {
|
||||
tdata := dat.(*DataGeneric)
|
||||
|
||||
pkt := tdata.rtpPackets[0]
|
||||
pkt := tdata.RTPPackets[0]
|
||||
|
||||
// remove padding
|
||||
pkt.Header.Padding = false
|
||||
@@ -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)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package formatprocessor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -67,19 +67,22 @@ func rtpH264ExtractSPSPPS(pkt *rtp.Packet) ([]byte, []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
type dataH264 struct {
|
||||
rtpPackets []*rtp.Packet
|
||||
ntp time.Time
|
||||
pts time.Duration
|
||||
au [][]byte
|
||||
// DataH264 is a H264 data unit.
|
||||
type DataH264 struct {
|
||||
RTPPackets []*rtp.Packet
|
||||
NTP time.Time
|
||||
PTS time.Duration
|
||||
AU [][]byte
|
||||
}
|
||||
|
||||
func (d *dataH264) getRTPPackets() []*rtp.Packet {
|
||||
return d.rtpPackets
|
||||
// GetRTPPackets implements Data.
|
||||
func (d *DataH264) GetRTPPackets() []*rtp.Packet {
|
||||
return d.RTPPackets
|
||||
}
|
||||
|
||||
func (d *dataH264) getNTP() time.Time {
|
||||
return d.ntp
|
||||
// GetNTP implements Data.
|
||||
func (d *DataH264) GetNTP() time.Time {
|
||||
return d.NTP
|
||||
}
|
||||
|
||||
type formatProcessorH264 struct {
|
||||
@@ -89,7 +92,7 @@ type formatProcessorH264 struct {
|
||||
decoder *rtph264.Decoder
|
||||
}
|
||||
|
||||
func newFormatProcessorH264(
|
||||
func newH264(
|
||||
forma *format.H264,
|
||||
allocateEncoder bool,
|
||||
) (*formatProcessorH264, error) {
|
||||
@@ -188,11 +191,11 @@ func (t *formatProcessorH264) remuxAccessUnit(nalus [][]byte) [][]byte {
|
||||
return filteredNALUs
|
||||
}
|
||||
|
||||
func (t *formatProcessorH264) process(dat data, hasNonRTSPReaders bool) error { //nolint:dupl
|
||||
tdata := dat.(*dataH264)
|
||||
func (t *formatProcessorH264) Process(dat Data, hasNonRTSPReaders bool) error { //nolint:dupl
|
||||
tdata := dat.(*DataH264)
|
||||
|
||||
if tdata.rtpPackets != nil {
|
||||
pkt := tdata.rtpPackets[0]
|
||||
if tdata.RTPPackets != nil {
|
||||
pkt := tdata.RTPPackets[0]
|
||||
t.updateTrackParametersFromRTPPacket(pkt)
|
||||
|
||||
if t.encoder == nil {
|
||||
@@ -223,11 +226,11 @@ func (t *formatProcessorH264) process(dat data, hasNonRTSPReaders bool) error {
|
||||
}
|
||||
|
||||
if t.encoder != nil {
|
||||
tdata.rtpPackets = nil
|
||||
tdata.RTPPackets = nil
|
||||
}
|
||||
|
||||
// DecodeUntilMarker() is necessary, otherwise Encode() generates partial groups
|
||||
au, pts, err := t.decoder.DecodeUntilMarker(pkt)
|
||||
au, PTS, err := t.decoder.DecodeUntilMarker(pkt)
|
||||
if err != nil {
|
||||
if err == rtph264.ErrNonStartingPacketAndNoPrevious || err == rtph264.ErrMorePacketsNeeded {
|
||||
return nil
|
||||
@@ -235,9 +238,9 @@ func (t *formatProcessorH264) process(dat data, hasNonRTSPReaders bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
tdata.au = au
|
||||
tdata.pts = pts
|
||||
tdata.au = t.remuxAccessUnit(tdata.au)
|
||||
tdata.AU = au
|
||||
tdata.PTS = PTS
|
||||
tdata.AU = t.remuxAccessUnit(tdata.AU)
|
||||
}
|
||||
|
||||
// route packet as is
|
||||
@@ -245,15 +248,15 @@ func (t *formatProcessorH264) process(dat data, hasNonRTSPReaders bool) error {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
t.updateTrackParametersFromNALUs(tdata.au)
|
||||
tdata.au = t.remuxAccessUnit(tdata.au)
|
||||
t.updateTrackParametersFromNALUs(tdata.AU)
|
||||
tdata.AU = t.remuxAccessUnit(tdata.AU)
|
||||
}
|
||||
|
||||
pkts, err := t.encoder.Encode(tdata.au, tdata.pts)
|
||||
pkts, err := t.encoder.Encode(tdata.AU, tdata.PTS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tdata.rtpPackets = pkts
|
||||
tdata.RTPPackets = pkts
|
||||
return nil
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package formatprocessor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -74,19 +74,22 @@ func rtpH265ExtractVPSSPSPPS(pkt *rtp.Packet) ([]byte, []byte, []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
type dataH265 struct {
|
||||
rtpPackets []*rtp.Packet
|
||||
ntp time.Time
|
||||
pts time.Duration
|
||||
au [][]byte
|
||||
// DataH265 is a H265 data unit.
|
||||
type DataH265 struct {
|
||||
RTPPackets []*rtp.Packet
|
||||
NTP time.Time
|
||||
PTS time.Duration
|
||||
AU [][]byte
|
||||
}
|
||||
|
||||
func (d *dataH265) getRTPPackets() []*rtp.Packet {
|
||||
return d.rtpPackets
|
||||
// GetRTPPackets implements Data.
|
||||
func (d *DataH265) GetRTPPackets() []*rtp.Packet {
|
||||
return d.RTPPackets
|
||||
}
|
||||
|
||||
func (d *dataH265) getNTP() time.Time {
|
||||
return d.ntp
|
||||
// GetNTP implements Data.
|
||||
func (d *DataH265) GetNTP() time.Time {
|
||||
return d.NTP
|
||||
}
|
||||
|
||||
type formatProcessorH265 struct {
|
||||
@@ -96,7 +99,7 @@ type formatProcessorH265 struct {
|
||||
decoder *rtph265.Decoder
|
||||
}
|
||||
|
||||
func newFormatProcessorH265(
|
||||
func newH265(
|
||||
forma *format.H265,
|
||||
allocateEncoder bool,
|
||||
) (*formatProcessorH265, error) {
|
||||
@@ -206,11 +209,11 @@ func (t *formatProcessorH265) remuxAccessUnit(nalus [][]byte) [][]byte {
|
||||
return filteredNALUs
|
||||
}
|
||||
|
||||
func (t *formatProcessorH265) process(dat data, hasNonRTSPReaders bool) error { //nolint:dupl
|
||||
tdata := dat.(*dataH265)
|
||||
func (t *formatProcessorH265) Process(dat Data, hasNonRTSPReaders bool) error { //nolint:dupl
|
||||
tdata := dat.(*DataH265)
|
||||
|
||||
if tdata.rtpPackets != nil {
|
||||
pkt := tdata.rtpPackets[0]
|
||||
if tdata.RTPPackets != nil {
|
||||
pkt := tdata.RTPPackets[0]
|
||||
t.updateTrackParametersFromRTPPacket(pkt)
|
||||
|
||||
if t.encoder == nil {
|
||||
@@ -241,11 +244,11 @@ func (t *formatProcessorH265) process(dat data, hasNonRTSPReaders bool) error {
|
||||
}
|
||||
|
||||
if t.encoder != nil {
|
||||
tdata.rtpPackets = nil
|
||||
tdata.RTPPackets = nil
|
||||
}
|
||||
|
||||
// DecodeUntilMarker() is necessary, otherwise Encode() generates partial groups
|
||||
au, pts, err := t.decoder.DecodeUntilMarker(pkt)
|
||||
au, PTS, err := t.decoder.DecodeUntilMarker(pkt)
|
||||
if err != nil {
|
||||
if err == rtph265.ErrNonStartingPacketAndNoPrevious || err == rtph265.ErrMorePacketsNeeded {
|
||||
return nil
|
||||
@@ -253,9 +256,9 @@ func (t *formatProcessorH265) process(dat data, hasNonRTSPReaders bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
tdata.au = au
|
||||
tdata.pts = pts
|
||||
tdata.au = t.remuxAccessUnit(tdata.au)
|
||||
tdata.AU = au
|
||||
tdata.PTS = PTS
|
||||
tdata.AU = t.remuxAccessUnit(tdata.AU)
|
||||
}
|
||||
|
||||
// route packet as is
|
||||
@@ -263,15 +266,15 @@ func (t *formatProcessorH265) process(dat data, hasNonRTSPReaders bool) error {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
t.updateTrackParametersFromNALUs(tdata.au)
|
||||
tdata.au = t.remuxAccessUnit(tdata.au)
|
||||
t.updateTrackParametersFromNALUs(tdata.AU)
|
||||
tdata.AU = t.remuxAccessUnit(tdata.AU)
|
||||
}
|
||||
|
||||
pkts, err := t.encoder.Encode(tdata.au, tdata.pts)
|
||||
pkts, err := t.encoder.Encode(tdata.AU, tdata.PTS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tdata.rtpPackets = pkts
|
||||
tdata.RTPPackets = pkts
|
||||
return nil
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
+23
-20
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package formatprocessor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -9,19 +9,22 @@ import (
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
type dataMPEG4Audio struct {
|
||||
rtpPackets []*rtp.Packet
|
||||
ntp time.Time
|
||||
pts time.Duration
|
||||
aus [][]byte
|
||||
// DataMPEG4Audio is a MPEG4-audio data unit.
|
||||
type DataMPEG4Audio struct {
|
||||
RTPPackets []*rtp.Packet
|
||||
NTP time.Time
|
||||
PTS time.Duration
|
||||
AUs [][]byte
|
||||
}
|
||||
|
||||
func (d *dataMPEG4Audio) getRTPPackets() []*rtp.Packet {
|
||||
return d.rtpPackets
|
||||
// GetRTPPackets implements Data.
|
||||
func (d *DataMPEG4Audio) GetRTPPackets() []*rtp.Packet {
|
||||
return d.RTPPackets
|
||||
}
|
||||
|
||||
func (d *dataMPEG4Audio) getNTP() time.Time {
|
||||
return d.ntp
|
||||
// GetNTP implements Data.
|
||||
func (d *DataMPEG4Audio) GetNTP() time.Time {
|
||||
return d.NTP
|
||||
}
|
||||
|
||||
type formatProcessorMPEG4Audio struct {
|
||||
@@ -30,7 +33,7 @@ type formatProcessorMPEG4Audio struct {
|
||||
decoder *rtpmpeg4audio.Decoder
|
||||
}
|
||||
|
||||
func newFormatProcessorMPEG4Audio(
|
||||
func newMPEG4Audio(
|
||||
forma *format.MPEG4Audio,
|
||||
allocateEncoder bool,
|
||||
) (*formatProcessorMPEG4Audio, error) {
|
||||
@@ -45,11 +48,11 @@ func newFormatProcessorMPEG4Audio(
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (t *formatProcessorMPEG4Audio) process(dat data, hasNonRTSPReaders bool) error { //nolint:dupl
|
||||
tdata := dat.(*dataMPEG4Audio)
|
||||
func (t *formatProcessorMPEG4Audio) Process(dat Data, hasNonRTSPReaders bool) error { //nolint:dupl
|
||||
tdata := dat.(*DataMPEG4Audio)
|
||||
|
||||
if tdata.rtpPackets != nil {
|
||||
pkt := tdata.rtpPackets[0]
|
||||
if tdata.RTPPackets != nil {
|
||||
pkt := tdata.RTPPackets[0]
|
||||
|
||||
// remove padding
|
||||
pkt.Header.Padding = false
|
||||
@@ -66,7 +69,7 @@ func (t *formatProcessorMPEG4Audio) process(dat data, hasNonRTSPReaders bool) er
|
||||
t.decoder = t.format.CreateDecoder()
|
||||
}
|
||||
|
||||
aus, pts, err := t.decoder.Decode(pkt)
|
||||
aus, PTS, err := t.decoder.Decode(pkt)
|
||||
if err != nil {
|
||||
if err == rtpmpeg4audio.ErrMorePacketsNeeded {
|
||||
return nil
|
||||
@@ -74,19 +77,19 @@ func (t *formatProcessorMPEG4Audio) process(dat data, hasNonRTSPReaders bool) er
|
||||
return err
|
||||
}
|
||||
|
||||
tdata.aus = aus
|
||||
tdata.pts = pts
|
||||
tdata.AUs = aus
|
||||
tdata.PTS = PTS
|
||||
}
|
||||
|
||||
// route packet as is
|
||||
return nil
|
||||
}
|
||||
|
||||
pkts, err := t.encoder.Encode(tdata.aus, tdata.pts)
|
||||
pkts, err := t.encoder.Encode(tdata.AUs, tdata.PTS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tdata.rtpPackets = pkts
|
||||
tdata.RTPPackets = pkts
|
||||
return nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package formatprocessor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -9,19 +9,22 @@ import (
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
type dataOpus struct {
|
||||
rtpPackets []*rtp.Packet
|
||||
ntp time.Time
|
||||
pts time.Duration
|
||||
frame []byte
|
||||
// DataOpus is a Opus data unit.
|
||||
type DataOpus struct {
|
||||
RTPPackets []*rtp.Packet
|
||||
NTP time.Time
|
||||
PTS time.Duration
|
||||
Frame []byte
|
||||
}
|
||||
|
||||
func (d *dataOpus) getRTPPackets() []*rtp.Packet {
|
||||
return d.rtpPackets
|
||||
// GetRTPPackets implements Data.
|
||||
func (d *DataOpus) GetRTPPackets() []*rtp.Packet {
|
||||
return d.RTPPackets
|
||||
}
|
||||
|
||||
func (d *dataOpus) getNTP() time.Time {
|
||||
return d.ntp
|
||||
// GetNTP implements Data.
|
||||
func (d *DataOpus) GetNTP() time.Time {
|
||||
return d.NTP
|
||||
}
|
||||
|
||||
type formatProcessorOpus struct {
|
||||
@@ -30,7 +33,7 @@ type formatProcessorOpus struct {
|
||||
decoder *rtpsimpleaudio.Decoder
|
||||
}
|
||||
|
||||
func newFormatProcessorOpus(
|
||||
func newOpus(
|
||||
forma *format.Opus,
|
||||
allocateEncoder bool,
|
||||
) (*formatProcessorOpus, error) {
|
||||
@@ -45,11 +48,11 @@ func newFormatProcessorOpus(
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (t *formatProcessorOpus) process(dat data, hasNonRTSPReaders bool) error { //nolint:dupl
|
||||
tdata := dat.(*dataOpus)
|
||||
func (t *formatProcessorOpus) Process(dat Data, hasNonRTSPReaders bool) error { //nolint:dupl
|
||||
tdata := dat.(*DataOpus)
|
||||
|
||||
if tdata.rtpPackets != nil {
|
||||
pkt := tdata.rtpPackets[0]
|
||||
if tdata.RTPPackets != nil {
|
||||
pkt := tdata.RTPPackets[0]
|
||||
|
||||
// remove padding
|
||||
pkt.Header.Padding = false
|
||||
@@ -66,24 +69,24 @@ func (t *formatProcessorOpus) process(dat data, hasNonRTSPReaders bool) error {
|
||||
t.decoder = t.format.CreateDecoder()
|
||||
}
|
||||
|
||||
frame, pts, err := t.decoder.Decode(pkt)
|
||||
frame, PTS, err := t.decoder.Decode(pkt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tdata.frame = frame
|
||||
tdata.pts = pts
|
||||
tdata.Frame = frame
|
||||
tdata.PTS = PTS
|
||||
}
|
||||
|
||||
// route packet as is
|
||||
return nil
|
||||
}
|
||||
|
||||
pkt, err := t.encoder.Encode(tdata.frame, tdata.pts)
|
||||
pkt, err := t.encoder.Encode(tdata.Frame, tdata.PTS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tdata.rtpPackets = []*rtp.Packet{pkt}
|
||||
tdata.RTPPackets = []*rtp.Packet{pkt}
|
||||
return nil
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package core //nolint:dupl
|
||||
package formatprocessor //nolint:dupl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -9,19 +9,22 @@ import (
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
type dataVP8 struct {
|
||||
rtpPackets []*rtp.Packet
|
||||
ntp time.Time
|
||||
pts time.Duration
|
||||
frame []byte
|
||||
// DataVP8 is a VP8 data unit.
|
||||
type DataVP8 struct {
|
||||
RTPPackets []*rtp.Packet
|
||||
NTP time.Time
|
||||
PTS time.Duration
|
||||
Frame []byte
|
||||
}
|
||||
|
||||
func (d *dataVP8) getRTPPackets() []*rtp.Packet {
|
||||
return d.rtpPackets
|
||||
// GetRTPPackets implements Data.
|
||||
func (d *DataVP8) GetRTPPackets() []*rtp.Packet {
|
||||
return d.RTPPackets
|
||||
}
|
||||
|
||||
func (d *dataVP8) getNTP() time.Time {
|
||||
return d.ntp
|
||||
// GetNTP implements Data.
|
||||
func (d *DataVP8) GetNTP() time.Time {
|
||||
return d.NTP
|
||||
}
|
||||
|
||||
type formatProcessorVP8 struct {
|
||||
@@ -30,7 +33,7 @@ type formatProcessorVP8 struct {
|
||||
decoder *rtpvp8.Decoder
|
||||
}
|
||||
|
||||
func newFormatProcessorVP8(
|
||||
func newVP8(
|
||||
forma *format.VP8,
|
||||
allocateEncoder bool,
|
||||
) (*formatProcessorVP8, error) {
|
||||
@@ -45,11 +48,11 @@ func newFormatProcessorVP8(
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (t *formatProcessorVP8) process(dat data, hasNonRTSPReaders bool) error { //nolint:dupl
|
||||
tdata := dat.(*dataVP8)
|
||||
func (t *formatProcessorVP8) Process(dat Data, hasNonRTSPReaders bool) error { //nolint:dupl
|
||||
tdata := dat.(*DataVP8)
|
||||
|
||||
if tdata.rtpPackets != nil {
|
||||
pkt := tdata.rtpPackets[0]
|
||||
if tdata.RTPPackets != nil {
|
||||
pkt := tdata.RTPPackets[0]
|
||||
|
||||
// remove padding
|
||||
pkt.Header.Padding = false
|
||||
@@ -66,7 +69,7 @@ func (t *formatProcessorVP8) process(dat data, hasNonRTSPReaders bool) error { /
|
||||
t.decoder = t.format.CreateDecoder()
|
||||
}
|
||||
|
||||
frame, pts, err := t.decoder.Decode(pkt)
|
||||
frame, PTS, err := t.decoder.Decode(pkt)
|
||||
if err != nil {
|
||||
if err == rtpvp8.ErrMorePacketsNeeded {
|
||||
return nil
|
||||
@@ -74,19 +77,19 @@ func (t *formatProcessorVP8) process(dat data, hasNonRTSPReaders bool) error { /
|
||||
return err
|
||||
}
|
||||
|
||||
tdata.frame = frame
|
||||
tdata.pts = pts
|
||||
tdata.Frame = frame
|
||||
tdata.PTS = PTS
|
||||
}
|
||||
|
||||
// route packet as is
|
||||
return nil
|
||||
}
|
||||
|
||||
pkts, err := t.encoder.Encode(tdata.frame, tdata.pts)
|
||||
pkts, err := t.encoder.Encode(tdata.Frame, tdata.PTS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tdata.rtpPackets = pkts
|
||||
tdata.RTPPackets = pkts
|
||||
return nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package core //nolint:dupl
|
||||
package formatprocessor //nolint:dupl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -9,19 +9,22 @@ import (
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
type dataVP9 struct {
|
||||
rtpPackets []*rtp.Packet
|
||||
ntp time.Time
|
||||
pts time.Duration
|
||||
frame []byte
|
||||
// DataVP9 is a VP9 data unit.
|
||||
type DataVP9 struct {
|
||||
RTPPackets []*rtp.Packet
|
||||
NTP time.Time
|
||||
PTS time.Duration
|
||||
Frame []byte
|
||||
}
|
||||
|
||||
func (d *dataVP9) getRTPPackets() []*rtp.Packet {
|
||||
return d.rtpPackets
|
||||
// GetRTPPackets implements Data.
|
||||
func (d *DataVP9) GetRTPPackets() []*rtp.Packet {
|
||||
return d.RTPPackets
|
||||
}
|
||||
|
||||
func (d *dataVP9) getNTP() time.Time {
|
||||
return d.ntp
|
||||
// GetNTP implements Data.
|
||||
func (d *DataVP9) GetNTP() time.Time {
|
||||
return d.NTP
|
||||
}
|
||||
|
||||
type formatProcessorVP9 struct {
|
||||
@@ -30,7 +33,7 @@ type formatProcessorVP9 struct {
|
||||
decoder *rtpvp9.Decoder
|
||||
}
|
||||
|
||||
func newFormatProcessorVP9(
|
||||
func newVP9(
|
||||
forma *format.VP9,
|
||||
allocateEncoder bool,
|
||||
) (*formatProcessorVP9, error) {
|
||||
@@ -45,11 +48,11 @@ func newFormatProcessorVP9(
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (t *formatProcessorVP9) process(dat data, hasNonRTSPReaders bool) error { //nolint:dupl
|
||||
tdata := dat.(*dataVP9)
|
||||
func (t *formatProcessorVP9) Process(dat Data, hasNonRTSPReaders bool) error { //nolint:dupl
|
||||
tdata := dat.(*DataVP9)
|
||||
|
||||
if tdata.rtpPackets != nil {
|
||||
pkt := tdata.rtpPackets[0]
|
||||
if tdata.RTPPackets != nil {
|
||||
pkt := tdata.RTPPackets[0]
|
||||
|
||||
// remove padding
|
||||
pkt.Header.Padding = false
|
||||
@@ -66,7 +69,7 @@ func (t *formatProcessorVP9) process(dat data, hasNonRTSPReaders bool) error { /
|
||||
t.decoder = t.format.CreateDecoder()
|
||||
}
|
||||
|
||||
frame, pts, err := t.decoder.Decode(pkt)
|
||||
frame, PTS, err := t.decoder.Decode(pkt)
|
||||
if err != nil {
|
||||
if err == rtpvp9.ErrMorePacketsNeeded {
|
||||
return nil
|
||||
@@ -74,19 +77,19 @@ func (t *formatProcessorVP9) process(dat data, hasNonRTSPReaders bool) error { /
|
||||
return err
|
||||
}
|
||||
|
||||
tdata.frame = frame
|
||||
tdata.pts = pts
|
||||
tdata.Frame = frame
|
||||
tdata.PTS = PTS
|
||||
}
|
||||
|
||||
// route packet as is
|
||||
return nil
|
||||
}
|
||||
|
||||
pkts, err := t.encoder.Encode(tdata.frame, tdata.pts)
|
||||
pkts, err := t.encoder.Encode(tdata.Frame, tdata.PTS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tdata.rtpPackets = pkts
|
||||
tdata.RTPPackets = pkts
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user