server: support reading with VLC and multicast

This commit is contained in:
aler9
2021-11-15 17:40:17 +01:00
parent 04b661d86c
commit 1411cb33f5
8 changed files with 101 additions and 34 deletions

View File

@@ -1142,7 +1142,7 @@ func (c *Client) doAnnounce(u *base.URL, tracks Tracks) (*base.Response, error)
Header: base.Header{
"Content-Type": base.HeaderValue{"application/sdp"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
}, false)
if err != nil {
return nil, err

View File

@@ -76,7 +76,7 @@ func TestClientReadTracks(t *testing.T) {
"Content-Type": base.HeaderValue{"application/sdp"},
"Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
}.Write(bconn.Writer)
require.NoError(t, err)
@@ -237,7 +237,7 @@ func TestClientRead(t *testing.T) {
"Content-Type": base.HeaderValue{"application/sdp"},
"Content-Base": base.HeaderValue{scheme + "://" + listenIP + ":8554/test/stream?param=value/"},
},
Body: Tracks{track}.Write(),
Body: Tracks{track}.Write(false),
}.Write(bconn.Writer)
require.NoError(t, err)
@@ -490,7 +490,7 @@ func TestClientReadNonStandardFrameSize(t *testing.T) {
"Content-Type": base.HeaderValue{"application/sdp"},
"Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"},
},
Body: Tracks{track}.Write(),
Body: Tracks{track}.Write(false),
}.Write(bconn.Writer)
require.NoError(t, err)
@@ -591,7 +591,7 @@ func TestClientReadPartial(t *testing.T) {
"Content-Type": base.HeaderValue{"application/sdp"},
"Content-Base": base.HeaderValue{"rtsp://" + listenIP + ":8554/teststream/"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
}.Write(bconn.Writer)
require.NoError(t, err)
@@ -728,7 +728,7 @@ func TestClientReadNoContentBase(t *testing.T) {
Header: base.Header{
"Content-Type": base.HeaderValue{"application/sdp"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
}.Write(bconn.Writer)
require.NoError(t, err)
@@ -840,7 +840,7 @@ func TestClientReadAnyPort(t *testing.T) {
"Content-Type": base.HeaderValue{"application/sdp"},
"Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
}.Write(bconn.Writer)
require.NoError(t, err)
@@ -968,7 +968,7 @@ func TestClientReadAutomaticProtocol(t *testing.T) {
"Content-Type": base.HeaderValue{"application/sdp"},
"Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
}.Write(bconn.Writer)
require.NoError(t, err)
@@ -1098,7 +1098,7 @@ func TestClientReadAutomaticProtocol(t *testing.T) {
"Content-Type": base.HeaderValue{"application/sdp"},
"Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
}.Write(bconn.Writer)
require.NoError(t, err)
@@ -1286,7 +1286,7 @@ func TestClientReadDifferentInterleavedIDs(t *testing.T) {
"Content-Type": base.HeaderValue{"application/sdp"},
"Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
}.Write(bconn.Writer)
require.NoError(t, err)
@@ -1443,7 +1443,7 @@ func TestClientReadRedirect(t *testing.T) {
"Content-Type": base.HeaderValue{"application/sdp"},
"Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
}.Write(bconn.Writer)
require.NoError(t, err)
@@ -1600,7 +1600,7 @@ func TestClientReadPause(t *testing.T) {
"Content-Type": base.HeaderValue{"application/sdp"},
"Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
}.Write(bconn.Writer)
require.NoError(t, err)
@@ -1768,7 +1768,7 @@ func TestClientReadRTCPReport(t *testing.T) {
"Content-Type": base.HeaderValue{"application/sdp"},
"Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
}.Write(bconn.Writer)
require.NoError(t, err)
@@ -1942,7 +1942,7 @@ func TestClientReadErrorTimeout(t *testing.T) {
"Content-Type": base.HeaderValue{"application/sdp"},
"Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
}.Write(bconn.Writer)
require.NoError(t, err)
@@ -2096,7 +2096,7 @@ func TestClientReadIgnoreTCPInvalidTrack(t *testing.T) {
"Content-Type": base.HeaderValue{"application/sdp"},
"Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
}.Write(bconn.Writer)
require.NoError(t, err)
@@ -2221,7 +2221,7 @@ func TestClientReadSeek(t *testing.T) {
"Content-Type": base.HeaderValue{"application/sdp"},
"Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
}.Write(bconn.Writer)
require.NoError(t, err)

View File

@@ -80,7 +80,7 @@ func TestClientSession(t *testing.T) {
"Content-Type": base.HeaderValue{"application/sdp"},
"Session": base.HeaderValue{"123456"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
}.Write(bconn.Writer)
require.NoError(t, err)
}()
@@ -161,7 +161,7 @@ func TestClientAuth(t *testing.T) {
Header: base.Header{
"Content-Type": base.HeaderValue{"application/sdp"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
}.Write(bconn.Writer)
require.NoError(t, err)
}()
@@ -225,7 +225,7 @@ func TestClientDescribeCharset(t *testing.T) {
"Content-Type": base.HeaderValue{"application/sdp; charset=utf-8"},
"Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"},
},
Body: Tracks{track1}.Write(),
Body: Tracks{track1}.Write(false),
}.Write(bconn.Writer)
require.NoError(t, err)
}()

View File

@@ -372,7 +372,7 @@ func TestServerPublishErrorSetupDifferentPaths(t *testing.T) {
"CSeq": base.HeaderValue{"1"},
"Content-Type": base.HeaderValue{"application/sdp"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
})
require.NoError(t, err)
require.Equal(t, base.StatusOK, res.StatusCode)
@@ -455,7 +455,7 @@ func TestServerPublishErrorSetupTrackTwice(t *testing.T) {
"CSeq": base.HeaderValue{"1"},
"Content-Type": base.HeaderValue{"application/sdp"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
})
require.NoError(t, err)
require.Equal(t, base.StatusOK, res.StatusCode)
@@ -558,7 +558,7 @@ func TestServerPublishErrorRecordPartialTracks(t *testing.T) {
"CSeq": base.HeaderValue{"1"},
"Content-Type": base.HeaderValue{"application/sdp"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
})
require.NoError(t, err)
require.Equal(t, base.StatusOK, res.StatusCode)
@@ -704,7 +704,7 @@ func TestServerPublish(t *testing.T) {
"CSeq": base.HeaderValue{"1"},
"Content-Type": base.HeaderValue{"application/sdp"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
})
require.NoError(t, err)
require.Equal(t, base.StatusOK, res.StatusCode)
@@ -892,7 +892,7 @@ func TestServerPublishNonStandardFrameSize(t *testing.T) {
"CSeq": base.HeaderValue{"1"},
"Content-Type": base.HeaderValue{"application/sdp"},
},
Body: Tracks{track}.Write(),
Body: Tracks{track}.Write(false),
})
require.NoError(t, err)
require.Equal(t, base.StatusOK, res.StatusCode)
@@ -996,7 +996,7 @@ func TestServerPublishErrorInvalidProtocol(t *testing.T) {
"CSeq": base.HeaderValue{"1"},
"Content-Type": base.HeaderValue{"application/sdp"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
})
require.NoError(t, err)
require.Equal(t, base.StatusOK, res.StatusCode)
@@ -1098,7 +1098,7 @@ func TestServerPublishRTCPReport(t *testing.T) {
"CSeq": base.HeaderValue{"1"},
"Content-Type": base.HeaderValue{"application/sdp"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
})
require.NoError(t, err)
require.Equal(t, base.StatusOK, res.StatusCode)
@@ -1258,7 +1258,7 @@ func TestServerPublishTimeout(t *testing.T) {
"CSeq": base.HeaderValue{"1"},
"Content-Type": base.HeaderValue{"application/sdp"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
})
require.NoError(t, err)
require.Equal(t, base.StatusOK, res.StatusCode)
@@ -1386,7 +1386,7 @@ func TestServerPublishWithoutTeardown(t *testing.T) {
"CSeq": base.HeaderValue{"1"},
"Content-Type": base.HeaderValue{"application/sdp"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
})
require.NoError(t, err)
require.Equal(t, base.StatusOK, res.StatusCode)
@@ -1506,7 +1506,7 @@ func TestServerPublishUDPChangeConn(t *testing.T) {
"CSeq": base.HeaderValue{"1"},
"Content-Type": base.HeaderValue{"application/sdp"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
})
require.NoError(t, err)
require.Equal(t, base.StatusOK, res.StatusCode)

View File

@@ -11,6 +11,7 @@ import (
"time"
"github.com/pion/rtp"
psdp "github.com/pion/sdp/v3"
"github.com/stretchr/testify/require"
"golang.org/x/net/ipv4"
@@ -570,6 +571,54 @@ func TestServerRead(t *testing.T) {
}
}
func TestServerReadVLCMulticast(t *testing.T) {
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
stream := NewServerStream(Tracks{track})
listenIP := multicastCapableIP(t)
s := &Server{
Handler: &testServerHandler{
onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) {
return &base.Response{
StatusCode: base.StatusOK,
}, stream, nil
},
},
RTSPAddress: listenIP + ":8554",
MulticastIPRange: "224.1.0.0/16",
MulticastRTPPort: 8000,
MulticastRTCPPort: 8001,
}
err = s.Start()
require.NoError(t, err)
defer s.Close()
nconn, err := net.Dial("tcp", listenIP+":8554")
require.NoError(t, err)
bconn := bufio.NewReadWriter(bufio.NewReader(nconn), bufio.NewWriter(nconn))
defer nconn.Close()
res, err := writeReqReadRes(bconn, base.Request{
Method: base.Describe,
URL: mustParseURL("rtsp://" + listenIP + ":8554/teststream?vlcmulticast"),
Header: base.Header{
"CSeq": base.HeaderValue{"1"},
},
})
require.NoError(t, err)
require.Equal(t, base.StatusOK, res.StatusCode)
var desc psdp.SessionDescription
err = desc.Unmarshal(res.Body)
require.NoError(t, err)
require.Equal(t, "224.1.0.0", desc.ConnectionInformation.Address.Address)
}
func TestServerReadNonStandardFrameSize(t *testing.T) {
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)

View File

@@ -1171,7 +1171,7 @@ func TestServerErrorInvalidPath(t *testing.T) {
"CSeq": base.HeaderValue{"1"},
"Content-Type": base.HeaderValue{"application/sdp"},
},
Body: tracks.Write(),
Body: tracks.Write(false),
})
require.NoError(t, err)
require.Equal(t, base.StatusOK, res.StatusCode)

View File

@@ -5,6 +5,7 @@ import (
"context"
"crypto/tls"
"net"
"net/url"
"strings"
"time"
@@ -377,8 +378,20 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
res.Header["Content-Base"] = base.HeaderValue{req.URL.String() + "/"}
res.Header["Content-Type"] = base.HeaderValue{"application/sdp"}
// VLC uses multicast if the SDP contains a multicast address.
// therefore, we introduce a special query (vlcmulticast) that allows
// to return a SDP that contains a multicast address.
multicast := false
if sc.s.MulticastIPRange != "" {
if q, err := url.ParseQuery(query); err == nil {
if _, ok := q["vlcmulticast"]; ok {
multicast = true
}
}
}
if stream != nil {
res.Body = stream.Tracks().Write()
res.Body = stream.Tracks().Write(multicast)
}
}

View File

@@ -128,7 +128,7 @@ type Tracks []*Track
// ReadTracks decodes tracks from SDP.
func ReadTracks(byts []byte) (Tracks, error) {
desc := sdp.SessionDescription{}
var desc sdp.SessionDescription
err := desc.Unmarshal(byts)
if err != nil {
return nil, err
@@ -192,7 +192,12 @@ func cloneAndClearTracks(ts Tracks) Tracks {
}
// Write encodes tracks into SDP.
func (ts Tracks) Write() []byte {
func (ts Tracks) Write(multicast bool) []byte {
address := "0.0.0.0"
if multicast {
address = "224.1.0.0"
}
sout := &sdp.SessionDescription{
SessionName: psdp.SessionName("Stream"),
Origin: psdp.Origin{
@@ -205,7 +210,7 @@ func (ts Tracks) Write() []byte {
ConnectionInformation: &psdp.ConnectionInformation{
NetworkType: "IN",
AddressType: "IP4",
Address: &psdp.Address{Address: "0.0.0.0"},
Address: &psdp.Address{Address: address},
},
TimeDescriptions: []psdp.TimeDescription{
{Timing: psdp.Timing{0, 0}}, //nolint:govet