mirror of
https://github.com/aler9/gortsplib
synced 2025-10-04 14:52:46 +08:00
server: provide path to OnSetup()
This commit is contained in:
@@ -72,7 +72,7 @@ func handleConn(conn *gortsplib.ServerConn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// called after receiving a SETUP request.
|
// called after receiving a SETUP request.
|
||||||
onSetup := func(req *base.Request, th *headers.Transport, trackID int) (*base.Response, error) {
|
onSetup := func(req *base.Request, th *headers.Transport, path string, trackID int) (*base.Response, error) {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
Header: base.Header{
|
Header: base.Header{
|
||||||
|
@@ -71,7 +71,7 @@ func handleConn(conn *gortsplib.ServerConn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// called after receiving a SETUP request.
|
// called after receiving a SETUP request.
|
||||||
onSetup := func(req *base.Request, th *headers.Transport, trackID int) (*base.Response, error) {
|
onSetup := func(req *base.Request, th *headers.Transport, path string, trackID int) (*base.Response, error) {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
Header: base.Header{
|
Header: base.Header{
|
||||||
|
@@ -71,7 +71,7 @@ func handleConn(conn *gortsplib.ServerConn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// called after receiving a SETUP request.
|
// called after receiving a SETUP request.
|
||||||
onSetup := func(req *base.Request, th *headers.Transport, trackID int) (*base.Response, error) {
|
onSetup := func(req *base.Request, th *headers.Transport, path string, trackID int) (*base.Response, error) {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
Header: base.Header{
|
Header: base.Header{
|
||||||
|
@@ -3,37 +3,9 @@ package base
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func stringsReverseIndex(s, substr string) int {
|
|
||||||
for i := len(s) - 1 - len(substr); i >= 0; i-- {
|
|
||||||
if s[i:i+len(substr)] == substr {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// PathSplitControlAttribute splits a path and query from a control attribute.
|
|
||||||
func PathSplitControlAttribute(pathAndQuery string) (int, string, bool) {
|
|
||||||
i := stringsReverseIndex(pathAndQuery, "/trackID=")
|
|
||||||
|
|
||||||
// URL doesn't contain trackID - we assume it's track 0
|
|
||||||
if i < 0 {
|
|
||||||
return 0, pathAndQuery, true
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp, err := strconv.ParseInt(pathAndQuery[i+len("/trackID="):], 10, 64)
|
|
||||||
if err != nil || tmp < 0 {
|
|
||||||
return 0, "", false
|
|
||||||
}
|
|
||||||
trackID := int(tmp)
|
|
||||||
|
|
||||||
return trackID, pathAndQuery[:i], true
|
|
||||||
}
|
|
||||||
|
|
||||||
// PathSplitQuery splits a path from a query.
|
// PathSplitQuery splits a path from a query.
|
||||||
func PathSplitQuery(pathAndQuery string) (string, string) {
|
func PathSplitQuery(pathAndQuery string) (string, string) {
|
||||||
i := strings.Index(pathAndQuery, "?")
|
i := strings.Index(pathAndQuery, "?")
|
||||||
|
120
serverconn.go
120
serverconn.go
@@ -6,6 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@@ -30,6 +31,61 @@ var (
|
|||||||
errServerCSeqMissing = errors.New("CSeq is missing")
|
errServerCSeqMissing = errors.New("CSeq is missing")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func stringsReverseIndex(s, substr string) int {
|
||||||
|
for i := len(s) - 1 - len(substr); i >= 0; i-- {
|
||||||
|
if s[i:i+len(substr)] == substr {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractTrackIDAndPath(url *base.URL,
|
||||||
|
thMode *headers.TransportMode,
|
||||||
|
publishTracks []ServerConnAnnouncedTrack,
|
||||||
|
publishPath string) (int, string, error) {
|
||||||
|
|
||||||
|
pathAndQuery, ok := url.RTSPPathAndQuery()
|
||||||
|
if !ok {
|
||||||
|
return 0, "", fmt.Errorf("invalid URL (%s)", url)
|
||||||
|
}
|
||||||
|
|
||||||
|
if thMode == nil || *thMode == headers.TransportModePlay {
|
||||||
|
i := stringsReverseIndex(pathAndQuery, "/trackID=")
|
||||||
|
|
||||||
|
// URL doesn't contain trackID - it's track zero
|
||||||
|
if i < 0 {
|
||||||
|
if !strings.HasSuffix(pathAndQuery, "/") {
|
||||||
|
return 0, "", fmt.Errorf("path must end with a slash (%v)", pathAndQuery)
|
||||||
|
}
|
||||||
|
pathAndQuery = pathAndQuery[:len(pathAndQuery)-1]
|
||||||
|
|
||||||
|
// we assume it's track 0
|
||||||
|
return 0, pathAndQuery, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp, err := strconv.ParseInt(pathAndQuery[i+len("/trackID="):], 10, 64)
|
||||||
|
if err != nil || tmp < 0 {
|
||||||
|
return 0, "", fmt.Errorf("unable to parse track ID (%v)", pathAndQuery)
|
||||||
|
}
|
||||||
|
trackID := int(tmp)
|
||||||
|
pathAndQuery = pathAndQuery[:i]
|
||||||
|
|
||||||
|
path, _ := base.PathSplitQuery(pathAndQuery)
|
||||||
|
|
||||||
|
return trackID, path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for trackID, track := range publishTracks {
|
||||||
|
u, _ := track.track.URL()
|
||||||
|
if u.String() == url.String() {
|
||||||
|
return trackID, publishPath, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, "", fmt.Errorf("invalid track path (%s)", pathAndQuery)
|
||||||
|
}
|
||||||
|
|
||||||
// ServerConnState is the state of the connection.
|
// ServerConnState is the state of the connection.
|
||||||
type ServerConnState int
|
type ServerConnState int
|
||||||
|
|
||||||
@@ -92,7 +148,7 @@ type ServerConnReadHandlers struct {
|
|||||||
OnAnnounce func(req *base.Request, tracks Tracks) (*base.Response, error)
|
OnAnnounce func(req *base.Request, tracks Tracks) (*base.Response, error)
|
||||||
|
|
||||||
// called after receiving a SETUP request.
|
// called after receiving a SETUP request.
|
||||||
OnSetup func(req *base.Request, th *headers.Transport, trackID int) (*base.Response, error)
|
OnSetup func(req *base.Request, th *headers.Transport, path string, trackID int) (*base.Response, error)
|
||||||
|
|
||||||
// called after receiving a PLAY request.
|
// called after receiving a PLAY request.
|
||||||
OnPlay func(req *base.Request) (*base.Response, error)
|
OnPlay func(req *base.Request) (*base.Response, error)
|
||||||
@@ -127,20 +183,22 @@ type ServerConn struct {
|
|||||||
br *bufio.Reader
|
br *bufio.Reader
|
||||||
bw *bufio.Writer
|
bw *bufio.Writer
|
||||||
state ServerConnState
|
state ServerConnState
|
||||||
readHandlers ServerConnReadHandlers
|
|
||||||
tracks map[int]ServerConnTrack
|
tracks map[int]ServerConnTrack
|
||||||
streamProtocol *StreamProtocol
|
streamProtocol *StreamProtocol
|
||||||
announcedTracks []ServerConnAnnouncedTrack
|
|
||||||
|
|
||||||
doEnableFrames bool
|
// frame mode only
|
||||||
framesEnabled bool
|
doEnableFrames bool
|
||||||
readTimeoutEnabled bool
|
framesEnabled bool
|
||||||
|
readTimeoutEnabled bool
|
||||||
// writer
|
|
||||||
frameRingBuffer *ringbuffer.RingBuffer
|
frameRingBuffer *ringbuffer.RingBuffer
|
||||||
backgroundWriteDone chan struct{}
|
backgroundWriteDone chan struct{}
|
||||||
|
|
||||||
// background record
|
// read only
|
||||||
|
readHandlers ServerConnReadHandlers
|
||||||
|
|
||||||
|
// publish only
|
||||||
|
publishPath string
|
||||||
|
publishTracks []ServerConnAnnouncedTrack
|
||||||
backgroundRecordTerminate chan struct{}
|
backgroundRecordTerminate chan struct{}
|
||||||
backgroundRecordDone chan struct{}
|
backgroundRecordDone chan struct{}
|
||||||
udpTimeout int32
|
udpTimeout int32
|
||||||
@@ -457,14 +515,14 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
|
|
||||||
if res.StatusCode == 200 {
|
if res.StatusCode == 200 {
|
||||||
sc.state = ServerConnStatePreRecord
|
sc.state = ServerConnStatePreRecord
|
||||||
|
sc.publishPath = reqPath
|
||||||
|
|
||||||
sc.announcedTracks = make([]ServerConnAnnouncedTrack, len(tracks))
|
sc.publishTracks = make([]ServerConnAnnouncedTrack, len(tracks))
|
||||||
|
|
||||||
for trackID, track := range tracks {
|
for trackID, track := range tracks {
|
||||||
clockRate, _ := track.ClockRate()
|
clockRate, _ := track.ClockRate()
|
||||||
v := time.Now().Unix()
|
v := time.Now().Unix()
|
||||||
|
|
||||||
sc.announcedTracks[trackID] = ServerConnAnnouncedTrack{
|
sc.publishTracks[trackID] = ServerConnAnnouncedTrack{
|
||||||
track: track,
|
track: track,
|
||||||
rtcpReceiver: rtcpreceiver.New(nil, clockRate),
|
rtcpReceiver: rtcpreceiver.New(nil, clockRate),
|
||||||
udpLastFrameTime: &v,
|
udpLastFrameTime: &v,
|
||||||
@@ -488,13 +546,6 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
}, err
|
}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pathAndQuery, ok := req.URL.RTSPPathAndQuery()
|
|
||||||
if !ok {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusBadRequest,
|
|
||||||
}, fmt.Errorf("invalid path (%s)", req.URL)
|
|
||||||
}
|
|
||||||
|
|
||||||
th, err := headers.ReadTransport(req.Header["Transport"])
|
th, err := headers.ReadTransport(req.Header["Transport"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
@@ -524,25 +575,8 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
trackID, err := func() (int, error) {
|
trackID, path, err := extractTrackIDAndPath(req.URL, th.Mode,
|
||||||
if th.Mode == nil || *th.Mode == headers.TransportModePlay {
|
sc.publishTracks, sc.publishPath)
|
||||||
trackID, _, ok := base.PathSplitControlAttribute(pathAndQuery)
|
|
||||||
if !ok {
|
|
||||||
return 0, fmt.Errorf("invalid track path (%s)", pathAndQuery)
|
|
||||||
}
|
|
||||||
|
|
||||||
return trackID, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for trackID, track := range sc.announcedTracks {
|
|
||||||
u, _ := track.track.URL()
|
|
||||||
if u.String() == req.URL.String() {
|
|
||||||
return trackID, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, fmt.Errorf("invalid track path (%s)", pathAndQuery)
|
|
||||||
}()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusBadRequest,
|
StatusCode: base.StatusBadRequest,
|
||||||
@@ -590,7 +624,7 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := sc.readHandlers.OnSetup(req, th, trackID)
|
res, err := sc.readHandlers.OnSetup(req, th, path, trackID)
|
||||||
|
|
||||||
if res.StatusCode == 200 {
|
if res.StatusCode == 200 {
|
||||||
sc.streamProtocol = &th.Protocol
|
sc.streamProtocol = &th.Protocol
|
||||||
@@ -697,7 +731,7 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
}, fmt.Errorf("no tracks have been setup")
|
}, fmt.Errorf("no tracks have been setup")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(sc.tracks) != len(sc.announcedTracks) {
|
if len(sc.tracks) != len(sc.publishTracks) {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusBadRequest,
|
StatusCode: base.StatusBadRequest,
|
||||||
}, fmt.Errorf("not all tracks have been setup")
|
}, fmt.Errorf("not all tracks have been setup")
|
||||||
@@ -860,7 +894,7 @@ outer:
|
|||||||
// forward frame only if it has been set up
|
// forward frame only if it has been set up
|
||||||
if _, ok := sc.tracks[frame.TrackID]; ok {
|
if _, ok := sc.tracks[frame.TrackID]; ok {
|
||||||
if sc.state == ServerConnStateRecord {
|
if sc.state == ServerConnStateRecord {
|
||||||
sc.announcedTracks[frame.TrackID].rtcpReceiver.ProcessFrame(time.Now(),
|
sc.publishTracks[frame.TrackID].rtcpReceiver.ProcessFrame(time.Now(),
|
||||||
frame.StreamType, frame.Payload)
|
frame.StreamType, frame.Payload)
|
||||||
}
|
}
|
||||||
sc.readHandlers.OnFrame(frame.TrackID, frame.StreamType, frame.Payload)
|
sc.readHandlers.OnFrame(frame.TrackID, frame.StreamType, frame.Payload)
|
||||||
@@ -961,7 +995,7 @@ func (sc *ServerConn) backgroundRecord() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
for _, track := range sc.announcedTracks {
|
for _, track := range sc.publishTracks {
|
||||||
last := time.Unix(atomic.LoadInt64(track.udpLastFrameTime), 0)
|
last := time.Unix(atomic.LoadInt64(track.udpLastFrameTime), 0)
|
||||||
|
|
||||||
if now.Sub(last) >= sc.conf.ReadTimeout {
|
if now.Sub(last) >= sc.conf.ReadTimeout {
|
||||||
@@ -973,7 +1007,7 @@ func (sc *ServerConn) backgroundRecord() {
|
|||||||
|
|
||||||
case <-receiverReportTicker.C:
|
case <-receiverReportTicker.C:
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
for trackID, track := range sc.announcedTracks {
|
for trackID, track := range sc.publishTracks {
|
||||||
r := track.rtcpReceiver.Report(now)
|
r := track.rtcpReceiver.Report(now)
|
||||||
sc.WriteFrame(trackID, StreamTypeRTP, r)
|
sc.WriteFrame(trackID, StreamTypeRTP, r)
|
||||||
}
|
}
|
||||||
|
@@ -145,30 +145,11 @@ func (ts *testServ) handleConn(conn *ServerConn) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
onSetup := func(req *base.Request, th *headers.Transport, trackID int) (*base.Response, error) {
|
onSetup := func(req *base.Request, th *headers.Transport, path string, trackID int) (*base.Response, error) {
|
||||||
switch conn.State() {
|
if path != "teststream" {
|
||||||
case ServerConnStateInitial, ServerConnStatePrePlay:
|
return &base.Response{
|
||||||
pathAndQuery, ok := req.URL.RTSPPathAndQuery()
|
StatusCode: base.StatusBadRequest,
|
||||||
if !ok {
|
}, fmt.Errorf("invalid path (%s)", req.URL)
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusBadRequest,
|
|
||||||
}, fmt.Errorf("invalid path (%s)", req.URL)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, pathAndQuery, ok = base.PathSplitControlAttribute(pathAndQuery)
|
|
||||||
if !ok {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusBadRequest,
|
|
||||||
}, fmt.Errorf("invalid path (%s)", req.URL)
|
|
||||||
}
|
|
||||||
|
|
||||||
reqPath, _ := base.PathSplitQuery(pathAndQuery)
|
|
||||||
|
|
||||||
if reqPath != "teststream" {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusBadRequest,
|
|
||||||
}, fmt.Errorf("invalid path (%s)", req.URL)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
|
@@ -21,35 +21,58 @@ func TestServerConnPublishSetupPath(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
control string
|
control string
|
||||||
url string
|
url string
|
||||||
|
path string
|
||||||
trackID int
|
trackID int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"normal",
|
"normal",
|
||||||
"trackID=0",
|
"trackID=0",
|
||||||
"rtsp://localhost:8554/teststream/trackID=0",
|
"rtsp://localhost:8554/teststream/trackID=0",
|
||||||
|
"teststream",
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"unordered id",
|
"unordered id",
|
||||||
"trackID=2",
|
"trackID=2",
|
||||||
"rtsp://localhost:8554/teststream/trackID=2",
|
"rtsp://localhost:8554/teststream/trackID=2",
|
||||||
|
"teststream",
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"custom param name",
|
"custom param name",
|
||||||
"testing=0",
|
"testing=0",
|
||||||
"rtsp://localhost:8554/teststream/testing=0",
|
"rtsp://localhost:8554/teststream/testing=0",
|
||||||
|
"teststream",
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"query",
|
"query",
|
||||||
"?testing=0",
|
"?testing=0",
|
||||||
"rtsp://localhost:8554/teststream?testing=0",
|
"rtsp://localhost:8554/teststream?testing=0",
|
||||||
|
"teststream",
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"subpath",
|
||||||
|
"trackID=0",
|
||||||
|
"rtsp://localhost:8554/test/stream/trackID=0",
|
||||||
|
"test/stream",
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"subpath and query",
|
||||||
|
"?testing=0",
|
||||||
|
"rtsp://localhost:8554/test/stream?testing=0",
|
||||||
|
"test/stream",
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(ca.name, func(t *testing.T) {
|
t.Run(ca.name, func(t *testing.T) {
|
||||||
setupDone := make(chan int)
|
type pathTrackIDPair struct {
|
||||||
|
path string
|
||||||
|
trackID int
|
||||||
|
}
|
||||||
|
setupDone := make(chan pathTrackIDPair)
|
||||||
|
|
||||||
s, err := Serve("127.0.0.1:8554")
|
s, err := Serve("127.0.0.1:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -70,8 +93,8 @@ func TestServerConnPublishSetupPath(t *testing.T) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
onSetup := func(req *base.Request, th *headers.Transport, trackID int) (*base.Response, error) {
|
onSetup := func(req *base.Request, th *headers.Transport, path string, trackID int) (*base.Response, error) {
|
||||||
setupDone <- trackID
|
setupDone <- pathTrackIDPair{path, trackID}
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -116,7 +139,7 @@ func TestServerConnPublishSetupPath(t *testing.T) {
|
|||||||
|
|
||||||
err = base.Request{
|
err = base.Request{
|
||||||
Method: base.Announce,
|
Method: base.Announce,
|
||||||
URL: base.MustParseURL("rtsp://localhost:8554/teststream"),
|
URL: base.MustParseURL("rtsp://localhost:8554/" + ca.path),
|
||||||
Header: base.Header{
|
Header: base.Header{
|
||||||
"CSeq": base.HeaderValue{"1"},
|
"CSeq": base.HeaderValue{"1"},
|
||||||
"Content-Type": base.HeaderValue{"application/sdp"},
|
"Content-Type": base.HeaderValue{"application/sdp"},
|
||||||
@@ -153,8 +176,9 @@ func TestServerConnPublishSetupPath(t *testing.T) {
|
|||||||
}.Write(bconn.Writer)
|
}.Write(bconn.Writer)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
trackID := <-setupDone
|
pair := <-setupDone
|
||||||
require.Equal(t, ca.trackID, trackID)
|
require.Equal(t, ca.path, pair.path)
|
||||||
|
require.Equal(t, ca.trackID, pair.trackID)
|
||||||
|
|
||||||
err = res.Read(bconn.Reader)
|
err = res.Read(bconn.Reader)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -197,7 +221,7 @@ func TestServerConnPublishReceivePackets(t *testing.T) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
onSetup := func(req *base.Request, th *headers.Transport, trackID int) (*base.Response, error) {
|
onSetup := func(req *base.Request, th *headers.Transport, path string, trackID int) (*base.Response, error) {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
|
@@ -17,21 +17,47 @@ func TestServerConnReadSetupPath(t *testing.T) {
|
|||||||
for _, ca := range []struct {
|
for _, ca := range []struct {
|
||||||
name string
|
name string
|
||||||
url string
|
url string
|
||||||
|
path string
|
||||||
trackID int
|
trackID int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"normal",
|
"normal",
|
||||||
"rtsp://localhost:8554/teststream/trackID=0",
|
"rtsp://localhost:8554/teststream/trackID=0",
|
||||||
|
"teststream",
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"unordered id",
|
"unordered id",
|
||||||
"rtsp://localhost:8554/teststream/trackID=2",
|
"rtsp://localhost:8554/teststream/trackID=2",
|
||||||
|
"teststream",
|
||||||
2,
|
2,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// this is needed to support reading mpegts with ffmpeg
|
||||||
|
"without track id",
|
||||||
|
"rtsp://localhost:8554/teststream/",
|
||||||
|
"teststream",
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"subpath",
|
||||||
|
"rtsp://localhost:8554/test/stream/trackID=0",
|
||||||
|
"test/stream",
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"subpath without track id",
|
||||||
|
"rtsp://localhost:8554/test/stream/",
|
||||||
|
"test/stream",
|
||||||
|
0,
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(ca.name, func(t *testing.T) {
|
t.Run(ca.name, func(t *testing.T) {
|
||||||
setupDone := make(chan int)
|
type pathTrackIDPair struct {
|
||||||
|
path string
|
||||||
|
trackID int
|
||||||
|
}
|
||||||
|
setupDone := make(chan pathTrackIDPair)
|
||||||
|
|
||||||
s, err := Serve("127.0.0.1:8554")
|
s, err := Serve("127.0.0.1:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -46,8 +72,8 @@ func TestServerConnReadSetupPath(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
onSetup := func(req *base.Request, th *headers.Transport, trackID int) (*base.Response, error) {
|
onSetup := func(req *base.Request, th *headers.Transport, path string, trackID int) (*base.Response, error) {
|
||||||
setupDone <- trackID
|
setupDone <- pathTrackIDPair{path, trackID}
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -87,8 +113,9 @@ func TestServerConnReadSetupPath(t *testing.T) {
|
|||||||
}.Write(bconn.Writer)
|
}.Write(bconn.Writer)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
trackID := <-setupDone
|
pair := <-setupDone
|
||||||
require.Equal(t, ca.trackID, trackID)
|
require.Equal(t, ca.path, pair.path)
|
||||||
|
require.Equal(t, ca.trackID, pair.trackID)
|
||||||
|
|
||||||
var res base.Response
|
var res base.Response
|
||||||
err = res.Read(bconn.Reader)
|
err = res.Read(bconn.Reader)
|
||||||
@@ -124,7 +151,7 @@ func TestServerConnReadReceivePackets(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
onSetup := func(req *base.Request, th *headers.Transport, trackID int) (*base.Response, error) {
|
onSetup := func(req *base.Request, th *headers.Transport, path string, trackID int) (*base.Response, error) {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -231,94 +258,6 @@ func TestServerConnReadReceivePackets(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServerConnReadWithoutSetupTrackID(t *testing.T) {
|
|
||||||
s, err := Serve("127.0.0.1:8554")
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer s.Close()
|
|
||||||
|
|
||||||
serverDone := make(chan struct{})
|
|
||||||
defer func() { <-serverDone }()
|
|
||||||
go func() {
|
|
||||||
defer close(serverDone)
|
|
||||||
|
|
||||||
conn, err := s.Accept()
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
onSetup := func(req *base.Request, th *headers.Transport, trackID int) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
onPlay := func(req *base.Request) (*base.Response, error) {
|
|
||||||
go func() {
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
conn.WriteFrame(0, StreamTypeRTP, []byte("\x00\x00\x00\x00"))
|
|
||||||
}()
|
|
||||||
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err = <-conn.Read(ServerConnReadHandlers{
|
|
||||||
OnSetup: onSetup,
|
|
||||||
OnPlay: onPlay,
|
|
||||||
})
|
|
||||||
require.Equal(t, io.EOF, err)
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "localhost:8554")
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer conn.Close()
|
|
||||||
bconn := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
|
|
||||||
|
|
||||||
err = base.Request{
|
|
||||||
Method: base.Setup,
|
|
||||||
URL: base.MustParseURL("rtsp://localhost:8554/teststream"),
|
|
||||||
Header: base.Header{
|
|
||||||
"CSeq": base.HeaderValue{"1"},
|
|
||||||
"Transport": headers.Transport{
|
|
||||||
Protocol: StreamProtocolTCP,
|
|
||||||
Delivery: func() *base.StreamDelivery {
|
|
||||||
v := base.StreamDeliveryUnicast
|
|
||||||
return &v
|
|
||||||
}(),
|
|
||||||
Mode: func() *headers.TransportMode {
|
|
||||||
v := headers.TransportModePlay
|
|
||||||
return &v
|
|
||||||
}(),
|
|
||||||
InterleavedIds: &[2]int{0, 1},
|
|
||||||
}.Write(),
|
|
||||||
},
|
|
||||||
}.Write(bconn.Writer)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var res base.Response
|
|
||||||
err = res.Read(bconn.Reader)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, base.StatusOK, res.StatusCode)
|
|
||||||
|
|
||||||
err = base.Request{
|
|
||||||
Method: base.Play,
|
|
||||||
URL: base.MustParseURL("rtsp://localhost:8554/teststream"),
|
|
||||||
Header: base.Header{
|
|
||||||
"CSeq": base.HeaderValue{"2"},
|
|
||||||
},
|
|
||||||
}.Write(bconn.Writer)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
err = res.Read(bconn.Reader)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, base.StatusOK, res.StatusCode)
|
|
||||||
|
|
||||||
var fr base.InterleavedFrame
|
|
||||||
fr.Payload = make([]byte, 2048)
|
|
||||||
err = fr.Read(bconn.Reader)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestServerConnReadTCPResponseBeforeFrames(t *testing.T) {
|
func TestServerConnReadTCPResponseBeforeFrames(t *testing.T) {
|
||||||
s, err := Serve("127.0.0.1:8554")
|
s, err := Serve("127.0.0.1:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -338,7 +277,7 @@ func TestServerConnReadTCPResponseBeforeFrames(t *testing.T) {
|
|||||||
writerTerminate := make(chan struct{})
|
writerTerminate := make(chan struct{})
|
||||||
defer close(writerTerminate)
|
defer close(writerTerminate)
|
||||||
|
|
||||||
onSetup := func(req *base.Request, th *headers.Transport, trackID int) (*base.Response, error) {
|
onSetup := func(req *base.Request, th *headers.Transport, path string, trackID int) (*base.Response, error) {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -446,7 +385,7 @@ func TestServerConnReadPlayMultiple(t *testing.T) {
|
|||||||
writerTerminate := make(chan struct{})
|
writerTerminate := make(chan struct{})
|
||||||
defer close(writerTerminate)
|
defer close(writerTerminate)
|
||||||
|
|
||||||
onSetup := func(req *base.Request, th *headers.Transport, trackID int) (*base.Response, error) {
|
onSetup := func(req *base.Request, th *headers.Transport, path string, trackID int) (*base.Response, error) {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -561,7 +500,7 @@ func TestServerConnReadPauseMultiple(t *testing.T) {
|
|||||||
writerTerminate := make(chan struct{})
|
writerTerminate := make(chan struct{})
|
||||||
defer close(writerTerminate)
|
defer close(writerTerminate)
|
||||||
|
|
||||||
onSetup := func(req *base.Request, th *headers.Transport, trackID int) (*base.Response, error) {
|
onSetup := func(req *base.Request, th *headers.Transport, path string, trackID int) (*base.Response, error) {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
|
@@ -121,8 +121,8 @@ func (s *serverUDPListener) run() {
|
|||||||
|
|
||||||
if clientData.isPublishing {
|
if clientData.isPublishing {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
atomic.StoreInt64(clientData.sc.announcedTracks[clientData.trackID].udpLastFrameTime, now.Unix())
|
atomic.StoreInt64(clientData.sc.publishTracks[clientData.trackID].udpLastFrameTime, now.Unix())
|
||||||
clientData.sc.announcedTracks[clientData.trackID].rtcpReceiver.ProcessFrame(now, s.streamType, buf[:n])
|
clientData.sc.publishTracks[clientData.trackID].rtcpReceiver.ProcessFrame(now, s.streamType, buf[:n])
|
||||||
}
|
}
|
||||||
|
|
||||||
clientData.sc.readHandlers.OnFrame(clientData.trackID, s.streamType, buf[:n])
|
clientData.sc.readHandlers.OnFrame(clientData.trackID, s.streamType, buf[:n])
|
||||||
|
Reference in New Issue
Block a user