diff --git a/client_play_test.go b/client_play_test.go index 8fc0f528..b8389614 100644 --- a/client_play_test.go +++ b/client_play_test.go @@ -3,7 +3,6 @@ package gortsplib import ( "bytes" "crypto/tls" - "fmt" "net" "strconv" "strings" @@ -72,7 +71,7 @@ func readAll(c *Client, ur string, cb func(*media.Media, format.Format, *rtp.Pac } func TestClientPlayFormats(t *testing.T) { - media1 := testH264Media.Clone() + media1 := testH264Media media2 := &media.Media{ Type: media.TypeAudio, @@ -156,7 +155,7 @@ func TestClientPlayFormats(t *testing.T) { req, err := conn.ReadRequest() require.NoError(t, err) require.Equal(t, base.Setup, req.Method) - require.Equal(t, mustParseURL(fmt.Sprintf("rtsp://localhost:8554/teststream/mediaID=%d", i)), req.URL) + require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream/"+medias[i].Control), req.URL) var inTH headers.Transport err = inTH.Unmarshal(req.Header["Transport"]) @@ -306,7 +305,7 @@ func TestClientPlay(t *testing.T) { require.NoError(t, err) require.Equal(t, base.Setup, req.Method) require.Equal(t, mustParseURL( - scheme+"://"+listenIP+":8554/test/stream?param=value/mediaID="+strconv.FormatInt(int64(i), 10)), req.URL) + scheme+"://"+listenIP+":8554/test/stream?param=value/"+medias[i].Control), req.URL) var inTH headers.Transport err = inTH.Unmarshal(req.Header["Transport"]) @@ -542,9 +541,22 @@ func TestClientPlayPartial(t *testing.T) { require.Equal(t, base.Describe, req.Method) require.Equal(t, mustParseURL("rtsp://"+listenIP+":8554/teststream"), req.URL) + forma := &format.Generic{ + PayloadTyp: 96, + RTPMap: "private/90000", + } + err = forma.Init() + require.NoError(t, err) + medias := media.Medias{ - testH264Media.Clone(), - testH264Media.Clone(), + &media.Media{ + Type: "application", + Formats: []format.Format{forma}, + }, + &media.Media{ + Type: "application", + Formats: []format.Format{forma}, + }, } medias.SetControls() @@ -561,7 +573,7 @@ func TestClientPlayPartial(t *testing.T) { req, err = conn.ReadRequest() require.NoError(t, err) require.Equal(t, base.Setup, req.Method) - require.Equal(t, mustParseURL("rtsp://"+listenIP+":8554/teststream/mediaID=1"), req.URL) + require.Equal(t, mustParseURL("rtsp://"+listenIP+":8554/teststream/"+medias[1].Control), req.URL) var inTH headers.Transport err = inTH.Unmarshal(req.Header["Transport"]) @@ -688,7 +700,7 @@ func TestClientPlayContentBase(t *testing.T) { require.Equal(t, base.Describe, req.Method) require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream"), req.URL) - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() switch ca { @@ -720,7 +732,7 @@ func TestClientPlayContentBase(t *testing.T) { req, err = conn.ReadRequest() require.NoError(t, err) require.Equal(t, base.Setup, req.Method) - require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), req.URL) + require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream/"+medias[0].Control), req.URL) var inTH headers.Transport err = inTH.Unmarshal(req.Header["Transport"]) @@ -818,7 +830,7 @@ func TestClientPlayAnyPort(t *testing.T) { require.NoError(t, err) require.Equal(t, base.Describe, req.Method) - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() err = conn.WriteResponse(&base.Response{ @@ -972,7 +984,7 @@ func TestClientPlayAutomaticProtocol(t *testing.T) { require.NoError(t, err) require.Equal(t, base.Describe, req.Method) - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() err = conn.WriteResponse(&base.Response{ @@ -1057,7 +1069,7 @@ func TestClientPlayAutomaticProtocol(t *testing.T) { go func() { defer close(serverDone) - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() func() { @@ -1116,7 +1128,7 @@ func TestClientPlayAutomaticProtocol(t *testing.T) { req, err = conn.ReadRequest() require.NoError(t, err) require.Equal(t, base.Setup, req.Method) - require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), req.URL) + require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream/"+medias[0].Control), req.URL) var inTH headers.Transport err = inTH.Unmarshal(req.Header["Transport"]) @@ -1212,7 +1224,7 @@ func TestClientPlayAutomaticProtocol(t *testing.T) { req, err = conn.ReadRequest() require.NoError(t, err) require.Equal(t, base.Setup, req.Method) - require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), req.URL) + require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream/"+medias[0].Control), req.URL) err = v.ValidateRequest(req, nil) require.NoError(t, err) @@ -1317,7 +1329,7 @@ func TestClientPlayDifferentInterleavedIDs(t *testing.T) { require.Equal(t, base.Describe, req.Method) require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream"), req.URL) - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() err = conn.WriteResponse(&base.Response{ @@ -1333,7 +1345,7 @@ func TestClientPlayDifferentInterleavedIDs(t *testing.T) { req, err = conn.ReadRequest() require.NoError(t, err) require.Equal(t, base.Setup, req.Method) - require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), req.URL) + require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream/"+medias[0].Control), req.URL) var inTH headers.Transport err = inTH.Unmarshal(req.Header["Transport"]) @@ -1512,7 +1524,7 @@ func TestClientPlayRedirect(t *testing.T) { require.Equal(t, base.Describe, req.Method) } - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() err = conn.WriteResponse(&base.Response{ @@ -1673,7 +1685,7 @@ func TestClientPlayPause(t *testing.T) { require.NoError(t, err) require.Equal(t, base.Describe, req.Method) - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() err = conn.WriteResponse(&base.Response{ @@ -1841,7 +1853,7 @@ func TestClientPlayRTCPReport(t *testing.T) { require.NoError(t, err) require.Equal(t, base.Describe, req.Method) - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() err = conn.WriteResponse(&base.Response{ @@ -2019,7 +2031,7 @@ func TestClientPlayErrorTimeout(t *testing.T) { require.NoError(t, err) require.Equal(t, base.Describe, req.Method) - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() err = conn.WriteResponse(&base.Response{ @@ -2165,7 +2177,7 @@ func TestClientPlayIgnoreTCPInvalidMedia(t *testing.T) { require.NoError(t, err) require.Equal(t, base.Describe, req.Method) - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() err = conn.WriteResponse(&base.Response{ @@ -2288,7 +2300,7 @@ func TestClientPlaySeek(t *testing.T) { require.NoError(t, err) require.Equal(t, base.Describe, req.Method) - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() err = conn.WriteResponse(&base.Response{ @@ -2452,7 +2464,7 @@ func TestClientPlayKeepaliveFromSession(t *testing.T) { require.NoError(t, err) require.Equal(t, base.Describe, req.Method) - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() err = conn.WriteResponse(&base.Response{ @@ -2575,7 +2587,7 @@ func TestClientPlayDifferentSource(t *testing.T) { require.Equal(t, base.Describe, req.Method) require.Equal(t, mustParseURL("rtsp://localhost:8554/test/stream?param=value"), req.URL) - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() err = conn.WriteResponse(&base.Response{ @@ -2591,7 +2603,7 @@ func TestClientPlayDifferentSource(t *testing.T) { req, err = conn.ReadRequest() require.NoError(t, err) require.Equal(t, base.Setup, req.Method) - require.Equal(t, mustParseURL("rtsp://localhost:8554/test/stream?param=value/mediaID=0"), req.URL) + require.Equal(t, mustParseURL("rtsp://localhost:8554/test/stream?param=value/"+medias[0].Control), req.URL) var inTH headers.Transport err = inTH.Unmarshal(req.Header["Transport"]) diff --git a/client_record_test.go b/client_record_test.go index 7662bd1b..900768e8 100644 --- a/client_record_test.go +++ b/client_record_test.go @@ -17,6 +17,7 @@ import ( "github.com/aler9/gortsplib/v2/pkg/format" "github.com/aler9/gortsplib/v2/pkg/headers" "github.com/aler9/gortsplib/v2/pkg/media" + "github.com/aler9/gortsplib/v2/pkg/sdp" "github.com/aler9/gortsplib/v2/pkg/url" ) @@ -155,6 +156,10 @@ func TestClientRecordSerial(t *testing.T) { require.Equal(t, base.Announce, req.Method) require.Equal(t, mustParseURL(scheme+"://localhost:8554/teststream"), req.URL) + var desc sdp.SessionDescription + err = desc.Unmarshal(req.Body) + require.NoError(t, err) + err = conn.WriteResponse(&base.Response{ StatusCode: base.StatusOK, }) @@ -163,7 +168,8 @@ func TestClientRecordSerial(t *testing.T) { req, err = conn.ReadRequest() require.NoError(t, err) require.Equal(t, base.Setup, req.Method) - require.Equal(t, mustParseURL(scheme+"://localhost:8554/teststream/mediaID=0"), req.URL) + require.Equal(t, mustParseURL( + scheme+"://localhost:8554/teststream/"+controlAttribute(desc.MediaDescriptions[0])), req.URL) var inTH headers.Transport err = inTH.Unmarshal(req.Header["Transport"]) @@ -275,7 +281,7 @@ func TestClientRecordSerial(t *testing.T) { }(), } - medi := testH264Media.Clone() + medi := testH264Media medias := media.Medias{medi} medias.SetControls() @@ -431,7 +437,7 @@ func TestClientRecordParallel(t *testing.T) { writerDone := make(chan struct{}) defer func() { <-writerDone }() - medi := testH264Media.Clone() + medi := testH264Media medias := media.Medias{medi} medias.SetControls() @@ -584,7 +590,7 @@ func TestClientRecordPauseSerial(t *testing.T) { }(), } - medi := testH264Media.Clone() + medi := testH264Media medias := media.Medias{medi} medias.SetControls() @@ -715,7 +721,7 @@ func TestClientRecordPauseParallel(t *testing.T) { }(), } - medi := testH264Media.Clone() + medi := testH264Media medias := media.Medias{medi} medias.SetControls() @@ -855,7 +861,7 @@ func TestClientRecordAutomaticProtocol(t *testing.T) { c := Client{} - medi := testH264Media.Clone() + medi := testH264Media medias := media.Medias{medi} medias.SetControls() @@ -1038,7 +1044,7 @@ func TestClientRecordDecodeErrors(t *testing.T) { }, } - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() err = record(&c, "rtsp://localhost:8554/stream", medias, nil) @@ -1196,7 +1202,7 @@ func TestClientRecordRTCPReport(t *testing.T) { senderReportPeriod: 500 * time.Millisecond, } - medi := testH264Media.Clone() + medi := testH264Media medias := media.Medias{medi} medias.SetControls() @@ -1326,7 +1332,7 @@ func TestClientRecordIgnoreTCPRTPPackets(t *testing.T) { }(), } - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() err = record(&c, "rtsp://localhost:8554/teststream", medias, diff --git a/client_test.go b/client_test.go index 64b3f061..6da894a2 100644 --- a/client_test.go +++ b/client_test.go @@ -106,7 +106,7 @@ func TestClientSession(t *testing.T) { require.Equal(t, base.HeaderValue{"123456"}, req.Header["Session"]) - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() err = conn.WriteResponse(&base.Response{ @@ -183,7 +183,7 @@ func TestClientAuth(t *testing.T) { err = v.ValidateRequest(req, nil) require.NoError(t, err) - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() err = conn.WriteResponse(&base.Response{ @@ -243,7 +243,7 @@ func TestClientDescribeCharset(t *testing.T) { require.Equal(t, base.Describe, req.Method) require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream"), req.URL) - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} err = conn.WriteResponse(&base.Response{ StatusCode: base.StatusOK, diff --git a/go.mod b/go.mod index 3bd82795..58af9ed1 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.17 require ( github.com/asticode/go-astits v1.10.0 + github.com/google/uuid v1.3.0 github.com/pion/rtcp v1.2.9 github.com/pion/rtp v1.7.13 github.com/pion/sdp/v3 v3.0.5 diff --git a/go.sum b/go.sum index a22ae27a..5cc2051b 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/asticode/go-astits v1.10.0 h1:ixKsRl84nWtjgHWcWKTDkUHNQ4kxbf9nKmjuSCn github.com/asticode/go-astits v1.10.0/go.mod h1:DkOWmBNQpnr9mv24KfZjq4JawCFX1FCqjLVGvO0DygQ= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= github.com/pion/rtcp v1.2.9 h1:1ujStwg++IOLIEoOiIQ2s+qBuJ1VN81KW+9pMPsif+U= diff --git a/pkg/format/format.go b/pkg/format/format.go index 5a13c4d4..5a1c94f9 100644 --- a/pkg/format/format.go +++ b/pkg/format/format.go @@ -48,9 +48,6 @@ type Format interface { // Marshal encodes the format in SDP format. Marshal() (string, string) - // Clone clones the format. - Clone() Format - // PTSEqualsDTS checks whether PTS is equal to DTS in the RTP packet. PTSEqualsDTS(*rtp.Packet) bool } diff --git a/pkg/format/g711.go b/pkg/format/g711.go index ff4499eb..59521dea 100644 --- a/pkg/format/g711.go +++ b/pkg/format/g711.go @@ -52,13 +52,6 @@ func (t *G711) Marshal() (string, string) { return "PCMA/8000", "" } -// Clone implements Format. -func (t *G711) Clone() Format { - return &G711{ - MULaw: t.MULaw, - } -} - // PTSEqualsDTS implements Format. func (t *G711) PTSEqualsDTS(*rtp.Packet) bool { return true diff --git a/pkg/format/g711_test.go b/pkg/format/g711_test.go index 6c774add..682365eb 100644 --- a/pkg/format/g711_test.go +++ b/pkg/format/g711_test.go @@ -22,14 +22,6 @@ func TestG711Attributes(t *testing.T) { require.Equal(t, uint8(0), format.PayloadType()) } -func TestG711Clone(t *testing.T) { - format := &G711{} - - clone := format.Clone() - require.NotSame(t, format, clone) - require.Equal(t, format, clone) -} - func TestG711MediaDescription(t *testing.T) { t.Run("pcma", func(t *testing.T) { format := &G711{} diff --git a/pkg/format/g722.go b/pkg/format/g722.go index 23b782d0..beee8006 100644 --- a/pkg/format/g722.go +++ b/pkg/format/g722.go @@ -41,11 +41,6 @@ func (t *G722) Marshal() (string, string) { return "G722/8000", "" } -// Clone implements Format. -func (t *G722) Clone() Format { - return &G722{} -} - // PTSEqualsDTS implements Format. func (t *G722) PTSEqualsDTS(*rtp.Packet) bool { return true diff --git a/pkg/format/g722_test.go b/pkg/format/g722_test.go index a9d8f1b4..9823bb58 100644 --- a/pkg/format/g722_test.go +++ b/pkg/format/g722_test.go @@ -15,14 +15,6 @@ func TestG722Attributes(t *testing.T) { require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) } -func TestG722Clone(t *testing.T) { - format := &G722{} - - clone := format.Clone() - // require.NotSame(t, format, clone) - require.Equal(t, format, clone) -} - func TestG722MediaDescription(t *testing.T) { format := &G722{} diff --git a/pkg/format/generic.go b/pkg/format/generic.go index 2cd3596a..8fd679fa 100644 --- a/pkg/format/generic.go +++ b/pkg/format/generic.go @@ -95,16 +95,6 @@ func (t *Generic) Marshal() (string, string) { return t.RTPMap, t.FMTP } -// Clone implements Format. -func (t *Generic) Clone() Format { - return &Generic{ - PayloadTyp: t.PayloadTyp, - RTPMap: t.RTPMap, - FMTP: t.FMTP, - ClockRat: t.ClockRat, - } -} - // PTSEqualsDTS implements Format. func (t *Generic) PTSEqualsDTS(*rtp.Packet) bool { return true diff --git a/pkg/format/generic_test.go b/pkg/format/generic_test.go index 3427d92b..c2ce5fae 100644 --- a/pkg/format/generic_test.go +++ b/pkg/format/generic_test.go @@ -23,21 +23,6 @@ func TestGenericAttributes(t *testing.T) { require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) } -func TestGenericClone(t *testing.T) { - format := &Generic{ - PayloadTyp: 98, - RTPMap: "H265/90000", - FMTP: "profile-id=1; sprop-vps=QAEMAf//AWAAAAMAAAMAAAMAAAMAlqwJ; " + - "sprop-sps=QgEBAWAAAAMAAAMAAAMAAAMAlqADwIAQ5Za5JMmuWcBSSgAAB9AAAHUwgkA=; sprop-pps=RAHgdrAwxmQ=", - } - err := format.Init() - require.NoError(t, err) - - clone := format.Clone() - require.NotSame(t, format, clone) - require.Equal(t, format, clone) -} - func TestGenericMediaDescription(t *testing.T) { format := &Generic{ PayloadTyp: 98, diff --git a/pkg/format/h264.go b/pkg/format/h264.go index a709bb0b..fcadf85e 100644 --- a/pkg/format/h264.go +++ b/pkg/format/h264.go @@ -175,16 +175,6 @@ func (t *H264) Marshal() (string, string) { return "H264/90000", fmtp } -// Clone implements Format. -func (t *H264) Clone() Format { - return &H264{ - PayloadTyp: t.PayloadTyp, - SPS: t.SafeSPS(), - PPS: t.SafePPS(), - PacketizationMode: t.PacketizationMode, - } -} - // PTSEqualsDTS implements Format. func (t *H264) PTSEqualsDTS(pkt *rtp.Packet) bool { return rtpH264ContainsIDR(pkt) diff --git a/pkg/format/h264_test.go b/pkg/format/h264_test.go index e7228b38..f7202def 100644 --- a/pkg/format/h264_test.go +++ b/pkg/format/h264_test.go @@ -42,19 +42,6 @@ func TestH264PTSEqualsDTS(t *testing.T) { })) } -func TestH264Clone(t *testing.T) { - format := &H264{ - PayloadTyp: 96, - SPS: []byte{0x01, 0x02}, - PPS: []byte{0x03, 0x04}, - PacketizationMode: 1, - } - - clone := format.Clone() - require.NotSame(t, format, clone) - require.Equal(t, format, clone) -} - func TestH264MediaDescription(t *testing.T) { t.Run("standard", func(t *testing.T) { format := &H264{ diff --git a/pkg/format/h265.go b/pkg/format/h265.go index 3bf8393f..c17bd56e 100644 --- a/pkg/format/h265.go +++ b/pkg/format/h265.go @@ -117,17 +117,6 @@ func (t *H265) Marshal() (string, string) { return "H265/90000", fmtp } -// Clone implements Format. -func (t *H265) Clone() Format { - return &H265{ - PayloadTyp: t.PayloadTyp, - VPS: t.SafeVPS(), - SPS: t.SafeSPS(), - PPS: t.SafePPS(), - MaxDONDiff: t.MaxDONDiff, - } -} - // PTSEqualsDTS implements Format. func (t *H265) PTSEqualsDTS(*rtp.Packet) bool { return true diff --git a/pkg/format/h265_test.go b/pkg/format/h265_test.go index 9174b30a..ce58930f 100644 --- a/pkg/format/h265_test.go +++ b/pkg/format/h265_test.go @@ -30,19 +30,6 @@ func TestH265Attributes(t *testing.T) { require.Equal(t, []byte{0x0B, 0x0C}, format.SafePPS()) } -func TestH265Clone(t *testing.T) { - format := &H265{ - PayloadTyp: 96, - VPS: []byte{0x01, 0x02}, - SPS: []byte{0x03, 0x04}, - PPS: []byte{0x05, 0x06}, - } - - clone := format.Clone() - require.NotSame(t, format, clone) - require.Equal(t, format, clone) -} - func TestH265MediaDescription(t *testing.T) { format := &H265{ PayloadTyp: 96, diff --git a/pkg/format/jpeg.go b/pkg/format/jpeg.go index 87efccb1..41c21f8a 100644 --- a/pkg/format/jpeg.go +++ b/pkg/format/jpeg.go @@ -31,11 +31,6 @@ func (t *JPEG) Marshal() (string, string) { return "JPEG/90000", "" } -// Clone implements Format. -func (t *JPEG) Clone() Format { - return &JPEG{} -} - // PTSEqualsDTS implements Format. func (t *JPEG) PTSEqualsDTS(*rtp.Packet) bool { return true diff --git a/pkg/format/jpeg_test.go b/pkg/format/jpeg_test.go index 3ef04bde..b0423f8c 100644 --- a/pkg/format/jpeg_test.go +++ b/pkg/format/jpeg_test.go @@ -15,14 +15,6 @@ func TestJPEGAttributes(t *testing.T) { require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) } -func TestJPEGClone(t *testing.T) { - format := &JPEG{} - - clone := format.Clone() - // require.NotSame(t, format, clone) - require.Equal(t, format, clone) -} - func TestJPEGMediaDescription(t *testing.T) { format := &JPEG{} diff --git a/pkg/format/lpcm.go b/pkg/format/lpcm.go index 5f9c5bea..b97cdbc1 100644 --- a/pkg/format/lpcm.go +++ b/pkg/format/lpcm.go @@ -89,16 +89,6 @@ func (t *LPCM) Marshal() (string, string) { "/" + strconv.FormatInt(int64(t.ChannelCount), 10), "" } -// Clone implements Format. -func (t *LPCM) Clone() Format { - return &LPCM{ - PayloadTyp: t.PayloadTyp, - BitDepth: t.BitDepth, - SampleRate: t.SampleRate, - ChannelCount: t.ChannelCount, - } -} - // PTSEqualsDTS implements Format. func (t *LPCM) PTSEqualsDTS(*rtp.Packet) bool { return true diff --git a/pkg/format/lpcm_test.go b/pkg/format/lpcm_test.go index ebd9f226..21ecf758 100644 --- a/pkg/format/lpcm_test.go +++ b/pkg/format/lpcm_test.go @@ -20,19 +20,6 @@ func TestLPCMAttributes(t *testing.T) { require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) } -func TestTracLPCMClone(t *testing.T) { - format := &LPCM{ - PayloadTyp: 96, - BitDepth: 16, - SampleRate: 48000, - ChannelCount: 2, - } - - clone := format.Clone() - require.NotSame(t, format, clone) - require.Equal(t, format, clone) -} - func TestLPCMMediaDescription(t *testing.T) { format := &LPCM{ PayloadTyp: 96, diff --git a/pkg/format/mpeg2audio.go b/pkg/format/mpeg2audio.go index 02fd1d1a..3c9a6206 100644 --- a/pkg/format/mpeg2audio.go +++ b/pkg/format/mpeg2audio.go @@ -31,11 +31,6 @@ func (t *MPEG2Audio) Marshal() (string, string) { return "", "" } -// Clone implements Format. -func (t *MPEG2Audio) Clone() Format { - return &MPEG2Audio{} -} - // PTSEqualsDTS implements Format. func (t *MPEG2Audio) PTSEqualsDTS(*rtp.Packet) bool { return true diff --git a/pkg/format/mpeg2audio_test.go b/pkg/format/mpeg2audio_test.go index 49aea3db..9333315a 100644 --- a/pkg/format/mpeg2audio_test.go +++ b/pkg/format/mpeg2audio_test.go @@ -15,14 +15,6 @@ func TestMPEG2AudioAttributes(t *testing.T) { require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) } -func TestMPEG2AudioClone(t *testing.T) { - format := &MPEG2Audio{} - - clone := format.Clone() - // require.NotSame(t, format, clone) - require.Equal(t, format, clone) -} - func TestMPEG2AudioMediaDescription(t *testing.T) { format := &MPEG2Audio{} diff --git a/pkg/format/mpeg2video.go b/pkg/format/mpeg2video.go index 6d4872c0..e5db07d1 100644 --- a/pkg/format/mpeg2video.go +++ b/pkg/format/mpeg2video.go @@ -31,11 +31,6 @@ func (t *MPEG2Video) Marshal() (string, string) { return "", "" } -// Clone implements Format. -func (t *MPEG2Video) Clone() Format { - return &MPEG2Video{} -} - // PTSEqualsDTS implements Format. func (t *MPEG2Video) PTSEqualsDTS(*rtp.Packet) bool { return true diff --git a/pkg/format/mpeg2video_test.go b/pkg/format/mpeg2video_test.go index b754b314..80956dc6 100644 --- a/pkg/format/mpeg2video_test.go +++ b/pkg/format/mpeg2video_test.go @@ -15,14 +15,6 @@ func TestMPEG2VideoAttributes(t *testing.T) { require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) } -func TestMPEG2VideoClone(t *testing.T) { - format := &MPEG2Video{} - - clone := format.Clone() - // require.NotSame(t, format, clone) - require.Equal(t, format, clone) -} - func TestMPEG2VideoMediaDescription(t *testing.T) { format := &MPEG2Video{} diff --git a/pkg/format/mpeg4audio.go b/pkg/format/mpeg4audio.go index 8707fc9e..a91695d5 100644 --- a/pkg/format/mpeg4audio.go +++ b/pkg/format/mpeg4audio.go @@ -140,17 +140,6 @@ func (t *MPEG4Audio) Marshal() (string, string) { "/" + strconv.FormatInt(int64(t.Config.ChannelCount), 10), fmtp } -// Clone implements Format. -func (t *MPEG4Audio) Clone() Format { - return &MPEG4Audio{ - PayloadTyp: t.PayloadTyp, - Config: t.Config, - SizeLength: t.SizeLength, - IndexLength: t.IndexLength, - IndexDeltaLength: t.IndexDeltaLength, - } -} - // PTSEqualsDTS implements Format. func (t *MPEG4Audio) PTSEqualsDTS(*rtp.Packet) bool { return true diff --git a/pkg/format/mpeg4audio_test.go b/pkg/format/mpeg4audio_test.go index c245bab9..1e401009 100644 --- a/pkg/format/mpeg4audio_test.go +++ b/pkg/format/mpeg4audio_test.go @@ -27,24 +27,6 @@ func TestMPEG4AudioAttributes(t *testing.T) { require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) } -func TestMPEG4AudioClone(t *testing.T) { - format := &MPEG4Audio{ - PayloadTyp: 96, - Config: &mpeg4audio.Config{ - Type: mpeg4audio.ObjectTypeAACLC, - SampleRate: 48000, - ChannelCount: 2, - }, - SizeLength: 13, - IndexLength: 3, - IndexDeltaLength: 3, - } - - clone := format.Clone() - require.NotSame(t, format, clone) - require.Equal(t, format, clone) -} - func TestMPEG4AudioMediaDescription(t *testing.T) { format := &MPEG4Audio{ PayloadTyp: 96, diff --git a/pkg/format/opus.go b/pkg/format/opus.go index c8be3108..3b07ebd5 100644 --- a/pkg/format/opus.go +++ b/pkg/format/opus.go @@ -68,15 +68,6 @@ func (t *Opus) Marshal() (string, string) { "/" + strconv.FormatInt(int64(t.ChannelCount), 10), fmtp } -// Clone implements Format. -func (t *Opus) Clone() Format { - return &Opus{ - PayloadTyp: t.PayloadTyp, - SampleRate: t.SampleRate, - ChannelCount: t.ChannelCount, - } -} - // PTSEqualsDTS implements Format. func (t *Opus) PTSEqualsDTS(*rtp.Packet) bool { return true diff --git a/pkg/format/opus_test.go b/pkg/format/opus_test.go index a61aad58..7d6aca52 100644 --- a/pkg/format/opus_test.go +++ b/pkg/format/opus_test.go @@ -19,18 +19,6 @@ func TestOpusAttributes(t *testing.T) { require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) } -func TestTracOpusClone(t *testing.T) { - format := &Opus{ - PayloadTyp: 96, - SampleRate: 48000, - ChannelCount: 2, - } - - clone := format.Clone() - require.NotSame(t, format, clone) - require.Equal(t, format, clone) -} - func TestOpusMediaDescription(t *testing.T) { format := &Opus{ PayloadTyp: 96, diff --git a/pkg/format/vorbis.go b/pkg/format/vorbis.go index 99cddd9f..0df1f421 100644 --- a/pkg/format/vorbis.go +++ b/pkg/format/vorbis.go @@ -92,16 +92,6 @@ func (t *Vorbis) Marshal() (string, string) { "configuration=" + base64.StdEncoding.EncodeToString(t.Configuration) } -// Clone implements Format. -func (t *Vorbis) Clone() Format { - return &Vorbis{ - PayloadTyp: t.PayloadTyp, - SampleRate: t.SampleRate, - ChannelCount: t.ChannelCount, - Configuration: t.Configuration, - } -} - // PTSEqualsDTS implements Format. func (t *Vorbis) PTSEqualsDTS(*rtp.Packet) bool { return true diff --git a/pkg/format/vorbis_test.go b/pkg/format/vorbis_test.go index 13b6fe33..7d56e694 100644 --- a/pkg/format/vorbis_test.go +++ b/pkg/format/vorbis_test.go @@ -20,19 +20,6 @@ func TestVorbisAttributes(t *testing.T) { require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) } -func TestTracVorbisClone(t *testing.T) { - format := &Vorbis{ - PayloadTyp: 96, - SampleRate: 48000, - ChannelCount: 2, - Configuration: []byte{0x01, 0x02, 0x03, 0x04}, - } - - clone := format.Clone() - require.NotSame(t, format, clone) - require.Equal(t, format, clone) -} - func TestVorbisMediaDescription(t *testing.T) { format := &Vorbis{ PayloadTyp: 96, diff --git a/pkg/format/vp8.go b/pkg/format/vp8.go index 4088ff75..a0a2f73f 100644 --- a/pkg/format/vp8.go +++ b/pkg/format/vp8.go @@ -88,15 +88,6 @@ func (t *VP8) Marshal() (string, string) { return "VP8/90000", fmtp } -// Clone implements Format. -func (t *VP8) Clone() Format { - return &VP8{ - PayloadTyp: t.PayloadTyp, - MaxFR: t.MaxFR, - MaxFS: t.MaxFS, - } -} - // PTSEqualsDTS implements Format. func (t *VP8) PTSEqualsDTS(*rtp.Packet) bool { return true diff --git a/pkg/format/vp8_test.go b/pkg/format/vp8_test.go index 69447811..b59c268c 100644 --- a/pkg/format/vp8_test.go +++ b/pkg/format/vp8_test.go @@ -17,20 +17,6 @@ func TestVP8ttributes(t *testing.T) { require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) } -func TestVP8Clone(t *testing.T) { - maxFR := 123 - maxFS := 456 - format := &VP8{ - PayloadTyp: 96, - MaxFR: &maxFR, - MaxFS: &maxFS, - } - - clone := format.Clone() - require.NotSame(t, format, clone) - require.Equal(t, format, clone) -} - func TestVP8MediaDescription(t *testing.T) { maxFR := 123 maxFS := 456 diff --git a/pkg/format/vp9.go b/pkg/format/vp9.go index a8731345..8acbc76e 100644 --- a/pkg/format/vp9.go +++ b/pkg/format/vp9.go @@ -100,16 +100,6 @@ func (t *VP9) Marshal() (string, string) { return "VP9/90000", fmtp } -// Clone implements Format. -func (t *VP9) Clone() Format { - return &VP9{ - PayloadTyp: t.PayloadTyp, - MaxFR: t.MaxFR, - MaxFS: t.MaxFS, - ProfileID: t.ProfileID, - } -} - // PTSEqualsDTS implements Format. func (t *VP9) PTSEqualsDTS(*rtp.Packet) bool { return true diff --git a/pkg/format/vp9_test.go b/pkg/format/vp9_test.go index 9a24384f..135832a9 100644 --- a/pkg/format/vp9_test.go +++ b/pkg/format/vp9_test.go @@ -17,22 +17,6 @@ func TestVP9Attributes(t *testing.T) { require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{})) } -func TestVP9Clone(t *testing.T) { - maxFR := 123 - maxFS := 456 - profileID := 789 - format := &VP9{ - PayloadTyp: 96, - MaxFR: &maxFR, - MaxFS: &maxFS, - ProfileID: &profileID, - } - - clone := format.Clone() - require.NotSame(t, format, clone) - require.Equal(t, format, clone) -} - func TestVP9MediaDescription(t *testing.T) { maxFR := 123 maxFS := 456 diff --git a/pkg/media/media.go b/pkg/media/media.go index e49257b1..b0075fd4 100644 --- a/pkg/media/media.go +++ b/pkg/media/media.go @@ -103,21 +103,6 @@ func (m *Media) Marshal() *psdp.MediaDescription { return md } -// Clone clones the media. -func (m Media) Clone() *Media { - ret := &Media{ - Type: m.Type, - Control: m.Control, - Formats: make([]format.Format, len(m.Formats)), - } - - for i, format := range m.Formats { - ret.Formats[i] = format.Clone() - } - - return ret -} - // URL returns the media URL. func (m Media) URL(contentBase *url.URL) (*url.URL, error) { if contentBase == nil { diff --git a/pkg/media/medias.go b/pkg/media/medias.go index d1fa81c7..f4b76896 100644 --- a/pkg/media/medias.go +++ b/pkg/media/medias.go @@ -3,8 +3,8 @@ package media import ( "fmt" "reflect" - "strconv" + "github.com/google/uuid" psdp "github.com/pion/sdp/v3" "github.com/aler9/gortsplib/v2/pkg/sdp" @@ -65,32 +65,15 @@ func (ms Medias) Marshal(multicast bool) *sdp.SessionDescription { return sout } -// Clone clones the media list. -func (ms Medias) Clone() Medias { - ret := make(Medias, len(ms)) - for i, media := range ms { - ret[i] = media.Clone() - } - return ret -} - -// CloneAndSetControls clones the media list and sets the control attribute -// of all medias in the list. -func (ms Medias) CloneAndSetControls() Medias { - ret := ms.Clone() - ret.SetControls() - return ret -} - // SetControls sets the control attribute of all medias in the list. func (ms Medias) SetControls() { - for i, media := range ms { - media.Control = "mediaID=" + strconv.FormatInt(int64(i), 10) + for _, media := range ms { + media.Control = "mediaUUID=" + uuid.New().String() } } // FindFormat finds a certain format among all the formats in all the medias. -// If the format is found, it is inserted into forma, and format media is returned. +// If the format is found, it is inserted into forma, and its media is returned. func (ms Medias) FindFormat(forma interface{}) *Media { for _, media := range ms { for _, formak := range media.Formats { diff --git a/server_play_test.go b/server_play_test.go index ada74a2b..0e6ceb6e 100644 --- a/server_play_test.go +++ b/server_play_test.go @@ -3,14 +3,17 @@ package gortsplib import ( "bytes" "crypto/tls" + "errors" "net" "strconv" + "strings" "sync/atomic" "testing" "time" "github.com/pion/rtcp" "github.com/pion/rtp" + psdp "github.com/pion/sdp/v3" "github.com/stretchr/testify/require" "golang.org/x/net/ipv4" @@ -49,6 +52,36 @@ func multicastCapableIP(t *testing.T) string { return "" } +func controlAttribute(md *psdp.MediaDescription) string { + v, _ := md.Attribute("control") + return v +} + +func doDescribe(conn *conn.Conn) (*sdp.SessionDescription, error) { + res, err := writeReqReadRes(conn, base.Request{ + Method: base.Describe, + URL: mustParseURL("rtsp://localhost:8554/pa"), + Header: base.Header{ + "CSeq": base.HeaderValue{"1"}, + }, + }) + if err != nil { + return nil, err + } + + if res.StatusCode != base.StatusOK { + return nil, errors.New("bad status code") + } + + var desc sdp.SessionDescription + err = desc.Unmarshal(res.Body) + if err != nil { + return nil, err + } + + return &desc, err +} + func TestServerPlaySetupPath(t *testing.T) { for _, ca := range []struct { name string @@ -57,12 +90,12 @@ func TestServerPlaySetupPath(t *testing.T) { }{ { "normal", - "rtsp://localhost:8554/teststream/mediaID=2", + "rtsp://localhost:8554/teststream/[control2]", "teststream", }, { "with query", - "rtsp://localhost:8554/teststream?testing=123/mediaID=4", + "rtsp://localhost:8554/teststream?testing=123/[control4]", "teststream", }, { @@ -73,7 +106,7 @@ func TestServerPlaySetupPath(t *testing.T) { }, { "subpath", - "rtsp://localhost:8554/test/stream/mediaID=0", + "rtsp://localhost:8554/test/stream/[control0]", "test/stream", }, { @@ -83,17 +116,27 @@ func TestServerPlaySetupPath(t *testing.T) { }, { "subpath with query", - "rtsp://localhost:8554/test/stream?testing=123/mediaID=4", + "rtsp://localhost:8554/test/stream?testing=123/[control4]", "test/stream", }, } { t.Run(ca.name, func(t *testing.T) { - medi := testH264Media.Clone() - stream := NewServerStream(media.Medias{medi, medi, medi, medi, medi}) + stream := NewServerStream(media.Medias{ + testH264Media, + testH264Media, + testH264Media, + testH264Media, + testH264Media, + }) defer stream.Close() s := &Server{ Handler: &testServerHandler{ + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { + return &base.Response{ + StatusCode: base.StatusOK, + }, stream, nil + }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { require.Equal(t, ca.path, ctx.Path) return &base.Response{ @@ -113,6 +156,9 @@ func TestServerPlaySetupPath(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) + desc, err := doDescribe(conn) + require.NoError(t, err) + th := &headers.Transport{ Protocol: headers.TransportProtocolTCP, Delivery: func() *headers.TransportDelivery { @@ -126,11 +172,16 @@ func TestServerPlaySetupPath(t *testing.T) { InterleavedIDs: &[2]int{0, 1}, } + for i, md := range desc.MediaDescriptions { + v, _ := md.Attribute("control") + ca.url = strings.ReplaceAll(ca.url, "[control"+strconv.FormatInt(int64(i), 10)+"]", v) + } + res, err := writeReqReadRes(conn, base.Request{ Method: base.Setup, URL: mustParseURL(ca.url), Header: base.Header{ - "CSeq": base.HeaderValue{"1"}, + "CSeq": base.HeaderValue{"2"}, "Transport": th.Marshal(), }, }) @@ -149,7 +200,7 @@ func TestServerPlaySetupErrors(t *testing.T) { t.Run(ca, func(t *testing.T) { nconnClosed := make(chan struct{}) - stream := NewServerStream(media.Medias{testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media}) if ca == "closed stream" { stream.Close() } else { @@ -171,6 +222,11 @@ func TestServerPlaySetupErrors(t *testing.T) { } close(nconnClosed) }, + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { + return &base.Response{ + StatusCode: base.StatusOK, + }, stream, nil + }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { return &base.Response{ StatusCode: base.StatusOK, @@ -189,6 +245,9 @@ func TestServerPlaySetupErrors(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) + desc, err := doDescribe(conn) + require.NoError(t, err) + th := &headers.Transport{ Protocol: headers.TransportProtocolTCP, Delivery: func() *headers.TransportDelivery { @@ -204,9 +263,9 @@ func TestServerPlaySetupErrors(t *testing.T) { res, err := writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ - "CSeq": base.HeaderValue{"1"}, + "CSeq": base.HeaderValue{"2"}, "Transport": th.Marshal(), }, }) @@ -223,9 +282,9 @@ func TestServerPlaySetupErrors(t *testing.T) { res, err = writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/test12stream/mediaID=1"), + URL: mustParseURL("rtsp://localhost:8554/test12stream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ - "CSeq": base.HeaderValue{"2"}, + "CSeq": base.HeaderValue{"3"}, "Transport": th.Marshal(), "Session": base.HeaderValue{sx.Session}, }, @@ -244,9 +303,9 @@ func TestServerPlaySetupErrors(t *testing.T) { res, err = writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ - "CSeq": base.HeaderValue{"2"}, + "CSeq": base.HeaderValue{"4"}, "Transport": th.Marshal(), "Session": base.HeaderValue{sx.Session}, }, @@ -265,7 +324,7 @@ func TestServerPlaySetupErrors(t *testing.T) { } func TestServerPlaySetupErrorSameUDPPortsAndIP(t *testing.T) { - stream := NewServerStream(media.Medias{testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media}) defer stream.Close() first := int32(1) errorRecv := make(chan struct{}) @@ -279,6 +338,11 @@ func TestServerPlaySetupErrorSameUDPPortsAndIP(t *testing.T) { close(errorRecv) } }, + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { + return &base.Response{ + StatusCode: base.StatusOK, + }, stream, nil + }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { return &base.Response{ StatusCode: base.StatusOK, @@ -318,11 +382,14 @@ func TestServerPlaySetupErrorSameUDPPortsAndIP(t *testing.T) { ClientPorts: &[2]int{35466, 35467}, } + desc, err := doDescribe(conn) + require.NoError(t, err) + res, err := writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ - "CSeq": base.HeaderValue{"1"}, + "CSeq": base.HeaderValue{"2"}, "Transport": inTH.Marshal(), }, }) @@ -352,7 +419,7 @@ func TestServerPlay(t *testing.T) { sessionClosed := make(chan struct{}) framesReceived := make(chan struct{}) - stream := NewServerStream(media.Medias{testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media}) defer stream.Close() counter := uint64(0) @@ -373,6 +440,11 @@ func TestServerPlay(t *testing.T) { onSessionClose: func(ctx *ServerHandlerOnSessionCloseCtx) { close(sessionClosed) }, + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { + return &base.Response{ + StatusCode: base.StatusOK, + }, stream, nil + }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { return &base.Response{ StatusCode: base.StatusOK, @@ -451,6 +523,9 @@ func TestServerPlay(t *testing.T) { <-nconnOpened + desc, err := doDescribe(conn) + require.NoError(t, err) + inTH := &headers.Transport{ Mode: func() *headers.TransportMode { v := headers.TransportModePlay @@ -479,9 +554,9 @@ func TestServerPlay(t *testing.T) { res, err := writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://" + listenIP + ":8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://" + listenIP + ":8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ - "CSeq": base.HeaderValue{"1"}, + "CSeq": base.HeaderValue{"2"}, "Transport": inTH.Marshal(), }, }) @@ -555,7 +630,7 @@ func TestServerPlay(t *testing.T) { Method: base.Play, URL: mustParseURL("rtsp://" + listenIP + ":8554/teststream"), Header: base.Header{ - "CSeq": base.HeaderValue{"2"}, + "CSeq": base.HeaderValue{"3"}, "Session": base.HeaderValue{sx.Session}, }, }) @@ -698,11 +773,16 @@ func TestServerPlayDecodeErrors(t *testing.T) { t.Run(ca.proto+" "+ca.name, func(t *testing.T) { errorRecv := make(chan struct{}) - stream := NewServerStream(media.Medias{testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media}) defer stream.Close() s := &Server{ Handler: &testServerHandler{ + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { + return &base.Response{ + StatusCode: base.StatusOK, + }, stream, nil + }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { return &base.Response{ StatusCode: base.StatusOK, @@ -744,6 +824,9 @@ func TestServerPlayDecodeErrors(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) + desc, err := doDescribe(conn) + require.NoError(t, err) + inTH := &headers.Transport{ Mode: func() *headers.TransportMode { v := headers.TransportModePlay @@ -765,9 +848,9 @@ func TestServerPlayDecodeErrors(t *testing.T) { res, err := writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ - "CSeq": base.HeaderValue{"1"}, + "CSeq": base.HeaderValue{"2"}, "Transport": inTH.Marshal(), }, }) @@ -799,7 +882,7 @@ func TestServerPlayDecodeErrors(t *testing.T) { Method: base.Play, URL: mustParseURL("rtsp://localhost:8554/teststream"), Header: base.Header{ - "CSeq": base.HeaderValue{"2"}, + "CSeq": base.HeaderValue{"3"}, "Session": base.HeaderValue{sx.Session}, }, }) @@ -842,11 +925,16 @@ func TestServerPlayDecodeErrors(t *testing.T) { func TestServerPlayRTCPReport(t *testing.T) { for _, ca := range []string{"udp", "tcp"} { t.Run(ca, func(t *testing.T) { - stream := NewServerStream(media.Medias{testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media}) defer stream.Close() s := &Server{ Handler: &testServerHandler{ + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { + return &base.Response{ + StatusCode: base.StatusOK, + }, stream, nil + }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { return &base.Response{ StatusCode: base.StatusOK, @@ -873,6 +961,9 @@ func TestServerPlayRTCPReport(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) + desc, err := doDescribe(conn) + require.NoError(t, err) + inTH := &headers.Transport{ Mode: func() *headers.TransportMode { v := headers.TransportModePlay @@ -894,9 +985,9 @@ func TestServerPlayRTCPReport(t *testing.T) { res, err := writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ - "CSeq": base.HeaderValue{"1"}, + "CSeq": base.HeaderValue{"2"}, "Transport": inTH.Marshal(), }, }) @@ -923,7 +1014,7 @@ func TestServerPlayRTCPReport(t *testing.T) { Method: base.Play, URL: mustParseURL("rtsp://localhost:8554/teststream"), Header: base.Header{ - "CSeq": base.HeaderValue{"2"}, + "CSeq": base.HeaderValue{"3"}, "Session": base.HeaderValue{sx.Session}, }, }) @@ -975,7 +1066,7 @@ func TestServerPlayRTCPReport(t *testing.T) { Method: base.Teardown, URL: mustParseURL("rtsp://localhost:8554/teststream"), Header: base.Header{ - "CSeq": base.HeaderValue{"3"}, + "CSeq": base.HeaderValue{"4"}, "Session": base.HeaderValue{sx.Session}, }, }) @@ -986,7 +1077,7 @@ func TestServerPlayRTCPReport(t *testing.T) { } func TestServerPlayVLCMulticast(t *testing.T) { - stream := NewServerStream(media.Medias{testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media}) defer stream.Close() listenIP := multicastCapableIP(t) @@ -1035,7 +1126,7 @@ func TestServerPlayTCPResponseBeforeFrames(t *testing.T) { writerDone := make(chan struct{}) writerTerminate := make(chan struct{}) - stream := NewServerStream(media.Medias{testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media}) defer stream.Close() s := &Server{ @@ -1045,6 +1136,11 @@ func TestServerPlayTCPResponseBeforeFrames(t *testing.T) { close(writerTerminate) <-writerDone }, + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { + return &base.Response{ + StatusCode: base.StatusOK, + }, stream, nil + }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { return &base.Response{ StatusCode: base.StatusOK, @@ -1087,11 +1183,14 @@ func TestServerPlayTCPResponseBeforeFrames(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) + desc, err := doDescribe(conn) + require.NoError(t, err) + res, err := writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ - "CSeq": base.HeaderValue{"1"}, + "CSeq": base.HeaderValue{"2"}, "Transport": headers.Transport{ Protocol: headers.TransportProtocolTCP, Delivery: func() *headers.TransportDelivery { @@ -1117,7 +1216,7 @@ func TestServerPlayTCPResponseBeforeFrames(t *testing.T) { Method: base.Play, URL: mustParseURL("rtsp://localhost:8554/teststream"), Header: base.Header{ - "CSeq": base.HeaderValue{"2"}, + "CSeq": base.HeaderValue{"3"}, "Session": base.HeaderValue{sx.Session}, }, }) @@ -1129,11 +1228,16 @@ func TestServerPlayTCPResponseBeforeFrames(t *testing.T) { } func TestServerPlayPlayPlay(t *testing.T) { - stream := NewServerStream(media.Medias{testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media}) defer stream.Close() s := &Server{ Handler: &testServerHandler{ + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { + return &base.Response{ + StatusCode: base.StatusOK, + }, stream, nil + }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { return &base.Response{ StatusCode: base.StatusOK, @@ -1159,11 +1263,14 @@ func TestServerPlayPlayPlay(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) + desc, err := doDescribe(conn) + require.NoError(t, err) + res, err := writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ - "CSeq": base.HeaderValue{"1"}, + "CSeq": base.HeaderValue{"2"}, "Transport": headers.Transport{ Protocol: headers.TransportProtocolUDP, Delivery: func() *headers.TransportDelivery { @@ -1189,7 +1296,7 @@ func TestServerPlayPlayPlay(t *testing.T) { Method: base.Play, URL: mustParseURL("rtsp://localhost:8554/teststream"), Header: base.Header{ - "CSeq": base.HeaderValue{"2"}, + "CSeq": base.HeaderValue{"3"}, "Session": base.HeaderValue{sx.Session}, }, }) @@ -1200,7 +1307,7 @@ func TestServerPlayPlayPlay(t *testing.T) { Method: base.Play, URL: mustParseURL("rtsp://localhost:8554/teststream"), Header: base.Header{ - "CSeq": base.HeaderValue{"3"}, + "CSeq": base.HeaderValue{"4"}, "Session": base.HeaderValue{sx.Session}, }, }) @@ -1213,7 +1320,7 @@ func TestServerPlayPlayPausePlay(t *testing.T) { writerDone := make(chan struct{}) writerTerminate := make(chan struct{}) - stream := NewServerStream(media.Medias{testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media}) defer stream.Close() s := &Server{ @@ -1222,6 +1329,11 @@ func TestServerPlayPlayPausePlay(t *testing.T) { close(writerTerminate) <-writerDone }, + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { + return &base.Response{ + StatusCode: base.StatusOK, + }, stream, nil + }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { return &base.Response{ StatusCode: base.StatusOK, @@ -1269,11 +1381,14 @@ func TestServerPlayPlayPausePlay(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) + desc, err := doDescribe(conn) + require.NoError(t, err) + res, err := writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ - "CSeq": base.HeaderValue{"1"}, + "CSeq": base.HeaderValue{"2"}, "Transport": headers.Transport{ Protocol: headers.TransportProtocolTCP, Delivery: func() *headers.TransportDelivery { @@ -1333,7 +1448,7 @@ func TestServerPlayPlayPausePause(t *testing.T) { writerDone := make(chan struct{}) writerTerminate := make(chan struct{}) - stream := NewServerStream(media.Medias{testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media}) defer stream.Close() s := &Server{ @@ -1342,6 +1457,11 @@ func TestServerPlayPlayPausePause(t *testing.T) { close(writerTerminate) <-writerDone }, + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { + return &base.Response{ + StatusCode: base.StatusOK, + }, stream, nil + }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { return &base.Response{ StatusCode: base.StatusOK, @@ -1386,11 +1506,14 @@ func TestServerPlayPlayPausePause(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) + desc, err := doDescribe(conn) + require.NoError(t, err) + res, err := writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ - "CSeq": base.HeaderValue{"1"}, + "CSeq": base.HeaderValue{"2"}, "Transport": headers.Transport{ Protocol: headers.TransportProtocolTCP, Delivery: func() *headers.TransportDelivery { @@ -1416,7 +1539,7 @@ func TestServerPlayPlayPausePause(t *testing.T) { Method: base.Play, URL: mustParseURL("rtsp://localhost:8554/teststream"), Header: base.Header{ - "CSeq": base.HeaderValue{"2"}, + "CSeq": base.HeaderValue{"3"}, "Session": base.HeaderValue{sx.Session}, }, }) @@ -1427,7 +1550,7 @@ func TestServerPlayPlayPausePause(t *testing.T) { Method: base.Pause, URL: mustParseURL("rtsp://localhost:8554/teststream"), Header: base.Header{ - "CSeq": base.HeaderValue{"2"}, + "CSeq": base.HeaderValue{"4"}, "Session": base.HeaderValue{sx.Session}, }, }) @@ -1441,7 +1564,7 @@ func TestServerPlayPlayPausePause(t *testing.T) { Method: base.Pause, URL: mustParseURL("rtsp://localhost:8554/teststream"), Header: base.Header{ - "CSeq": base.HeaderValue{"2"}, + "CSeq": base.HeaderValue{"5"}, "Session": base.HeaderValue{sx.Session}, }, }) @@ -1461,7 +1584,7 @@ func TestServerPlayTimeout(t *testing.T) { t.Run(transport, func(t *testing.T) { sessionClosed := make(chan struct{}) - stream := NewServerStream(media.Medias{testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media}) defer stream.Close() s := &Server{ @@ -1469,10 +1592,10 @@ func TestServerPlayTimeout(t *testing.T) { onSessionClose: func(ctx *ServerHandlerOnSessionCloseCtx) { close(sessionClosed) }, - onAnnounce: func(ctx *ServerHandlerOnAnnounceCtx) (*base.Response, error) { + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { return &base.Response{ StatusCode: base.StatusOK, - }, nil + }, stream, nil }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { return &base.Response{ @@ -1510,6 +1633,9 @@ func TestServerPlayTimeout(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) + desc, err := doDescribe(conn) + require.NoError(t, err) + inTH := &headers.Transport{ Mode: func() *headers.TransportMode { v := headers.TransportModePlay @@ -1532,9 +1658,9 @@ func TestServerPlayTimeout(t *testing.T) { res, err := writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ - "CSeq": base.HeaderValue{"1"}, + "CSeq": base.HeaderValue{"2"}, "Transport": inTH.Marshal(), }, }) @@ -1549,7 +1675,7 @@ func TestServerPlayTimeout(t *testing.T) { Method: base.Play, URL: mustParseURL("rtsp://localhost:8554/teststream"), Header: base.Header{ - "CSeq": base.HeaderValue{"2"}, + "CSeq": base.HeaderValue{"3"}, "Session": base.HeaderValue{sx.Session}, }, }) @@ -1570,7 +1696,7 @@ func TestServerPlayWithoutTeardown(t *testing.T) { nconnClosed := make(chan struct{}) sessionClosed := make(chan struct{}) - stream := NewServerStream(media.Medias{testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media}) defer stream.Close() s := &Server{ @@ -1581,10 +1707,10 @@ func TestServerPlayWithoutTeardown(t *testing.T) { onSessionClose: func(ctx *ServerHandlerOnSessionCloseCtx) { close(sessionClosed) }, - onAnnounce: func(ctx *ServerHandlerOnAnnounceCtx) (*base.Response, error) { + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { return &base.Response{ StatusCode: base.StatusOK, - }, nil + }, stream, nil }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { return &base.Response{ @@ -1616,6 +1742,9 @@ func TestServerPlayWithoutTeardown(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) + desc, err := doDescribe(conn) + require.NoError(t, err) + inTH := &headers.Transport{ Delivery: func() *headers.TransportDelivery { v := headers.TransportDeliveryUnicast @@ -1637,9 +1766,9 @@ func TestServerPlayWithoutTeardown(t *testing.T) { res, err := writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ - "CSeq": base.HeaderValue{"1"}, + "CSeq": base.HeaderValue{"2"}, "Transport": inTH.Marshal(), }, }) @@ -1654,7 +1783,7 @@ func TestServerPlayWithoutTeardown(t *testing.T) { Method: base.Play, URL: mustParseURL("rtsp://localhost:8554/teststream"), Header: base.Header{ - "CSeq": base.HeaderValue{"2"}, + "CSeq": base.HeaderValue{"3"}, "Session": base.HeaderValue{sx.Session}, }, }) @@ -1670,11 +1799,16 @@ func TestServerPlayWithoutTeardown(t *testing.T) { } func TestServerPlayUDPChangeConn(t *testing.T) { - stream := NewServerStream(media.Medias{testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media}) defer stream.Close() s := &Server{ Handler: &testServerHandler{ + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { + return &base.Response{ + StatusCode: base.StatusOK, + }, stream, nil + }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { return &base.Response{ StatusCode: base.StatusOK, @@ -1708,6 +1842,9 @@ func TestServerPlayUDPChangeConn(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) + desc, err := doDescribe(conn) + require.NoError(t, err) + inTH := &headers.Transport{ Delivery: func() *headers.TransportDelivery { v := headers.TransportDeliveryUnicast @@ -1723,9 +1860,9 @@ func TestServerPlayUDPChangeConn(t *testing.T) { res, err := writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ - "CSeq": base.HeaderValue{"1"}, + "CSeq": base.HeaderValue{"2"}, "Transport": inTH.Marshal(), }, }) @@ -1740,7 +1877,7 @@ func TestServerPlayUDPChangeConn(t *testing.T) { Method: base.Play, URL: mustParseURL("rtsp://localhost:8554/teststream"), Header: base.Header{ - "CSeq": base.HeaderValue{"2"}, + "CSeq": base.HeaderValue{"3"}, "Session": base.HeaderValue{sx.Session}, }, }) @@ -1760,7 +1897,7 @@ func TestServerPlayUDPChangeConn(t *testing.T) { Method: base.GetParameter, URL: mustParseURL("rtsp://localhost:8554/teststream/"), Header: base.Header{ - "CSeq": base.HeaderValue{"1"}, + "CSeq": base.HeaderValue{"4"}, "Session": base.HeaderValue{sxID}, }, }) @@ -1770,11 +1907,16 @@ func TestServerPlayUDPChangeConn(t *testing.T) { } func TestServerPlayPartialMedias(t *testing.T) { - stream := NewServerStream(media.Medias{testH264Media.Clone(), testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media, testH264Media}) defer stream.Close() s := &Server{ Handler: &testServerHandler{ + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { + return &base.Response{ + StatusCode: base.StatusOK, + }, stream, nil + }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { return &base.Response{ StatusCode: base.StatusOK, @@ -1804,6 +1946,9 @@ func TestServerPlayPartialMedias(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) + desc, err := doDescribe(conn) + require.NoError(t, err) + inTH := &headers.Transport{ Delivery: func() *headers.TransportDelivery { v := headers.TransportDeliveryUnicast @@ -1819,9 +1964,9 @@ func TestServerPlayPartialMedias(t *testing.T) { res, err := writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=1"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[1])), Header: base.Header{ - "CSeq": base.HeaderValue{"1"}, + "CSeq": base.HeaderValue{"2"}, "Transport": inTH.Marshal(), }, }) @@ -1836,7 +1981,7 @@ func TestServerPlayPartialMedias(t *testing.T) { Method: base.Play, URL: mustParseURL("rtsp://localhost:8554/teststream"), Header: base.Header{ - "CSeq": base.HeaderValue{"2"}, + "CSeq": base.HeaderValue{"3"}, "Session": base.HeaderValue{sx.Session}, }, }) @@ -1856,7 +2001,8 @@ func TestServerPlayAdditionalInfos(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) - ssrcs := make([]*uint32, 2) + desc, err := doDescribe(conn) + require.NoError(t, err) inTH := &headers.Transport{ Delivery: func() *headers.TransportDelivery { @@ -1873,9 +2019,9 @@ func TestServerPlayAdditionalInfos(t *testing.T) { res, err := writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ - "CSeq": base.HeaderValue{"1"}, + "CSeq": base.HeaderValue{"2"}, "Transport": inTH.Marshal(), }, }) @@ -1885,6 +2031,7 @@ func TestServerPlayAdditionalInfos(t *testing.T) { var th headers.Transport err = th.Unmarshal(res.Header["Transport"]) require.NoError(t, err) + ssrcs := make([]*uint32, 2) ssrcs[0] = th.SSRC inTH = &headers.Transport{ @@ -1906,9 +2053,9 @@ func TestServerPlayAdditionalInfos(t *testing.T) { res, err = writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=1"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[1])), Header: base.Header{ - "CSeq": base.HeaderValue{"2"}, + "CSeq": base.HeaderValue{"3"}, "Transport": inTH.Marshal(), "Session": base.HeaderValue{sx.Session}, }, @@ -1925,7 +2072,7 @@ func TestServerPlayAdditionalInfos(t *testing.T) { Method: base.Play, URL: mustParseURL("rtsp://localhost:8554/teststream"), Header: base.Header{ - "CSeq": base.HeaderValue{"3"}, + "CSeq": base.HeaderValue{"4"}, "Session": base.HeaderValue{sx.Session}, }, }) @@ -1946,16 +2093,25 @@ func TestServerPlayAdditionalInfos(t *testing.T) { err := forma.Init() require.NoError(t, err) - medi := &media.Media{ - Type: "application", - Formats: []format.Format{forma}, - } - - stream := NewServerStream(media.Medias{medi.Clone(), medi.Clone()}) + stream := NewServerStream(media.Medias{ + &media.Media{ + Type: "application", + Formats: []format.Format{forma}, + }, + &media.Media{ + Type: "application", + Formats: []format.Format{forma}, + }, + }) defer stream.Close() s := &Server{ Handler: &testServerHandler{ + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { + return &base.Response{ + StatusCode: base.StatusOK, + }, stream, nil + }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { return &base.Response{ StatusCode: base.StatusOK, @@ -1991,7 +2147,7 @@ func TestServerPlayAdditionalInfos(t *testing.T) { URL: (&url.URL{ Scheme: "rtsp", Host: "localhost:8554", - Path: "/teststream/mediaID=0", + Path: mustParseURL((*rtpInfo)[0].URL).Path, }).String(), SequenceNumber: func() *uint16 { v := uint16(557) @@ -2025,7 +2181,7 @@ func TestServerPlayAdditionalInfos(t *testing.T) { URL: (&url.URL{ Scheme: "rtsp", Host: "localhost:8554", - Path: "/teststream/mediaID=0", + Path: mustParseURL((*rtpInfo)[0].URL).Path, }).String(), SequenceNumber: func() *uint16 { v := uint16(557) @@ -2037,7 +2193,7 @@ func TestServerPlayAdditionalInfos(t *testing.T) { URL: (&url.URL{ Scheme: "rtsp", Host: "localhost:8554", - Path: "/teststream/mediaID=1", + Path: mustParseURL((*rtpInfo)[1].URL).Path, }).String(), SequenceNumber: func() *uint16 { v := uint16(88) diff --git a/server_record_test.go b/server_record_test.go index 9c5b7347..1e125e7a 100644 --- a/server_record_test.go +++ b/server_record_test.go @@ -30,7 +30,7 @@ func invalidURLAnnounceReq(t *testing.T, control string) base.Request { "Content-Type": base.HeaderValue{"application/sdp"}, }, Body: func() []byte { - medi := testH264Media.Clone() + medi := testH264Media medi.Control = control sout := &sdp.SessionDescription{ @@ -207,7 +207,7 @@ func TestServerRecordSetupPath(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) - media := testH264Media.Clone() + media := testH264Media media.Control = ca.control sout := &sdp.SessionDescription{ @@ -298,7 +298,7 @@ func TestServerRecordErrorSetupMediaTwice(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() res, err := writeReqReadRes(conn, base.Request{ @@ -315,7 +315,7 @@ func TestServerRecordErrorSetupMediaTwice(t *testing.T) { res, err = writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + medias[0].Control), Header: base.Header{ "CSeq": base.HeaderValue{"2"}, "Transport": headers.Transport{ @@ -341,7 +341,7 @@ func TestServerRecordErrorSetupMediaTwice(t *testing.T) { res, err = writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + medias[0].Control), Header: base.Header{ "CSeq": base.HeaderValue{"3"}, "Transport": headers.Transport{ @@ -402,7 +402,23 @@ func TestServerRecordErrorRecordPartialMedias(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) - medias := media.Medias{testH264Media.Clone(), testH264Media.Clone()} + forma := &format.Generic{ + PayloadTyp: 96, + RTPMap: "private/90000", + } + err = forma.Init() + require.NoError(t, err) + + medias := media.Medias{ + &media.Media{ + Type: "application", + Formats: []format.Format{forma}, + }, + &media.Media{ + Type: "application", + Formats: []format.Format{forma}, + }, + } medias.SetControls() res, err := writeReqReadRes(conn, base.Request{ @@ -432,7 +448,7 @@ func TestServerRecordErrorRecordPartialMedias(t *testing.T) { res, err = writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + medias[0].Control), Header: base.Header{ "CSeq": base.HeaderValue{"2"}, "Transport": th.Marshal(), @@ -556,7 +572,26 @@ func TestServerRecord(t *testing.T) { <-nconnOpened - medias := media.Medias{testH264Media.Clone(), testH264Media.Clone()} + medias := media.Medias{ + &media.Media{ + Type: media.TypeVideo, + Formats: []format.Format{&format.H264{ + PayloadTyp: 96, + SPS: []byte{0x01, 0x02, 0x03, 0x04}, + PPS: []byte{0x01, 0x02, 0x03, 0x04}, + PacketizationMode: 1, + }}, + }, + &media.Media{ + Type: media.TypeVideo, + Formats: []format.Format{&format.H264{ + PayloadTyp: 96, + SPS: []byte{0x01, 0x02, 0x03, 0x04}, + PPS: []byte{0x01, 0x02, 0x03, 0x04}, + PacketizationMode: 1, + }}, + }, + } medias.SetControls() res, err := writeReqReadRes(conn, base.Request{ @@ -608,7 +643,7 @@ func TestServerRecord(t *testing.T) { res, err = writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=" + strconv.FormatInt(int64(i), 10)), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + medias[i].Control), Header: base.Header{ "CSeq": base.HeaderValue{"2"}, "Transport": inTH.Marshal(), @@ -760,7 +795,7 @@ func TestServerRecordErrorInvalidProtocol(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() res, err := writeReqReadRes(conn, base.Request{ @@ -790,7 +825,7 @@ func TestServerRecordErrorInvalidProtocol(t *testing.T) { res, err = writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + medias[0].Control), Header: base.Header{ "CSeq": base.HeaderValue{"2"}, "Transport": inTH.Marshal(), @@ -861,7 +896,7 @@ func TestServerRecordRTCPReport(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() res, err := writeReqReadRes(conn, base.Request{ @@ -886,7 +921,7 @@ func TestServerRecordRTCPReport(t *testing.T) { res, err = writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + medias[0].Control), Header: base.Header{ "CSeq": base.HeaderValue{"2"}, "Transport": headers.Transport{ @@ -1035,7 +1070,7 @@ func TestServerRecordTimeout(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() res, err := writeReqReadRes(conn, base.Request{ @@ -1071,7 +1106,7 @@ func TestServerRecordTimeout(t *testing.T) { res, err = writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + medias[0].Control), Header: base.Header{ "CSeq": base.HeaderValue{"2"}, "Transport": inTH.Marshal(), @@ -1158,7 +1193,7 @@ func TestServerRecordWithoutTeardown(t *testing.T) { require.NoError(t, err) conn := conn.NewConn(nconn) - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() res, err := writeReqReadRes(conn, base.Request{ @@ -1194,7 +1229,7 @@ func TestServerRecordWithoutTeardown(t *testing.T) { res, err = writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + medias[0].Control), Header: base.Header{ "CSeq": base.HeaderValue{"2"}, "Transport": inTH.Marshal(), @@ -1271,7 +1306,7 @@ func TestServerRecordUDPChangeConn(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} medias.SetControls() res, err := writeReqReadRes(conn, base.Request{ @@ -1301,7 +1336,7 @@ func TestServerRecordUDPChangeConn(t *testing.T) { res, err = writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + medias[0].Control), Header: base.Header{ "CSeq": base.HeaderValue{"2"}, "Transport": inTH.Marshal(), @@ -1475,7 +1510,7 @@ func TestServerRecordDecodeErrors(t *testing.T) { res, err = writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + medias[0].Control), Header: base.Header{ "CSeq": base.HeaderValue{"2"}, "Transport": inTH.Marshal(), diff --git a/server_test.go b/server_test.go index f1d6401b..2271c1ca 100644 --- a/server_test.go +++ b/server_test.go @@ -315,6 +315,14 @@ type testServerErrMethodNotImplemented struct { stream *ServerStream } +func (s *testServerErrMethodNotImplemented) OnDescribe( + ctx *ServerHandlerOnDescribeCtx, +) (*base.Response, *ServerStream, error) { + return &base.Response{ + StatusCode: base.StatusOK, + }, s.stream, nil +} + func (s *testServerErrMethodNotImplemented) OnSetup( ctx *ServerHandlerOnSetupCtx, ) (*base.Response, *ServerStream, error) { @@ -326,7 +334,7 @@ func (s *testServerErrMethodNotImplemented) OnSetup( func TestServerErrorMethodNotImplemented(t *testing.T) { for _, ca := range []string{"outside session", "inside session"} { t.Run(ca, func(t *testing.T) { - stream := NewServerStream(media.Medias{testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media}) defer stream.Close() s := &Server{ @@ -343,12 +351,15 @@ func TestServerErrorMethodNotImplemented(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) + desc, err := doDescribe(conn) + require.NoError(t, err) + var sx headers.Session if ca == "inside session" { res, err := writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ "CSeq": base.HeaderValue{"1"}, "Transport": headers.Transport{ @@ -380,7 +391,7 @@ func TestServerErrorMethodNotImplemented(t *testing.T) { res, err := writeReqReadRes(conn, base.Request{ Method: base.SetParameter, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: headers, }) require.NoError(t, err) @@ -395,7 +406,7 @@ func TestServerErrorMethodNotImplemented(t *testing.T) { res, err = writeReqReadRes(conn, base.Request{ Method: base.Options, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/"), Header: headers, }) require.NoError(t, err) @@ -405,11 +416,16 @@ func TestServerErrorMethodNotImplemented(t *testing.T) { } func TestServerErrorTCPTwoConnOneSession(t *testing.T) { - stream := NewServerStream(media.Medias{testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media}) defer stream.Close() s := &Server{ Handler: &testServerHandler{ + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { + return &base.Response{ + StatusCode: base.StatusOK, + }, stream, nil + }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { return &base.Response{ StatusCode: base.StatusOK, @@ -438,9 +454,12 @@ func TestServerErrorTCPTwoConnOneSession(t *testing.T) { defer nconn1.Close() conn1 := conn.NewConn(nconn1) + desc1, err := doDescribe(conn1) + require.NoError(t, err) + res, err := writeReqReadRes(conn1, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc1.MediaDescriptions[0])), Header: base.Header{ "CSeq": base.HeaderValue{"1"}, "Transport": headers.Transport{ @@ -480,9 +499,12 @@ func TestServerErrorTCPTwoConnOneSession(t *testing.T) { defer nconn2.Close() conn2 := conn.NewConn(nconn2) + desc2, err := doDescribe(conn2) + require.NoError(t, err) + res, err = writeReqReadRes(conn2, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc2.MediaDescriptions[0])), Header: base.Header{ "CSeq": base.HeaderValue{"1"}, "Transport": headers.Transport{ @@ -505,11 +527,16 @@ func TestServerErrorTCPTwoConnOneSession(t *testing.T) { } func TestServerErrorTCPOneConnTwoSessions(t *testing.T) { - stream := NewServerStream(media.Medias{testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media}) defer stream.Close() s := &Server{ Handler: &testServerHandler{ + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { + return &base.Response{ + StatusCode: base.StatusOK, + }, stream, nil + }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { return &base.Response{ StatusCode: base.StatusOK, @@ -538,9 +565,12 @@ func TestServerErrorTCPOneConnTwoSessions(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) + desc, err := doDescribe(conn) + require.NoError(t, err) + res, err := writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ "CSeq": base.HeaderValue{"1"}, "Transport": headers.Transport{ @@ -577,7 +607,7 @@ func TestServerErrorTCPOneConnTwoSessions(t *testing.T) { res, err = writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ "CSeq": base.HeaderValue{"3"}, "Transport": headers.Transport{ @@ -599,11 +629,16 @@ func TestServerErrorTCPOneConnTwoSessions(t *testing.T) { } func TestServerSetupMultipleTransports(t *testing.T) { - stream := NewServerStream(media.Medias{testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media}) defer stream.Close() s := &Server{ Handler: &testServerHandler{ + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { + return &base.Response{ + StatusCode: base.StatusOK, + }, stream, nil + }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { return &base.Response{ StatusCode: base.StatusOK, @@ -622,6 +657,9 @@ func TestServerSetupMultipleTransports(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) + desc, err := doDescribe(conn) + require.NoError(t, err) + inTHS := headers.Transports{ { Delivery: func() *headers.TransportDelivery { @@ -651,7 +689,7 @@ func TestServerSetupMultipleTransports(t *testing.T) { res, err := writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ "CSeq": base.HeaderValue{"1"}, "Transport": inTHS.Marshal(), @@ -676,13 +714,18 @@ func TestServerSetupMultipleTransports(t *testing.T) { func TestServerGetSetParameter(t *testing.T) { for _, ca := range []string{"inside session", "outside session"} { t.Run(ca, func(t *testing.T) { - stream := NewServerStream(media.Medias{testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media}) defer stream.Close() var params []byte s := &Server{ Handler: &testServerHandler{ + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { + return &base.Response{ + StatusCode: base.StatusOK, + }, stream, nil + }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { ctx.Session.SetUserData(123) return &base.Response{ @@ -725,14 +768,17 @@ func TestServerGetSetParameter(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) + desc, err := doDescribe(conn) + require.NoError(t, err) + var sx headers.Session if ca == "inside session" { res, err := writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ - "CSeq": base.HeaderValue{"1"}, + "CSeq": base.HeaderValue{"2"}, "Transport": headers.Transport{ Protocol: headers.TransportProtocolTCP, Delivery: func() *headers.TransportDelivery { @@ -755,7 +801,7 @@ func TestServerGetSetParameter(t *testing.T) { } headers := base.Header{ - "CSeq": base.HeaderValue{"2"}, + "CSeq": base.HeaderValue{"3"}, } if ca == "inside session" { headers["Session"] = base.HeaderValue{sx.Session} @@ -771,7 +817,7 @@ func TestServerGetSetParameter(t *testing.T) { require.Equal(t, base.StatusOK, res.StatusCode) headers = base.Header{ - "CSeq": base.HeaderValue{"3"}, + "CSeq": base.HeaderValue{"4"}, } if ca == "inside session" { headers["Session"] = base.HeaderValue{sx.Session} @@ -843,7 +889,7 @@ func TestServerErrorInvalidSession(t *testing.T) { } func TestServerSessionClose(t *testing.T) { - stream := NewServerStream(media.Medias{testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media}) defer stream.Close() var session *ServerSession @@ -853,6 +899,11 @@ func TestServerSessionClose(t *testing.T) { onSessionOpen: func(ctx *ServerHandlerOnSessionOpenCtx) { session = ctx.Session }, + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { + return &base.Response{ + StatusCode: base.StatusOK, + }, stream, nil + }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { return &base.Response{ StatusCode: base.StatusOK, @@ -871,9 +922,12 @@ func TestServerSessionClose(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) + desc, err := doDescribe(conn) + require.NoError(t, err) + res, err := writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ "CSeq": base.HeaderValue{"1"}, "Transport": headers.Transport{ @@ -913,7 +967,7 @@ func TestServerSessionAutoClose(t *testing.T) { t.Run(ca, func(t *testing.T) { sessionClosed := make(chan struct{}) - stream := NewServerStream(media.Medias{testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media}) defer stream.Close() s := &Server{ @@ -921,6 +975,11 @@ func TestServerSessionAutoClose(t *testing.T) { onSessionClose: func(ctx *ServerHandlerOnSessionCloseCtx) { close(sessionClosed) }, + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { + return &base.Response{ + StatusCode: base.StatusOK, + }, stream, nil + }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { if ca == "200" { return &base.Response{ @@ -944,9 +1003,12 @@ func TestServerSessionAutoClose(t *testing.T) { require.NoError(t, err) conn := conn.NewConn(nconn) + desc, err := doDescribe(conn) + require.NoError(t, err) + _, err = writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ "CSeq": base.HeaderValue{"1"}, "Transport": headers.Transport{ @@ -973,11 +1035,16 @@ func TestServerSessionAutoClose(t *testing.T) { } func TestServerSessionTeardown(t *testing.T) { - stream := NewServerStream(media.Medias{testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media}) defer stream.Close() s := &Server{ Handler: &testServerHandler{ + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { + return &base.Response{ + StatusCode: base.StatusOK, + }, stream, nil + }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { return &base.Response{ StatusCode: base.StatusOK, @@ -996,9 +1063,12 @@ func TestServerSessionTeardown(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) + desc, err := doDescribe(conn) + require.NoError(t, err) + res, err := writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ "CSeq": base.HeaderValue{"1"}, "Transport": headers.Transport{ @@ -1049,7 +1119,7 @@ func TestServerErrorInvalidPath(t *testing.T) { t.Run(ca, func(t *testing.T) { nconnClosed := make(chan struct{}) - stream := NewServerStream(media.Medias{testH264Media.Clone()}) + stream := NewServerStream(media.Medias{testH264Media}) defer stream.Close() s := &Server{ @@ -1058,6 +1128,11 @@ func TestServerErrorInvalidPath(t *testing.T) { require.EqualError(t, ctx.Error, "invalid path") close(nconnClosed) }, + onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { + return &base.Response{ + StatusCode: base.StatusOK, + }, stream, nil + }, onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) { return &base.Response{ StatusCode: base.StatusOK, @@ -1076,10 +1151,13 @@ func TestServerErrorInvalidPath(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) + desc, err := doDescribe(conn) + require.NoError(t, err) + if ca == "inside session" { res, err := writeReqReadRes(conn, base.Request{ Method: base.Setup, - URL: mustParseURL("rtsp://localhost:8554/teststream/mediaID=0"), + URL: mustParseURL("rtsp://localhost:8554/teststream/" + controlAttribute(desc.MediaDescriptions[0])), Header: base.Header{ "CSeq": base.HeaderValue{"1"}, "Transport": headers.Transport{ @@ -1163,7 +1241,7 @@ func TestServerAuth(t *testing.T) { defer nconn.Close() conn := conn.NewConn(nconn) - medias := media.Medias{testH264Media.Clone()} + medias := media.Medias{testH264Media} req := base.Request{ Method: base.Announce, diff --git a/serverconn.go b/serverconn.go index bf8b6f2d..49e3aa64 100644 --- a/serverconn.go +++ b/serverconn.go @@ -14,6 +14,7 @@ import ( "github.com/aler9/gortsplib/v2/pkg/bytecounter" "github.com/aler9/gortsplib/v2/pkg/conn" "github.com/aler9/gortsplib/v2/pkg/liberrors" + "github.com/aler9/gortsplib/v2/pkg/media" "github.com/aler9/gortsplib/v2/pkg/url" ) @@ -377,8 +378,17 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) { } } + mediasCopy := make(media.Medias, len(stream.medias)) + for i, medi := range stream.medias { + mediasCopy[i] = &media.Media{ + Type: medi.Type, + Formats: medi.Formats, + Control: "mediaUUID=" + stream.streamMedias[medi].uuid.String(), + } + } + if stream != nil { - byts, _ := stream.Medias().CloneAndSetControls().Marshal(multicast).Marshal() + byts, _ := mediasCopy.Marshal(multicast).Marshal() res.Body = byts } } diff --git a/serversession.go b/serversession.go index e844bbce..0fe12b70 100644 --- a/serversession.go +++ b/serversession.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "net" - "strconv" "strings" "sync/atomic" "time" @@ -30,47 +29,52 @@ func stringsReverseIndex(s, substr string) int { return -1 } -func serverParseURLForPlay(u *url.URL) (string, string, int, error) { +func serverParseURLForPlay(u *url.URL) (string, string, string, error) { pathAndQuery, ok := u.RTSPPathAndQuery() if !ok { - return "", "", -1, liberrors.ErrServerInvalidPath{} + return "", "", "", liberrors.ErrServerInvalidPath{} } - i := stringsReverseIndex(pathAndQuery, "/mediaID=") + i := stringsReverseIndex(pathAndQuery, "/mediaUUID=") if i < 0 { if !strings.HasSuffix(pathAndQuery, "/") { - return "", "", -1, fmt.Errorf("path of a SETUP request must end with a slash. " + + return "", "", "", fmt.Errorf("path of a SETUP request must end with a slash. " + "This typically happens when VLC fails a request, and then switches to an " + "unsupported RTSP dialect") } path, query := url.PathSplitQuery(pathAndQuery[:len(pathAndQuery)-1]) - return path, query, 0, nil + return path, query, "", nil } - var t string - pathAndQuery, t = pathAndQuery[:i], pathAndQuery[i+len("/mediaID="):] + var mediaUUID string + pathAndQuery, mediaUUID = pathAndQuery[:i], pathAndQuery[i+len("/mediaUUID="):] path, query := url.PathSplitQuery(pathAndQuery) - tmp, _ := strconv.ParseInt(t, 10, 64) - return path, query, int(tmp), nil + return path, query, mediaUUID, nil } -func findMediaByURL(medias media.Medias, baseURL *url.URL, u *url.URL) (*media.Media, bool) { +func findMediaByURL(medias media.Medias, baseURL *url.URL, u *url.URL) *media.Media { for _, media := range medias { u1, err := media.URL(baseURL) if err == nil && u1.String() == u.String() { - return media, true + return media } } - return nil, false + return nil } -func findMediaByID(medias media.Medias, id int) (*media.Media, bool) { - if len(medias) <= id { - return nil, false +func findMediaByUUID(st *ServerStream, uuid string) *media.Media { + if uuid == "" { + return st.medias[0] } - return medias[id], true + + for _, sm := range st.streamMedias { + if sm.uuid.String() == uuid { + return sm.media + } + } + return nil } func findFirstSupportedTransportHeader(s *Server, tsh headers.Transports) *headers.Transport { @@ -602,11 +606,11 @@ func (ss *ServerSession) handleRequest(sc *ServerConn, req *base.Request) (*base var path string var query string - var mediaID int + var mediaUUID string switch ss.state { case ServerSessionStateInitial, ServerSessionStatePrePlay: // play var err error - path, query, mediaID, err = serverParseURLForPlay(req.URL) + path, query, mediaUUID, err = serverParseURLForPlay(req.URL) if err != nil { return &base.Response{ StatusCode: base.StatusBadRequest, @@ -686,12 +690,11 @@ func (ss *ServerSession) handleRequest(sc *ServerConn, req *base.Request) (*base } var medi *media.Media - var ok bool switch ss.state { case ServerSessionStateInitial, ServerSessionStatePrePlay: // play - medi, ok = findMediaByID(stream.medias, mediaID) + medi = findMediaByUUID(stream, mediaUUID) default: // record - medi, ok = findMediaByURL(ss.announcedMedias, &url.URL{ + medi = findMediaByURL(ss.announcedMedias, &url.URL{ Scheme: req.URL.Scheme, Host: req.URL.Host, Path: path, @@ -699,7 +702,7 @@ func (ss *ServerSession) handleRequest(sc *ServerConn, req *base.Request) (*base }, req.URL) } - if !ok { + if medi == nil { return &base.Response{ StatusCode: base.StatusBadRequest, }, fmt.Errorf("media not found") @@ -881,15 +884,14 @@ func (ss *ServerSession) handleRequest(sc *ServerConn, req *base.Request) (*base var ri headers.RTPInfo now := time.Now() - for i, sm := range ss.setuppedMediasOrdered { + for _, sm := range ss.setuppedMediasOrdered { entry := ss.setuppedStream.rtpInfoEntry(sm.media, now) if entry != nil { entry.URL = (&url.URL{ Scheme: req.URL.Scheme, Host: req.URL.Host, - Path: "/" + *ss.setuppedPath + "/mediaID=" + strconv.FormatInt(int64(i), 10), + Path: "/" + *ss.setuppedPath + "/mediaUUID=" + ss.setuppedStream.streamMedias[sm.media].uuid.String(), }).String() - ri = append(ri, entry) } } diff --git a/serverstream.go b/serverstream.go index 30ab9938..b6721f94 100644 --- a/serverstream.go +++ b/serverstream.go @@ -11,7 +11,6 @@ import ( "github.com/aler9/gortsplib/v2/pkg/headers" "github.com/aler9/gortsplib/v2/pkg/liberrors" "github.com/aler9/gortsplib/v2/pkg/media" - "github.com/aler9/gortsplib/v2/pkg/rtcpsender" ) // ServerStream represents a data stream. @@ -39,27 +38,8 @@ func NewServerStream(medias media.Medias) *ServerStream { } st.streamMedias = make(map[*media.Media]*serverStreamMedia, len(medias)) - for _, media := range medias { - ssm := newServerStreamMedia(media) - - ssm.formats = make(map[uint8]*serverStreamFormat) - for _, forma := range media.Formats { - tr := &serverStreamFormat{ - format: forma, - } - - cmedia := media - tr.rtcpSender = rtcpsender.New( - forma.ClockRate(), - func(pkt rtcp.Packet) { - st.WritePacketRTCP(cmedia, pkt) - }, - ) - - ssm.formats[forma.PayloadType()] = tr - } - - st.streamMedias[media] = ssm + for _, medi := range medias { + st.streamMedias[medi] = newServerStreamMedia(st, medi) } return st @@ -242,8 +222,8 @@ func (st *ServerStream) readerSetActive(ss *ServerSession) { } if *ss.setuppedTransport == TransportUDPMulticast { - for mediaID, sm := range ss.setuppedMedias { - streamMedia := st.streamMedias[mediaID] + for medi, sm := range ss.setuppedMedias { + streamMedia := st.streamMedias[medi] streamMedia.multicastHandler.rtcpl.addClient( ss.author.ip(), streamMedia.multicastHandler.rtcpl.port(), sm) } @@ -261,8 +241,8 @@ func (st *ServerStream) readerSetInactive(ss *ServerSession) { } if *ss.setuppedTransport == TransportUDPMulticast { - for mediaID, sm := range ss.setuppedMedias { - streamMedia := st.streamMedias[mediaID] + for medi, sm := range ss.setuppedMedias { + streamMedia := st.streamMedias[medi] streamMedia.multicastHandler.rtcpl.removeClient(sm) } } else { diff --git a/serverstreammedia.go b/serverstreammedia.go index 04330210..fb651609 100644 --- a/serverstreammedia.go +++ b/serverstreammedia.go @@ -3,22 +3,45 @@ package gortsplib import ( "time" + "github.com/google/uuid" "github.com/pion/rtcp" "github.com/pion/rtp" "github.com/aler9/gortsplib/v2/pkg/media" + "github.com/aler9/gortsplib/v2/pkg/rtcpsender" ) type serverStreamMedia struct { + uuid uuid.UUID media *media.Media formats map[uint8]*serverStreamFormat multicastHandler *serverMulticastHandler } -func newServerStreamMedia(medi *media.Media) *serverStreamMedia { - return &serverStreamMedia{ +func newServerStreamMedia(st *ServerStream, medi *media.Media) *serverStreamMedia { + sm := &serverStreamMedia{ + uuid: uuid.New(), media: medi, } + + sm.formats = make(map[uint8]*serverStreamFormat) + for _, forma := range medi.Formats { + tr := &serverStreamFormat{ + format: forma, + } + + cmedia := medi + tr.rtcpSender = rtcpsender.New( + forma.ClockRate(), + func(pkt rtcp.Packet) { + st.WritePacketRTCP(cmedia, pkt) + }, + ) + + sm.formats[forma.PayloadType()] = tr + } + + return sm } func (sm *serverStreamMedia) close() {