rtsp: avoid setupping back channels (#5074) (#5289)

This commit is contained in:
Alessandro Ros
2025-12-24 20:44:56 +01:00
committed by GitHub
parent c83a13cf8e
commit 1704fba5e9
2 changed files with 201 additions and 7 deletions
+177 -2
View File
@@ -11,6 +11,7 @@ import (
"github.com/bluenviron/gortsplib/v5/pkg/auth"
"github.com/bluenviron/gortsplib/v5/pkg/base"
"github.com/bluenviron/gortsplib/v5/pkg/description"
"github.com/bluenviron/gortsplib/v5/pkg/format"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
@@ -19,6 +20,10 @@ import (
"github.com/bluenviron/mediamtx/internal/test"
)
func ptrOf[T any](v T) *T {
return &v
}
type testServer struct {
onDescribe func(*gortsplib.ServerHandlerOnDescribeCtx) (*base.Response, *gortsplib.ServerStream, error)
onSetup func(*gortsplib.ServerHandlerOnSetupCtx) (*base.Response, *gortsplib.ServerStream, error)
@@ -189,7 +194,7 @@ func TestSource(t *testing.T) {
}
}
func TestSourceNoPassword(t *testing.T) {
func TestNoPassword(t *testing.T) {
var strm *gortsplib.ServerStream
nonce, err := auth.GenerateNonce()
@@ -290,7 +295,7 @@ func TestSourceNoPassword(t *testing.T) {
<-p.Unit
}
func TestSourceRange(t *testing.T) {
func TestRange(t *testing.T) {
for _, ca := range []string{"clock", "npt", "smpte"} {
t.Run(ca, func(t *testing.T) {
var strm *gortsplib.ServerStream
@@ -403,3 +408,173 @@ func TestSourceRange(t *testing.T) {
})
}
}
func TestSkipBackChannel(t *testing.T) {
media0 := test.UniqueMediaH264()
media1 := test.UniqueMediaMPEG4Audio()
backChannelMedia := &description.Media{
Type: description.MediaTypeAudio,
Formats: []format.Format{&format.Opus{PayloadTyp: 96, ChannelCount: 2}},
IsBackChannel: true,
}
var strm *gortsplib.ServerStream
setupCount := 0
s := gortsplib.Server{
Handler: &testServer{
onDescribe: func(_ *gortsplib.ServerHandlerOnDescribeCtx) (*base.Response, *gortsplib.ServerStream, error) {
return &base.Response{
StatusCode: base.StatusOK,
}, strm, nil
},
onSetup: func(_ *gortsplib.ServerHandlerOnSetupCtx) (*base.Response, *gortsplib.ServerStream, error) {
setupCount++
return &base.Response{
StatusCode: base.StatusOK,
}, strm, nil
},
onPlay: func(_ *gortsplib.ServerHandlerOnPlayCtx) (*base.Response, error) {
go func() {
time.Sleep(100 * time.Millisecond)
err := strm.WritePacketRTP(media0, &rtp.Packet{
Header: rtp.Header{
Version: 0x02,
PayloadType: 96,
SequenceNumber: 57899,
Timestamp: 345234345,
SSRC: 978651231,
Marker: true,
},
Payload: []byte{5, 1, 2, 3, 4},
})
require.NoError(t, err)
}()
return &base.Response{
StatusCode: base.StatusOK,
}, nil
},
},
RTSPAddress: "127.0.0.1:8555",
}
err := s.Start()
require.NoError(t, err)
defer s.Close()
strm = &gortsplib.ServerStream{
Server: &s,
Desc: &description.Session{Medias: []*description.Media{media0, media1, backChannelMedia}},
}
err = strm.Initialize()
require.NoError(t, err)
defer strm.Close()
var sp conf.RTSPTransport
sp.UnmarshalJSON([]byte(`"tcp"`)) //nolint:errcheck
p := &test.StaticSourceParent{}
p.Initialize()
defer p.Close()
so := &Source{
ReadTimeout: conf.Duration(10 * time.Second),
WriteTimeout: conf.Duration(10 * time.Second),
WriteQueueSize: 2048,
Parent: p,
}
done := make(chan struct{})
defer func() { <-done }()
ctx, ctxCancel := context.WithCancel(context.Background())
defer ctxCancel()
go func() {
so.Run(defs.StaticSourceRunParams{ //nolint:errcheck
Context: ctx,
ResolvedSource: "rtsp://127.0.0.1:8555/teststream",
Conf: &conf.Path{
RTSPTransport: conf.RTSPTransport{Protocol: ptrOf(gortsplib.ProtocolTCP)},
},
})
close(done)
}()
<-p.Unit
require.Equal(t, 2, setupCount)
}
func TestOnlyBackChannelsError(t *testing.T) {
backChannelMedia1 := &description.Media{
Type: description.MediaTypeAudio,
Formats: []format.Format{&format.Opus{PayloadTyp: 96, ChannelCount: 2}},
IsBackChannel: true,
}
backChannelMedia2 := &description.Media{
Type: description.MediaTypeAudio,
Formats: []format.Format{&format.G711{PayloadTyp: 8, SampleRate: 8000, ChannelCount: 1}},
IsBackChannel: true,
}
var strm *gortsplib.ServerStream
s := gortsplib.Server{
Handler: &testServer{
onDescribe: func(_ *gortsplib.ServerHandlerOnDescribeCtx) (*base.Response, *gortsplib.ServerStream, error) {
return &base.Response{
StatusCode: base.StatusOK,
}, strm, nil
},
onSetup: func(_ *gortsplib.ServerHandlerOnSetupCtx) (*base.Response, *gortsplib.ServerStream, error) {
return &base.Response{
StatusCode: base.StatusOK,
}, strm, nil
},
onPlay: func(_ *gortsplib.ServerHandlerOnPlayCtx) (*base.Response, error) {
return &base.Response{
StatusCode: base.StatusOK,
}, nil
},
},
RTSPAddress: "127.0.0.1:8555",
}
err := s.Start()
require.NoError(t, err)
defer s.Close()
strm = &gortsplib.ServerStream{
Server: &s,
Desc: &description.Session{Medias: []*description.Media{backChannelMedia1, backChannelMedia2}},
}
err = strm.Initialize()
require.NoError(t, err)
defer strm.Close()
p := &test.StaticSourceParent{}
p.Initialize()
so := &Source{
ReadTimeout: conf.Duration(10 * time.Second),
WriteTimeout: conf.Duration(10 * time.Second),
WriteQueueSize: 2048,
Parent: p,
}
ctx, ctxCancel := context.WithCancel(context.Background())
defer ctxCancel()
err = so.Run(defs.StaticSourceRunParams{
Context: ctx,
ResolvedSource: "rtsp://127.0.0.1:8555/teststream",
Conf: &conf.Path{
RTSPTransport: conf.RTSPTransport{Protocol: ptrOf(gortsplib.ProtocolTCP)},
},
})
require.Error(t, err)
require.Contains(t, err.Error(), "no media")
}