Browse Source

move format processors into dedicated folder

pull/1364/head
aler9 3 years ago
parent
commit
e3fff72b7c
  1. 13
      internal/core/data.go
  2. 34
      internal/core/formatprocessor.go
  3. 91
      internal/core/formatprocessor_generic_test.go
  4. 450
      internal/core/formatprocessor_h264_test.go
  5. 51
      internal/core/hls_muxer.go
  6. 17
      internal/core/hls_source.go
  7. 9
      internal/core/rpicamera_source.go
  8. 63
      internal/core/rtmp_conn.go
  9. 17
      internal/core/rtmp_source.go
  10. 31
      internal/core/rtsp_server_test.go
  11. 43
      internal/core/rtsp_session.go
  12. 43
      internal/core/rtsp_source.go
  13. 6
      internal/core/stream.go
  14. 20
      internal/core/streamformat.go
  15. 49
      internal/core/webrtc_conn.go
  16. 27
      internal/formatprocessor/generic.go
  17. 51
      internal/formatprocessor/generic_test.go
  18. 51
      internal/formatprocessor/h264.go
  19. 140
      internal/formatprocessor/h264_test.go
  20. 51
      internal/formatprocessor/h265.go
  21. 125
      internal/formatprocessor/h265_test.go
  22. 43
      internal/formatprocessor/mpeg4audio.go
  23. 43
      internal/formatprocessor/opus.go
  24. 48
      internal/formatprocessor/processor.go
  25. 43
      internal/formatprocessor/vp8.go
  26. 43
      internal/formatprocessor/vp9.go

13
internal/core/data.go

@ -1,13 +0,0 @@ @@ -1,13 +0,0 @@
package core
import (
"time"
"github.com/pion/rtp"
)
// data is the data unit routed across the server.
type data interface {
getRTPPackets() []*rtp.Packet
getNTP() time.Time
}

34
internal/core/formatprocessor.go

@ -1,34 +0,0 @@ @@ -1,34 +0,0 @@
package core
import (
"github.com/aler9/gortsplib/v2/pkg/format"
)
type formatProcessor interface {
process(data, bool) error
}
func newFormatProcessor(forma format.Format, generateRTPPackets bool) (formatProcessor, error) {
switch forma := forma.(type) {
case *format.H264:
return newFormatProcessorH264(forma, generateRTPPackets)
case *format.H265:
return newFormatProcessorH265(forma, generateRTPPackets)
case *format.VP8:
return newFormatProcessorVP8(forma, generateRTPPackets)
case *format.VP9:
return newFormatProcessorVP9(forma, generateRTPPackets)
case *format.MPEG4Audio:
return newFormatProcessorMPEG4Audio(forma, generateRTPPackets)
case *format.Opus:
return newFormatProcessorOpus(forma, generateRTPPackets)
default:
return newFormatProcessorGeneric(forma, generateRTPPackets)
}
}

91
internal/core/formatprocessor_generic_test.go

@ -1,91 +0,0 @@ @@ -1,91 +0,0 @@
package core
import (
"testing"
"github.com/aler9/gortsplib/v2"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/media"
"github.com/aler9/gortsplib/v2/pkg/url"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
)
func TestFormatProcessorRemovePadding(t *testing.T) {
p, ok := newInstance("rtmpDisable: yes\n" +
"hlsDisable: yes\n" +
"webrtcDisable: yes\n" +
"paths:\n" +
" all:\n")
require.Equal(t, true, ok)
defer p.Close()
forma := &format.Generic{
PayloadTyp: 96,
RTPMap: "private/90000",
}
forma.Init()
medi := &media.Media{
Type: media.TypeApplication,
Formats: []format.Format{forma},
}
source := gortsplib.Client{}
err := source.StartRecording(
"rtsp://localhost:8554/stream",
media.Medias{medi})
require.NoError(t, err)
defer source.Close()
c := gortsplib.Client{}
u, err := url.Parse("rtsp://127.0.0.1:8554/stream")
require.NoError(t, err)
err = c.Start(u.Scheme, u.Host)
require.NoError(t, err)
defer c.Close()
medias, baseURL, _, err := c.Describe(u)
require.NoError(t, err)
err = c.SetupAll(medias, baseURL)
require.NoError(t, err)
packetRecv := make(chan struct{})
c.OnPacketRTP(medias[0], medias[0].Formats[0], func(pkt *rtp.Packet) {
require.Equal(t, &rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 123,
Timestamp: 45343,
SSRC: 563423,
CSRC: []uint32{},
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
}, pkt)
close(packetRecv)
})
_, err = c.Play(nil)
require.NoError(t, err)
source.WritePacketRTP(medi, &rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 123,
Timestamp: 45343,
SSRC: 563423,
Padding: true,
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
PaddingSize: 20,
})
<-packetRecv
}

450
internal/core/formatprocessor_h264_test.go

@ -1,450 +0,0 @@ @@ -1,450 +0,0 @@
package core
import (
"bytes"
"net"
"testing"
"github.com/aler9/gortsplib/v2"
"github.com/aler9/gortsplib/v2/pkg/base"
"github.com/aler9/gortsplib/v2/pkg/codecs/h265"
"github.com/aler9/gortsplib/v2/pkg/conn"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/headers"
"github.com/aler9/gortsplib/v2/pkg/media"
"github.com/aler9/gortsplib/v2/pkg/url"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
)
func TestFormatProcessorDynamicParams(t *testing.T) {
checkTrack := func(t *testing.T, forma format.Format) {
c := gortsplib.Client{}
u, err := url.Parse("rtsp://127.0.0.1:8554/stream")
require.NoError(t, err)
err = c.Start(u.Scheme, u.Host)
require.NoError(t, err)
defer c.Close()
medias, _, _, err := c.Describe(u)
require.NoError(t, err)
forma1 := medias[0].Formats[0]
require.Equal(t, forma, forma1)
}
for _, ca := range []string{"h264", "h265"} {
t.Run(ca, func(t *testing.T) {
p, ok := newInstance("rtmpDisable: yes\n" +
"hlsDisable: yes\n" +
"webrtcDisable: yes\n" +
"paths:\n" +
" all:\n")
require.Equal(t, true, ok)
defer p.Close()
formah264 := &format.H264{
PayloadTyp: 96,
PacketizationMode: 1,
}
formah265 := &format.H265{
PayloadTyp: 96,
}
var forma format.Format
if ca == "h264" {
forma = formah264
} else {
forma = formah265
}
medi := &media.Media{
Type: media.TypeVideo,
Formats: []format.Format{forma},
}
source := gortsplib.Client{}
err := source.StartRecording(
"rtsp://localhost:8554/stream",
media.Medias{medi})
require.NoError(t, err)
defer source.Close()
if ca == "h264" {
enc := formah264.CreateEncoder()
pkts, err := enc.Encode([][]byte{{7, 1, 2, 3}}, 0) // SPS
require.NoError(t, err)
source.WritePacketRTP(medi, pkts[0])
pkts, err = enc.Encode([][]byte{{8}}, 0) // PPS
require.NoError(t, err)
source.WritePacketRTP(medi, pkts[0])
checkTrack(t, &format.H264{
PayloadTyp: 96,
PacketizationMode: 1,
SPS: []byte{7, 1, 2, 3},
PPS: []byte{8},
})
pkts, err = enc.Encode([][]byte{{7, 4, 5, 6}}, 0) // SPS
require.NoError(t, err)
source.WritePacketRTP(medi, pkts[0])
pkts, err = enc.Encode([][]byte{{8, 1}}, 0) // PPS
require.NoError(t, err)
source.WritePacketRTP(medi, pkts[0])
checkTrack(t, &format.H264{
PayloadTyp: 96,
PacketizationMode: 1,
SPS: []byte{7, 4, 5, 6},
PPS: []byte{8, 1},
})
} else {
enc := formah265.CreateEncoder()
pkts, err := enc.Encode([][]byte{{byte(h265.NALUType_VPS_NUT) << 1, 1, 2, 3}}, 0)
require.NoError(t, err)
source.WritePacketRTP(medi, pkts[0])
pkts, err = enc.Encode([][]byte{{byte(h265.NALUType_SPS_NUT) << 1, 4, 5, 6}}, 0)
require.NoError(t, err)
source.WritePacketRTP(medi, pkts[0])
pkts, err = enc.Encode([][]byte{{byte(h265.NALUType_PPS_NUT) << 1, 7, 8, 9}}, 0)
require.NoError(t, err)
source.WritePacketRTP(medi, pkts[0])
checkTrack(t, &format.H265{
PayloadTyp: 96,
VPS: []byte{byte(h265.NALUType_VPS_NUT) << 1, 1, 2, 3},
SPS: []byte{byte(h265.NALUType_SPS_NUT) << 1, 4, 5, 6},
PPS: []byte{byte(h265.NALUType_PPS_NUT) << 1, 7, 8, 9},
})
pkts, err = enc.Encode([][]byte{{byte(h265.NALUType_VPS_NUT) << 1, 10, 11, 12}}, 0)
require.NoError(t, err)
source.WritePacketRTP(medi, pkts[0])
pkts, err = enc.Encode([][]byte{{byte(h265.NALUType_SPS_NUT) << 1, 13, 14, 15}}, 0)
require.NoError(t, err)
source.WritePacketRTP(medi, pkts[0])
pkts, err = enc.Encode([][]byte{{byte(h265.NALUType_PPS_NUT) << 1, 16, 17, 18}}, 0)
require.NoError(t, err)
source.WritePacketRTP(medi, pkts[0])
checkTrack(t, &format.H265{
PayloadTyp: 96,
VPS: []byte{byte(h265.NALUType_VPS_NUT) << 1, 10, 11, 12},
SPS: []byte{byte(h265.NALUType_SPS_NUT) << 1, 13, 14, 15},
PPS: []byte{byte(h265.NALUType_PPS_NUT) << 1, 16, 17, 18},
})
}
})
}
}
func TestFormatProcessorOversizedPackets(t *testing.T) {
for _, ca := range []string{"h264", "h265"} {
t.Run(ca, func(t *testing.T) {
p, ok := newInstance("rtmpDisable: yes\n" +
"hlsDisable: yes\n" +
"webrtcDisable: yes\n" +
"paths:\n" +
" all:\n")
require.Equal(t, true, ok)
defer p.Close()
nconn, err := net.Dial("tcp", "localhost:8554")
require.NoError(t, err)
defer nconn.Close()
conn := conn.NewConn(nconn)
var forma format.Format
if ca == "h264" {
forma = &format.H264{
PayloadTyp: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
}
} else {
forma = &format.H265{
PayloadTyp: 96,
VPS: []byte{byte(h265.NALUType_VPS_NUT) << 1, 10, 11, 12},
SPS: []byte{byte(h265.NALUType_SPS_NUT) << 1, 13, 14, 15},
PPS: []byte{byte(h265.NALUType_PPS_NUT) << 1, 16, 17, 18},
}
}
medi := &media.Media{
Type: media.TypeVideo,
Formats: []format.Format{forma},
}
medias := media.Medias{medi}
medias.SetControls()
res, err := writeReqReadRes(conn, base.Request{
Method: base.Announce,
URL: mustParseURL("rtsp://localhost:8554/stream"),
Header: base.Header{
"CSeq": base.HeaderValue{"1"},
"Content-Type": base.HeaderValue{"application/sdp"},
},
Body: mustMarshalSDP(medias.Marshal(false)),
})
require.NoError(t, err)
require.Equal(t, base.StatusOK, res.StatusCode)
var sx headers.Session
inTH := &headers.Transport{
Delivery: func() *headers.TransportDelivery {
v := headers.TransportDeliveryUnicast
return &v
}(),
Mode: func() *headers.TransportMode {
v := headers.TransportModeRecord
return &v
}(),
Protocol: headers.TransportProtocolTCP,
InterleavedIDs: &[2]int{0, 1},
}
res, err = writeReqReadRes(conn, base.Request{
Method: base.Setup,
URL: mustParseURL("rtsp://localhost:8554/stream/" + medias[0].Control),
Header: base.Header{
"CSeq": base.HeaderValue{"2"},
"Transport": inTH.Marshal(),
},
})
require.NoError(t, err)
require.Equal(t, base.StatusOK, res.StatusCode)
err = sx.Unmarshal(res.Header["Session"])
require.NoError(t, err)
res, err = writeReqReadRes(conn, base.Request{
Method: base.Record,
URL: mustParseURL("rtsp://localhost:8554/stream"),
Header: base.Header{
"CSeq": base.HeaderValue{"3"},
"Session": base.HeaderValue{sx.Session},
},
})
require.NoError(t, err)
require.Equal(t, base.StatusOK, res.StatusCode)
c := gortsplib.Client{}
u, err := url.Parse("rtsp://127.0.0.1:8554/stream")
require.NoError(t, err)
err = c.Start(u.Scheme, u.Host)
require.NoError(t, err)
defer c.Close()
medias, baseURL, _, err := c.Describe(u)
require.NoError(t, err)
err = c.SetupAll(medias, baseURL)
require.NoError(t, err)
packetRecv := make(chan struct{})
i := 0
var expected []*rtp.Packet
if ca == "h264" {
expected = []*rtp.Packet{
{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 123,
Timestamp: 45343,
SSRC: 563423,
CSRC: []uint32{},
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
},
{
Header: rtp.Header{
Version: 2,
Marker: false,
PayloadType: 96,
SequenceNumber: 124,
Timestamp: 45343,
SSRC: 563423,
CSRC: []uint32{},
},
Payload: append(
append([]byte{0x1c, 0x80}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 364)...),
[]byte{0x01, 0x02}...,
),
},
{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 125,
Timestamp: 45343,
SSRC: 563423,
CSRC: []uint32{},
},
Payload: append(
[]byte{0x1c, 0x40, 0x03, 0x04},
bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 136)...,
),
},
}
} else {
expected = []*rtp.Packet{
{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 123,
Timestamp: 45343,
SSRC: 563423,
CSRC: []uint32{},
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
},
{
Header: rtp.Header{
Version: 2,
Marker: false,
PayloadType: 96,
SequenceNumber: 124,
Timestamp: 45343,
SSRC: 563423,
CSRC: []uint32{},
},
Payload: append(
append([]byte{0x63, 0x02, 0x80, 0x03, 0x04}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 363)...),
[]byte{0x01, 0x02, 0x03}...,
),
},
{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 125,
Timestamp: 45343,
SSRC: 563423,
CSRC: []uint32{},
},
Payload: append(
[]byte{0x63, 0x02, 0x40, 0x04},
bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 135)...,
),
},
}
}
c.OnPacketRTP(medias[0], medias[0].Formats[0], func(pkt *rtp.Packet) {
require.Equal(t, expected[i], pkt)
i++
if i >= len(expected) {
close(packetRecv)
}
})
_, err = c.Play(nil)
require.NoError(t, err)
var tosend []*rtp.Packet
if ca == "h264" {
tosend = []*rtp.Packet{
{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 123,
Timestamp: 45343,
SSRC: 563423,
Padding: true,
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
},
{
Header: rtp.Header{
Version: 2,
Marker: false,
PayloadType: 96,
SequenceNumber: 124,
Timestamp: 45343,
SSRC: 563423,
Padding: true,
},
Payload: append([]byte{0x1c, 0b10000000}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 2000/4)...),
},
{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 125,
Timestamp: 45343,
SSRC: 563423,
Padding: true,
},
Payload: []byte{0x1c, 0b01000000, 0x01, 0x02, 0x03, 0x04},
},
}
} else {
tosend = []*rtp.Packet{
{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 123,
Timestamp: 45343,
SSRC: 563423,
Padding: true,
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
},
{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 124,
Timestamp: 45343,
SSRC: 563423,
Padding: true,
},
Payload: bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 2000/4),
},
}
}
for _, pkt := range tosend {
byts, _ := pkt.Marshal()
err = conn.WriteInterleavedFrame(&base.InterleavedFrame{
Channel: 0,
Payload: byts,
}, make([]byte, 2048))
require.NoError(t, err)
}
<-packetRecv
})
}
}

51
internal/core/hls_muxer.go

@ -19,6 +19,7 @@ import ( @@ -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) @@ -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) @@ -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) @@ -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) @@ -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)
}

17
internal/core/hls_source.go

@ -7,6 +7,7 @@ import ( @@ -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 { @@ -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 { @@ -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)

9
internal/core/rpicamera_source.go

@ -7,6 +7,7 @@ import ( @@ -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 { @@ -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)

63
internal/core/rtmp_conn.go

@ -20,6 +20,7 @@ import ( @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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
internal/core/rtmp_source.go

@ -17,6 +17,7 @@ import ( @@ -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 { @@ -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 { @@ -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)

31
internal/core/rtsp_server_test.go

@ -4,43 +4,12 @@ import ( @@ -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",

43
internal/core/rtsp_session.go

@ -17,6 +17,7 @@ import ( @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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)

43
internal/core/rtsp_source.go

@ -16,6 +16,7 @@ import ( @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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)

6
internal/core/stream.go

@ -4,6 +4,8 @@ import ( @@ -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 { @@ -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) { @@ -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)

20
internal/core/streamformat.go

@ -6,29 +6,31 @@ import ( @@ -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) { @@ -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

49
internal/core/webrtc_conn.go

@ -29,6 +29,7 @@ import ( @@ -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 { @@ -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: @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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)
}
},

27
internal/core/formatprocessor_generic.go → internal/formatprocessor/generic.go

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
package core
package formatprocessor
import (
"fmt"
@ -13,22 +13,25 @@ const ( @@ -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 @@ -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

51
internal/formatprocessor/generic_test.go

@ -0,0 +1,51 @@ @@ -0,0 +1,51 @@
package formatprocessor
import (
"testing"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
)
func TestGenericRemovePadding(t *testing.T) {
forma := &format.Generic{
PayloadTyp: 96,
RTPMap: "private/90000",
}
forma.Init()
p, err := New(forma, false)
require.NoError(t, err)
pkt := &rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 123,
Timestamp: 45343,
SSRC: 563423,
Padding: true,
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
PaddingSize: 20,
}
err = p.Process(&DataGeneric{
RTPPackets: []*rtp.Packet{pkt},
}, false)
require.NoError(t, err)
require.Equal(t, &rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 123,
Timestamp: 45343,
SSRC: 563423,
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
}, pkt)
}

51
internal/core/formatprocessor_h264.go → internal/formatprocessor/h264.go

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
package core
package formatprocessor
import (
"bytes"
@ -67,19 +67,22 @@ func rtpH264ExtractSPSPPS(pkt *rtp.Packet) ([]byte, []byte) { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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
}

140
internal/formatprocessor/h264_test.go

@ -0,0 +1,140 @@ @@ -0,0 +1,140 @@
package formatprocessor
import (
"bytes"
"testing"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
)
func TestH264DynamicParams(t *testing.T) {
forma := &format.H264{
PayloadTyp: 96,
PacketizationMode: 1,
}
p, err := New(forma, false)
require.NoError(t, err)
enc := forma.CreateEncoder()
pkts, err := enc.Encode([][]byte{{7, 1, 2, 3}}, 0) // SPS
require.NoError(t, err)
p.Process(&DataH264{RTPPackets: []*rtp.Packet{pkts[0]}}, false)
pkts, err = enc.Encode([][]byte{{8}}, 0) // PPS
require.NoError(t, err)
p.Process(&DataH264{RTPPackets: []*rtp.Packet{pkts[0]}}, false)
pkts, err = enc.Encode([][]byte{{7, 4, 5, 6}}, 0) // SPS
require.NoError(t, err)
p.Process(&DataH264{RTPPackets: []*rtp.Packet{pkts[0]}}, false)
pkts, err = enc.Encode([][]byte{{8, 1}}, 0) // PPS
require.NoError(t, err)
p.Process(&DataH264{RTPPackets: []*rtp.Packet{pkts[0]}}, false)
require.Equal(t, []byte{7, 4, 5, 6}, forma.SPS)
require.Equal(t, []byte{8, 1}, forma.PPS)
}
func TestH264OversizedPackets(t *testing.T) {
forma := &format.H264{
PayloadTyp: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
}
p, err := New(forma, false)
require.NoError(t, err)
var out []*rtp.Packet
for _, pkt := range []*rtp.Packet{
{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 123,
Timestamp: 45343,
SSRC: 563423,
Padding: true,
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
},
{
Header: rtp.Header{
Version: 2,
Marker: false,
PayloadType: 96,
SequenceNumber: 124,
Timestamp: 45343,
SSRC: 563423,
Padding: true,
},
Payload: append([]byte{0x1c, 0b10000000}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 2000/4)...),
},
{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 125,
Timestamp: 45343,
SSRC: 563423,
Padding: true,
},
Payload: []byte{0x1c, 0b01000000, 0x01, 0x02, 0x03, 0x04},
},
} {
data := &DataH264{RTPPackets: []*rtp.Packet{pkt}}
p.Process(data, false)
out = append(out, data.RTPPackets...)
}
require.Equal(t, []*rtp.Packet{
{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 123,
Timestamp: 45343,
SSRC: 563423,
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
},
{
Header: rtp.Header{
Version: 2,
Marker: false,
PayloadType: 96,
SequenceNumber: 124,
Timestamp: 45343,
SSRC: 563423,
},
Payload: append(
append([]byte{0x1c, 0x80}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 364)...),
[]byte{0x01, 0x02}...,
),
},
{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 125,
Timestamp: 45343,
SSRC: 563423,
},
Payload: append(
[]byte{0x1c, 0x40, 0x03, 0x04},
bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 136)...,
),
},
}, out)
}

51
internal/core/formatprocessor_h265.go → internal/formatprocessor/h265.go

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
package core
package formatprocessor
import (
"bytes"
@ -74,19 +74,22 @@ func rtpH265ExtractVPSSPSPPS(pkt *rtp.Packet) ([]byte, []byte, []byte) { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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
}

125
internal/formatprocessor/h265_test.go

@ -0,0 +1,125 @@ @@ -0,0 +1,125 @@
package formatprocessor
import (
"bytes"
"testing"
"github.com/aler9/gortsplib/v2/pkg/codecs/h265"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
)
func TestH265DynamicParams(t *testing.T) {
forma := &format.H265{
PayloadTyp: 96,
}
p, err := New(forma, false)
require.NoError(t, err)
enc := forma.CreateEncoder()
pkts, err := enc.Encode([][]byte{{byte(h265.NALUType_VPS_NUT) << 1, 1, 2, 3}}, 0)
require.NoError(t, err)
p.Process(&DataH265{RTPPackets: []*rtp.Packet{pkts[0]}}, false)
pkts, err = enc.Encode([][]byte{{byte(h265.NALUType_SPS_NUT) << 1, 4, 5, 6}}, 0)
require.NoError(t, err)
p.Process(&DataH265{RTPPackets: []*rtp.Packet{pkts[0]}}, false)
pkts, err = enc.Encode([][]byte{{byte(h265.NALUType_PPS_NUT) << 1, 7, 8, 9}}, 0)
require.NoError(t, err)
p.Process(&DataH265{RTPPackets: []*rtp.Packet{pkts[0]}}, false)
require.Equal(t, []byte{byte(h265.NALUType_VPS_NUT) << 1, 1, 2, 3}, forma.VPS)
require.Equal(t, []byte{byte(h265.NALUType_SPS_NUT) << 1, 4, 5, 6}, forma.SPS)
require.Equal(t, []byte{byte(h265.NALUType_PPS_NUT) << 1, 7, 8, 9}, forma.PPS)
}
func TestH265OversizedPackets(t *testing.T) {
forma := &format.H265{
PayloadTyp: 96,
VPS: []byte{byte(h265.NALUType_VPS_NUT) << 1, 10, 11, 12},
SPS: []byte{byte(h265.NALUType_SPS_NUT) << 1, 13, 14, 15},
PPS: []byte{byte(h265.NALUType_PPS_NUT) << 1, 16, 17, 18},
}
p, err := New(forma, false)
require.NoError(t, err)
var out []*rtp.Packet
for _, pkt := range []*rtp.Packet{
{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 123,
Timestamp: 45343,
SSRC: 563423,
Padding: true,
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
},
{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 124,
Timestamp: 45343,
SSRC: 563423,
Padding: true,
},
Payload: bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 2000/4),
},
} {
data := &DataH265{RTPPackets: []*rtp.Packet{pkt}}
p.Process(data, false)
out = append(out, data.RTPPackets...)
}
require.Equal(t, []*rtp.Packet{
{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 123,
Timestamp: 45343,
SSRC: 563423,
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
},
{
Header: rtp.Header{
Version: 2,
Marker: false,
PayloadType: 96,
SequenceNumber: 124,
Timestamp: 45343,
SSRC: 563423,
},
Payload: append(
append([]byte{0x63, 0x02, 0x80, 0x03, 0x04}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 363)...),
[]byte{0x01, 0x02, 0x03}...,
),
},
{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 125,
Timestamp: 45343,
SSRC: 563423,
},
Payload: append(
[]byte{0x63, 0x02, 0x40, 0x04},
bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 135)...,
),
},
}, out)
}

43
internal/core/formatprocessor_mpeg4audio.go → internal/formatprocessor/mpeg4audio.go

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
package core
package formatprocessor
import (
"fmt"
@ -9,19 +9,22 @@ import ( @@ -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 { @@ -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( @@ -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 @@ -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 @@ -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
}

43
internal/core/formatprocessor_opus.go → internal/formatprocessor/opus.go

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
package core
package formatprocessor
import (
"fmt"
@ -9,19 +9,22 @@ import ( @@ -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 { @@ -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( @@ -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 { @@ -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
}

48
internal/formatprocessor/processor.go

@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
// Package formatprocessor contains code to cleanup and normalize streams.
package formatprocessor
import (
"time"
"github.com/pion/rtp"
"github.com/aler9/gortsplib/v2/pkg/format"
)
// Data is the elementary data unit routed across the server.
type Data interface {
GetRTPPackets() []*rtp.Packet
GetNTP() time.Time
}
// Processor allows to cleanup and normalize streams.
type Processor interface {
// cleanups and normalizes a data unit.
Process(Data, bool) error
}
// New allocates a Processor.
func New(forma format.Format, generateRTPPackets bool) (Processor, error) {
switch forma := forma.(type) {
case *format.H264:
return newH264(forma, generateRTPPackets)
case *format.H265:
return newH265(forma, generateRTPPackets)
case *format.VP8:
return newVP8(forma, generateRTPPackets)
case *format.VP9:
return newVP9(forma, generateRTPPackets)
case *format.MPEG4Audio:
return newMPEG4Audio(forma, generateRTPPackets)
case *format.Opus:
return newOpus(forma, generateRTPPackets)
default:
return newGeneric(forma, generateRTPPackets)
}
}

43
internal/core/formatprocessor_vp8.go → internal/formatprocessor/vp8.go

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
package core //nolint:dupl
package formatprocessor //nolint:dupl
import (
"fmt"
@ -9,19 +9,22 @@ import ( @@ -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 { @@ -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( @@ -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 { / @@ -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 { / @@ -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
}

43
internal/core/formatprocessor_vp9.go → internal/formatprocessor/vp9.go

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
package core //nolint:dupl
package formatprocessor //nolint:dupl
import (
"fmt"
@ -9,19 +9,22 @@ import ( @@ -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 { @@ -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( @@ -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 { / @@ -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 { / @@ -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
}
Loading…
Cancel
Save