switch to gortsplib/v4 (#2244)

This commit is contained in:
Alessandro Ros
2023-08-26 18:54:28 +02:00
committed by GitHub
parent 055767fef0
commit cf86dbb303
79 changed files with 1241 additions and 1325 deletions

View File

@@ -52,11 +52,11 @@ linters-settings:
- (github.com/datarhei/gosrt.Conn).Close
- (github.com/datarhei/gosrt.Conn).SetReadDeadline
- (github.com/datarhei/gosrt.Conn).SetWriteDeadline
- (*github.com/bluenviron/gortsplib/v3.Client).Close
- (*github.com/bluenviron/gortsplib/v3.Server).Close
- (*github.com/bluenviron/gortsplib/v3.ServerSession).Close
- (*github.com/bluenviron/gortsplib/v3.ServerStream).Close
- (*github.com/bluenviron/gortsplib/v3.ServerConn).Close
- (*github.com/bluenviron/gortsplib/v4.Client).Close
- (*github.com/bluenviron/gortsplib/v4.Server).Close
- (*github.com/bluenviron/gortsplib/v4.ServerSession).Close
- (*github.com/bluenviron/gortsplib/v4.ServerStream).Close
- (*github.com/bluenviron/gortsplib/v4.ServerConn).Close
govet:
enable-all: true

2
go.mod
View File

@@ -7,7 +7,7 @@ require (
github.com/abema/go-mp4 v0.13.0
github.com/alecthomas/kong v0.8.0
github.com/bluenviron/gohlslib v1.0.0
github.com/bluenviron/gortsplib/v3 v3.10.0
github.com/bluenviron/gortsplib/v4 v4.0.0-20230826160945-3bdae4ed4630
github.com/bluenviron/mediacommon v1.0.0
github.com/datarhei/gosrt v0.5.3
github.com/fsnotify/fsnotify v1.6.0

4
go.sum
View File

@@ -16,8 +16,8 @@ github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c h1:8XZeJrs4+ZYh
github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c/go.mod h1:x1vxHcL/9AVzuk5HOloOEPrtJY0MaalYr78afXZ+pWI=
github.com/bluenviron/gohlslib v1.0.0 h1:UOI7wW7EdXPnnoflPL+WRiUB+bDSyrR9AXtu029n5EY=
github.com/bluenviron/gohlslib v1.0.0/go.mod h1:fwqXogd2G/CJ/0kD6TTALmWI3KAm66nZoI+06O02YKI=
github.com/bluenviron/gortsplib/v3 v3.10.0 h1:E2ytPD1/b6JgzHYVSsyaG2xtXsvaGw9sxTdZ0Wnwsd4=
github.com/bluenviron/gortsplib/v3 v3.10.0/go.mod h1:prNU1aMVBmgmmKwlvLiEdjBbTEpTw4BRsqVcqEARgMY=
github.com/bluenviron/gortsplib/v4 v4.0.0-20230826160945-3bdae4ed4630 h1:Xe2em0Hv8ftQ1G3YgKVIcnor63h+OuXBHL7sNDMpfZA=
github.com/bluenviron/gortsplib/v4 v4.0.0-20230826160945-3bdae4ed4630/go.mod h1:UU9EkvmpvyXdRIDBBUK7zdFt2LsdnV4iz9cHP3JaB8s=
github.com/bluenviron/mediacommon v1.0.0 h1:hKelTQKfetasCmXaXMiL1ihID0GRmItyWZt1/pqiKKk=
github.com/bluenviron/mediacommon v1.0.0/go.mod h1:nt5oKCO0WcZ+AH1oc12gs2ldp67xW2vl88c2StNmPlI=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=

View File

@@ -6,7 +6,7 @@ import (
"sort"
"strings"
"github.com/bluenviron/gortsplib/v3/pkg/headers"
"github.com/bluenviron/gortsplib/v4/pkg/headers"
)
// AuthMethods is the authMethods parameter.

View File

@@ -12,8 +12,8 @@ import (
"time"
"github.com/bluenviron/gohlslib"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v3/pkg/headers"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/headers"
"github.com/bluenviron/mediamtx/internal/conf/decrypt"
"github.com/bluenviron/mediamtx/internal/conf/env"

View File

@@ -8,7 +8,7 @@ import (
"testing"
"time"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v4"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/nacl/secretbox"

View File

@@ -11,8 +11,8 @@ import (
"strings"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/headers"
"github.com/bluenviron/gortsplib/v3/pkg/url"
"github.com/bluenviron/gortsplib/v4/pkg/headers"
"github.com/bluenviron/gortsplib/v4/pkg/url"
)
var rePathName = regexp.MustCompile(`^[0-9a-zA-Z_\-/\.~]+$`)

View File

@@ -6,7 +6,7 @@ import (
"sort"
"strings"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v4"
)
// Protocol is a RTSP transport.

View File

@@ -4,7 +4,7 @@ import (
"encoding/json"
"fmt"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v4"
)
// SourceProtocol is the sourceProtocol parameter.

View File

@@ -14,9 +14,9 @@ import (
"testing"
"time"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
"github.com/bluenviron/mediacommon/pkg/formats/mpegts"
"github.com/datarhei/gosrt"
@@ -27,7 +27,7 @@ import (
"github.com/bluenviron/mediamtx/internal/rtmp"
)
var testFormatH264 = &formats.H264{
var testFormatH264 = &format.H264{
PayloadTyp: 96,
SPS: []byte{ // 1920x1080 baseline
0x67, 0x42, 0xc0, 0x28, 0xd9, 0x00, 0x78, 0x02,
@@ -38,9 +38,9 @@ var testFormatH264 = &formats.H264{
PacketizationMode: 1,
}
var testMediaH264 = &media.Media{
Type: media.TypeVideo,
Formats: []formats.Format{testFormatH264},
var testMediaH264 = &description.Media{
Type: description.MediaTypeVideo,
Formats: []format.Format{testFormatH264},
}
func httpRequest(t *testing.T, hc *http.Client, method string, ur string, in interface{}, out interface{}) {
@@ -245,11 +245,11 @@ func TestAPIPathsList(t *testing.T) {
source := gortsplib.Client{}
err := source.StartRecording(
"rtsp://localhost:8554/mypath",
media.Medias{
&description.Session{Medias: []*description.Media{
media0,
{
Type: media.TypeAudio,
Formats: []formats.Format{&formats.MPEG4Audio{
Type: description.MediaTypeAudio,
Formats: []format.Format{&format.MPEG4Audio{
PayloadTyp: 96,
Config: &mpeg4audio.Config{
Type: 2,
@@ -261,7 +261,7 @@ func TestAPIPathsList(t *testing.T) {
IndexDeltaLength: 3,
}},
},
})
}})
require.NoError(t, err)
defer source.Close()
@@ -270,7 +270,7 @@ func TestAPIPathsList(t *testing.T) {
Version: 2,
PayloadType: 96,
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
Payload: []byte{5, 1, 2, 3, 4},
})
require.NoError(t, err)
@@ -286,7 +286,7 @@ func TestAPIPathsList(t *testing.T) {
},
Ready: true,
Tracks: []string{"H264", "MPEG-4 Audio"},
BytesReceived: 16,
BytesReceived: 17,
}},
}, out)
})
@@ -311,29 +311,28 @@ func TestAPIPathsList(t *testing.T) {
hc := &http.Client{Transport: &http.Transport{}}
medias := media.Medias{
{
Type: media.TypeVideo,
Formats: []formats.Format{testFormatH264},
},
{
Type: media.TypeAudio,
Formats: []formats.Format{&formats.MPEG4Audio{
PayloadTyp: 97,
Config: &mpeg4audio.Config{
Type: 2,
SampleRate: 44100,
ChannelCount: 2,
},
SizeLength: 13,
IndexLength: 3,
IndexDeltaLength: 3,
}},
},
}
source := gortsplib.Client{TLSConfig: &tls.Config{InsecureSkipVerify: true}}
err = source.StartRecording("rtsps://localhost:8322/mypath", medias)
err = source.StartRecording("rtsps://localhost:8322/mypath",
&description.Session{Medias: []*description.Media{
{
Type: description.MediaTypeVideo,
Formats: []format.Format{testFormatH264},
},
{
Type: description.MediaTypeAudio,
Formats: []format.Format{&format.MPEG4Audio{
PayloadTyp: 97,
Config: &mpeg4audio.Config{
Type: 2,
SampleRate: 44100,
ChannelCount: 2,
},
SizeLength: 13,
IndexLength: 3,
IndexDeltaLength: 3,
}},
},
}})
require.NoError(t, err)
defer source.Close()
@@ -471,7 +470,8 @@ func TestAPIPathsGet(t *testing.T) {
if ca == "ok" || ca == "ok-nested" {
source := gortsplib.Client{}
err := source.StartRecording("rtsp://localhost:8554/"+pathName, media.Medias{testMediaH264})
err := source.StartRecording("rtsp://localhost:8554/"+pathName,
&description.Session{Medias: []*description.Media{testMediaH264}})
require.NoError(t, err)
defer source.Close()
@@ -546,7 +546,8 @@ func TestAPIProtocolList(t *testing.T) {
case "rtsp conns", "rtsp sessions":
source := gortsplib.Client{}
err := source.StartRecording("rtsp://localhost:8554/mypath", media.Medias{medi})
err := source.StartRecording("rtsp://localhost:8554/mypath",
&description.Session{Medias: []*description.Media{medi}})
require.NoError(t, err)
defer source.Close()
@@ -555,7 +556,8 @@ func TestAPIProtocolList(t *testing.T) {
TLSConfig: &tls.Config{InsecureSkipVerify: true},
}
err := source.StartRecording("rtsps://localhost:8322/mypath", media.Medias{medi})
err := source.StartRecording("rtsps://localhost:8322/mypath",
&description.Session{Medias: []*description.Media{medi}})
require.NoError(t, err)
defer source.Close()
@@ -590,7 +592,7 @@ func TestAPIProtocolList(t *testing.T) {
case "hls":
source := gortsplib.Client{}
err := source.StartRecording("rtsp://localhost:8554/mypath",
media.Medias{medi})
&description.Session{Medias: []*description.Media{medi}})
require.NoError(t, err)
defer source.Close()
@@ -647,7 +649,7 @@ func TestAPIProtocolList(t *testing.T) {
case "webrtc":
source := gortsplib.Client{}
err := source.StartRecording("rtsp://localhost:8554/mypath",
media.Medias{medi})
&description.Session{Medias: []*description.Media{medi}})
require.NoError(t, err)
defer source.Close()
@@ -665,7 +667,7 @@ func TestAPIProtocolList(t *testing.T) {
Timestamp: 45343,
SSRC: 563423,
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
Payload: []byte{5, 1, 2, 3, 4},
})
require.NoError(t, err)
@@ -830,7 +832,8 @@ func TestAPIProtocolGet(t *testing.T) {
case "rtsp conns", "rtsp sessions":
source := gortsplib.Client{}
err := source.StartRecording("rtsp://localhost:8554/mypath", media.Medias{medi})
err := source.StartRecording("rtsp://localhost:8554/mypath",
&description.Session{Medias: []*description.Media{medi}})
require.NoError(t, err)
defer source.Close()
@@ -839,7 +842,8 @@ func TestAPIProtocolGet(t *testing.T) {
TLSConfig: &tls.Config{InsecureSkipVerify: true},
}
err := source.StartRecording("rtsps://localhost:8322/mypath", media.Medias{medi})
err := source.StartRecording("rtsps://localhost:8322/mypath",
&description.Session{Medias: []*description.Media{medi}})
require.NoError(t, err)
defer source.Close()
@@ -874,7 +878,7 @@ func TestAPIProtocolGet(t *testing.T) {
case "hls":
source := gortsplib.Client{}
err := source.StartRecording("rtsp://localhost:8554/mypath",
media.Medias{medi})
&description.Session{Medias: []*description.Media{medi}})
require.NoError(t, err)
defer source.Close()
@@ -931,7 +935,7 @@ func TestAPIProtocolGet(t *testing.T) {
case "webrtc":
source := gortsplib.Client{}
err := source.StartRecording("rtsp://localhost:8554/mypath",
media.Medias{medi})
&description.Session{Medias: []*description.Media{medi}})
require.NoError(t, err)
defer source.Close()
@@ -949,7 +953,7 @@ func TestAPIProtocolGet(t *testing.T) {
Timestamp: 45343,
SSRC: 563423,
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
Payload: []byte{5, 1, 2, 3, 4},
})
require.NoError(t, err)
@@ -1196,7 +1200,7 @@ func TestAPIProtocolKick(t *testing.T) {
source := gortsplib.Client{}
err := source.StartRecording("rtsp://localhost:8554/mypath",
media.Medias{medi})
&description.Session{Medias: []*description.Media{medi}})
require.NoError(t, err)
defer source.Close()
@@ -1206,7 +1210,7 @@ func TestAPIProtocolKick(t *testing.T) {
}
err := source.StartRecording("rtsps://localhost:8322/mypath",
media.Medias{medi})
&description.Session{Medias: []*description.Media{medi}})
require.NoError(t, err)
defer source.Close()

View File

@@ -11,10 +11,10 @@ import (
"net/http"
"strings"
"github.com/bluenviron/gortsplib/v3/pkg/auth"
"github.com/bluenviron/gortsplib/v3/pkg/base"
"github.com/bluenviron/gortsplib/v3/pkg/headers"
"github.com/bluenviron/gortsplib/v3/pkg/url"
"github.com/bluenviron/gortsplib/v4/pkg/auth"
"github.com/bluenviron/gortsplib/v4/pkg/base"
"github.com/bluenviron/gortsplib/v4/pkg/headers"
"github.com/bluenviron/gortsplib/v4/pkg/url"
"github.com/google/uuid"
"github.com/bluenviron/mediamtx/internal/conf"

View File

@@ -9,7 +9,7 @@ import (
"reflect"
"github.com/alecthomas/kong"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v4"
"github.com/gin-gonic/gin"
"github.com/bluenviron/mediamtx/internal/conf"
@@ -213,29 +213,27 @@ func (p *Core) createResources(initial bool) error {
p.externalCmdPool = externalcmd.NewPool()
}
if p.conf.Metrics {
if p.metrics == nil {
p.metrics, err = newMetrics(
p.conf.MetricsAddress,
p.conf.ReadTimeout,
p,
)
if err != nil {
return err
}
if p.conf.Metrics &&
p.metrics == nil {
p.metrics, err = newMetrics(
p.conf.MetricsAddress,
p.conf.ReadTimeout,
p,
)
if err != nil {
return err
}
}
if p.conf.PPROF {
if p.pprof == nil {
p.pprof, err = newPPROF(
p.conf.PPROFAddress,
p.conf.ReadTimeout,
p,
)
if err != nil {
return err
}
if p.conf.PPROF &&
p.pprof == nil {
p.pprof, err = newPPROF(
p.conf.PPROFAddress,
p.conf.ReadTimeout,
p,
)
if err != nil {
return err
}
}
@@ -257,219 +255,212 @@ func (p *Core) createResources(initial bool) error {
if p.conf.RTSP &&
(p.conf.Encryption == conf.EncryptionNo ||
p.conf.Encryption == conf.EncryptionOptional) {
if p.rtspServer == nil {
_, useUDP := p.conf.Protocols[conf.Protocol(gortsplib.TransportUDP)]
_, useMulticast := p.conf.Protocols[conf.Protocol(gortsplib.TransportUDPMulticast)]
p.rtspServer, err = newRTSPServer(
p.conf.RTSPAddress,
p.conf.AuthMethods,
p.conf.ReadTimeout,
p.conf.WriteTimeout,
p.conf.WriteQueueSize,
useUDP,
useMulticast,
p.conf.RTPAddress,
p.conf.RTCPAddress,
p.conf.MulticastIPRange,
p.conf.MulticastRTPPort,
p.conf.MulticastRTCPPort,
false,
"",
"",
p.conf.RTSPAddress,
p.conf.Protocols,
p.conf.RunOnConnect,
p.conf.RunOnConnectRestart,
p.externalCmdPool,
p.metrics,
p.pathManager,
p,
)
if err != nil {
return err
}
p.conf.Encryption == conf.EncryptionOptional) &&
p.rtspServer == nil {
_, useUDP := p.conf.Protocols[conf.Protocol(gortsplib.TransportUDP)]
_, useMulticast := p.conf.Protocols[conf.Protocol(gortsplib.TransportUDPMulticast)]
p.rtspServer, err = newRTSPServer(
p.conf.RTSPAddress,
p.conf.AuthMethods,
p.conf.ReadTimeout,
p.conf.WriteTimeout,
p.conf.WriteQueueSize,
useUDP,
useMulticast,
p.conf.RTPAddress,
p.conf.RTCPAddress,
p.conf.MulticastIPRange,
p.conf.MulticastRTPPort,
p.conf.MulticastRTCPPort,
false,
"",
"",
p.conf.RTSPAddress,
p.conf.Protocols,
p.conf.RunOnConnect,
p.conf.RunOnConnectRestart,
p.externalCmdPool,
p.metrics,
p.pathManager,
p,
)
if err != nil {
return err
}
}
if p.conf.RTSP &&
(p.conf.Encryption == conf.EncryptionStrict ||
p.conf.Encryption == conf.EncryptionOptional) {
if p.rtspsServer == nil {
p.rtspsServer, err = newRTSPServer(
p.conf.RTSPSAddress,
p.conf.AuthMethods,
p.conf.ReadTimeout,
p.conf.WriteTimeout,
p.conf.WriteQueueSize,
false,
false,
"",
"",
"",
0,
0,
true,
p.conf.ServerCert,
p.conf.ServerKey,
p.conf.RTSPAddress,
p.conf.Protocols,
p.conf.RunOnConnect,
p.conf.RunOnConnectRestart,
p.externalCmdPool,
p.metrics,
p.pathManager,
p,
)
if err != nil {
return err
}
p.conf.Encryption == conf.EncryptionOptional) &&
p.rtspsServer == nil {
p.rtspsServer, err = newRTSPServer(
p.conf.RTSPSAddress,
p.conf.AuthMethods,
p.conf.ReadTimeout,
p.conf.WriteTimeout,
p.conf.WriteQueueSize,
false,
false,
"",
"",
"",
0,
0,
true,
p.conf.ServerCert,
p.conf.ServerKey,
p.conf.RTSPAddress,
p.conf.Protocols,
p.conf.RunOnConnect,
p.conf.RunOnConnectRestart,
p.externalCmdPool,
p.metrics,
p.pathManager,
p,
)
if err != nil {
return err
}
}
if p.conf.RTMP &&
(p.conf.RTMPEncryption == conf.EncryptionNo ||
p.conf.RTMPEncryption == conf.EncryptionOptional) {
if p.rtmpServer == nil {
p.rtmpServer, err = newRTMPServer(
p.conf.RTMPAddress,
p.conf.ReadTimeout,
p.conf.WriteTimeout,
p.conf.WriteQueueSize,
false,
"",
"",
p.conf.RTSPAddress,
p.conf.RunOnConnect,
p.conf.RunOnConnectRestart,
p.externalCmdPool,
p.metrics,
p.pathManager,
p,
)
if err != nil {
return err
}
p.conf.RTMPEncryption == conf.EncryptionOptional) &&
p.rtmpServer == nil {
p.rtmpServer, err = newRTMPServer(
p.conf.RTMPAddress,
p.conf.ReadTimeout,
p.conf.WriteTimeout,
p.conf.WriteQueueSize,
false,
"",
"",
p.conf.RTSPAddress,
p.conf.RunOnConnect,
p.conf.RunOnConnectRestart,
p.externalCmdPool,
p.metrics,
p.pathManager,
p,
)
if err != nil {
return err
}
}
if p.conf.RTMP &&
(p.conf.RTMPEncryption == conf.EncryptionStrict ||
p.conf.RTMPEncryption == conf.EncryptionOptional) {
if p.rtmpsServer == nil {
p.rtmpsServer, err = newRTMPServer(
p.conf.RTMPSAddress,
p.conf.ReadTimeout,
p.conf.WriteTimeout,
p.conf.WriteQueueSize,
true,
p.conf.RTMPServerCert,
p.conf.RTMPServerKey,
p.conf.RTSPAddress,
p.conf.RunOnConnect,
p.conf.RunOnConnectRestart,
p.externalCmdPool,
p.metrics,
p.pathManager,
p,
)
if err != nil {
return err
}
p.conf.RTMPEncryption == conf.EncryptionOptional) &&
p.rtmpsServer == nil {
p.rtmpsServer, err = newRTMPServer(
p.conf.RTMPSAddress,
p.conf.ReadTimeout,
p.conf.WriteTimeout,
p.conf.WriteQueueSize,
true,
p.conf.RTMPServerCert,
p.conf.RTMPServerKey,
p.conf.RTSPAddress,
p.conf.RunOnConnect,
p.conf.RunOnConnectRestart,
p.externalCmdPool,
p.metrics,
p.pathManager,
p,
)
if err != nil {
return err
}
}
if p.conf.HLS {
if p.hlsManager == nil {
p.hlsManager, err = newHLSManager(
p.conf.HLSAddress,
p.conf.HLSEncryption,
p.conf.HLSServerKey,
p.conf.HLSServerCert,
p.conf.ExternalAuthenticationURL,
p.conf.HLSAlwaysRemux,
p.conf.HLSVariant,
p.conf.HLSSegmentCount,
p.conf.HLSSegmentDuration,
p.conf.HLSPartDuration,
p.conf.HLSSegmentMaxSize,
p.conf.HLSAllowOrigin,
p.conf.HLSTrustedProxies,
p.conf.HLSDirectory,
p.conf.ReadTimeout,
p.conf.WriteQueueSize,
p.pathManager,
p.metrics,
p,
)
if err != nil {
return err
}
if p.conf.HLS &&
p.hlsManager == nil {
p.hlsManager, err = newHLSManager(
p.conf.HLSAddress,
p.conf.HLSEncryption,
p.conf.HLSServerKey,
p.conf.HLSServerCert,
p.conf.ExternalAuthenticationURL,
p.conf.HLSAlwaysRemux,
p.conf.HLSVariant,
p.conf.HLSSegmentCount,
p.conf.HLSSegmentDuration,
p.conf.HLSPartDuration,
p.conf.HLSSegmentMaxSize,
p.conf.HLSAllowOrigin,
p.conf.HLSTrustedProxies,
p.conf.HLSDirectory,
p.conf.ReadTimeout,
p.conf.WriteQueueSize,
p.pathManager,
p.metrics,
p,
)
if err != nil {
return err
}
}
if p.conf.WebRTC {
if p.webRTCManager == nil {
p.webRTCManager, err = newWebRTCManager(
p.conf.WebRTCAddress,
p.conf.WebRTCEncryption,
p.conf.WebRTCServerKey,
p.conf.WebRTCServerCert,
p.conf.WebRTCAllowOrigin,
p.conf.WebRTCTrustedProxies,
p.conf.WebRTCICEServers2,
p.conf.ReadTimeout,
p.conf.WriteQueueSize,
p.conf.WebRTCICEHostNAT1To1IPs,
p.conf.WebRTCICEUDPMuxAddress,
p.conf.WebRTCICETCPMuxAddress,
p.pathManager,
p.metrics,
p,
)
if err != nil {
return err
}
if p.conf.WebRTC &&
p.webRTCManager == nil {
p.webRTCManager, err = newWebRTCManager(
p.conf.WebRTCAddress,
p.conf.WebRTCEncryption,
p.conf.WebRTCServerKey,
p.conf.WebRTCServerCert,
p.conf.WebRTCAllowOrigin,
p.conf.WebRTCTrustedProxies,
p.conf.WebRTCICEServers2,
p.conf.ReadTimeout,
p.conf.WriteQueueSize,
p.conf.WebRTCICEHostNAT1To1IPs,
p.conf.WebRTCICEUDPMuxAddress,
p.conf.WebRTCICETCPMuxAddress,
p.pathManager,
p.metrics,
p,
)
if err != nil {
return err
}
}
if p.conf.SRT {
if p.srtServer == nil {
p.srtServer, err = newSRTServer(
p.conf.SRTAddress,
p.conf.ReadTimeout,
p.conf.WriteTimeout,
p.conf.WriteQueueSize,
p.conf.UDPMaxPayloadSize,
p.externalCmdPool,
p.pathManager,
p,
)
if err != nil {
return err
}
if p.conf.SRT &&
p.srtServer == nil {
p.srtServer, err = newSRTServer(
p.conf.SRTAddress,
p.conf.ReadTimeout,
p.conf.WriteTimeout,
p.conf.WriteQueueSize,
p.conf.UDPMaxPayloadSize,
p.externalCmdPool,
p.pathManager,
p,
)
if err != nil {
return err
}
}
if p.conf.API {
if p.api == nil {
p.api, err = newAPI(
p.conf.APIAddress,
p.conf.ReadTimeout,
p.conf,
p.pathManager,
p.rtspServer,
p.rtspsServer,
p.rtmpServer,
p.rtmpsServer,
p.hlsManager,
p.webRTCManager,
p.srtServer,
p,
)
if err != nil {
return err
}
if p.conf.API &&
p.api == nil {
p.api, err = newAPI(
p.conf.APIAddress,
p.conf.ReadTimeout,
p.conf,
p.pathManager,
p.rtspServer,
p.rtspsServer,
p.rtmpServer,
p.rtmpsServer,
p.hlsManager,
p.webRTCManager,
p.srtServer,
p,
)
if err != nil {
return err
}
}

View File

@@ -6,8 +6,8 @@ import (
"testing"
"time"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/stretchr/testify/require"
)
@@ -111,7 +111,8 @@ func TestCoreHotReloading(t *testing.T) {
medi := testMediaH264
c := gortsplib.Client{}
err = c.StartRecording("rtsp://localhost:8554/test1", media.Medias{medi})
err = c.StartRecording("rtsp://localhost:8554/test1",
&description.Session{Medias: []*description.Media{medi}})
require.EqualError(t, err, "bad status code: 401 (Unauthorized)")
}()
@@ -126,7 +127,8 @@ func TestCoreHotReloading(t *testing.T) {
medi := testMediaH264
conn := gortsplib.Client{}
err = conn.StartRecording("rtsp://localhost:8554/test1", media.Medias{medi})
err = conn.StartRecording("rtsp://localhost:8554/test1",
&description.Session{Medias: []*description.Media{medi}})
require.NoError(t, err)
defer conn.Close()
}()

View File

@@ -9,9 +9,9 @@ import (
"testing"
"time"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
)
@@ -118,9 +118,9 @@ func TestHLSRead(t *testing.T) {
require.Equal(t, true, ok)
defer p.Close()
medi := &media.Media{
Type: media.TypeVideo,
Formats: []formats.Format{&formats.H264{
medi := &description.Media{
Type: description.MediaTypeVideo,
Formats: []format.Format{&format.H264{
PayloadTyp: 96,
PacketizationMode: 1,
SPS: []byte{ // 1920x1080 baseline
@@ -136,7 +136,8 @@ func TestHLSRead(t *testing.T) {
source := gortsplib.Client{
Transport: &v,
}
err := source.StartRecording("rtsp://localhost:8554/stream", media.Medias{medi})
err := source.StartRecording("rtsp://localhost:8554/stream",
&description.Session{Medias: []*description.Media{medi}})
require.NoError(t, err)
defer source.Close()

View File

@@ -13,9 +13,9 @@ import (
"github.com/bluenviron/gohlslib"
"github.com/bluenviron/gohlslib/pkg/codecs"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v3/pkg/ringbuffer"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/ringbuffer"
"github.com/gin-gonic/gin"
"github.com/bluenviron/mediamtx/internal/conf"
@@ -256,7 +256,7 @@ func (m *hlsMuxer) runInner(innerCtx context.Context, innerReady chan struct{})
m.ringBuffer, _ = ringbuffer.New(uint64(m.writeQueueSize))
var medias media.Medias
var medias []*description.Media
videoMedia, videoTrack := m.createVideoTrack(res.stream)
if videoMedia != nil {
@@ -335,14 +335,11 @@ func (m *hlsMuxer) runInner(innerCtx context.Context, innerReady chan struct{})
}
}
func (m *hlsMuxer) createVideoTrack(stream *stream.Stream) (*media.Media, *gohlslib.Track) {
var videoFormatAV1 *formats.AV1
videoMedia := stream.Medias().FindFormat(&videoFormatAV1)
func (m *hlsMuxer) createVideoTrack(stream *stream.Stream) (*description.Media, *gohlslib.Track) {
var videoFormatAV1 *format.AV1
videoMedia := stream.Desc().FindFormat(&videoFormatAV1)
if videoFormatAV1 != nil {
startPTSFilled := false
var startPTS time.Duration
stream.AddReader(m, videoMedia, videoFormatAV1, func(u unit.Unit) {
m.ringBuffer.Push(func() error {
tunit := u.(*unit.AV1)
@@ -351,12 +348,7 @@ func (m *hlsMuxer) createVideoTrack(stream *stream.Stream) (*media.Media, *gohls
return nil
}
if !startPTSFilled {
startPTSFilled = true
startPTS = tunit.PTS
}
pts := tunit.PTS - startPTS
pts := tunit.PTS
err := m.muxer.WriteAV1(tunit.NTP, pts, tunit.TU)
if err != nil {
return fmt.Errorf("muxer error: %v", err)
@@ -371,13 +363,10 @@ func (m *hlsMuxer) createVideoTrack(stream *stream.Stream) (*media.Media, *gohls
}
}
var videoFormatVP9 *formats.VP9
videoMedia = stream.Medias().FindFormat(&videoFormatVP9)
var videoFormatVP9 *format.VP9
videoMedia = stream.Desc().FindFormat(&videoFormatVP9)
if videoFormatVP9 != nil {
startPTSFilled := false
var startPTS time.Duration
stream.AddReader(m, videoMedia, videoFormatVP9, func(u unit.Unit) {
m.ringBuffer.Push(func() error {
tunit := u.(*unit.VP9)
@@ -386,12 +375,7 @@ func (m *hlsMuxer) createVideoTrack(stream *stream.Stream) (*media.Media, *gohls
return nil
}
if !startPTSFilled {
startPTSFilled = true
startPTS = tunit.PTS
}
pts := tunit.PTS - startPTS
pts := tunit.PTS
err := m.muxer.WriteVP9(tunit.NTP, pts, tunit.Frame)
if err != nil {
return fmt.Errorf("muxer error: %v", err)
@@ -406,13 +390,10 @@ func (m *hlsMuxer) createVideoTrack(stream *stream.Stream) (*media.Media, *gohls
}
}
var videoFormatH265 *formats.H265
videoMedia = stream.Medias().FindFormat(&videoFormatH265)
var videoFormatH265 *format.H265
videoMedia = stream.Desc().FindFormat(&videoFormatH265)
if videoFormatH265 != nil {
startPTSFilled := false
var startPTS time.Duration
stream.AddReader(m, videoMedia, videoFormatH265, func(u unit.Unit) {
m.ringBuffer.Push(func() error {
tunit := u.(*unit.H265)
@@ -421,12 +402,7 @@ func (m *hlsMuxer) createVideoTrack(stream *stream.Stream) (*media.Media, *gohls
return nil
}
if !startPTSFilled {
startPTSFilled = true
startPTS = tunit.PTS
}
pts := tunit.PTS - startPTS
pts := tunit.PTS
err := m.muxer.WriteH26x(tunit.NTP, pts, tunit.AU)
if err != nil {
return fmt.Errorf("muxer error: %v", err)
@@ -447,13 +423,10 @@ func (m *hlsMuxer) createVideoTrack(stream *stream.Stream) (*media.Media, *gohls
}
}
var videoFormatH264 *formats.H264
videoMedia = stream.Medias().FindFormat(&videoFormatH264)
var videoFormatH264 *format.H264
videoMedia = stream.Desc().FindFormat(&videoFormatH264)
if videoFormatH264 != nil {
startPTSFilled := false
var startPTS time.Duration
stream.AddReader(m, videoMedia, videoFormatH264, func(u unit.Unit) {
m.ringBuffer.Push(func() error {
tunit := u.(*unit.H264)
@@ -462,12 +435,7 @@ func (m *hlsMuxer) createVideoTrack(stream *stream.Stream) (*media.Media, *gohls
return nil
}
if !startPTSFilled {
startPTSFilled = true
startPTS = tunit.PTS
}
pts := tunit.PTS - startPTS
pts := tunit.PTS
err := m.muxer.WriteH26x(tunit.NTP, pts, tunit.AU)
if err != nil {
return fmt.Errorf("muxer error: %v", err)
@@ -490,24 +458,16 @@ func (m *hlsMuxer) createVideoTrack(stream *stream.Stream) (*media.Media, *gohls
return nil, nil
}
func (m *hlsMuxer) createAudioTrack(stream *stream.Stream) (*media.Media, *gohlslib.Track) {
var audioFormatOpus *formats.Opus
audioMedia := stream.Medias().FindFormat(&audioFormatOpus)
func (m *hlsMuxer) createAudioTrack(stream *stream.Stream) (*description.Media, *gohlslib.Track) {
var audioFormatOpus *format.Opus
audioMedia := stream.Desc().FindFormat(&audioFormatOpus)
if audioMedia != nil {
audioStartPTSFilled := false
var audioStartPTS time.Duration
stream.AddReader(m, audioMedia, audioFormatOpus, func(u unit.Unit) {
m.ringBuffer.Push(func() error {
tunit := u.(*unit.Opus)
if !audioStartPTSFilled {
audioStartPTSFilled = true
audioStartPTS = tunit.PTS
}
pts := tunit.PTS - audioStartPTS
pts := tunit.PTS
err := m.muxer.WriteOpus(
tunit.NTP,
pts,
@@ -532,13 +492,10 @@ func (m *hlsMuxer) createAudioTrack(stream *stream.Stream) (*media.Media, *gohls
}
}
var audioFormatMPEG4AudioGeneric *formats.MPEG4AudioGeneric
audioMedia = stream.Medias().FindFormat(&audioFormatMPEG4AudioGeneric)
var audioFormatMPEG4AudioGeneric *format.MPEG4AudioGeneric
audioMedia = stream.Desc().FindFormat(&audioFormatMPEG4AudioGeneric)
if audioMedia != nil {
audioStartPTSFilled := false
var audioStartPTS time.Duration
stream.AddReader(m, audioMedia, audioFormatMPEG4AudioGeneric, func(u unit.Unit) {
m.ringBuffer.Push(func() error {
tunit := u.(*unit.MPEG4AudioGeneric)
@@ -547,12 +504,7 @@ func (m *hlsMuxer) createAudioTrack(stream *stream.Stream) (*media.Media, *gohls
return nil
}
if !audioStartPTSFilled {
audioStartPTSFilled = true
audioStartPTS = tunit.PTS
}
pts := tunit.PTS - audioStartPTS
pts := tunit.PTS
err := m.muxer.WriteMPEG4Audio(
tunit.NTP,
pts,
@@ -572,16 +524,13 @@ func (m *hlsMuxer) createAudioTrack(stream *stream.Stream) (*media.Media, *gohls
}
}
var audioFormatMPEG4AudioLATM *formats.MPEG4AudioLATM
audioMedia = stream.Medias().FindFormat(&audioFormatMPEG4AudioLATM)
var audioFormatMPEG4AudioLATM *format.MPEG4AudioLATM
audioMedia = stream.Desc().FindFormat(&audioFormatMPEG4AudioLATM)
if audioMedia != nil &&
audioFormatMPEG4AudioLATM.Config != nil &&
len(audioFormatMPEG4AudioLATM.Config.Programs) == 1 &&
len(audioFormatMPEG4AudioLATM.Config.Programs[0].Layers) == 1 {
audioStartPTSFilled := false
var audioStartPTS time.Duration
stream.AddReader(m, audioMedia, audioFormatMPEG4AudioLATM, func(u unit.Unit) {
m.ringBuffer.Push(func() error {
tunit := u.(*unit.MPEG4AudioLATM)
@@ -590,12 +539,7 @@ func (m *hlsMuxer) createAudioTrack(stream *stream.Stream) (*media.Media, *gohls
return nil
}
if !audioStartPTSFilled {
audioStartPTSFilled = true
audioStartPTS = tunit.PTS
}
pts := tunit.PTS - audioStartPTS
pts := tunit.PTS
err := m.muxer.WriteMPEG4Audio(
tunit.NTP,
pts,

View File

@@ -7,8 +7,8 @@ import (
"github.com/bluenviron/gohlslib"
"github.com/bluenviron/gohlslib/pkg/codecs"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/mediamtx/internal/conf"
"github.com/bluenviron/mediamtx/internal/logger"
@@ -69,48 +69,48 @@ func (s *hlsSource) run(ctx context.Context, cnf *conf.PathConf, reloadConf chan
s.Log(logger.Warn, err.Error())
},
OnTracks: func(tracks []*gohlslib.Track) error {
var medias media.Medias
var medias []*description.Media
for _, track := range tracks {
var medi *media.Media
var medi *description.Media
switch tcodec := track.Codec.(type) {
case *codecs.AV1:
medi = &media.Media{
Type: media.TypeVideo,
Formats: []formats.Format{&formats.AV1{}},
medi = &description.Media{
Type: description.MediaTypeVideo,
Formats: []format.Format{&format.AV1{}},
}
c.OnDataAV1(track, func(pts time.Duration, tu [][]byte) {
stream.WriteUnit(medi, medi.Formats[0], &unit.AV1{
Base: unit.Base{
NTP: time.Now(),
PTS: pts,
},
PTS: pts,
TU: tu,
TU: tu,
})
})
case *codecs.VP9:
medi = &media.Media{
Type: media.TypeVideo,
Formats: []formats.Format{&formats.VP9{}},
medi = &description.Media{
Type: description.MediaTypeVideo,
Formats: []format.Format{&format.VP9{}},
}
c.OnDataVP9(track, func(pts time.Duration, frame []byte) {
stream.WriteUnit(medi, medi.Formats[0], &unit.VP9{
Base: unit.Base{
NTP: time.Now(),
PTS: pts,
},
PTS: pts,
Frame: frame,
})
})
case *codecs.H264:
medi = &media.Media{
Type: media.TypeVideo,
Formats: []formats.Format{&formats.H264{
medi = &description.Media{
Type: description.MediaTypeVideo,
Formats: []format.Format{&format.H264{
PayloadTyp: 96,
PacketizationMode: 1,
SPS: tcodec.SPS,
@@ -122,16 +122,16 @@ func (s *hlsSource) run(ctx context.Context, cnf *conf.PathConf, reloadConf chan
stream.WriteUnit(medi, medi.Formats[0], &unit.H264{
Base: unit.Base{
NTP: time.Now(),
PTS: pts,
},
PTS: pts,
AU: au,
AU: au,
})
})
case *codecs.H265:
medi = &media.Media{
Type: media.TypeVideo,
Formats: []formats.Format{&formats.H265{
medi = &description.Media{
Type: description.MediaTypeVideo,
Formats: []format.Format{&format.H265{
PayloadTyp: 96,
VPS: tcodec.VPS,
SPS: tcodec.SPS,
@@ -143,16 +143,16 @@ func (s *hlsSource) run(ctx context.Context, cnf *conf.PathConf, reloadConf chan
stream.WriteUnit(medi, medi.Formats[0], &unit.H265{
Base: unit.Base{
NTP: time.Now(),
PTS: pts,
},
PTS: pts,
AU: au,
AU: au,
})
})
case *codecs.MPEG4Audio:
medi = &media.Media{
Type: media.TypeAudio,
Formats: []formats.Format{&formats.MPEG4Audio{
medi = &description.Media{
Type: description.MediaTypeAudio,
Formats: []format.Format{&format.MPEG4Audio{
PayloadTyp: 96,
SizeLength: 13,
IndexLength: 3,
@@ -165,16 +165,16 @@ func (s *hlsSource) run(ctx context.Context, cnf *conf.PathConf, reloadConf chan
stream.WriteUnit(medi, medi.Formats[0], &unit.MPEG4AudioGeneric{
Base: unit.Base{
NTP: time.Now(),
PTS: pts,
},
PTS: pts,
AUs: aus,
})
})
case *codecs.Opus:
medi = &media.Media{
Type: media.TypeAudio,
Formats: []formats.Format{&formats.Opus{
medi = &description.Media{
Type: description.MediaTypeAudio,
Formats: []format.Format{&format.Opus{
PayloadTyp: 96,
IsStereo: (tcodec.ChannelCount == 2),
}},
@@ -184,8 +184,8 @@ func (s *hlsSource) run(ctx context.Context, cnf *conf.PathConf, reloadConf chan
stream.WriteUnit(medi, medi.Formats[0], &unit.Opus{
Base: unit.Base{
NTP: time.Now(),
PTS: pts,
},
PTS: pts,
Packets: packets,
})
})
@@ -195,7 +195,7 @@ func (s *hlsSource) run(ctx context.Context, cnf *conf.PathConf, reloadConf chan
}
res := s.parent.setReady(pathSourceStaticSetReadyReq{
medias: medias,
desc: &description.Session{Medias: medias},
generateRTPPackets: true,
})
if res.err != nil {

View File

@@ -8,10 +8,10 @@ import (
"net/http"
"testing"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v3/pkg/url"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/url"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
"github.com/bluenviron/mediacommon/pkg/formats/mpegts"
"github.com/gin-gonic/gin"
@@ -135,25 +135,25 @@ func TestHLSSource(t *testing.T) {
require.NoError(t, err)
defer c.Close()
medias, baseURL, _, err := c.Describe(u)
desc, _, err := c.Describe(u)
require.NoError(t, err)
require.Equal(t, media.Medias{
require.Equal(t, []*description.Media{
{
Type: media.TypeVideo,
Control: medias[0].Control,
Formats: []formats.Format{
&formats.H264{
Type: description.MediaTypeVideo,
Control: desc.Medias[0].Control,
Formats: []format.Format{
&format.H264{
PayloadTyp: 96,
PacketizationMode: 1,
},
},
},
{
Type: media.TypeAudio,
Control: medias[1].Control,
Formats: []formats.Format{
&formats.MPEG4Audio{
Type: description.MediaTypeAudio,
Control: desc.Medias[1].Control,
Formats: []format.Format{
&format.MPEG4Audio{
PayloadTyp: 96,
ProfileLevelID: 1,
Config: &mpeg4audio.Config{
@@ -167,12 +167,12 @@ func TestHLSSource(t *testing.T) {
},
},
},
}, medias)
}, desc.Medias)
var forma *formats.H264
medi := medias.FindFormat(&forma)
var forma *format.H264
medi := desc.FindFormat(&forma)
_, err = c.Setup(medi, baseURL, 0, 0)
_, err = c.Setup(desc.BaseURL, medi, 0, 0)
require.NoError(t, err)
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {

View File

@@ -232,15 +232,15 @@ func (m *metrics) setHLSManager(s apiHLSManager) {
m.hlsManager = s
}
// rtspServerSet is called by rtspServer (plain).
func (m *metrics) rtspServerSet(s apiRTSPServer) {
// setRTSPServer is called by rtspServer (plain).
func (m *metrics) setRTSPServer(s apiRTSPServer) {
m.mutex.Lock()
defer m.mutex.Unlock()
m.rtspServer = s
}
// rtspsServerSet is called by rtspServer (tls).
func (m *metrics) rtspsServerSet(s apiRTSPServer) {
// setRTSPSServer is called by rtspServer (tls).
func (m *metrics) setRTSPSServer(s apiRTSPServer) {
m.mutex.Lock()
defer m.mutex.Unlock()
m.rtspsServer = s

View File

@@ -9,9 +9,9 @@ import (
"testing"
"time"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/stretchr/testify/require"
"github.com/bluenviron/mediamtx/internal/rtmp"
@@ -69,13 +69,13 @@ webrtc_sessions_bytes_sent 0
source := gortsplib.Client{}
err = source.StartRecording("rtsp://localhost:8554/rtsp_path",
media.Medias{medi})
&description.Session{Medias: []*description.Media{medi}})
require.NoError(t, err)
defer source.Close()
source2 := gortsplib.Client{TLSConfig: &tls.Config{InsecureSkipVerify: true}}
err = source2.StartRecording("rtsps://localhost:8322/rtsps_path",
media.Medias{medi})
&description.Session{Medias: []*description.Media{medi}})
require.NoError(t, err)
defer source2.Close()
@@ -89,7 +89,7 @@ webrtc_sessions_bytes_sent 0
conn, err := rtmp.NewClientConn(nconn, u, true)
require.NoError(t, err)
videoTrack := &formats.H264{
videoTrack := &format.H264{
PayloadTyp: 96,
SPS: []byte{ // 1920x1080 baseline
0x67, 0x42, 0xc0, 0x28, 0xd9, 0x00, 0x78, 0x02,

View File

@@ -10,8 +10,8 @@ import (
"sync/atomic"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v3/pkg/url"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/url"
"github.com/bluenviron/mediamtx/internal/conf"
"github.com/bluenviron/mediamtx/internal/externalcmd"
@@ -56,7 +56,7 @@ type pathSourceStaticSetReadyRes struct {
}
type pathSourceStaticSetReadyReq struct {
medias media.Medias
desc *description.Session
generateRTPPackets bool
res chan pathSourceStaticSetReadyRes
}
@@ -135,7 +135,7 @@ type pathStartPublisherRes struct {
type pathStartPublisherReq struct {
author publisher
medias media.Medias
desc *description.Session
generateRTPPackets bool
res chan pathStartPublisherRes
}
@@ -589,10 +589,10 @@ func (pa *path) onDemandStopPublisher() {
}
}
func (pa *path) setReady(medias media.Medias, allocateEncoder bool) error {
func (pa *path) setReady(desc *description.Session, allocateEncoder bool) error {
stream, err := stream.New(
pa.udpMaxPayloadSize,
medias,
desc,
allocateEncoder,
pa.bytesReceived,
pa.source,
@@ -654,7 +654,7 @@ func (pa *path) doPublisherRemove() {
}
func (pa *path) handleSourceStaticSetReady(req pathSourceStaticSetReadyReq) {
err := pa.setReady(req.medias, req.generateRTPPackets)
err := pa.setReady(req.desc, req.generateRTPPackets)
if err != nil {
req.res <- pathSourceStaticSetReadyRes{err: err}
return
@@ -782,7 +782,7 @@ func (pa *path) handleStartPublisher(req pathStartPublisherReq) {
return
}
err := pa.setReady(req.medias, req.generateRTPPackets)
err := pa.setReady(req.desc, req.generateRTPPackets)
if err != nil {
req.res <- pathStartPublisherRes{err: err}
return
@@ -790,7 +790,7 @@ func (pa *path) handleStartPublisher(req pathStartPublisherReq) {
req.author.Log(logger.Info, "is publishing to path '%s', %s",
pa.name,
sourceMediaInfo(req.medias))
sourceMediaInfo(req.desc.Medias))
if pa.conf.HasOnDemandPublisher() {
pa.onDemandPublisherReadyTimer.Stop()
@@ -920,7 +920,7 @@ func (pa *path) handleAPIPathsGet(req pathAPIPathsGetReq) {
if pa.stream == nil {
return []string{}
}
return mediasDescription(pa.stream.Medias())
return mediasDescription(pa.stream.Desc().Medias)
}(),
BytesReceived: atomic.LoadUint64(pa.bytesReceived),
Readers: func() []interface{} {

View File

@@ -10,12 +10,12 @@ import (
"testing"
"time"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v3/pkg/base"
"github.com/bluenviron/gortsplib/v3/pkg/headers"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v3/pkg/sdp"
"github.com/bluenviron/gortsplib/v3/pkg/url"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/base"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/headers"
"github.com/bluenviron/gortsplib/v4/pkg/sdp"
"github.com/bluenviron/gortsplib/v4/pkg/url"
"github.com/stretchr/testify/require"
)
@@ -103,9 +103,9 @@ import (
"os"
"os/signal"
"syscall"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
)
func main() {
@@ -113,9 +113,9 @@ func main() {
panic("environment not set")
}
medi := &media.Media{
Type: media.TypeVideo,
Formats: []formats.Format{&formats.H264{
medi := &description.Media{
Type: description.MediaTypeVideo,
Formats: []format.Format{&format.H264{
PayloadTyp: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
@@ -127,7 +127,7 @@ func main() {
err := source.StartRecording(
"rtsp://localhost:" + os.Getenv("RTSP_PORT") + "/" + os.Getenv("MTX_PATH"),
media.Medias{medi})
&description.Session{Medias: []*description.Media{medi}})
if err != nil {
panic(err)
}
@@ -264,7 +264,7 @@ func TestPathRunOnReady(t *testing.T) {
err := c.StartRecording(
"rtsp://localhost:8554/test",
media.Medias{medi})
&description.Session{Medias: []*description.Media{medi}})
require.NoError(t, err)
defer c.Close()
@@ -284,7 +284,7 @@ func TestPathMaxReaders(t *testing.T) {
source := gortsplib.Client{}
err := source.StartRecording(
"rtsp://localhost:8554/mystream",
media.Medias{testMediaH264})
&description.Session{Medias: []*description.Media{testMediaH264}})
require.NoError(t, err)
defer source.Close()
@@ -298,10 +298,10 @@ func TestPathMaxReaders(t *testing.T) {
require.NoError(t, err)
defer reader.Close()
medias, baseURL, _, err := reader.Describe(u)
desc, _, err := reader.Describe(u)
require.NoError(t, err)
err = reader.SetupAll(medias, baseURL)
err = reader.SetupAll(desc.BaseURL, desc.Medias)
if i != 1 {
require.NoError(t, err)
} else {

View File

@@ -4,8 +4,8 @@ import (
"context"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/mediamtx/internal/conf"
"github.com/bluenviron/mediamtx/internal/logger"
@@ -75,20 +75,20 @@ func (s *rpiCameraSource) Log(level logger.Level, format string, args ...interfa
// run implements sourceStaticImpl.
func (s *rpiCameraSource) run(ctx context.Context, cnf *conf.PathConf, reloadConf chan *conf.PathConf) error {
medi := &media.Media{
Type: media.TypeVideo,
Formats: []formats.Format{&formats.H264{
medi := &description.Media{
Type: description.MediaTypeVideo,
Formats: []format.Format{&format.H264{
PayloadTyp: 96,
PacketizationMode: 1,
}},
}
medias := media.Medias{medi}
medias := []*description.Media{medi}
var stream *stream.Stream
onData := func(dts time.Duration, au [][]byte) {
if stream == nil {
res := s.parent.setReady(pathSourceStaticSetReadyReq{
medias: medias,
desc: &description.Session{Medias: medias},
generateRTPPackets: true,
})
if res.err != nil {
@@ -101,9 +101,9 @@ func (s *rpiCameraSource) run(ctx context.Context, cnf *conf.PathConf, reloadCon
stream.WriteUnit(medi, medi.Formats[0], &unit.H264{
Base: unit.Base{
NTP: time.Now(),
PTS: dts,
},
PTS: dts,
AU: au,
AU: au,
})
}

View File

@@ -10,9 +10,9 @@ import (
"sync"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v3/pkg/ringbuffer"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/ringbuffer"
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg1audio"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
@@ -247,17 +247,13 @@ func (c *rtmpConn) runRead(conn *rtmp.Conn, u *url.URL) error {
ringBuffer.Close()
}()
var medias media.Medias
videoFirstIDRFound := false
var videoStartDTS time.Duration
var medias []*description.Media
var w *rtmp.Writer
videoMedia, videoFormat := c.setupVideo(
&w,
res.stream,
ringBuffer,
&videoFirstIDRFound,
&videoStartDTS)
ringBuffer)
if videoMedia != nil {
medias = append(medias, videoMedia)
}
@@ -265,10 +261,7 @@ func (c *rtmpConn) runRead(conn *rtmp.Conn, u *url.URL) error {
audioMedia, audioFormat := c.setupAudio(
&w,
res.stream,
ringBuffer,
videoFormat,
&videoFirstIDRFound,
&videoStartDTS)
ringBuffer)
if audioFormat != nil {
medias = append(medias, audioMedia)
}
@@ -327,15 +320,11 @@ func (c *rtmpConn) setupVideo(
w **rtmp.Writer,
stream *stream.Stream,
ringBuffer *ringbuffer.RingBuffer,
videoFirstIDRFound *bool,
videoStartDTS *time.Duration,
) (*media.Media, formats.Format) {
var videoFormatH264 *formats.H264
videoMedia := stream.Medias().FindFormat(&videoFormatH264)
) (*description.Media, format.Format) {
var videoFormatH264 *format.H264
videoMedia := stream.Desc().FindFormat(&videoFormatH264)
if videoFormatH264 != nil {
startPTSFilled := false
var startPTS time.Duration
var videoDTSExtractor *h264.DTSExtractor
stream.AddReader(c, videoMedia, videoFormatH264, func(u unit.Unit) {
@@ -346,11 +335,7 @@ func (c *rtmpConn) setupVideo(
return nil
}
if !startPTSFilled {
startPTSFilled = true
startPTS = tunit.PTS
}
pts := tunit.PTS - startPTS
pts := tunit.PTS
idrPresent := false
nonIDRPresent := false
@@ -369,12 +354,11 @@ func (c *rtmpConn) setupVideo(
var dts time.Duration
// wait until we receive an IDR
if !*videoFirstIDRFound {
if videoDTSExtractor == nil {
if !idrPresent {
return nil
}
*videoFirstIDRFound = true
videoDTSExtractor = h264.NewDTSExtractor()
var err error
@@ -382,10 +366,6 @@ func (c *rtmpConn) setupVideo(
if err != nil {
return err
}
*videoStartDTS = dts
dts = 0
pts -= *videoStartDTS
} else {
if !idrPresent && !nonIDRPresent {
return nil
@@ -396,9 +376,6 @@ func (c *rtmpConn) setupVideo(
if err != nil {
return err
}
dts -= *videoStartDTS
pts -= *videoStartDTS
}
c.nconn.SetWriteDeadline(time.Now().Add(time.Duration(c.writeTimeout)))
@@ -416,17 +393,11 @@ func (c *rtmpConn) setupAudio(
w **rtmp.Writer,
stream *stream.Stream,
ringBuffer *ringbuffer.RingBuffer,
videoFormat formats.Format,
videoFirstIDRFound *bool,
videoStartDTS *time.Duration,
) (*media.Media, formats.Format) {
var audioFormatMPEG4Generic *formats.MPEG4AudioGeneric
audioMedia := stream.Medias().FindFormat(&audioFormatMPEG4Generic)
) (*description.Media, format.Format) {
var audioFormatMPEG4Generic *format.MPEG4AudioGeneric
audioMedia := stream.Desc().FindFormat(&audioFormatMPEG4Generic)
if audioMedia != nil {
startPTSFilled := false
var startPTS time.Duration
stream.AddReader(c, audioMedia, audioFormatMPEG4Generic, func(u unit.Unit) {
ringBuffer.Push(func() error {
tunit := u.(*unit.MPEG4AudioGeneric)
@@ -435,22 +406,7 @@ func (c *rtmpConn) setupAudio(
return nil
}
if !startPTSFilled {
startPTSFilled = true
startPTS = tunit.PTS
}
pts := tunit.PTS - startPTS
if videoFormat != nil {
if !*videoFirstIDRFound {
return nil
}
pts -= *videoStartDTS
if pts < 0 {
return nil
}
}
pts := tunit.PTS
for i, au := range tunit.AUs {
c.nconn.SetWriteDeadline(time.Now().Add(time.Duration(c.writeTimeout)))
@@ -471,16 +427,13 @@ func (c *rtmpConn) setupAudio(
return audioMedia, audioFormatMPEG4Generic
}
var audioFormatMPEG4AudioLATM *formats.MPEG4AudioLATM
audioMedia = stream.Medias().FindFormat(&audioFormatMPEG4AudioLATM)
var audioFormatMPEG4AudioLATM *format.MPEG4AudioLATM
audioMedia = stream.Desc().FindFormat(&audioFormatMPEG4AudioLATM)
if audioMedia != nil &&
audioFormatMPEG4AudioLATM.Config != nil &&
len(audioFormatMPEG4AudioLATM.Config.Programs) == 1 &&
len(audioFormatMPEG4AudioLATM.Config.Programs[0].Layers) == 1 {
startPTSFilled := false
var startPTS time.Duration
stream.AddReader(c, audioMedia, audioFormatMPEG4AudioLATM, func(u unit.Unit) {
ringBuffer.Push(func() error {
tunit := u.(*unit.MPEG4AudioLATM)
@@ -489,22 +442,7 @@ func (c *rtmpConn) setupAudio(
return nil
}
if !startPTSFilled {
startPTSFilled = true
startPTS = tunit.PTS
}
pts := tunit.PTS - startPTS
if videoFormat != nil {
if !*videoFirstIDRFound {
return nil
}
pts -= *videoStartDTS
if pts < 0 {
return nil
}
}
pts := tunit.PTS
c.nconn.SetWriteDeadline(time.Now().Add(time.Duration(c.writeTimeout)))
return (*w).WriteMPEG4Audio(pts, tunit.AU)
@@ -514,33 +452,15 @@ func (c *rtmpConn) setupAudio(
return audioMedia, audioFormatMPEG4AudioLATM
}
var audioFormatMPEG1 *formats.MPEG1Audio
audioMedia = stream.Medias().FindFormat(&audioFormatMPEG1)
var audioFormatMPEG1 *format.MPEG1Audio
audioMedia = stream.Desc().FindFormat(&audioFormatMPEG1)
if audioMedia != nil {
startPTSFilled := false
var startPTS time.Duration
stream.AddReader(c, audioMedia, audioFormatMPEG1, func(u unit.Unit) {
ringBuffer.Push(func() error {
tunit := u.(*unit.MPEG1Audio)
if !startPTSFilled {
startPTSFilled = true
startPTS = tunit.PTS
}
pts := tunit.PTS - startPTS
if videoFormat != nil {
if !*videoFirstIDRFound {
return nil
}
pts -= *videoStartDTS
if pts < 0 {
return nil
}
}
pts := tunit.PTS
for _, frame := range tunit.Frames {
var h mpeg1audio.FrameHeader
@@ -611,58 +531,58 @@ func (c *rtmpConn) runPublish(conn *rtmp.Conn, u *url.URL) error {
}
videoFormat, audioFormat := r.Tracks()
var medias media.Medias
var medias []*description.Media
var stream *stream.Stream
if videoFormat != nil {
videoMedia := &media.Media{
Type: media.TypeVideo,
Formats: []formats.Format{videoFormat},
videoMedia := &description.Media{
Type: description.MediaTypeVideo,
Formats: []format.Format{videoFormat},
}
medias = append(medias, videoMedia)
switch videoFormat.(type) {
case *formats.AV1:
case *format.AV1:
r.OnDataAV1(func(pts time.Duration, tu [][]byte) {
stream.WriteUnit(videoMedia, videoFormat, &unit.AV1{
Base: unit.Base{
NTP: time.Now(),
PTS: pts,
},
PTS: pts,
TU: tu,
TU: tu,
})
})
case *formats.VP9:
case *format.VP9:
r.OnDataVP9(func(pts time.Duration, frame []byte) {
stream.WriteUnit(videoMedia, videoFormat, &unit.VP9{
Base: unit.Base{
NTP: time.Now(),
PTS: pts,
},
PTS: pts,
Frame: frame,
})
})
case *formats.H265:
case *format.H265:
r.OnDataH265(func(pts time.Duration, au [][]byte) {
stream.WriteUnit(videoMedia, videoFormat, &unit.H265{
Base: unit.Base{
NTP: time.Now(),
PTS: pts,
},
PTS: pts,
AU: au,
AU: au,
})
})
case *formats.H264:
case *format.H264:
r.OnDataH264(func(pts time.Duration, au [][]byte) {
stream.WriteUnit(videoMedia, videoFormat, &unit.H264{
Base: unit.Base{
NTP: time.Now(),
PTS: pts,
},
PTS: pts,
AU: au,
AU: au,
})
})
@@ -672,31 +592,31 @@ func (c *rtmpConn) runPublish(conn *rtmp.Conn, u *url.URL) error {
}
if audioFormat != nil { //nolint:dupl
audioMedia := &media.Media{
Type: media.TypeAudio,
Formats: []formats.Format{audioFormat},
audioMedia := &description.Media{
Type: description.MediaTypeAudio,
Formats: []format.Format{audioFormat},
}
medias = append(medias, audioMedia)
switch audioFormat.(type) {
case *formats.MPEG4AudioGeneric:
case *format.MPEG4AudioGeneric:
r.OnDataMPEG4Audio(func(pts time.Duration, au []byte) {
stream.WriteUnit(audioMedia, audioFormat, &unit.MPEG4AudioGeneric{
Base: unit.Base{
NTP: time.Now(),
PTS: pts,
},
PTS: pts,
AUs: [][]byte{au},
})
})
case *formats.MPEG1Audio:
case *format.MPEG1Audio:
r.OnDataMPEG1Audio(func(pts time.Duration, frame []byte) {
stream.WriteUnit(audioMedia, audioFormat, &unit.MPEG1Audio{
Base: unit.Base{
NTP: time.Now(),
PTS: pts,
},
PTS: pts,
Frames: [][]byte{frame},
})
})
@@ -708,7 +628,7 @@ func (c *rtmpConn) runPublish(conn *rtmp.Conn, u *url.URL) error {
rres := res.path.startPublisher(pathStartPublisherReq{
author: c,
medias: medias,
desc: &description.Session{Medias: medias},
generateRTPPackets: true,
})
if rres.err != nil {

View File

@@ -8,7 +8,7 @@ import (
"testing"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
"github.com/stretchr/testify/require"
@@ -128,7 +128,7 @@ func TestRTMPServer(t *testing.T) {
conn1, err := rtmp.NewClientConn(nconn1, u1, true)
require.NoError(t, err)
videoTrack := &formats.H264{
videoTrack := &format.H264{
PayloadTyp: 96,
SPS: []byte{ // 1920x1080 baseline
0x67, 0x42, 0xc0, 0x28, 0xd9, 0x00, 0x78, 0x02,
@@ -139,7 +139,7 @@ func TestRTMPServer(t *testing.T) {
PacketizationMode: 1,
}
audioTrack := &formats.MPEG4Audio{
audioTrack := &format.MPEG4Audio{
PayloadTyp: 96,
Config: &mpeg4audio.Config{
Type: 2,
@@ -238,7 +238,7 @@ func TestRTMPServerAuthFail(t *testing.T) {
conn1, err := rtmp.NewClientConn(nconn1, u1, true)
require.NoError(t, err)
videoTrack := &formats.H264{
videoTrack := &format.H264{
PayloadTyp: 96,
SPS: []byte{
0x67, 0x64, 0x00, 0x0c, 0xac, 0x3b, 0x50, 0xb0,
@@ -290,7 +290,7 @@ func TestRTMPServerAuthFail(t *testing.T) {
conn1, err := rtmp.NewClientConn(nconn1, u1, true)
require.NoError(t, err)
videoTrack := &formats.H264{
videoTrack := &format.H264{
PayloadTyp: 96,
SPS: []byte{
0x67, 0x64, 0x00, 0x0c, 0xac, 0x3b, 0x50, 0xb0,
@@ -343,7 +343,7 @@ func TestRTMPServerAuthFail(t *testing.T) {
conn1, err := rtmp.NewClientConn(nconn1, u1, true)
require.NoError(t, err)
videoTrack := &formats.H264{
videoTrack := &format.H264{
PayloadTyp: 96,
SPS: []byte{
0x67, 0x64, 0x00, 0x0c, 0xac, 0x3b, 0x50, 0xb0,

View File

@@ -8,8 +8,8 @@ import (
"net/url"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/mediamtx/internal/conf"
"github.com/bluenviron/mediamtx/internal/logger"
@@ -113,25 +113,25 @@ func (s *rtmpSource) runReader(u *url.URL, nconn net.Conn) error {
videoFormat, audioFormat := mc.Tracks()
var medias media.Medias
var medias []*description.Media
var stream *stream.Stream
if videoFormat != nil {
videoMedia := &media.Media{
Type: media.TypeVideo,
Formats: []formats.Format{videoFormat},
videoMedia := &description.Media{
Type: description.MediaTypeVideo,
Formats: []format.Format{videoFormat},
}
medias = append(medias, videoMedia)
switch videoFormat.(type) {
case *formats.H264:
case *format.H264:
mc.OnDataH264(func(pts time.Duration, au [][]byte) {
stream.WriteUnit(videoMedia, videoFormat, &unit.H264{
Base: unit.Base{
NTP: time.Now(),
PTS: pts,
},
PTS: pts,
AU: au,
AU: au,
})
})
@@ -141,31 +141,31 @@ func (s *rtmpSource) runReader(u *url.URL, nconn net.Conn) error {
}
if audioFormat != nil { //nolint:dupl
audioMedia := &media.Media{
Type: media.TypeAudio,
Formats: []formats.Format{audioFormat},
audioMedia := &description.Media{
Type: description.MediaTypeAudio,
Formats: []format.Format{audioFormat},
}
medias = append(medias, audioMedia)
switch audioFormat.(type) {
case *formats.MPEG4AudioGeneric:
case *format.MPEG4AudioGeneric:
mc.OnDataMPEG4Audio(func(pts time.Duration, au []byte) {
stream.WriteUnit(audioMedia, audioFormat, &unit.MPEG4AudioGeneric{
Base: unit.Base{
NTP: time.Now(),
PTS: pts,
},
PTS: pts,
AUs: [][]byte{au},
})
})
case *formats.MPEG1Audio:
case *format.MPEG1Audio:
mc.OnDataMPEG1Audio(func(pts time.Duration, frame []byte) {
stream.WriteUnit(audioMedia, audioFormat, &unit.MPEG1Audio{
Base: unit.Base{
NTP: time.Now(),
PTS: pts,
},
PTS: pts,
Frames: [][]byte{frame},
})
})
@@ -176,7 +176,7 @@ func (s *rtmpSource) runReader(u *url.URL, nconn net.Conn) error {
}
res := s.parent.setReady(pathSourceStaticSetReadyReq{
medias: medias,
desc: &description.Session{Medias: medias},
generateRTPPackets: true,
})
if res.err != nil {

View File

@@ -6,9 +6,9 @@ import (
"os"
"testing"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/url"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/url"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
@@ -56,7 +56,7 @@ func TestRTMPSource(t *testing.T) {
conn, _, _, err := rtmp.NewServerConn(nconn)
require.NoError(t, err)
videoTrack := &formats.H264{
videoTrack := &format.H264{
PayloadTyp: 96,
SPS: []byte{ // 1920x1080 baseline
0x67, 0x42, 0xc0, 0x28, 0xd9, 0x00, 0x78, 0x02,
@@ -67,7 +67,7 @@ func TestRTMPSource(t *testing.T) {
PacketizationMode: 1,
}
audioTrack := &formats.MPEG4Audio{
audioTrack := &format.MPEG4Audio{
PayloadTyp: 96,
Config: &mpeg4audio.Config{
Type: 2,
@@ -116,13 +116,13 @@ func TestRTMPSource(t *testing.T) {
require.NoError(t, err)
defer c.Close()
medias, baseURL, _, err := c.Describe(u)
desc, _, err := c.Describe(u)
require.NoError(t, err)
var forma *formats.H264
medi := medias.FindFormat(&forma)
var forma *format.H264
medi := desc.FindFormat(&forma)
_, err = c.Setup(medi, baseURL, 0, 0)
_, err = c.Setup(desc.BaseURL, medi, 0, 0)
require.NoError(t, err)
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {

View File

@@ -5,10 +5,10 @@ import (
"net"
"time"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v3/pkg/auth"
"github.com/bluenviron/gortsplib/v3/pkg/base"
"github.com/bluenviron/gortsplib/v3/pkg/headers"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/auth"
"github.com/bluenviron/gortsplib/v4/pkg/base"
"github.com/bluenviron/gortsplib/v4/pkg/headers"
"github.com/google/uuid"
"github.com/bluenviron/mediamtx/internal/conf"
@@ -22,6 +22,8 @@ const (
type rtspConnParent interface {
logger.Writer
getISTLS() bool
getServer() *gortsplib.Server
}
type rtspConn struct {
@@ -138,7 +140,7 @@ func (c *rtspConn) onDescribe(ctx *gortsplib.ServerHandlerOnDescribeCtx,
if c.authNonce == "" {
var err error
c.authNonce, err = auth.GenerateNonce2()
c.authNonce, err = auth.GenerateNonce()
if err != nil {
return &base.Response{
StatusCode: base.StatusInternalServerError,
@@ -186,9 +188,16 @@ func (c *rtspConn) onDescribe(ctx *gortsplib.ServerHandlerOnDescribeCtx,
}, nil, nil
}
var stream *gortsplib.ServerStream
if !c.parent.getISTLS() {
stream = res.stream.RTSPStream(c.parent.getServer())
} else {
stream = res.stream.RTSPSStream(c.parent.getServer())
}
return &base.Response{
StatusCode: base.StatusOK,
}, res.stream.RTSPStream(), nil
}, stream, nil
}
func (c *rtspConn) handleAuthError(authErr error) (*base.Response, error) {

View File

@@ -9,10 +9,10 @@ import (
"sync"
"time"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v3/pkg/base"
"github.com/bluenviron/gortsplib/v3/pkg/headers"
"github.com/bluenviron/gortsplib/v3/pkg/liberrors"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/base"
"github.com/bluenviron/gortsplib/v4/pkg/headers"
"github.com/bluenviron/gortsplib/v4/pkg/liberrors"
"github.com/google/uuid"
"github.com/bluenviron/mediamtx/internal/conf"
@@ -108,12 +108,11 @@ func newRTSPServer(
}
s.srv = &gortsplib.Server{
Handler: s,
ReadTimeout: time.Duration(readTimeout),
WriteTimeout: time.Duration(writeTimeout),
ReadBufferCount: writeQueueSize,
WriteBufferCount: writeQueueSize,
RTSPAddress: address,
Handler: s,
ReadTimeout: time.Duration(readTimeout),
WriteTimeout: time.Duration(writeTimeout),
WriteQueueSize: writeQueueSize,
RTSPAddress: address,
}
if useUDP {
@@ -145,9 +144,9 @@ func newRTSPServer(
if metrics != nil {
if !isTLS {
metrics.rtspServerSet(s)
metrics.setRTSPServer(s)
} else {
metrics.rtspsServerSet(s)
metrics.setRTSPSServer(s)
}
}
@@ -167,6 +166,14 @@ func (s *rtspServer) Log(level logger.Level, format string, args ...interface{})
s.parent.Log(level, "[%s] "+format, append([]interface{}{label}, args...)...)
}
func (s *rtspServer) getISTLS() bool {
return s.isTLS
}
func (s *rtspServer) getServer() *gortsplib.Server {
return s.srv
}
func (s *rtspServer) close() {
s.Log(logger.Info, "listener is closing")
s.ctxCancel()
@@ -197,9 +204,9 @@ outer:
if s.metrics != nil {
if !s.isTLS {
s.metrics.rtspServerSet(nil)
s.metrics.setRTSPServer(nil)
} else {
s.metrics.rtspsServerSet(nil)
s.metrics.setRTSPSServer(nil)
}
}
}

View File

@@ -5,9 +5,9 @@ import (
"testing"
"time"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v3/pkg/url"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/url"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
)
@@ -29,7 +29,7 @@ func TestRTSPServerRunOnConnect(t *testing.T) {
err = source.StartRecording(
"rtsp://127.0.0.1:8554/mypath",
media.Medias{testMediaH264})
&description.Session{Medias: []*description.Media{testMediaH264}})
require.NoError(t, err)
defer source.Close()
@@ -88,7 +88,7 @@ func TestRTSPServer(t *testing.T) {
err := source.StartRecording(
"rtsp://testpublisher:testpass@127.0.0.1:8554/teststream?param=value",
media.Medias{medi})
&description.Session{Medias: []*description.Media{medi}})
require.NoError(t, err)
defer source.Close()
@@ -107,10 +107,10 @@ func TestRTSPServer(t *testing.T) {
require.NoError(t, err)
defer reader.Close()
medias, baseURL, _, err := reader.Describe(u)
desc, _, err := reader.Describe(u)
require.NoError(t, err)
err = reader.SetupAll(medias, baseURL)
err = reader.SetupAll(desc.BaseURL, desc.Medias)
require.NoError(t, err)
_, err = reader.Play(nil)
@@ -137,7 +137,7 @@ func TestRTSPServerAuthHashed(t *testing.T) {
err := source.StartRecording(
"rtsp://testuser:testpass@127.0.0.1:8554/test/stream",
media.Medias{medi})
&description.Session{Medias: []*description.Media{medi}})
require.NoError(t, err)
defer source.Close()
}
@@ -181,7 +181,7 @@ func TestRTSPServerAuthFail(t *testing.T) {
err := c.StartRecording(
"rtsp://"+ca.user+":"+ca.pass+"@localhost:8554/test/stream",
media.Medias{medi},
&description.Session{Medias: []*description.Media{medi}},
)
require.EqualError(t, err, "bad status code: 401 (Unauthorized)")
})
@@ -228,7 +228,7 @@ func TestRTSPServerAuthFail(t *testing.T) {
require.NoError(t, err)
defer c.Close()
_, _, _, err = c.Describe(u)
_, _, err = c.Describe(u)
require.EqualError(t, err, "bad status code: 401 (Unauthorized)")
})
}
@@ -249,7 +249,7 @@ func TestRTSPServerAuthFail(t *testing.T) {
err := c.StartRecording(
"rtsp://localhost:8554/test/stream",
media.Medias{medi},
&description.Session{Medias: []*description.Media{medi}},
)
require.EqualError(t, err, "bad status code: 401 (Unauthorized)")
})
@@ -270,7 +270,7 @@ func TestRTSPServerAuthFail(t *testing.T) {
err := c.StartRecording(
"rtsp://testpublisher2:testpass@localhost:8554/teststream?param=value",
media.Medias{medi},
&description.Session{Medias: []*description.Media{medi}},
)
require.EqualError(t, err, "bad status code: 401 (Unauthorized)")
})
@@ -298,13 +298,15 @@ func TestRTSPServerPublisherOverride(t *testing.T) {
s1 := gortsplib.Client{}
err := s1.StartRecording("rtsp://localhost:8554/teststream", media.Medias{medi})
err := s1.StartRecording("rtsp://localhost:8554/teststream",
&description.Session{Medias: []*description.Media{medi}})
require.NoError(t, err)
defer s1.Close()
s2 := gortsplib.Client{}
err = s2.StartRecording("rtsp://localhost:8554/teststream", media.Medias{medi})
err = s2.StartRecording("rtsp://localhost:8554/teststream",
&description.Session{Medias: []*description.Media{medi}})
if ca == "enabled" {
require.NoError(t, err)
defer s2.Close()
@@ -323,17 +325,17 @@ func TestRTSPServerPublisherOverride(t *testing.T) {
require.NoError(t, err)
defer c.Close()
medias, baseURL, _, err := c.Describe(u)
desc, _, err := c.Describe(u)
require.NoError(t, err)
err = c.SetupAll(medias, baseURL)
err = c.SetupAll(desc.BaseURL, desc.Medias)
require.NoError(t, err)
c.OnPacketRTP(medias[0], medias[0].Formats[0], func(pkt *rtp.Packet) {
c.OnPacketRTP(desc.Medias[0], desc.Medias[0].Formats[0], func(pkt *rtp.Packet) {
if ca == "enabled" {
require.Equal(t, []byte{0x05, 0x06, 0x07, 0x08}, pkt.Payload)
require.Equal(t, []byte{5, 15, 16, 17, 18}, pkt.Payload)
} else {
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, pkt.Payload)
require.Equal(t, []byte{5, 11, 12, 13, 14}, pkt.Payload)
}
close(frameRecv)
})
@@ -354,7 +356,7 @@ func TestRTSPServerPublisherOverride(t *testing.T) {
SSRC: 978651231,
Marker: true,
},
Payload: []byte{0x05, 0x06, 0x07, 0x08},
Payload: []byte{5, 15, 16, 17, 18},
})
require.NoError(t, err)
} else {
@@ -367,7 +369,7 @@ func TestRTSPServerPublisherOverride(t *testing.T) {
SSRC: 978651231,
Marker: true,
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
Payload: []byte{5, 11, 12, 13, 14},
})
require.NoError(t, err)
}
@@ -402,7 +404,7 @@ func TestRTSPServerFallback(t *testing.T) {
source := gortsplib.Client{}
err := source.StartRecording("rtsp://localhost:8554/path2",
media.Medias{testMediaH264})
&description.Session{Medias: []*description.Media{testMediaH264}})
require.NoError(t, err)
defer source.Close()
@@ -414,9 +416,9 @@ func TestRTSPServerFallback(t *testing.T) {
require.NoError(t, err)
defer dest.Close()
medias, _, _, err := dest.Describe(u)
desc, _, err := dest.Describe(u)
require.NoError(t, err)
require.Equal(t, 1, len(medias))
require.Equal(t, 1, len(desc.Medias))
})
}
}

View File

@@ -7,10 +7,10 @@ import (
"sync"
"time"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v3/pkg/auth"
"github.com/bluenviron/gortsplib/v3/pkg/base"
"github.com/bluenviron/gortsplib/v3/pkg/url"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/auth"
"github.com/bluenviron/gortsplib/v4/pkg/base"
"github.com/bluenviron/gortsplib/v4/pkg/url"
"github.com/google/uuid"
"github.com/pion/rtp"
@@ -27,6 +27,8 @@ type rtspSessionPathManager interface {
type rtspSessionParent interface {
logger.Writer
getISTLS() bool
getServer() *gortsplib.Server
}
type rtspSession struct {
@@ -124,7 +126,7 @@ func (s *rtspSession) onAnnounce(c *rtspConn, ctx *gortsplib.ServerHandlerOnAnno
if c.authNonce == "" {
var err error
c.authNonce, err = auth.GenerateNonce2()
c.authNonce, err = auth.GenerateNonce()
if err != nil {
return &base.Response{
StatusCode: base.StatusInternalServerError,
@@ -209,7 +211,7 @@ func (s *rtspSession) onSetup(c *rtspConn, ctx *gortsplib.ServerHandlerOnSetupCt
if c.authNonce == "" {
var err error
c.authNonce, err = auth.GenerateNonce2()
c.authNonce, err = auth.GenerateNonce()
if err != nil {
return &base.Response{
StatusCode: base.StatusInternalServerError,
@@ -257,9 +259,16 @@ func (s *rtspSession) onSetup(c *rtspConn, ctx *gortsplib.ServerHandlerOnSetupCt
s.pathName = ctx.Path
s.mutex.Unlock()
var stream *gortsplib.ServerStream
if !s.parent.getISTLS() {
stream = res.stream.RTSPStream(s.parent.getServer())
} else {
stream = res.stream.RTSPSStream(s.parent.getServer())
}
return &base.Response{
StatusCode: base.StatusOK,
}, res.stream.RTSPStream(), nil
}, stream, nil
default: // record
return &base.Response{
@@ -308,7 +317,7 @@ func (s *rtspSession) onPlay(_ *gortsplib.ServerHandlerOnPlayCtx) (*base.Respons
func (s *rtspSession) onRecord(_ *gortsplib.ServerHandlerOnRecordCtx) (*base.Response, error) {
res := s.path.startPublisher(pathStartPublisherReq{
author: s,
medias: s.session.AnnouncedMedias(),
desc: s.session.AnnouncedDescription(),
generateRTPPackets: false,
})
if res.err != nil {
@@ -319,13 +328,18 @@ func (s *rtspSession) onRecord(_ *gortsplib.ServerHandlerOnRecordCtx) (*base.Res
s.stream = res.stream
for _, medi := range s.session.AnnouncedMedias() {
for _, medi := range s.session.AnnouncedDescription().Medias {
for _, forma := range medi.Formats {
cmedi := medi
cforma := forma
s.session.OnPacketRTP(cmedi, cforma, func(pkt *rtp.Packet) {
res.stream.WriteRTPPacket(cmedi, cforma, pkt, time.Now())
pts, ok := s.session.PacketPTS(cmedi, pkt)
if !ok {
return
}
res.stream.WriteRTPPacket(cmedi, cforma, pkt, time.Now(), pts)
})
}
}

View File

@@ -4,12 +4,12 @@ import (
"context"
"time"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v3/pkg/base"
"github.com/bluenviron/gortsplib/v3/pkg/headers"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/base"
"github.com/bluenviron/gortsplib/v4/pkg/headers"
"github.com/pion/rtp"
"github.com/bluenviron/gortsplib/v3/pkg/url"
"github.com/bluenviron/gortsplib/v4/pkg/url"
"github.com/bluenviron/mediamtx/internal/conf"
"github.com/bluenviron/mediamtx/internal/logger"
)
@@ -95,13 +95,12 @@ func (s *rtspSource) run(ctx context.Context, cnf *conf.PathConf, reloadConf cha
s.Log(logger.Debug, "connecting")
c := &gortsplib.Client{
Transport: cnf.SourceProtocol.Transport,
TLSConfig: tlsConfigForFingerprint(cnf.SourceFingerprint),
ReadTimeout: time.Duration(s.readTimeout),
WriteTimeout: time.Duration(s.writeTimeout),
ReadBufferCount: s.writeQueueSize,
WriteBufferCount: s.writeQueueSize,
AnyPortEnable: cnf.SourceAnyPortEnable,
Transport: cnf.SourceProtocol.Transport,
TLSConfig: tlsConfigForFingerprint(cnf.SourceFingerprint),
ReadTimeout: time.Duration(s.readTimeout),
WriteTimeout: time.Duration(s.writeTimeout),
WriteQueueSize: s.writeQueueSize,
AnyPortEnable: cnf.SourceAnyPortEnable,
OnRequest: func(req *base.Request) {
s.Log(logger.Debug, "c->s %v", req)
},
@@ -133,18 +132,18 @@ func (s *rtspSource) run(ctx context.Context, cnf *conf.PathConf, reloadConf cha
readErr := make(chan error)
go func() {
readErr <- func() error {
medias, baseURL, _, err := c.Describe(u)
desc, _, err := c.Describe(u)
if err != nil {
return err
}
err = c.SetupAll(medias, baseURL)
err = c.SetupAll(desc.BaseURL, desc.Medias)
if err != nil {
return err
}
res := s.parent.setReady(pathSourceStaticSetReadyReq{
medias: medias,
desc: desc,
generateRTPPackets: false,
})
if res.err != nil {
@@ -153,13 +152,18 @@ func (s *rtspSource) run(ctx context.Context, cnf *conf.PathConf, reloadConf cha
defer s.parent.setNotReady(pathSourceStaticSetNotReadyReq{})
for _, medi := range medias {
for _, medi := range desc.Medias {
for _, forma := range medi.Formats {
cmedi := medi
cforma := forma
c.OnPacketRTP(cmedi, cforma, func(pkt *rtp.Packet) {
res.stream.WriteRTPPacket(cmedi, cforma, pkt, time.Now())
pts, ok := c.PacketPTS(cmedi, pkt)
if !ok {
return
}
res.stream.WriteRTPPacket(cmedi, cforma, pkt, time.Now(), pts)
})
}
}

View File

@@ -6,12 +6,12 @@ import (
"testing"
"time"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v3/pkg/auth"
"github.com/bluenviron/gortsplib/v3/pkg/base"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v3/pkg/url"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/auth"
"github.com/bluenviron/gortsplib/v4/pkg/base"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/url"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
)
@@ -43,9 +43,9 @@ func TestRTSPSource(t *testing.T) {
} {
t.Run(source, func(t *testing.T) {
serverMedia := testMediaH264
stream := gortsplib.NewServerStream(media.Medias{serverMedia})
var stream *gortsplib.ServerStream
nonce, err := auth.GenerateNonce2()
nonce, err := auth.GenerateNonce()
require.NoError(t, err)
s := gortsplib.Server{
@@ -74,7 +74,7 @@ func TestRTSPSource(t *testing.T) {
onPlay: func(ctx *gortsplib.ServerHandlerOnPlayCtx) (*base.Response, error) {
go func() {
time.Sleep(1 * time.Second)
stream.WritePacketRTP(serverMedia, &rtp.Packet{
err := stream.WritePacketRTP(serverMedia, &rtp.Packet{
Header: rtp.Header{
Version: 0x02,
PayloadType: 96,
@@ -83,8 +83,9 @@ func TestRTSPSource(t *testing.T) {
SSRC: 978651231,
Marker: true,
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
Payload: []byte{5, 1, 2, 3, 4},
})
require.NoError(t, err)
}()
return &base.Response{
@@ -120,6 +121,9 @@ func TestRTSPSource(t *testing.T) {
defer s.Wait() //nolint:errcheck
defer s.Close()
stream = gortsplib.NewServerStream(&s, &description.Session{Medias: []*description.Media{serverMedia}})
defer stream.Close()
if source == "udp" || source == "tcp" {
p, ok := newInstance("paths:\n" +
" proxied:\n" +
@@ -149,17 +153,17 @@ func TestRTSPSource(t *testing.T) {
require.NoError(t, err)
defer c.Close()
medias, baseURL, _, err := c.Describe(u)
desc, _, err := c.Describe(u)
require.NoError(t, err)
var forma *formats.H264
medi := medias.FindFormat(&forma)
var forma *format.H264
medi := desc.FindFormat(&forma)
_, err = c.Setup(medi, baseURL, 0, 0)
_, err = c.Setup(desc.BaseURL, medi, 0, 0)
require.NoError(t, err)
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, pkt.Payload)
require.Equal(t, []byte{5, 1, 2, 3, 4}, pkt.Payload)
close(received)
})
@@ -172,9 +176,9 @@ func TestRTSPSource(t *testing.T) {
}
func TestRTSPSourceNoPassword(t *testing.T) {
stream := gortsplib.NewServerStream(media.Medias{testMediaH264})
var stream *gortsplib.ServerStream
nonce, err := auth.GenerateNonce2()
nonce, err := auth.GenerateNonce()
require.NoError(t, err)
done := make(chan struct{})
@@ -210,11 +214,15 @@ func TestRTSPSourceNoPassword(t *testing.T) {
},
RTSPAddress: "127.0.0.1:8555",
}
err = s.Start()
require.NoError(t, err)
defer s.Wait() //nolint:errcheck
defer s.Close()
stream = gortsplib.NewServerStream(&s, &description.Session{Medias: []*description.Media{testMediaH264}})
defer stream.Close()
p, ok := newInstance("rtmp: no\n" +
"hls: no\n" +
"webrtc: no\n" +
@@ -231,7 +239,7 @@ func TestRTSPSourceNoPassword(t *testing.T) {
func TestRTSPSourceRange(t *testing.T) {
for _, ca := range []string{"clock", "npt", "smpte"} {
t.Run(ca, func(t *testing.T) {
stream := gortsplib.NewServerStream(media.Medias{testMediaH264})
var stream *gortsplib.ServerStream
done := make(chan struct{})
s := gortsplib.Server{
@@ -266,11 +274,15 @@ func TestRTSPSourceRange(t *testing.T) {
},
RTSPAddress: "127.0.0.1:8555",
}
err := s.Start()
require.NoError(t, err)
defer s.Wait() //nolint:errcheck
defer s.Close()
stream = gortsplib.NewServerStream(&s, &description.Session{Medias: []*description.Media{testMediaH264}})
defer stream.Close()
var addConf string
switch ca {
case "clock":

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"strings"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/mediamtx/internal/logger"
)
@@ -19,7 +19,7 @@ type source interface {
apiSourceDescribe() pathAPISourceOrReader
}
func mediaDescription(media *media.Media) string {
func mediaDescription(media *description.Media) string {
ret := make([]string, len(media.Formats))
for i, forma := range media.Formats {
ret[i] = forma.Codec()
@@ -27,7 +27,7 @@ func mediaDescription(media *media.Media) string {
return strings.Join(ret, "/")
}
func mediasDescription(medias media.Medias) []string {
func mediasDescription(medias []*description.Media) []string {
ret := make([]string, len(medias))
for i, media := range medias {
ret[i] = mediaDescription(media)
@@ -35,7 +35,7 @@ func mediasDescription(medias media.Medias) []string {
return ret
}
func sourceMediaInfo(medias media.Medias) string {
func sourceMediaInfo(medias []*description.Media) string {
return fmt.Sprintf("%d %s (%s)",
len(medias),
func() string {

View File

@@ -224,7 +224,7 @@ func (s *sourceStatic) setReady(req pathSourceStaticSetReadyReq) pathSourceStati
res := <-req.res
if res.err == nil {
s.impl.Log(logger.Info, "ready: %s", sourceMediaInfo(req.medias))
s.impl.Log(logger.Info, "ready: %s", sourceMediaInfo(req.desc.Medias))
}
return res

View File

@@ -10,9 +10,9 @@ import (
"sync"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v3/pkg/ringbuffer"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/ringbuffer"
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
"github.com/bluenviron/mediacommon/pkg/codecs/h265"
"github.com/bluenviron/mediacommon/pkg/formats/mpegts"
@@ -235,7 +235,7 @@ func (c *srtConn) runPublishReader(sconn srt.Conn, path *path) error {
return err
}
var medias media.Medias
var medias []*description.Media //nolint:prealloc
var stream *stream.Stream
var td *mpegts.TimeDecoder
@@ -247,13 +247,13 @@ func (c *srtConn) runPublishReader(sconn srt.Conn, path *path) error {
}
for _, track := range r.Tracks() { //nolint:dupl
var medi *media.Media
var medi *description.Media
switch tcodec := track.Codec.(type) {
case *mpegts.CodecH264:
medi = &media.Media{
Type: media.TypeVideo,
Formats: []formats.Format{&formats.H264{
medi = &description.Media{
Type: description.MediaTypeVideo,
Formats: []format.Format{&format.H264{
PayloadTyp: 96,
PacketizationMode: 1,
}},
@@ -263,17 +263,17 @@ func (c *srtConn) runPublishReader(sconn srt.Conn, path *path) error {
stream.WriteUnit(medi, medi.Formats[0], &unit.H264{
Base: unit.Base{
NTP: time.Now(),
PTS: decodeTime(pts),
},
PTS: decodeTime(pts),
AU: au,
AU: au,
})
return nil
})
case *mpegts.CodecH265:
medi = &media.Media{
Type: media.TypeVideo,
Formats: []formats.Format{&formats.H265{
medi = &description.Media{
Type: description.MediaTypeVideo,
Formats: []format.Format{&format.H265{
PayloadTyp: 96,
}},
}
@@ -282,17 +282,17 @@ func (c *srtConn) runPublishReader(sconn srt.Conn, path *path) error {
stream.WriteUnit(medi, medi.Formats[0], &unit.H265{
Base: unit.Base{
NTP: time.Now(),
PTS: decodeTime(pts),
},
PTS: decodeTime(pts),
AU: au,
AU: au,
})
return nil
})
case *mpegts.CodecMPEG4Audio:
medi = &media.Media{
Type: media.TypeAudio,
Formats: []formats.Format{&formats.MPEG4Audio{
medi = &description.Media{
Type: description.MediaTypeAudio,
Formats: []format.Format{&format.MPEG4Audio{
PayloadTyp: 96,
SizeLength: 13,
IndexLength: 3,
@@ -305,17 +305,17 @@ func (c *srtConn) runPublishReader(sconn srt.Conn, path *path) error {
stream.WriteUnit(medi, medi.Formats[0], &unit.MPEG4AudioGeneric{
Base: unit.Base{
NTP: time.Now(),
PTS: decodeTime(pts),
},
PTS: decodeTime(pts),
AUs: aus,
})
return nil
})
case *mpegts.CodecOpus:
medi = &media.Media{
Type: media.TypeAudio,
Formats: []formats.Format{&formats.Opus{
medi = &description.Media{
Type: description.MediaTypeAudio,
Formats: []format.Format{&format.Opus{
PayloadTyp: 96,
IsStereo: (tcodec.ChannelCount == 2),
}},
@@ -325,25 +325,25 @@ func (c *srtConn) runPublishReader(sconn srt.Conn, path *path) error {
stream.WriteUnit(medi, medi.Formats[0], &unit.Opus{
Base: unit.Base{
NTP: time.Now(),
PTS: decodeTime(pts),
},
PTS: decodeTime(pts),
Packets: packets,
})
return nil
})
case *mpegts.CodecMPEG1Audio:
medi = &media.Media{
Type: media.TypeAudio,
Formats: []formats.Format{&formats.MPEG1Audio{}},
medi = &description.Media{
Type: description.MediaTypeAudio,
Formats: []format.Format{&format.MPEG1Audio{}},
}
r.OnDataMPEG1Audio(track, func(pts int64, frames [][]byte) error {
stream.WriteUnit(medi, medi.Formats[0], &unit.MPEG1Audio{
Base: unit.Base{
NTP: time.Now(),
PTS: decodeTime(pts),
},
PTS: decodeTime(pts),
Frames: frames,
})
return nil
@@ -362,7 +362,7 @@ func (c *srtConn) runPublishReader(sconn srt.Conn, path *path) error {
rres := path.startPublisher(pathStartPublisherReq{
author: c,
medias: medias,
desc: &description.Session{Medias: medias},
generateRTPPackets: true,
})
if rres.err != nil {
@@ -424,14 +424,10 @@ func (c *srtConn) runRead(req srtNewConnReq, pathName string, user string, pass
var w *mpegts.Writer
var tracks []*mpegts.Track
var medias media.Medias
var medias []*description.Media
bw := bufio.NewWriterSize(sconn, srtMaxPayloadSize(c.udpMaxPayloadSize))
leadingTrackChosen := false
leadingTrackInitialized := false
var leadingTrackStartDTS time.Duration
addTrack := func(medi *media.Media, codec mpegts.Codec) *mpegts.Track {
addTrack := func(medi *description.Media, codec mpegts.Codec) *mpegts.Track {
track := &mpegts.Track{
Codec: codec,
}
@@ -440,37 +436,22 @@ func (c *srtConn) runRead(req srtNewConnReq, pathName string, user string, pass
return track
}
for _, medi := range res.stream.Medias() {
for _, format := range medi.Formats {
switch format := format.(type) {
case *formats.H265: //nolint:dupl
for _, medi := range res.stream.Desc().Medias {
for _, forma := range medi.Formats {
switch forma := forma.(type) {
case *format.H265: //nolint:dupl
track := addTrack(medi, &mpegts.CodecH265{})
var startPTS time.Duration
startPTSFilled := false
var isLeadingTrack bool
if !leadingTrackChosen {
isLeadingTrack = true
} else {
isLeadingTrack = false
}
randomAccessReceived := false
dtsExtractor := h265.NewDTSExtractor()
res.stream.AddReader(c, medi, format, func(u unit.Unit) {
res.stream.AddReader(c, medi, forma, func(u unit.Unit) {
ringBuffer.Push(func() error {
tunit := u.(*unit.H265)
if tunit.AU == nil {
return nil
}
if !startPTSFilled {
startPTS = tunit.PTS
startPTSFilled = true
}
randomAccess := h265.IsRandomAccess(tunit.AU)
if !randomAccessReceived {
@@ -480,24 +461,12 @@ func (c *srtConn) runRead(req srtNewConnReq, pathName string, user string, pass
randomAccessReceived = true
}
pts := tunit.PTS - startPTS
pts := tunit.PTS
dts, err := dtsExtractor.Extract(tunit.AU, pts)
if err != nil {
return err
}
if !leadingTrackInitialized {
if isLeadingTrack {
leadingTrackStartDTS = dts
leadingTrackInitialized = true
} else {
return nil
}
}
dts -= leadingTrackStartDTS
pts -= leadingTrackStartDTS
sconn.SetWriteDeadline(time.Now().Add(time.Duration(c.writeTimeout)))
err = w.WriteH26x(track, durationGoToMPEGTS(pts), durationGoToMPEGTS(dts), randomAccess, tunit.AU)
if err != nil {
@@ -507,34 +476,19 @@ func (c *srtConn) runRead(req srtNewConnReq, pathName string, user string, pass
})
})
case *formats.H264: //nolint:dupl
case *format.H264: //nolint:dupl
track := addTrack(medi, &mpegts.CodecH264{})
var startPTS time.Duration
startPTSFilled := false
var isLeadingTrack bool
if !leadingTrackChosen {
isLeadingTrack = true
} else {
isLeadingTrack = false
}
firstIDRReceived := false
dtsExtractor := h264.NewDTSExtractor()
res.stream.AddReader(c, medi, format, func(u unit.Unit) {
res.stream.AddReader(c, medi, forma, func(u unit.Unit) {
ringBuffer.Push(func() error {
tunit := u.(*unit.H264)
if tunit.AU == nil {
return nil
}
if !startPTSFilled {
startPTS = tunit.PTS
startPTSFilled = true
}
idrPresent := h264.IDRPresent(tunit.AU)
if !firstIDRReceived {
@@ -544,24 +498,12 @@ func (c *srtConn) runRead(req srtNewConnReq, pathName string, user string, pass
firstIDRReceived = true
}
pts := tunit.PTS - startPTS
pts := tunit.PTS
dts, err := dtsExtractor.Extract(tunit.AU, pts)
if err != nil {
return err
}
if !leadingTrackInitialized {
if isLeadingTrack {
leadingTrackStartDTS = dts
leadingTrackInitialized = true
} else {
return nil
}
}
dts -= leadingTrackStartDTS
pts -= leadingTrackStartDTS
sconn.SetWriteDeadline(time.Now().Add(time.Duration(c.writeTimeout)))
err = w.WriteH26x(track, durationGoToMPEGTS(pts), durationGoToMPEGTS(dts), idrPresent, tunit.AU)
if err != nil {
@@ -571,33 +513,19 @@ func (c *srtConn) runRead(req srtNewConnReq, pathName string, user string, pass
})
})
case *formats.MPEG4AudioGeneric:
case *format.MPEG4AudioGeneric:
track := addTrack(medi, &mpegts.CodecMPEG4Audio{
Config: *format.Config,
Config: *forma.Config,
})
var startPTS time.Duration
startPTSFilled := false
res.stream.AddReader(c, medi, format, func(u unit.Unit) {
res.stream.AddReader(c, medi, forma, func(u unit.Unit) {
ringBuffer.Push(func() error {
tunit := u.(*unit.MPEG4AudioGeneric)
if tunit.AUs == nil {
return nil
}
if !startPTSFilled {
startPTS = tunit.PTS
startPTSFilled = true
}
if leadingTrackChosen && !leadingTrackInitialized {
return nil
}
pts := tunit.PTS
pts -= startPTS
pts -= leadingTrackStartDTS
sconn.SetWriteDeadline(time.Now().Add(time.Duration(c.writeTimeout)))
err = w.WriteMPEG4Audio(track, durationGoToMPEGTS(pts), tunit.AUs)
@@ -608,36 +536,22 @@ func (c *srtConn) runRead(req srtNewConnReq, pathName string, user string, pass
})
})
case *formats.MPEG4AudioLATM:
if format.Config != nil &&
len(format.Config.Programs) == 1 &&
len(format.Config.Programs[0].Layers) == 1 {
case *format.MPEG4AudioLATM:
if forma.Config != nil &&
len(forma.Config.Programs) == 1 &&
len(forma.Config.Programs[0].Layers) == 1 {
track := addTrack(medi, &mpegts.CodecMPEG4Audio{
Config: *format.Config.Programs[0].Layers[0].AudioSpecificConfig,
Config: *forma.Config.Programs[0].Layers[0].AudioSpecificConfig,
})
var startPTS time.Duration
startPTSFilled := false
res.stream.AddReader(c, medi, format, func(u unit.Unit) {
res.stream.AddReader(c, medi, forma, func(u unit.Unit) {
ringBuffer.Push(func() error {
tunit := u.(*unit.MPEG4AudioLATM)
if tunit.AU == nil {
return nil
}
if !startPTSFilled {
startPTS = tunit.PTS
startPTSFilled = true
}
if leadingTrackChosen && !leadingTrackInitialized {
return nil
}
pts := tunit.PTS
pts -= startPTS
pts -= leadingTrackStartDTS
sconn.SetWriteDeadline(time.Now().Add(time.Duration(c.writeTimeout)))
err = w.WriteMPEG4Audio(track, durationGoToMPEGTS(pts), [][]byte{tunit.AU})
@@ -649,38 +563,24 @@ func (c *srtConn) runRead(req srtNewConnReq, pathName string, user string, pass
})
}
case *formats.Opus:
case *format.Opus:
track := addTrack(medi, &mpegts.CodecOpus{
ChannelCount: func() int {
if format.IsStereo {
if forma.IsStereo {
return 2
}
return 1
}(),
})
var startPTS time.Duration
startPTSFilled := false
res.stream.AddReader(c, medi, format, func(u unit.Unit) {
res.stream.AddReader(c, medi, forma, func(u unit.Unit) {
ringBuffer.Push(func() error {
tunit := u.(*unit.Opus)
if tunit.Packets == nil {
return nil
}
if !startPTSFilled {
startPTS = tunit.PTS
startPTSFilled = true
}
if leadingTrackChosen && !leadingTrackInitialized {
return nil
}
pts := tunit.PTS
pts -= startPTS
pts -= leadingTrackStartDTS
sconn.SetWriteDeadline(time.Now().Add(time.Duration(c.writeTimeout)))
err = w.WriteOpus(track, durationGoToMPEGTS(pts), tunit.Packets)
@@ -691,31 +591,17 @@ func (c *srtConn) runRead(req srtNewConnReq, pathName string, user string, pass
})
})
case *formats.MPEG1Audio:
case *format.MPEG1Audio:
track := addTrack(medi, &mpegts.CodecMPEG1Audio{})
var startPTS time.Duration
startPTSFilled := false
res.stream.AddReader(c, medi, format, func(u unit.Unit) {
res.stream.AddReader(c, medi, forma, func(u unit.Unit) {
ringBuffer.Push(func() error {
tunit := u.(*unit.MPEG1Audio)
if tunit.Frames == nil {
return nil
}
if !startPTSFilled {
startPTS = tunit.PTS
startPTSFilled = true
}
if leadingTrackChosen && !leadingTrackInitialized {
return nil
}
pts := tunit.PTS
pts -= startPTS
pts -= leadingTrackStartDTS
sconn.SetWriteDeadline(time.Now().Add(time.Duration(c.writeTimeout)))
err = w.WriteMPEG1Audio(track, durationGoToMPEGTS(pts), tunit.Frames)

View File

@@ -5,8 +5,8 @@ import (
"fmt"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/mediacommon/pkg/formats/mpegts"
"github.com/datarhei/gosrt"
@@ -91,7 +91,7 @@ func (s *srtSource) runReader(sconn srt.Conn) error {
return err
}
var medias media.Medias
var medias []*description.Media //nolint:prealloc
var stream *stream.Stream
var td *mpegts.TimeDecoder
@@ -103,13 +103,13 @@ func (s *srtSource) runReader(sconn srt.Conn) error {
}
for _, track := range r.Tracks() { //nolint:dupl
var medi *media.Media
var medi *description.Media
switch tcodec := track.Codec.(type) {
case *mpegts.CodecH264:
medi = &media.Media{
Type: media.TypeVideo,
Formats: []formats.Format{&formats.H264{
medi = &description.Media{
Type: description.MediaTypeVideo,
Formats: []format.Format{&format.H264{
PayloadTyp: 96,
PacketizationMode: 1,
}},
@@ -119,17 +119,17 @@ func (s *srtSource) runReader(sconn srt.Conn) error {
stream.WriteUnit(medi, medi.Formats[0], &unit.H264{
Base: unit.Base{
NTP: time.Now(),
PTS: decodeTime(pts),
},
PTS: decodeTime(pts),
AU: au,
AU: au,
})
return nil
})
case *mpegts.CodecH265:
medi = &media.Media{
Type: media.TypeVideo,
Formats: []formats.Format{&formats.H265{
medi = &description.Media{
Type: description.MediaTypeVideo,
Formats: []format.Format{&format.H265{
PayloadTyp: 96,
}},
}
@@ -138,17 +138,17 @@ func (s *srtSource) runReader(sconn srt.Conn) error {
stream.WriteUnit(medi, medi.Formats[0], &unit.H265{
Base: unit.Base{
NTP: time.Now(),
PTS: decodeTime(pts),
},
PTS: decodeTime(pts),
AU: au,
AU: au,
})
return nil
})
case *mpegts.CodecMPEG4Audio:
medi = &media.Media{
Type: media.TypeAudio,
Formats: []formats.Format{&formats.MPEG4Audio{
medi = &description.Media{
Type: description.MediaTypeAudio,
Formats: []format.Format{&format.MPEG4Audio{
PayloadTyp: 96,
SizeLength: 13,
IndexLength: 3,
@@ -161,17 +161,17 @@ func (s *srtSource) runReader(sconn srt.Conn) error {
stream.WriteUnit(medi, medi.Formats[0], &unit.MPEG4AudioGeneric{
Base: unit.Base{
NTP: time.Now(),
PTS: decodeTime(pts),
},
PTS: decodeTime(pts),
AUs: aus,
})
return nil
})
case *mpegts.CodecOpus:
medi = &media.Media{
Type: media.TypeAudio,
Formats: []formats.Format{&formats.Opus{
medi = &description.Media{
Type: description.MediaTypeAudio,
Formats: []format.Format{&format.Opus{
PayloadTyp: 96,
IsStereo: (tcodec.ChannelCount == 2),
}},
@@ -181,25 +181,25 @@ func (s *srtSource) runReader(sconn srt.Conn) error {
stream.WriteUnit(medi, medi.Formats[0], &unit.Opus{
Base: unit.Base{
NTP: time.Now(),
PTS: decodeTime(pts),
},
PTS: decodeTime(pts),
Packets: packets,
})
return nil
})
case *mpegts.CodecMPEG1Audio:
medi = &media.Media{
Type: media.TypeAudio,
Formats: []formats.Format{&formats.MPEG1Audio{}},
medi = &description.Media{
Type: description.MediaTypeAudio,
Formats: []format.Format{&format.MPEG1Audio{}},
}
r.OnDataMPEG1Audio(track, func(pts int64, frames [][]byte) error {
stream.WriteUnit(medi, medi.Formats[0], &unit.MPEG1Audio{
Base: unit.Base{
NTP: time.Now(),
PTS: decodeTime(pts),
},
PTS: decodeTime(pts),
Frames: frames,
})
return nil
@@ -217,7 +217,7 @@ func (s *srtSource) runReader(sconn srt.Conn) error {
}
res := s.parent.setReady(pathSourceStaticSetReadyReq{
medias: medias,
desc: &description.Session{Medias: medias},
generateRTPPackets: true,
})
if res.err != nil {

View File

@@ -4,9 +4,9 @@ import (
"bufio"
"testing"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/url"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/url"
"github.com/bluenviron/mediacommon/pkg/formats/mpegts"
"github.com/datarhei/gosrt"
"github.com/pion/rtp"
@@ -82,13 +82,13 @@ func TestSRTSource(t *testing.T) {
require.NoError(t, err)
defer c.Close()
medias, baseURL, _, err := c.Describe(u)
desc, _, err := c.Describe(u)
require.NoError(t, err)
var forma *formats.H264
medi := medias.FindFormat(&forma)
var forma *format.H264
medi := desc.FindFormat(&forma)
_, err = c.Setup(medi, baseURL, 0, 0)
_, err = c.Setup(desc.BaseURL, medi, 0, 0)
require.NoError(t, err)
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {

View File

@@ -6,8 +6,8 @@ import (
"net"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/mediacommon/pkg/formats/mpegts"
"golang.org/x/net/ipv4"
@@ -140,7 +140,7 @@ func (s *udpSource) runReader(pc net.PacketConn) error {
return err
}
var medias media.Medias
var medias []*description.Media //nolint:prealloc
var stream *stream.Stream
var td *mpegts.TimeDecoder
@@ -152,13 +152,13 @@ func (s *udpSource) runReader(pc net.PacketConn) error {
}
for _, track := range r.Tracks() { //nolint:dupl
var medi *media.Media
var medi *description.Media
switch tcodec := track.Codec.(type) {
case *mpegts.CodecH264:
medi = &media.Media{
Type: media.TypeVideo,
Formats: []formats.Format{&formats.H264{
medi = &description.Media{
Type: description.MediaTypeVideo,
Formats: []format.Format{&format.H264{
PayloadTyp: 96,
PacketizationMode: 1,
}},
@@ -168,17 +168,17 @@ func (s *udpSource) runReader(pc net.PacketConn) error {
stream.WriteUnit(medi, medi.Formats[0], &unit.H264{
Base: unit.Base{
NTP: time.Now(),
PTS: decodeTime(pts),
},
PTS: decodeTime(pts),
AU: au,
AU: au,
})
return nil
})
case *mpegts.CodecH265:
medi = &media.Media{
Type: media.TypeVideo,
Formats: []formats.Format{&formats.H265{
medi = &description.Media{
Type: description.MediaTypeVideo,
Formats: []format.Format{&format.H265{
PayloadTyp: 96,
}},
}
@@ -187,17 +187,17 @@ func (s *udpSource) runReader(pc net.PacketConn) error {
stream.WriteUnit(medi, medi.Formats[0], &unit.H265{
Base: unit.Base{
NTP: time.Now(),
PTS: decodeTime(pts),
},
PTS: decodeTime(pts),
AU: au,
AU: au,
})
return nil
})
case *mpegts.CodecMPEG4Audio:
medi = &media.Media{
Type: media.TypeAudio,
Formats: []formats.Format{&formats.MPEG4Audio{
medi = &description.Media{
Type: description.MediaTypeAudio,
Formats: []format.Format{&format.MPEG4Audio{
PayloadTyp: 96,
SizeLength: 13,
IndexLength: 3,
@@ -210,17 +210,17 @@ func (s *udpSource) runReader(pc net.PacketConn) error {
stream.WriteUnit(medi, medi.Formats[0], &unit.MPEG4AudioGeneric{
Base: unit.Base{
NTP: time.Now(),
PTS: decodeTime(pts),
},
PTS: decodeTime(pts),
AUs: aus,
})
return nil
})
case *mpegts.CodecOpus:
medi = &media.Media{
Type: media.TypeAudio,
Formats: []formats.Format{&formats.Opus{
medi = &description.Media{
Type: description.MediaTypeAudio,
Formats: []format.Format{&format.Opus{
PayloadTyp: 96,
IsStereo: (tcodec.ChannelCount == 2),
}},
@@ -230,25 +230,25 @@ func (s *udpSource) runReader(pc net.PacketConn) error {
stream.WriteUnit(medi, medi.Formats[0], &unit.Opus{
Base: unit.Base{
NTP: time.Now(),
PTS: decodeTime(pts),
},
PTS: decodeTime(pts),
Packets: packets,
})
return nil
})
case *mpegts.CodecMPEG1Audio:
medi = &media.Media{
Type: media.TypeAudio,
Formats: []formats.Format{&formats.MPEG1Audio{}},
medi = &description.Media{
Type: description.MediaTypeAudio,
Formats: []format.Format{&format.MPEG1Audio{}},
}
r.OnDataMPEG1Audio(track, func(pts int64, frames [][]byte) error {
stream.WriteUnit(medi, medi.Formats[0], &unit.MPEG1Audio{
Base: unit.Base{
NTP: time.Now(),
PTS: decodeTime(pts),
},
PTS: decodeTime(pts),
Frames: frames,
})
return nil
@@ -266,7 +266,7 @@ func (s *udpSource) runReader(pc net.PacketConn) error {
}
res := s.parent.setReady(pathSourceStaticSetReadyReq{
medias: medias,
desc: &description.Session{Medias: medias},
generateRTPPackets: true,
})
if res.err != nil {

View File

@@ -6,9 +6,9 @@ import (
"testing"
"time"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/url"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/url"
"github.com/bluenviron/mediacommon/pkg/formats/mpegts"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
@@ -68,13 +68,13 @@ func TestUDPSource(t *testing.T) {
require.NoError(t, err)
}()
medias, baseURL, _, err := c.Describe(u)
desc, _, err := c.Describe(u)
require.NoError(t, err)
var forma *formats.H264
medi := medias.FindFormat(&forma)
var forma *format.H264
medi := desc.FindFormat(&forma)
_, err = c.Setup(medi, baseURL, 0, 0)
_, err = c.Setup(desc.BaseURL, medi, 0, 0)
require.NoError(t, err)
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {

View File

@@ -5,9 +5,11 @@ import (
"strings"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
"github.com/pion/rtcp"
"github.com/pion/rtp"
"github.com/pion/webrtc/v3"
"github.com/bluenviron/mediamtx/internal/stream"
@@ -22,9 +24,9 @@ type webRTCIncomingTrack struct {
receiver *webrtc.RTPReceiver
writeRTCP func([]rtcp.Packet) error
mediaType media.Type
format formats.Format
media *media.Media
mediaType description.MediaType
format format.Format
media *description.Media
}
func newWebRTCIncomingTrack(
@@ -40,49 +42,49 @@ func newWebRTCIncomingTrack(
switch strings.ToLower(track.Codec().MimeType) {
case strings.ToLower(webrtc.MimeTypeAV1):
t.mediaType = media.TypeVideo
t.format = &formats.AV1{
t.mediaType = description.MediaTypeVideo
t.format = &format.AV1{
PayloadTyp: uint8(track.PayloadType()),
}
case strings.ToLower(webrtc.MimeTypeVP9):
t.mediaType = media.TypeVideo
t.format = &formats.VP9{
t.mediaType = description.MediaTypeVideo
t.format = &format.VP9{
PayloadTyp: uint8(track.PayloadType()),
}
case strings.ToLower(webrtc.MimeTypeVP8):
t.mediaType = media.TypeVideo
t.format = &formats.VP8{
t.mediaType = description.MediaTypeVideo
t.format = &format.VP8{
PayloadTyp: uint8(track.PayloadType()),
}
case strings.ToLower(webrtc.MimeTypeH264):
t.mediaType = media.TypeVideo
t.format = &formats.H264{
t.mediaType = description.MediaTypeVideo
t.format = &format.H264{
PayloadTyp: uint8(track.PayloadType()),
PacketizationMode: 1,
}
case strings.ToLower(webrtc.MimeTypeOpus):
t.mediaType = media.TypeAudio
t.format = &formats.Opus{
t.mediaType = description.MediaTypeAudio
t.format = &format.Opus{
PayloadTyp: uint8(track.PayloadType()),
}
case strings.ToLower(webrtc.MimeTypeG722):
t.mediaType = media.TypeAudio
t.format = &formats.G722{}
t.mediaType = description.MediaTypeAudio
t.format = &format.G722{}
case strings.ToLower(webrtc.MimeTypePCMU):
t.mediaType = media.TypeAudio
t.format = &formats.G711{
t.mediaType = description.MediaTypeAudio
t.format = &format.G711{
MULaw: true,
}
case strings.ToLower(webrtc.MimeTypePCMA):
t.mediaType = media.TypeAudio
t.format = &formats.G711{
t.mediaType = description.MediaTypeAudio
t.format = &format.G711{
MULaw: false,
}
@@ -90,15 +92,29 @@ func newWebRTCIncomingTrack(
return nil, fmt.Errorf("unsupported codec: %v", track.Codec())
}
t.media = &media.Media{
t.media = &description.Media{
Type: t.mediaType,
Formats: []formats.Format{t.format},
Formats: []format.Format{t.format},
}
return t, nil
}
func (t *webRTCIncomingTrack) start(stream *stream.Stream) {
type webrtcTrackWrapper struct {
clockRate int
}
func (w webrtcTrackWrapper) ClockRate() int {
return w.clockRate
}
func (webrtcTrackWrapper) PTSEqualsDTS(*rtp.Packet) bool {
return true
}
func (t *webRTCIncomingTrack) start(stream *stream.Stream, timeDecoder *rtptime.GlobalDecoder) {
trackWrapper := &webrtcTrackWrapper{clockRate: int(t.track.Codec().ClockRate)}
go func() {
for {
pkt, _, err := t.track.ReadRTP()
@@ -111,7 +127,12 @@ func (t *webRTCIncomingTrack) start(stream *stream.Stream) {
continue
}
stream.WriteRTPPacket(t.media, t.format, pkt, time.Now())
pts, ok := timeDecoder.Decode(trackWrapper, pkt)
if !ok {
continue
}
stream.WriteRTPPacket(t.media, t.format, pkt, time.Now(), pts)
}
}()
@@ -126,7 +147,7 @@ func (t *webRTCIncomingTrack) start(stream *stream.Stream) {
}
}()
if t.mediaType == media.TypeVideo {
if t.mediaType == description.MediaTypeVideo {
go func() {
keyframeTicker := time.NewTicker(keyFrameInterval)
defer keyframeTicker.Stop()

View File

@@ -7,10 +7,10 @@ import (
"testing"
"time"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v3/pkg/url"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/url"
"github.com/pion/rtp"
"github.com/pion/webrtc/v3"
"github.com/stretchr/testify/require"
@@ -193,9 +193,9 @@ func TestWebRTCRead(t *testing.T) {
a = newTestHTTPAuthenticator(t, "rtsp", "publish")
}
medi := &media.Media{
Type: media.TypeVideo,
Formats: []formats.Format{&formats.H264{
medi := &description.Media{
Type: description.MediaTypeVideo,
Formats: []format.Format{&format.H264{
PayloadTyp: 96,
PacketizationMode: 1,
}},
@@ -206,7 +206,8 @@ func TestWebRTCRead(t *testing.T) {
Transport: &v,
}
err := source.StartRecording(
"rtsp://testpublisher:testpass@localhost:8554/teststream?param=value", media.Medias{medi})
"rtsp://testpublisher:testpass@localhost:8554/teststream?param=value",
&description.Session{Medias: []*description.Media{medi}})
require.NoError(t, err)
defer source.Close()
@@ -251,7 +252,7 @@ func TestWebRTCRead(t *testing.T) {
Timestamp: 45343,
SSRC: 563423,
},
Payload: []byte{3},
Payload: []byte{5, 3},
})
require.NoError(t, err)
@@ -269,7 +270,7 @@ func TestWebRTCRead(t *testing.T) {
SSRC: pkt.SSRC,
CSRC: []uint32{},
},
Payload: []byte{3},
Payload: []byte{5, 3},
}, pkt)
})
}
@@ -407,13 +408,13 @@ func TestWebRTCPublish(t *testing.T) {
require.NoError(t, err)
defer c.Close()
medias, baseURL, _, err := c.Describe(u)
desc, _, err := c.Describe(u)
require.NoError(t, err)
var forma *formats.VP8
medi := medias.FindFormat(&forma)
var forma *format.VP8
medi := desc.FindFormat(&forma)
_, err = c.Setup(medi, baseURL, 0, 0)
_, err = c.Setup(desc.BaseURL, medi, 0, 0)
require.NoError(t, err)
received := make(chan struct{})

View File

@@ -5,13 +5,13 @@ import (
"fmt"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/formats/rtpav1"
"github.com/bluenviron/gortsplib/v3/pkg/formats/rtph264"
"github.com/bluenviron/gortsplib/v3/pkg/formats/rtpvp8"
"github.com/bluenviron/gortsplib/v3/pkg/formats/rtpvp9"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v3/pkg/ringbuffer"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpav1"
"github.com/bluenviron/gortsplib/v4/pkg/format/rtph264"
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpvp8"
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpvp9"
"github.com/bluenviron/gortsplib/v4/pkg/ringbuffer"
"github.com/pion/webrtc/v3"
"github.com/bluenviron/mediamtx/internal/stream"
@@ -20,15 +20,15 @@ import (
type webRTCOutgoingTrack struct {
sender *webrtc.RTPSender
media *media.Media
format formats.Format
media *description.Media
format format.Format
track *webrtc.TrackLocalStaticRTP
cb func(unit.Unit) error
}
func newWebRTCOutgoingTrackVideo(medias media.Medias) (*webRTCOutgoingTrack, error) {
var av1Format *formats.AV1
videoMedia := medias.FindFormat(&av1Format)
func newWebRTCOutgoingTrackVideo(desc *description.Session) (*webRTCOutgoingTrack, error) {
var av1Format *format.AV1
videoMedia := desc.FindFormat(&av1Format)
if videoMedia != nil {
webRTCTrak, err := webrtc.NewTrackLocalStaticRTP(
@@ -63,12 +63,13 @@ func newWebRTCOutgoingTrackVideo(medias media.Medias) (*webRTCOutgoingTrack, err
return nil
}
packets, err := encoder.Encode(tunit.TU, tunit.PTS)
packets, err := encoder.Encode(tunit.TU)
if err != nil {
return nil //nolint:nilerr
}
for _, pkt := range packets {
pkt.Timestamp = tunit.RTPPackets[0].Timestamp
webRTCTrak.WriteRTP(pkt) //nolint:errcheck
}
@@ -77,10 +78,10 @@ func newWebRTCOutgoingTrackVideo(medias media.Medias) (*webRTCOutgoingTrack, err
}, nil
}
var vp9Format *formats.VP9
videoMedia = medias.FindFormat(&vp9Format)
var vp9Format *format.VP9
videoMedia = desc.FindFormat(&vp9Format)
if videoMedia != nil {
if videoMedia != nil { //nolint:dupl
webRTCTrak, err := webrtc.NewTrackLocalStaticRTP(
webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypeVP9,
@@ -113,12 +114,13 @@ func newWebRTCOutgoingTrackVideo(medias media.Medias) (*webRTCOutgoingTrack, err
return nil
}
packets, err := encoder.Encode(tunit.Frame, tunit.PTS)
packets, err := encoder.Encode(tunit.Frame)
if err != nil {
return nil //nolint:nilerr
}
for _, pkt := range packets {
pkt.Timestamp = tunit.RTPPackets[0].Timestamp
webRTCTrak.WriteRTP(pkt) //nolint:errcheck
}
@@ -127,10 +129,10 @@ func newWebRTCOutgoingTrackVideo(medias media.Medias) (*webRTCOutgoingTrack, err
}, nil
}
var vp8Format *formats.VP8
videoMedia = medias.FindFormat(&vp8Format)
var vp8Format *format.VP8
videoMedia = desc.FindFormat(&vp8Format)
if videoMedia != nil {
if videoMedia != nil { //nolint:dupl
webRTCTrak, err := webrtc.NewTrackLocalStaticRTP(
webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypeVP8,
@@ -163,12 +165,13 @@ func newWebRTCOutgoingTrackVideo(medias media.Medias) (*webRTCOutgoingTrack, err
return nil
}
packets, err := encoder.Encode(tunit.Frame, tunit.PTS)
packets, err := encoder.Encode(tunit.Frame)
if err != nil {
return nil //nolint:nilerr
}
for _, pkt := range packets {
pkt.Timestamp = tunit.RTPPackets[0].Timestamp
webRTCTrak.WriteRTP(pkt) //nolint:errcheck
}
@@ -177,8 +180,8 @@ func newWebRTCOutgoingTrackVideo(medias media.Medias) (*webRTCOutgoingTrack, err
}, nil
}
var h264Format *formats.H264
videoMedia = medias.FindFormat(&h264Format)
var h264Format *format.H264
videoMedia = desc.FindFormat(&h264Format)
if videoMedia != nil {
webRTCTrak, err := webrtc.NewTrackLocalStaticRTP(
@@ -226,12 +229,13 @@ func newWebRTCOutgoingTrackVideo(medias media.Medias) (*webRTCOutgoingTrack, err
lastPTS = tunit.PTS
}
packets, err := encoder.Encode(tunit.AU, tunit.PTS)
packets, err := encoder.Encode(tunit.AU)
if err != nil {
return nil //nolint:nilerr
}
for _, pkt := range packets {
pkt.Timestamp = tunit.RTPPackets[0].Timestamp
webRTCTrak.WriteRTP(pkt) //nolint:errcheck
}
@@ -243,9 +247,9 @@ func newWebRTCOutgoingTrackVideo(medias media.Medias) (*webRTCOutgoingTrack, err
return nil, nil
}
func newWebRTCOutgoingTrackAudio(medias media.Medias) (*webRTCOutgoingTrack, error) {
var opusFormat *formats.Opus
audioMedia := medias.FindFormat(&opusFormat)
func newWebRTCOutgoingTrackAudio(desc *description.Session) (*webRTCOutgoingTrack, error) {
var opusFormat *format.Opus
audioMedia := desc.FindFormat(&opusFormat)
if audioMedia != nil {
webRTCTrak, err := webrtc.NewTrackLocalStaticRTP(
@@ -275,8 +279,8 @@ func newWebRTCOutgoingTrackAudio(medias media.Medias) (*webRTCOutgoingTrack, err
}, nil
}
var g722Format *formats.G722
audioMedia = medias.FindFormat(&g722Format)
var g722Format *format.G722
audioMedia = desc.FindFormat(&g722Format)
if audioMedia != nil {
webRTCTrak, err := webrtc.NewTrackLocalStaticRTP(
@@ -305,8 +309,8 @@ func newWebRTCOutgoingTrackAudio(medias media.Medias) (*webRTCOutgoingTrack, err
}, nil
}
var g711Format *formats.G711
audioMedia = medias.FindFormat(&g711Format)
var g711Format *format.G711
audioMedia = desc.FindFormat(&g711Format)
if audioMedia != nil {
var mtyp string

View File

@@ -10,8 +10,9 @@ import (
"sync"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v3/pkg/ringbuffer"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/ringbuffer"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
"github.com/google/uuid"
"github.com/pion/sdp/v3"
"github.com/pion/webrtc/v3"
@@ -25,16 +26,16 @@ type trackRecvPair struct {
receiver *webrtc.RTPReceiver
}
func webrtcMediasOfOutgoingTracks(tracks []*webRTCOutgoingTrack) media.Medias {
ret := make(media.Medias, len(tracks))
func webrtcMediasOfOutgoingTracks(tracks []*webRTCOutgoingTrack) []*description.Media {
ret := make([]*description.Media, len(tracks))
for i, track := range tracks {
ret[i] = track.media
}
return ret
}
func webrtcMediasOfIncomingTracks(tracks []*webRTCIncomingTrack) media.Medias {
ret := make(media.Medias, len(tracks))
func webrtcMediasOfIncomingTracks(tracks []*webRTCIncomingTrack) []*description.Media {
ret := make([]*description.Media, len(tracks))
for i, track := range tracks {
ret[i] = track.media
}
@@ -72,10 +73,10 @@ outer:
return nil
}
func webrtcGatherOutgoingTracks(medias media.Medias) ([]*webRTCOutgoingTrack, error) {
func webrtcGatherOutgoingTracks(desc *description.Session) ([]*webRTCOutgoingTrack, error) {
var tracks []*webRTCOutgoingTrack
videoTrack, err := newWebRTCOutgoingTrackVideo(medias)
videoTrack, err := newWebRTCOutgoingTrackVideo(desc)
if err != nil {
return nil, err
}
@@ -84,7 +85,7 @@ func webrtcGatherOutgoingTracks(medias media.Medias) ([]*webRTCOutgoingTrack, er
tracks = append(tracks, videoTrack)
}
audioTrack, err := newWebRTCOutgoingTrackAudio(medias)
audioTrack, err := newWebRTCOutgoingTrackAudio(desc)
if err != nil {
return nil, err
}
@@ -395,15 +396,17 @@ func (s *webRTCSession) runPublish() (int, error) {
rres := res.path.startPublisher(pathStartPublisherReq{
author: s,
medias: medias,
desc: &description.Session{Medias: medias},
generateRTPPackets: false,
})
if rres.err != nil {
return 0, rres.err
}
timeDecoder := rtptime.NewGlobalDecoder()
for _, track := range tracks {
track.start(rres.stream)
track.start(rres.stream, timeDecoder)
}
select {
@@ -447,7 +450,7 @@ func (s *webRTCSession) runRead() (int, error) {
defer res.path.removeReader(pathRemoveReaderReq{author: s})
tracks, err := webrtcGatherOutgoingTracks(res.stream.Medias())
tracks, err := webrtcGatherOutgoingTracks(res.stream.Desc())
if err != nil {
return http.StatusBadRequest, err
}

View File

@@ -8,6 +8,8 @@ import (
"strings"
"time"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
"github.com/pion/sdp/v3"
"github.com/pion/webrtc/v3"
@@ -144,7 +146,7 @@ func (s *webRTCSource) run(ctx context.Context, cnf *conf.PathConf, _ chan *conf
medias := webrtcMediasOfIncomingTracks(tracks)
rres := s.parent.setReady(pathSourceStaticSetReadyReq{
medias: medias,
desc: &description.Session{Medias: medias},
generateRTPPackets: true,
})
if rres.err != nil {
@@ -153,8 +155,10 @@ func (s *webRTCSource) run(ctx context.Context, cnf *conf.PathConf, _ chan *conf
defer s.parent.setNotReady(pathSourceStaticSetNotReadyReq{})
timeDecoder := rtptime.NewGlobalDecoder()
for _, track := range tracks {
track.start(rres.stream)
track.start(rres.stream, timeDecoder)
}
select {

View File

@@ -7,9 +7,9 @@ import (
"net/http"
"testing"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/url"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/url"
"github.com/pion/rtp"
"github.com/pion/webrtc/v3"
"github.com/stretchr/testify/require"
@@ -105,7 +105,7 @@ func TestWebRTCSource(t *testing.T) {
Timestamp: 45343,
SSRC: 563423,
},
Payload: []byte{1},
Payload: []byte{5, 1},
})
require.NoError(t, err)
@@ -118,7 +118,7 @@ func TestWebRTCSource(t *testing.T) {
Timestamp: 45343,
SSRC: 563423,
},
Payload: []byte{2},
Payload: []byte{5, 2},
})
require.NoError(t, err)
}()
@@ -152,19 +152,19 @@ func TestWebRTCSource(t *testing.T) {
require.NoError(t, err)
defer c.Close()
medias, baseURL, _, err := c.Describe(u)
desc, _, err := c.Describe(u)
require.NoError(t, err)
var forma *formats.VP8
medi := medias.FindFormat(&forma)
var forma *format.VP8
medi := desc.FindFormat(&forma)
_, err = c.Setup(medi, baseURL, 0, 0)
_, err = c.Setup(desc.BaseURL, medi, 0, 0)
require.NoError(t, err)
received := make(chan struct{})
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
require.Equal(t, []byte{3}, pkt.Payload)
require.Equal(t, []byte{5, 3}, pkt.Payload)
close(received)
})
@@ -180,7 +180,7 @@ func TestWebRTCSource(t *testing.T) {
Timestamp: 45343,
SSRC: 563423,
},
Payload: []byte{3},
Payload: []byte{5, 3},
})
require.NoError(t, err)

View File

@@ -4,8 +4,8 @@ import (
"fmt"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/formats/rtpav1"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpav1"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
@@ -14,7 +14,7 @@ import (
type formatProcessorAV1 struct {
udpMaxPayloadSize int
format *formats.AV1
format *format.AV1
log logger.Writer
encoder *rtpav1.Encoder
@@ -23,7 +23,7 @@ type formatProcessorAV1 struct {
func newAV1(
udpMaxPayloadSize int,
forma *formats.AV1,
forma *format.AV1,
generateRTPPackets bool,
log logger.Writer,
) (*formatProcessorAV1, error) {
@@ -69,14 +69,13 @@ func (t *formatProcessorAV1) Process(u unit.Unit, hasNonRTSPReaders bool) error
if hasNonRTSPReaders || t.decoder != nil {
if t.decoder == nil {
var err error
t.decoder, err = t.format.CreateDecoder2()
t.decoder, err = t.format.CreateDecoder()
if err != nil {
return err
}
}
// DecodeUntilMarker() is necessary, otherwise Encode() generates partial groups
tu, pts, err := t.decoder.DecodeUntilMarker(pkt)
tu, err := t.decoder.Decode(pkt)
if err != nil {
if err == rtpav1.ErrNonStartingPacketAndNoPrevious || err == rtpav1.ErrMorePacketsNeeded {
return nil
@@ -85,7 +84,6 @@ func (t *formatProcessorAV1) Process(u unit.Unit, hasNonRTSPReaders bool) error
}
tunit.TU = tu
tunit.PTS = pts
}
// route packet as is
@@ -93,20 +91,22 @@ func (t *formatProcessorAV1) Process(u unit.Unit, hasNonRTSPReaders bool) error
}
// encode into RTP
pkts, err := t.encoder.Encode(tunit.TU, tunit.PTS)
pkts, err := t.encoder.Encode(tunit.TU)
if err != nil {
return err
}
setTimestamp(pkts, tunit.RTPPackets, t.format.ClockRate(), tunit.PTS)
tunit.RTPPackets = pkts
return nil
}
func (t *formatProcessorAV1) UnitForRTPPacket(pkt *rtp.Packet, ntp time.Time) unit.Unit {
func (t *formatProcessorAV1) UnitForRTPPacket(pkt *rtp.Packet, ntp time.Time, pts time.Duration) Unit {
return &unit.AV1{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
}

View File

@@ -0,0 +1,29 @@
package formatprocessor
import (
"time"
"github.com/pion/rtp"
)
// BaseUnit contains fields shared across all units.
type BaseUnit struct {
RTPPackets []*rtp.Packet
NTP time.Time
PTS time.Duration
}
// GetRTPPackets implements Unit.
func (u *BaseUnit) GetRTPPackets() []*rtp.Packet {
return u.RTPPackets
}
// GetNTP implements Unit.
func (u *BaseUnit) GetNTP() time.Time {
return u.NTP
}
// GetPTS implements Unit.
func (u *BaseUnit) GetPTS() time.Duration {
return u.PTS
}

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
@@ -17,7 +17,7 @@ type formatProcessorGeneric struct {
func newGeneric(
udpMaxPayloadSize int,
forma formats.Format,
forma format.Format,
generateRTPPackets bool,
_ logger.Writer,
) (*formatProcessorGeneric, error) {
@@ -47,11 +47,12 @@ func (t *formatProcessorGeneric) Process(u unit.Unit, _ bool) error {
return nil
}
func (t *formatProcessorGeneric) UnitForRTPPacket(pkt *rtp.Packet, ntp time.Time) unit.Unit {
func (t *formatProcessorGeneric) UnitForRTPPacket(pkt *rtp.Packet, ntp time.Time, pts time.Duration) Unit {
return &unit.Generic{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
}

View File

@@ -3,7 +3,7 @@ package formatprocessor
import (
"testing"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
@@ -11,7 +11,7 @@ import (
)
func TestGenericRemovePadding(t *testing.T) {
forma := &formats.Generic{
forma := &format.Generic{
PayloadTyp: 96,
RTPMa: "private/90000",
}

View File

@@ -4,8 +4,8 @@ import (
"bytes"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/formats/rtph264"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/format/rtph264"
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
"github.com/pion/rtp"
@@ -72,7 +72,7 @@ func rtpH264ExtractSPSPPS(pkt *rtp.Packet) ([]byte, []byte) {
type formatProcessorH264 struct {
udpMaxPayloadSize int
format *formats.H264
format *format.H264
log logger.Writer
encoder *rtph264.Encoder
@@ -81,7 +81,7 @@ type formatProcessorH264 struct {
func newH264(
udpMaxPayloadSize int,
forma *formats.H264,
forma *format.H264,
generateRTPPackets bool,
log logger.Writer,
) (*formatProcessorH264, error) {
@@ -92,7 +92,7 @@ func newH264(
}
if generateRTPPackets {
err := t.createEncoder(nil, nil, nil)
err := t.createEncoder(nil, nil)
if err != nil {
return nil, err
}
@@ -102,14 +102,14 @@ func newH264(
}
func (t *formatProcessorH264) createEncoder(
ssrc *uint32, initialSequenceNumber *uint16, initialTimestamp *uint32,
ssrc *uint32,
initialSequenceNumber *uint16,
) error {
t.encoder = &rtph264.Encoder{
PayloadMaxSize: t.udpMaxPayloadSize - 12,
PayloadType: t.format.PayloadTyp,
SSRC: ssrc,
InitialSequenceNumber: initialSequenceNumber,
InitialTimestamp: initialTimestamp,
PacketizationMode: t.format.PacketizationMode,
}
return t.encoder.Init()
@@ -240,8 +240,7 @@ func (t *formatProcessorH264) Process(u unit.Unit, hasNonRTSPReaders bool) error
if pkt.MarshalSize() > t.udpMaxPayloadSize {
v1 := pkt.SSRC
v2 := pkt.SequenceNumber
v3 := pkt.Timestamp
err := t.createEncoder(&v1, &v2, &v3)
err := t.createEncoder(&v1, &v2)
if err != nil {
return err
}
@@ -252,27 +251,24 @@ func (t *formatProcessorH264) Process(u unit.Unit, hasNonRTSPReaders bool) error
if hasNonRTSPReaders || t.decoder != nil || t.encoder != nil {
if t.decoder == nil {
var err error
t.decoder, err = t.format.CreateDecoder2()
t.decoder, err = t.format.CreateDecoder()
if err != nil {
return err
}
}
if t.encoder != nil {
tunit.RTPPackets = nil
}
// DecodeUntilMarker() is necessary, otherwise Encode() generates partial groups
au, pts, err := t.decoder.DecodeUntilMarker(pkt)
au, err := t.decoder.Decode(pkt)
if err != nil {
if err == rtph264.ErrNonStartingPacketAndNoPrevious || err == rtph264.ErrMorePacketsNeeded {
if t.encoder != nil {
tunit.RTPPackets = nil
}
return nil
}
return err
}
tunit.AU = t.remuxAccessUnit(au)
tunit.PTS = pts
}
// route packet as is
@@ -286,10 +282,11 @@ func (t *formatProcessorH264) Process(u unit.Unit, hasNonRTSPReaders bool) error
// encode into RTP
if len(tunit.AU) != 0 {
pkts, err := t.encoder.Encode(tunit.AU, tunit.PTS)
pkts, err := t.encoder.Encode(tunit.AU)
if err != nil {
return err
}
setTimestamp(pkts, tunit.RTPPackets, t.format.ClockRate(), tunit.PTS)
tunit.RTPPackets = pkts
} else {
tunit.RTPPackets = nil
@@ -298,11 +295,12 @@ func (t *formatProcessorH264) Process(u unit.Unit, hasNonRTSPReaders bool) error
return nil
}
func (t *formatProcessorH264) UnitForRTPPacket(pkt *rtp.Packet, ntp time.Time) unit.Unit {
func (t *formatProcessorH264) UnitForRTPPacket(pkt *rtp.Packet, ntp time.Time, pts time.Duration) Unit {
return &unit.H264{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
}

View File

@@ -4,7 +4,7 @@ import (
"bytes"
"testing"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
@@ -13,7 +13,7 @@ import (
)
func TestH264DynamicParams(t *testing.T) {
forma := &formats.H264{
forma := &format.H264{
PayloadTyp: 96,
PacketizationMode: 1,
}
@@ -21,10 +21,10 @@ func TestH264DynamicParams(t *testing.T) {
p, err := New(1472, forma, false, nil)
require.NoError(t, err)
enc, err := forma.CreateEncoder2()
enc, err := forma.CreateEncoder()
require.NoError(t, err)
pkts, err := enc.Encode([][]byte{{byte(h264.NALUTypeIDR)}}, 0)
pkts, err := enc.Encode([][]byte{{byte(h264.NALUTypeIDR)}})
require.NoError(t, err)
data := &unit.H264{
@@ -39,7 +39,7 @@ func TestH264DynamicParams(t *testing.T) {
{byte(h264.NALUTypeIDR)},
}, data.AU)
pkts, err = enc.Encode([][]byte{{7, 4, 5, 6}}, 0) // SPS
pkts, err = enc.Encode([][]byte{{7, 4, 5, 6}}) // SPS
require.NoError(t, err)
err = p.Process(&unit.H264{
@@ -49,7 +49,7 @@ func TestH264DynamicParams(t *testing.T) {
}, false)
require.NoError(t, err)
pkts, err = enc.Encode([][]byte{{8, 1}}, 0) // PPS
pkts, err = enc.Encode([][]byte{{8, 1}}) // PPS
require.NoError(t, err)
err = p.Process(&unit.H264{
@@ -62,7 +62,7 @@ func TestH264DynamicParams(t *testing.T) {
require.Equal(t, []byte{7, 4, 5, 6}, forma.SPS)
require.Equal(t, []byte{8, 1}, forma.PPS)
pkts, err = enc.Encode([][]byte{{byte(h264.NALUTypeIDR)}}, 0)
pkts, err = enc.Encode([][]byte{{byte(h264.NALUTypeIDR)}})
require.NoError(t, err)
data = &unit.H264{
@@ -81,7 +81,7 @@ func TestH264DynamicParams(t *testing.T) {
}
func TestH264OversizedPackets(t *testing.T) {
forma := &formats.H264{
forma := &format.H264{
PayloadTyp: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
@@ -186,7 +186,7 @@ func TestH264OversizedPackets(t *testing.T) {
}
func TestH264EmptyPacket(t *testing.T) {
forma := &formats.H264{
forma := &format.H264{
PayloadTyp: 96,
PacketizationMode: 1,
}

View File

@@ -4,8 +4,8 @@ import (
"bytes"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/formats/rtph265"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/format/rtph265"
"github.com/bluenviron/mediacommon/pkg/codecs/h265"
"github.com/pion/rtp"
@@ -79,7 +79,7 @@ func rtpH265ExtractVPSSPSPPS(pkt *rtp.Packet) ([]byte, []byte, []byte) {
type formatProcessorH265 struct {
udpMaxPayloadSize int
format *formats.H265
format *format.H265
log logger.Writer
encoder *rtph265.Encoder
@@ -88,7 +88,7 @@ type formatProcessorH265 struct {
func newH265(
udpMaxPayloadSize int,
forma *formats.H265,
forma *format.H265,
generateRTPPackets bool,
log logger.Writer,
) (*formatProcessorH265, error) {
@@ -99,7 +99,7 @@ func newH265(
}
if generateRTPPackets {
err := t.createEncoder(nil, nil, nil)
err := t.createEncoder(nil, nil)
if err != nil {
return nil, err
}
@@ -109,14 +109,14 @@ func newH265(
}
func (t *formatProcessorH265) createEncoder(
ssrc *uint32, initialSequenceNumber *uint16, initialTimestamp *uint32,
ssrc *uint32,
initialSequenceNumber *uint16,
) error {
t.encoder = &rtph265.Encoder{
PayloadMaxSize: t.udpMaxPayloadSize - 12,
PayloadType: t.format.PayloadTyp,
SSRC: ssrc,
InitialSequenceNumber: initialSequenceNumber,
InitialTimestamp: initialTimestamp,
MaxDONDiff: t.format.MaxDONDiff,
}
return t.encoder.Init()
@@ -262,8 +262,7 @@ func (t *formatProcessorH265) Process(u unit.Unit, hasNonRTSPReaders bool) error
if pkt.MarshalSize() > t.udpMaxPayloadSize {
v1 := pkt.SSRC
v2 := pkt.SequenceNumber
v3 := pkt.Timestamp
err := t.createEncoder(&v1, &v2, &v3)
err := t.createEncoder(&v1, &v2)
if err != nil {
return err
}
@@ -274,27 +273,24 @@ func (t *formatProcessorH265) Process(u unit.Unit, hasNonRTSPReaders bool) error
if hasNonRTSPReaders || t.decoder != nil || t.encoder != nil {
if t.decoder == nil {
var err error
t.decoder, err = t.format.CreateDecoder2()
t.decoder, err = t.format.CreateDecoder()
if err != nil {
return err
}
}
if t.encoder != nil {
tunit.RTPPackets = nil
}
// DecodeUntilMarker() is necessary, otherwise Encode() generates partial groups
au, pts, err := t.decoder.DecodeUntilMarker(pkt)
au, err := t.decoder.Decode(pkt)
if err != nil {
if err == rtph265.ErrNonStartingPacketAndNoPrevious || err == rtph265.ErrMorePacketsNeeded {
if t.encoder != nil {
tunit.RTPPackets = nil
}
return nil
}
return err
}
tunit.AU = t.remuxAccessUnit(au)
tunit.PTS = pts
}
// route packet as is
@@ -308,10 +304,11 @@ func (t *formatProcessorH265) Process(u unit.Unit, hasNonRTSPReaders bool) error
// encode into RTP
if len(tunit.AU) != 0 {
pkts, err := t.encoder.Encode(tunit.AU, tunit.PTS)
pkts, err := t.encoder.Encode(tunit.AU)
if err != nil {
return err
}
setTimestamp(pkts, tunit.RTPPackets, t.format.ClockRate(), tunit.PTS)
tunit.RTPPackets = pkts
} else {
tunit.RTPPackets = nil
@@ -320,11 +317,12 @@ func (t *formatProcessorH265) Process(u unit.Unit, hasNonRTSPReaders bool) error
return nil
}
func (t *formatProcessorH265) UnitForRTPPacket(pkt *rtp.Packet, ntp time.Time) unit.Unit {
func (t *formatProcessorH265) UnitForRTPPacket(pkt *rtp.Packet, ntp time.Time, pts time.Duration) Unit {
return &unit.H265{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
}

View File

@@ -4,7 +4,7 @@ import (
"bytes"
"testing"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/mediacommon/pkg/codecs/h265"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
@@ -13,17 +13,17 @@ import (
)
func TestH265DynamicParams(t *testing.T) {
forma := &formats.H265{
forma := &format.H265{
PayloadTyp: 96,
}
p, err := New(1472, forma, false, nil)
require.NoError(t, err)
enc, err := forma.CreateEncoder2()
enc, err := forma.CreateEncoder()
require.NoError(t, err)
pkts, err := enc.Encode([][]byte{{byte(h265.NALUType_CRA_NUT) << 1, 0}}, 0)
pkts, err := enc.Encode([][]byte{{byte(h265.NALUType_CRA_NUT) << 1, 0}})
require.NoError(t, err)
data := &unit.H265{
@@ -38,7 +38,7 @@ func TestH265DynamicParams(t *testing.T) {
{byte(h265.NALUType_CRA_NUT) << 1, 0},
}, data.AU)
pkts, err = enc.Encode([][]byte{{byte(h265.NALUType_VPS_NUT) << 1, 1, 2, 3}}, 0)
pkts, err = enc.Encode([][]byte{{byte(h265.NALUType_VPS_NUT) << 1, 1, 2, 3}})
require.NoError(t, err)
err = p.Process(&unit.H265{
@@ -48,7 +48,7 @@ func TestH265DynamicParams(t *testing.T) {
}, false)
require.NoError(t, err)
pkts, err = enc.Encode([][]byte{{byte(h265.NALUType_SPS_NUT) << 1, 4, 5, 6}}, 0)
pkts, err = enc.Encode([][]byte{{byte(h265.NALUType_SPS_NUT) << 1, 4, 5, 6}})
require.NoError(t, err)
err = p.Process(&unit.H265{
@@ -58,7 +58,7 @@ func TestH265DynamicParams(t *testing.T) {
}, false)
require.NoError(t, err)
pkts, err = enc.Encode([][]byte{{byte(h265.NALUType_PPS_NUT) << 1, 7, 8, 9}}, 0)
pkts, err = enc.Encode([][]byte{{byte(h265.NALUType_PPS_NUT) << 1, 7, 8, 9}})
require.NoError(t, err)
err = p.Process(&unit.H265{
@@ -72,7 +72,7 @@ func TestH265DynamicParams(t *testing.T) {
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)
pkts, err = enc.Encode([][]byte{{byte(h265.NALUType_CRA_NUT) << 1, 0}}, 0)
pkts, err = enc.Encode([][]byte{{byte(h265.NALUType_CRA_NUT) << 1, 0}})
require.NoError(t, err)
data = &unit.H265{
@@ -92,7 +92,7 @@ func TestH265DynamicParams(t *testing.T) {
}
func TestH265OversizedPackets(t *testing.T) {
forma := &formats.H265{
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},
@@ -185,7 +185,7 @@ func TestH265OversizedPackets(t *testing.T) {
}
func TestH265EmptyPacket(t *testing.T) {
forma := &formats.H265{
forma := &format.H265{
PayloadTyp: 96,
}

View File

@@ -4,8 +4,8 @@ import (
"fmt"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/formats/rtpmpeg1audio"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg1audio"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
@@ -14,14 +14,14 @@ import (
type formatProcessorMPEG1Audio struct {
udpMaxPayloadSize int
format *formats.MPEG1Audio
format *format.MPEG1Audio
encoder *rtpmpeg1audio.Encoder
decoder *rtpmpeg1audio.Decoder
}
func newMPEG1Audio(
udpMaxPayloadSize int,
forma *formats.MPEG1Audio,
forma *format.MPEG1Audio,
generateRTPPackets bool,
_ logger.Writer,
) (*formatProcessorMPEG1Audio, error) {
@@ -66,13 +66,13 @@ func (t *formatProcessorMPEG1Audio) Process(u unit.Unit, hasNonRTSPReaders bool)
if hasNonRTSPReaders || t.decoder != nil {
if t.decoder == nil {
var err error
t.decoder, err = t.format.CreateDecoder2()
t.decoder, err = t.format.CreateDecoder()
if err != nil {
return err
}
}
frames, pts, err := t.decoder.Decode(pkt)
frames, err := t.decoder.Decode(pkt)
if err != nil {
if err == rtpmpeg1audio.ErrNonStartingPacketAndNoPrevious || err == rtpmpeg1audio.ErrMorePacketsNeeded {
return nil
@@ -81,7 +81,6 @@ func (t *formatProcessorMPEG1Audio) Process(u unit.Unit, hasNonRTSPReaders bool)
}
tunit.Frames = frames
tunit.PTS = pts
}
// route packet as is
@@ -89,20 +88,22 @@ func (t *formatProcessorMPEG1Audio) Process(u unit.Unit, hasNonRTSPReaders bool)
}
// encode into RTP
pkts, err := t.encoder.Encode(tunit.Frames, tunit.PTS)
pkts, err := t.encoder.Encode(tunit.Frames)
if err != nil {
return err
}
setTimestamp(pkts, tunit.RTPPackets, t.format.ClockRate(), tunit.PTS)
tunit.RTPPackets = pkts
return nil
}
func (t *formatProcessorMPEG1Audio) UnitForRTPPacket(pkt *rtp.Packet, ntp time.Time) unit.Unit {
func (t *formatProcessorMPEG1Audio) UnitForRTPPacket(pkt *rtp.Packet, ntp time.Time, pts time.Duration) Unit {
return &unit.MPEG1Audio{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
}

View File

@@ -4,8 +4,8 @@ import (
"fmt"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/formats/rtpmpeg4audio"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4audio"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
@@ -14,14 +14,14 @@ import (
type formatProcessorMPEG4AudioGeneric struct {
udpMaxPayloadSize int
format *formats.MPEG4Audio
format *format.MPEG4Audio
encoder *rtpmpeg4audio.Encoder
decoder *rtpmpeg4audio.Decoder
}
func newMPEG4AudioGeneric(
udpMaxPayloadSize int,
forma *formats.MPEG4Audio,
forma *format.MPEG4Audio,
generateRTPPackets bool,
_ logger.Writer,
) (*formatProcessorMPEG4AudioGeneric, error) {
@@ -44,7 +44,6 @@ func (t *formatProcessorMPEG4AudioGeneric) createEncoder() error {
t.encoder = &rtpmpeg4audio.Encoder{
PayloadMaxSize: t.udpMaxPayloadSize - 12,
PayloadType: t.format.PayloadTyp,
SampleRate: t.format.Config.SampleRate,
SizeLength: t.format.SizeLength,
IndexLength: t.format.IndexLength,
IndexDeltaLength: t.format.IndexDeltaLength,
@@ -71,13 +70,13 @@ func (t *formatProcessorMPEG4AudioGeneric) Process(u unit.Unit, hasNonRTSPReader
if hasNonRTSPReaders || t.decoder != nil || true {
if t.decoder == nil {
var err error
t.decoder, err = t.format.CreateDecoder2()
t.decoder, err = t.format.CreateDecoder()
if err != nil {
return err
}
}
aus, pts, err := t.decoder.Decode(pkt)
aus, err := t.decoder.Decode(pkt)
if err != nil {
if err == rtpmpeg4audio.ErrMorePacketsNeeded {
return nil
@@ -86,7 +85,6 @@ func (t *formatProcessorMPEG4AudioGeneric) Process(u unit.Unit, hasNonRTSPReader
}
tunit.AUs = aus
tunit.PTS = pts
}
// route packet as is
@@ -94,20 +92,22 @@ func (t *formatProcessorMPEG4AudioGeneric) Process(u unit.Unit, hasNonRTSPReader
}
// encode into RTP
pkts, err := t.encoder.Encode(tunit.AUs, tunit.PTS)
pkts, err := t.encoder.Encode(tunit.AUs)
if err != nil {
return err
}
setTimestamp(pkts, tunit.RTPPackets, t.format.ClockRate(), tunit.PTS)
tunit.RTPPackets = pkts
return nil
}
func (t *formatProcessorMPEG4AudioGeneric) UnitForRTPPacket(pkt *rtp.Packet, ntp time.Time) unit.Unit {
func (t *formatProcessorMPEG4AudioGeneric) UnitForRTPPacket(pkt *rtp.Packet, ntp time.Time, pts time.Duration) Unit {
return &unit.MPEG4AudioGeneric{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
}

View File

@@ -4,8 +4,8 @@ import (
"fmt"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/formats/rtpmpeg4audiolatm"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4audiolatm"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
@@ -14,14 +14,14 @@ import (
type formatProcessorMPEG4AudioLATM struct {
udpMaxPayloadSize int
format *formats.MPEG4AudioLATM
format *format.MPEG4AudioLATM
encoder *rtpmpeg4audiolatm.Encoder
decoder *rtpmpeg4audiolatm.Decoder
}
func newMPEG4AudioLATM(
udpMaxPayloadSize int,
forma *formats.MPEG4AudioLATM,
forma *format.MPEG4AudioLATM,
generateRTPPackets bool,
_ logger.Writer,
) (*formatProcessorMPEG4AudioLATM, error) {
@@ -43,7 +43,6 @@ func newMPEG4AudioLATM(
func (t *formatProcessorMPEG4AudioLATM) createEncoder() error {
t.encoder = &rtpmpeg4audiolatm.Encoder{
PayloadType: t.format.PayloadTyp,
Config: t.format.Config,
}
return t.encoder.Init()
}
@@ -67,13 +66,13 @@ func (t *formatProcessorMPEG4AudioLATM) Process(u unit.Unit, hasNonRTSPReaders b
if hasNonRTSPReaders || t.decoder != nil {
if t.decoder == nil {
var err error
t.decoder, err = t.format.CreateDecoder2()
t.decoder, err = t.format.CreateDecoder()
if err != nil {
return err
}
}
au, pts, err := t.decoder.Decode(pkt)
au, err := t.decoder.Decode(pkt)
if err != nil {
if err == rtpmpeg4audiolatm.ErrMorePacketsNeeded {
return nil
@@ -82,7 +81,6 @@ func (t *formatProcessorMPEG4AudioLATM) Process(u unit.Unit, hasNonRTSPReaders b
}
tunit.AU = au
tunit.PTS = pts
}
// route packet as is
@@ -90,20 +88,22 @@ func (t *formatProcessorMPEG4AudioLATM) Process(u unit.Unit, hasNonRTSPReaders b
}
// encode into RTP
pkts, err := t.encoder.Encode(tunit.AU, tunit.PTS)
pkts, err := t.encoder.Encode(tunit.AU)
if err != nil {
return err
}
setTimestamp(pkts, tunit.RTPPackets, t.format.ClockRate(), tunit.PTS)
tunit.RTPPackets = pkts
return nil
}
func (t *formatProcessorMPEG4AudioLATM) UnitForRTPPacket(pkt *rtp.Packet, ntp time.Time) unit.Unit {
func (t *formatProcessorMPEG4AudioLATM) UnitForRTPPacket(pkt *rtp.Packet, ntp time.Time, pts time.Duration) Unit {
return &unit.MPEG4AudioLATM{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
}

View File

@@ -4,8 +4,8 @@ import (
"fmt"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/formats/rtpsimpleaudio"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpsimpleaudio"
"github.com/bluenviron/mediacommon/pkg/codecs/opus"
"github.com/pion/rtp"
@@ -15,14 +15,14 @@ import (
type formatProcessorOpus struct {
udpMaxPayloadSize int
format *formats.Opus
format *format.Opus
encoder *rtpsimpleaudio.Encoder
decoder *rtpsimpleaudio.Decoder
}
func newOpus(
udpMaxPayloadSize int,
forma *formats.Opus,
forma *format.Opus,
generateRTPPackets bool,
_ logger.Writer,
) (*formatProcessorOpus, error) {
@@ -45,7 +45,6 @@ func (t *formatProcessorOpus) createEncoder() error {
t.encoder = &rtpsimpleaudio.Encoder{
PayloadMaxSize: t.udpMaxPayloadSize - 12,
PayloadType: t.format.PayloadTyp,
SampleRate: 48000,
}
return t.encoder.Init()
}
@@ -69,19 +68,18 @@ func (t *formatProcessorOpus) Process(u unit.Unit, hasNonRTSPReaders bool) error
if hasNonRTSPReaders || t.decoder != nil {
if t.decoder == nil {
var err error
t.decoder, err = t.format.CreateDecoder2()
t.decoder, err = t.format.CreateDecoder()
if err != nil {
return err
}
}
packet, pts, err := t.decoder.Decode(pkt)
packet, err := t.decoder.Decode(pkt)
if err != nil {
return err
}
tunit.Packets = [][]byte{packet}
tunit.PTS = pts
}
// route packet as is
@@ -92,24 +90,26 @@ func (t *formatProcessorOpus) Process(u unit.Unit, hasNonRTSPReaders bool) error
var rtpPackets []*rtp.Packet //nolint:prealloc
pts := tunit.PTS
for _, packet := range tunit.Packets {
pkt, err := t.encoder.Encode(packet, pts)
pkt, err := t.encoder.Encode(packet)
if err != nil {
return err
}
setTimestamp([]*rtp.Packet{pkt}, tunit.RTPPackets, t.format.ClockRate(), pts)
rtpPackets = append(rtpPackets, pkt)
pts += opus.PacketDuration(packet)
}
tunit.RTPPackets = rtpPackets
return nil
}
func (t *formatProcessorOpus) UnitForRTPPacket(pkt *rtp.Packet, ntp time.Time) unit.Unit {
func (t *formatProcessorOpus) UnitForRTPPacket(pkt *rtp.Packet, ntp time.Time, pts time.Duration) Unit {
return &unit.Opus{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
}

View File

@@ -4,55 +4,75 @@ package formatprocessor
import (
"time"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/unit"
)
// avoid an int64 overflow and preserve resolution by splitting division into two parts:
// first add the integer part, then the decimal part.
func multiplyAndDivide(v, m, d time.Duration) time.Duration {
secs := v / d
dec := v % d
return (secs*m + dec*m/d)
}
func setTimestamp(newPackets []*rtp.Packet, oldPackets []*rtp.Packet, clockRate int, pts time.Duration) {
if oldPackets != nil { // get timestamp from old packets
for _, pkt := range newPackets {
pkt.Timestamp = oldPackets[0].Timestamp
}
} else { // get timestamp from PTS
for _, pkt := range newPackets {
pkt.Timestamp = uint32(multiplyAndDivide(pts, time.Duration(clockRate), time.Second))
}
}
}
// Processor cleans and normalizes streams.
type Processor interface {
// cleans and normalizes a data unit.
Process(unit.Unit, bool) error
// wraps a RTP packet into a Unit.
UnitForRTPPacket(pkt *rtp.Packet, ntp time.Time) unit.Unit
UnitForRTPPacket(pkt *rtp.Packet, ntp time.Time, pts time.Duration) Unit
}
// New allocates a Processor.
func New(
udpMaxPayloadSize int,
forma formats.Format,
forma format.Format,
generateRTPPackets bool,
log logger.Writer,
) (Processor, error) {
switch forma := forma.(type) {
case *formats.H264:
case *format.H264:
return newH264(udpMaxPayloadSize, forma, generateRTPPackets, log)
case *formats.H265:
case *format.H265:
return newH265(udpMaxPayloadSize, forma, generateRTPPackets, log)
case *formats.VP8:
case *format.VP8:
return newVP8(udpMaxPayloadSize, forma, generateRTPPackets, log)
case *formats.VP9:
case *format.VP9:
return newVP9(udpMaxPayloadSize, forma, generateRTPPackets, log)
case *formats.AV1:
case *format.AV1:
return newAV1(udpMaxPayloadSize, forma, generateRTPPackets, log)
case *formats.MPEG1Audio:
case *format.MPEG1Audio:
return newMPEG1Audio(udpMaxPayloadSize, forma, generateRTPPackets, log)
case *formats.MPEG4AudioGeneric:
case *format.MPEG4AudioGeneric:
return newMPEG4AudioGeneric(udpMaxPayloadSize, forma, generateRTPPackets, log)
case *formats.MPEG4AudioLATM:
case *format.MPEG4AudioLATM:
return newMPEG4AudioLATM(udpMaxPayloadSize, forma, generateRTPPackets, log)
case *formats.Opus:
case *format.Opus:
return newOpus(udpMaxPayloadSize, forma, generateRTPPackets, log)
default:

View File

@@ -0,0 +1,19 @@
package formatprocessor
import (
"time"
"github.com/pion/rtp"
)
// Unit is the elementary data unit routed across the server.
type Unit interface {
// returns RTP packets contained into the unit.
GetRTPPackets() []*rtp.Packet
// returns the NTP timestamp of the unit.
GetNTP() time.Time
// returns the PTS of the unit.
GetPTS() time.Duration
}

View File

@@ -4,8 +4,8 @@ import (
"fmt"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/formats/rtpvp8"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpvp8"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
@@ -14,14 +14,14 @@ import (
type formatProcessorVP8 struct {
udpMaxPayloadSize int
format *formats.VP8
format *format.VP8
encoder *rtpvp8.Encoder
decoder *rtpvp8.Decoder
}
func newVP8(
udpMaxPayloadSize int,
forma *formats.VP8,
forma *format.VP8,
generateRTPPackets bool,
_ logger.Writer,
) (*formatProcessorVP8, error) {
@@ -67,13 +67,13 @@ func (t *formatProcessorVP8) Process(y unit.Unit, hasNonRTSPReaders bool) error
if hasNonRTSPReaders || t.decoder != nil {
if t.decoder == nil {
var err error
t.decoder, err = t.format.CreateDecoder2()
t.decoder, err = t.format.CreateDecoder()
if err != nil {
return err
}
}
frame, pts, err := t.decoder.Decode(pkt)
frame, err := t.decoder.Decode(pkt)
if err != nil {
if err == rtpvp8.ErrNonStartingPacketAndNoPrevious || err == rtpvp8.ErrMorePacketsNeeded {
return nil
@@ -82,7 +82,6 @@ func (t *formatProcessorVP8) Process(y unit.Unit, hasNonRTSPReaders bool) error
}
tunit.Frame = frame
tunit.PTS = pts
}
// route packet as is
@@ -90,20 +89,22 @@ func (t *formatProcessorVP8) Process(y unit.Unit, hasNonRTSPReaders bool) error
}
// encode into RTP
pkts, err := t.encoder.Encode(tunit.Frame, tunit.PTS)
pkts, err := t.encoder.Encode(tunit.Frame)
if err != nil {
return err
}
setTimestamp(pkts, tunit.RTPPackets, t.format.ClockRate(), tunit.PTS)
tunit.RTPPackets = pkts
return nil
}
func (t *formatProcessorVP8) UnitForRTPPacket(pkt *rtp.Packet, ntp time.Time) unit.Unit {
func (t *formatProcessorVP8) UnitForRTPPacket(pkt *rtp.Packet, ntp time.Time, pts time.Duration) Unit {
return &unit.VP8{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
}

View File

@@ -4,8 +4,8 @@ import (
"fmt"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/formats/rtpvp9"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpvp9"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
@@ -14,14 +14,14 @@ import (
type formatProcessorVP9 struct {
udpMaxPayloadSize int
format *formats.VP9
format *format.VP9
encoder *rtpvp9.Encoder
decoder *rtpvp9.Decoder
}
func newVP9(
udpMaxPayloadSize int,
forma *formats.VP9,
forma *format.VP9,
generateRTPPackets bool,
_ logger.Writer,
) (*formatProcessorVP9, error) {
@@ -67,13 +67,13 @@ func (t *formatProcessorVP9) Process(u unit.Unit, hasNonRTSPReaders bool) error
if hasNonRTSPReaders || t.decoder != nil {
if t.decoder == nil {
var err error
t.decoder, err = t.format.CreateDecoder2()
t.decoder, err = t.format.CreateDecoder()
if err != nil {
return err
}
}
frame, pts, err := t.decoder.Decode(pkt)
frame, err := t.decoder.Decode(pkt)
if err != nil {
if err == rtpvp9.ErrNonStartingPacketAndNoPrevious || err == rtpvp9.ErrMorePacketsNeeded {
return nil
@@ -82,7 +82,6 @@ func (t *formatProcessorVP9) Process(u unit.Unit, hasNonRTSPReaders bool) error
}
tunit.Frame = frame
tunit.PTS = pts
}
// route packet as is
@@ -90,20 +89,22 @@ func (t *formatProcessorVP9) Process(u unit.Unit, hasNonRTSPReaders bool) error
}
// encode into RTP
pkts, err := t.encoder.Encode(tunit.Frame, tunit.PTS)
pkts, err := t.encoder.Encode(tunit.Frame)
if err != nil {
return err
}
setTimestamp(pkts, tunit.RTPPackets, t.format.ClockRate(), tunit.PTS)
tunit.RTPPackets = pkts
return nil
}
func (t *formatProcessorVP9) UnitForRTPPacket(pkt *rtp.Packet, ntp time.Time) unit.Unit {
func (t *formatProcessorVP9) UnitForRTPPacket(pkt *rtp.Packet, ntp time.Time, pts time.Duration) Unit {
return &unit.VP9{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
}

View File

@@ -7,7 +7,7 @@ import (
"time"
"github.com/abema/go-mp4"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/mediacommon/pkg/codecs/av1"
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
"github.com/bluenviron/mediacommon/pkg/codecs/h265"
@@ -43,14 +43,14 @@ func h265FindNALU(array []mp4.HEVCNaluArray, typ h265.NALUType) []byte {
return nil
}
func trackFromH264DecoderConfig(data []byte) (formats.Format, error) {
func trackFromH264DecoderConfig(data []byte) (format.Format, error) {
var conf h264conf.Conf
err := conf.Unmarshal(data)
if err != nil {
return nil, fmt.Errorf("unable to parse H264 config: %v", err)
}
return &formats.H264{
return &format.H264{
PayloadTyp: 96,
SPS: conf.SPS,
PPS: conf.PPS,
@@ -58,14 +58,14 @@ func trackFromH264DecoderConfig(data []byte) (formats.Format, error) {
}, nil
}
func trackFromAACDecoderConfig(data []byte) (formats.Format, error) {
func trackFromAACDecoderConfig(data []byte) (format.Format, error) {
var mpegConf mpeg4audio.Config
err := mpegConf.Unmarshal(data)
if err != nil {
return nil, err
}
return &formats.MPEG4Audio{
return &format.MPEG4Audio{
PayloadTyp: 96,
Config: &mpegConf,
SizeLength: 13,
@@ -76,7 +76,7 @@ func trackFromAACDecoderConfig(data []byte) (formats.Format, error) {
var errEmptyMetadata = errors.New("metadata is empty")
func tracksFromMetadata(conn *Conn, payload []interface{}) (formats.Format, formats.Format, error) {
func tracksFromMetadata(conn *Conn, payload []interface{}) (format.Format, format.Format, error) {
if len(payload) != 1 {
return nil, nil, fmt.Errorf("invalid metadata")
}
@@ -86,8 +86,8 @@ func tracksFromMetadata(conn *Conn, payload []interface{}) (formats.Format, form
return nil, nil, fmt.Errorf("invalid metadata")
}
var videoTrack formats.Format
var audioTrack formats.Format
var videoTrack format.Format
var audioTrack format.Format
hasVideo, err := func() (bool, error) {
v, ok := md.GetV("videocodecid")
@@ -131,7 +131,7 @@ func tracksFromMetadata(conn *Conn, payload []interface{}) (formats.Format, form
return false, nil
case message.CodecMPEG1Audio:
audioTrack = &formats.MPEG1Audio{}
audioTrack = &format.MPEG1Audio{}
return true, nil
case message.CodecMPEG4Audio:
@@ -205,7 +205,7 @@ func tracksFromMetadata(conn *Conn, payload []interface{}) (formats.Format, form
}
if vps != nil && sps != nil && pps != nil {
videoTrack = &formats.H265{
videoTrack = &format.H265{
PayloadTyp: 96,
VPS: vps,
SPS: sps,
@@ -232,7 +232,7 @@ func tracksFromMetadata(conn *Conn, payload []interface{}) (formats.Format, form
return nil, nil, fmt.Errorf("H265 parameters are missing")
}
videoTrack = &formats.H265{
videoTrack = &format.H265{
PayloadTyp: 96,
VPS: vps,
SPS: sps,
@@ -252,7 +252,7 @@ func tracksFromMetadata(conn *Conn, payload []interface{}) (formats.Format, form
return nil, nil, fmt.Errorf("invalid AV1 configuration: %v", err)
}
videoTrack = &formats.AV1{}
videoTrack = &format.AV1{}
default: // VP9
var vpcc mp4.VpcC
@@ -261,7 +261,7 @@ func tracksFromMetadata(conn *Conn, payload []interface{}) (formats.Format, form
return nil, nil, fmt.Errorf("invalid VP9 configuration: %v", err)
}
videoTrack = &formats.VP9{}
videoTrack = &format.VP9{}
}
}
@@ -282,10 +282,10 @@ func tracksFromMetadata(conn *Conn, payload []interface{}) (formats.Format, form
}
}
func tracksFromMessages(conn *Conn, msg message.Message) (formats.Format, formats.Format, error) {
func tracksFromMessages(conn *Conn, msg message.Message) (format.Format, format.Format, error) {
var startTime *time.Duration
var videoTrack formats.Format
var audioTrack formats.Format
var videoTrack format.Format
var audioTrack format.Format
// analyze 1 second of packets
outer:
@@ -359,8 +359,8 @@ outer:
// Reader is a wrapper around Conn that provides utilities to demux incoming data.
type Reader struct {
conn *Conn
videoTrack formats.Format
audioTrack formats.Format
videoTrack format.Format
audioTrack format.Format
onDataVideo func(message.Message) error
onDataAudio func(*message.Audio) error
}
@@ -380,7 +380,7 @@ func NewReader(conn *Conn) (*Reader, error) {
return r, nil
}
func (r *Reader) readTracks() (formats.Format, formats.Format, error) {
func (r *Reader) readTracks() (format.Format, format.Format, error) {
msg, err := func() (message.Message, error) {
for {
msg, err := r.conn.Read()
@@ -439,7 +439,7 @@ func (r *Reader) readTracks() (formats.Format, formats.Format, error) {
}
// Tracks returns detected tracks
func (r *Reader) Tracks() (formats.Format, formats.Format) {
func (r *Reader) Tracks() (format.Format, format.Format) {
return r.videoTrack, r.audioTrack
}

View File

@@ -6,7 +6,7 @@ import (
"time"
"github.com/abema/go-mp4"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
"github.com/bluenviron/mediacommon/pkg/codecs/h265"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
@@ -104,18 +104,18 @@ func TestReadTracks(t *testing.T) {
for _, ca := range []struct {
name string
videoTrack formats.Format
audioTrack formats.Format
videoTrack format.Format
audioTrack format.Format
}{
{
"video+audio",
&formats.H264{
&format.H264{
PayloadTyp: 96,
SPS: h264SPS,
PPS: h264PPS,
PacketizationMode: 1,
},
&formats.MPEG4Audio{
&format.MPEG4Audio{
PayloadTyp: 96,
Config: &mpeg4audio.Config{
Type: 2,
@@ -129,7 +129,7 @@ func TestReadTracks(t *testing.T) {
},
{
"video",
&formats.H264{
&format.H264{
PayloadTyp: 96,
SPS: h264SPS,
PPS: h264PPS,
@@ -139,13 +139,13 @@ func TestReadTracks(t *testing.T) {
},
{
"metadata without codec id, video+audio",
&formats.H264{
&format.H264{
PayloadTyp: 96,
SPS: h264SPS,
PPS: h264PPS,
PacketizationMode: 1,
},
&formats.MPEG4Audio{
&format.MPEG4Audio{
PayloadTyp: 96,
Config: &mpeg4audio.Config{
Type: 2,
@@ -159,7 +159,7 @@ func TestReadTracks(t *testing.T) {
},
{
"metadata without codec id, video only",
&formats.H264{
&format.H264{
PayloadTyp: 96,
SPS: h264SPS,
PPS: h264PPS,
@@ -169,13 +169,13 @@ func TestReadTracks(t *testing.T) {
},
{
"missing metadata, video+audio",
&formats.H264{
&format.H264{
PayloadTyp: 96,
SPS: h264SPS,
PPS: h264PPS,
PacketizationMode: 1,
},
&formats.MPEG4Audio{
&format.MPEG4Audio{
PayloadTyp: 96,
Config: &mpeg4audio.Config{
Type: 2,
@@ -190,7 +190,7 @@ func TestReadTracks(t *testing.T) {
{
"missing metadata, audio",
nil,
&formats.MPEG4Audio{
&format.MPEG4Audio{
PayloadTyp: 96,
Config: &mpeg4audio.Config{
Type: 2,
@@ -204,13 +204,13 @@ func TestReadTracks(t *testing.T) {
},
{
"obs studio pre 29.1 h265",
&formats.H265{
&format.H265{
PayloadTyp: 96,
VPS: h265VPS,
SPS: h265SPS,
PPS: h265PPS,
},
&formats.MPEG4Audio{
&format.MPEG4Audio{
PayloadTyp: 96,
Config: &mpeg4audio.Config{
Type: 2,
@@ -224,7 +224,7 @@ func TestReadTracks(t *testing.T) {
},
{
"xplit broadcaster",
&formats.H265{
&format.H265{
PayloadTyp: 96,
VPS: h265VPS,
SPS: h265SPS,
@@ -234,7 +234,7 @@ func TestReadTracks(t *testing.T) {
},
{
"obs 30",
&formats.H265{
&format.H265{
PayloadTyp: 96,
VPS: h265VPS,
SPS: h265SPS,

View File

@@ -3,7 +3,7 @@ package rtmp
import (
"time"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg1audio"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
@@ -39,7 +39,7 @@ type Writer struct {
}
// NewWriter allocates a Writer.
func NewWriter(conn *Conn, videoTrack formats.Format, audioTrack formats.Format) (*Writer, error) {
func NewWriter(conn *Conn, videoTrack format.Format, audioTrack format.Format) (*Writer, error) {
w := &Writer{
conn: conn,
}
@@ -52,7 +52,7 @@ func NewWriter(conn *Conn, videoTrack formats.Format, audioTrack formats.Format)
return w, nil
}
func (w *Writer) writeTracks(videoTrack formats.Format, audioTrack formats.Format) error {
func (w *Writer) writeTracks(videoTrack format.Format, audioTrack format.Format) error {
err := w.conn.Write(&message.DataAMF0{
ChunkStreamID: 4,
MessageStreamID: 0x1000000,
@@ -68,7 +68,7 @@ func (w *Writer) writeTracks(videoTrack formats.Format, audioTrack formats.Forma
K: "videocodecid",
V: func() float64 {
switch videoTrack.(type) {
case *formats.H264:
case *format.H264:
return message.CodecH264
default:
@@ -84,10 +84,10 @@ func (w *Writer) writeTracks(videoTrack formats.Format, audioTrack formats.Forma
K: "audiocodecid",
V: func() float64 {
switch audioTrack.(type) {
case *formats.MPEG1Audio:
case *format.MPEG1Audio:
return message.CodecMPEG1Audio
case *formats.MPEG4AudioGeneric, *formats.MPEG4AudioLATM:
case *format.MPEG4AudioGeneric, *format.MPEG4AudioLATM:
return message.CodecMPEG4Audio
default:
@@ -102,7 +102,7 @@ func (w *Writer) writeTracks(videoTrack formats.Format, audioTrack formats.Forma
return err
}
if videoTrack, ok := videoTrack.(*formats.H264); ok {
if videoTrack, ok := videoTrack.(*format.H264); ok {
// write decoder config only if SPS and PPS are available.
// if they're not available yet, they're sent later.
if sps, pps := videoTrack.SafeParams(); sps != nil && pps != nil {
@@ -128,10 +128,10 @@ func (w *Writer) writeTracks(videoTrack formats.Format, audioTrack formats.Forma
var audioConfig *mpeg4audio.AudioSpecificConfig
switch track := audioTrack.(type) {
case *formats.MPEG4Audio:
case *format.MPEG4Audio:
audioConfig = track.Config
case *formats.MPEG4AudioLATM:
case *format.MPEG4AudioLATM:
audioConfig = track.Config.Programs[0].Layers[0].AudioSpecificConfig
}

View File

@@ -4,7 +4,7 @@ import (
"bytes"
"testing"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
"github.com/notedit/rtmp/format/flv/flvio"
"github.com/stretchr/testify/require"
@@ -14,7 +14,7 @@ import (
)
func TestWriteTracks(t *testing.T) {
videoTrack := &formats.H264{
videoTrack := &format.H264{
PayloadTyp: 96,
SPS: []byte{
0x67, 0x64, 0x00, 0x0c, 0xac, 0x3b, 0x50, 0xb0,
@@ -27,7 +27,7 @@ func TestWriteTracks(t *testing.T) {
PacketizationMode: 1,
}
audioTrack := &formats.MPEG4Audio{
audioTrack := &format.MPEG4Audio{
PayloadTyp: 96,
Config: &mpeg4audio.Config{
Type: 2,

View File

@@ -2,11 +2,12 @@
package stream
import (
"sync"
"time"
"github.com/bluenviron/gortsplib/v3"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v4"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
@@ -16,28 +17,31 @@ import (
// Stream is a media stream.
// It stores tracks, readers and allow to write data to readers.
type Stream struct {
desc *description.Session
bytesReceived *uint64
rtspStream *gortsplib.ServerStream
smedias map[*media.Media]*streamMedia
smedias map[*description.Media]*streamMedia
mutex sync.RWMutex
rtspStream *gortsplib.ServerStream
rtspsStream *gortsplib.ServerStream
}
// New allocates a Stream.
func New(
udpMaxPayloadSize int,
medias media.Medias,
desc *description.Session,
generateRTPPackets bool,
bytesReceived *uint64,
source logger.Writer,
) (*Stream, error) {
s := &Stream{
bytesReceived: bytesReceived,
rtspStream: gortsplib.NewServerStream(medias),
desc: desc,
}
s.smedias = make(map[*media.Media]*streamMedia)
s.smedias = make(map[*description.Media]*streamMedia)
for _, media := range s.rtspStream.Medias() {
for _, media := range desc.Medias {
var err error
s.smedias[media], err = newStreamMedia(udpMaxPayloadSize, media, generateRTPPackets, source)
if err != nil {
@@ -50,21 +54,46 @@ func New(
// Close closes all resources of the stream.
func (s *Stream) Close() {
s.rtspStream.Close()
if s.rtspStream != nil {
s.rtspStream.Close()
}
if s.rtspsStream != nil {
s.rtspsStream.Close()
}
}
// Medias returns medias of the stream.
func (s *Stream) Medias() media.Medias {
return s.rtspStream.Medias()
// Desc returns description of the stream.
func (s *Stream) Desc() *description.Session {
return s.desc
}
// RTSPStream returns the RTSP stream.
func (s *Stream) RTSPStream() *gortsplib.ServerStream {
func (s *Stream) RTSPStream(server *gortsplib.Server) *gortsplib.ServerStream {
s.mutex.Lock()
defer s.mutex.Unlock()
if s.rtspStream == nil {
s.rtspStream = gortsplib.NewServerStream(server, s.desc)
}
return s.rtspStream
}
// RTSPSStream returns the RTSPS stream.
func (s *Stream) RTSPSStream(server *gortsplib.Server) *gortsplib.ServerStream {
s.mutex.Lock()
defer s.mutex.Unlock()
if s.rtspsStream == nil {
s.rtspsStream = gortsplib.NewServerStream(server, s.desc)
}
return s.rtspsStream
}
// AddReader adds a reader.
func (s *Stream) AddReader(r interface{}, medi *media.Media, forma formats.Format, cb func(unit.Unit)) {
func (s *Stream) AddReader(r interface{}, medi *description.Media, forma format.Format, cb func(unit.Unit)) {
s.mutex.Lock()
defer s.mutex.Unlock()
sm := s.smedias[medi]
sf := sm.formats[forma]
sf.addReader(r, cb)
@@ -72,6 +101,9 @@ func (s *Stream) AddReader(r interface{}, medi *media.Media, forma formats.Forma
// RemoveReader removes a reader.
func (s *Stream) RemoveReader(r interface{}) {
s.mutex.Lock()
defer s.mutex.Unlock()
for _, sm := range s.smedias {
for _, sf := range sm.formats {
sf.removeReader(r)
@@ -80,15 +112,29 @@ func (s *Stream) RemoveReader(r interface{}) {
}
// WriteUnit writes a Unit.
func (s *Stream) WriteUnit(medi *media.Media, forma formats.Format, data unit.Unit) {
func (s *Stream) WriteUnit(medi *description.Media, forma format.Format, data unit.Unit) {
sm := s.smedias[medi]
sf := sm.formats[forma]
s.mutex.RLock()
defer s.mutex.RUnlock()
sf.writeUnit(s, medi, data)
}
// WriteRTPPacket writes a RTP packet.
func (s *Stream) WriteRTPPacket(medi *media.Media, forma formats.Format, pkt *rtp.Packet, ntp time.Time) {
func (s *Stream) WriteRTPPacket(
medi *description.Media,
forma format.Format,
pkt *rtp.Packet,
ntp time.Time,
pts time.Duration,
) {
sm := s.smedias[medi]
sf := sm.formats[forma]
sf.writeRTPPacket(s, medi, pkt, ntp)
s.mutex.RLock()
defer s.mutex.RUnlock()
sf.writeRTPPacket(s, medi, pkt, ntp, pts)
}

View File

@@ -1,12 +1,11 @@
package stream
import (
"sync"
"sync/atomic"
"time"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/formatprocessor"
@@ -17,13 +16,12 @@ import (
type streamFormat struct {
source logger.Writer
proc formatprocessor.Processor
mutex sync.RWMutex
nonRTSPReaders map[interface{}]func(unit.Unit)
}
func newStreamFormat(
udpMaxPayloadSize int,
forma formats.Format,
forma format.Format,
generateRTPPackets bool,
source logger.Writer,
) (*streamFormat, error) {
@@ -42,21 +40,14 @@ func newStreamFormat(
}
func (sf *streamFormat) addReader(r interface{}, cb func(unit.Unit)) {
sf.mutex.Lock()
defer sf.mutex.Unlock()
sf.nonRTSPReaders[r] = cb
}
func (sf *streamFormat) removeReader(r interface{}) {
sf.mutex.Lock()
defer sf.mutex.Unlock()
delete(sf.nonRTSPReaders, r)
}
func (sf *streamFormat) writeUnit(s *Stream, medi *media.Media, data unit.Unit) {
sf.mutex.RLock()
defer sf.mutex.RUnlock()
func (sf *streamFormat) writeUnit(s *Stream, medi *description.Media, data unit.Unit) {
hasNonRTSPReaders := len(sf.nonRTSPReaders) > 0
err := sf.proc.Process(data, hasNonRTSPReaders)
@@ -65,10 +56,22 @@ func (sf *streamFormat) writeUnit(s *Stream, medi *media.Media, data unit.Unit)
return
}
// forward RTP packets to RTSP readers
n := uint64(0)
for _, pkt := range data.GetRTPPackets() {
atomic.AddUint64(s.bytesReceived, uint64(pkt.MarshalSize()))
s.rtspStream.WritePacketRTPWithNTP(medi, pkt, data.GetNTP())
n += uint64(pkt.MarshalSize())
}
atomic.AddUint64(s.bytesReceived, n)
if s.rtspStream != nil {
for _, pkt := range data.GetRTPPackets() {
s.rtspStream.WritePacketRTPWithNTP(medi, pkt, data.GetNTP()) //nolint:errcheck
}
}
if s.rtspsStream != nil {
for _, pkt := range data.GetRTPPackets() {
s.rtspsStream.WritePacketRTPWithNTP(medi, pkt, data.GetNTP()) //nolint:errcheck
}
}
// forward decoded frames to non-RTSP readers
@@ -77,6 +80,12 @@ func (sf *streamFormat) writeUnit(s *Stream, medi *media.Media, data unit.Unit)
}
}
func (sf *streamFormat) writeRTPPacket(s *Stream, medi *media.Media, pkt *rtp.Packet, ntp time.Time) {
sf.writeUnit(s, medi, sf.proc.UnitForRTPPacket(pkt, ntp))
func (sf *streamFormat) writeRTPPacket(
s *Stream,
medi *description.Media,
pkt *rtp.Packet,
ntp time.Time,
pts time.Duration,
) {
sf.writeUnit(s, medi, sf.proc.UnitForRTPPacket(pkt, ntp, pts))
}

View File

@@ -1,23 +1,23 @@
package stream
import (
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/gortsplib/v3/pkg/media"
"github.com/bluenviron/gortsplib/v4/pkg/description"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/mediamtx/internal/logger"
)
type streamMedia struct {
formats map[formats.Format]*streamFormat
formats map[format.Format]*streamFormat
}
func newStreamMedia(udpMaxPayloadSize int,
medi *media.Media,
medi *description.Media,
generateRTPPackets bool,
source logger.Writer,
) (*streamMedia, error) {
sm := &streamMedia{
formats: make(map[formats.Format]*streamFormat),
formats: make(map[format.Format]*streamFormat),
}
for _, forma := range medi.Formats {

View File

@@ -1,12 +1,7 @@
package unit
import (
"time"
)
// AV1 is an AV1 data unit.
type AV1 struct {
Base
PTS time.Duration
TU [][]byte
TU [][]byte
}

View File

@@ -10,6 +10,7 @@ import (
type Base struct {
RTPPackets []*rtp.Packet
NTP time.Time
PTS time.Duration
}
// GetRTPPackets implements Unit.
@@ -21,3 +22,8 @@ func (u *Base) GetRTPPackets() []*rtp.Packet {
func (u *Base) GetNTP() time.Time {
return u.NTP
}
// GetPTS implements Unit.
func (u *Base) GetPTS() time.Duration {
return u.PTS
}

View File

@@ -1,12 +1,7 @@
package unit
import (
"time"
)
// H264 is a H264 data unit.
type H264 struct {
Base
PTS time.Duration
AU [][]byte
AU [][]byte
}

View File

@@ -1,12 +1,7 @@
package unit
import (
"time"
)
// H265 is a H265 data unit.
type H265 struct {
Base
PTS time.Duration
AU [][]byte
AU [][]byte
}

View File

@@ -1,12 +1,7 @@
package unit
import (
"time"
)
// MPEG1Audio is a MPEG-1/2 Audio data unit.
type MPEG1Audio struct {
Base
PTS time.Duration
Frames [][]byte
}

View File

@@ -1,12 +1,7 @@
package unit
import (
"time"
)
// MPEG4AudioGeneric is a MPEG-4 Audio data unit.
type MPEG4AudioGeneric struct {
Base
PTS time.Duration
AUs [][]byte
}

View File

@@ -1,12 +1,7 @@
package unit
import (
"time"
)
// MPEG4AudioLATM is a MPEG-4 Audio data unit.
type MPEG4AudioLATM struct {
Base
PTS time.Duration
AU []byte
AU []byte
}

View File

@@ -1,12 +1,7 @@
package unit
import (
"time"
)
// Opus is a Opus data unit.
type Opus struct {
Base
PTS time.Duration
Packets [][]byte
}

View File

@@ -14,4 +14,7 @@ type Unit interface {
// returns the NTP timestamp of the unit.
GetNTP() time.Time
// returns the PTS of the unit.
GetPTS() time.Duration
}

View File

@@ -1,12 +1,7 @@
package unit
import (
"time"
)
// VP8 is a VP8 data unit.
type VP8 struct {
Base
PTS time.Duration
Frame []byte
}

View File

@@ -1,12 +1,7 @@
package unit
import (
"time"
)
// VP9 is a VP9 data unit.
type VP9 struct {
Base
PTS time.Duration
Frame []byte
}