mirror of
https://github.com/aler9/gortsplib
synced 2025-10-05 15:16:51 +08:00
server: rewrite working principle
This commit is contained in:
@@ -53,7 +53,6 @@ Features:
|
|||||||
* [client-publish-options](examples/client-publish-options/main.go)
|
* [client-publish-options](examples/client-publish-options/main.go)
|
||||||
* [client-publish-pause](examples/client-publish-pause/main.go)
|
* [client-publish-pause](examples/client-publish-pause/main.go)
|
||||||
* [server](examples/server/main.go)
|
* [server](examples/server/main.go)
|
||||||
* [server-udp](examples/server-udp/main.go)
|
|
||||||
* [server-tls](examples/server-tls/main.go)
|
* [server-tls](examples/server-tls/main.go)
|
||||||
|
|
||||||
## API Documentation
|
## API Documentation
|
||||||
|
@@ -15,24 +15,40 @@ import (
|
|||||||
// 2. allow a single client to publish a stream with TCP
|
// 2. allow a single client to publish a stream with TCP
|
||||||
// 3. allow multiple clients to read that stream with TCP
|
// 3. allow multiple clients to read that stream with TCP
|
||||||
|
|
||||||
var mutex sync.Mutex
|
type serverHandler struct {
|
||||||
var publisher *gortsplib.ServerConn
|
mutex sync.Mutex
|
||||||
var readers = make(map[*gortsplib.ServerConn]struct{})
|
publisher *gortsplib.ServerConn
|
||||||
var sdp []byte
|
readers map[*gortsplib.ServerConn]struct{}
|
||||||
|
sdp []byte
|
||||||
|
}
|
||||||
|
|
||||||
// this is called for each incoming connection
|
// called when a connection is opened.
|
||||||
func handleConn(conn *gortsplib.ServerConn) {
|
func (sh *serverHandler) OnConnOpen(sc *gortsplib.ServerConn) {
|
||||||
defer conn.Close()
|
log.Printf("conn opened")
|
||||||
|
}
|
||||||
|
|
||||||
log.Printf("client connected")
|
// called when a connection is closed.
|
||||||
|
func (sh *serverHandler) OnConnClose(sc *gortsplib.ServerConn, err error) {
|
||||||
|
log.Println("conn closed (%v)", err)
|
||||||
|
|
||||||
// called after receiving a DESCRIBE request.
|
sh.mutex.Lock()
|
||||||
onDescribe := func(ctx *gortsplib.ServerConnDescribeCtx) (*base.Response, []byte, error) {
|
defer sh.mutex.Unlock()
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
if sc == sh.publisher {
|
||||||
|
sh.publisher = nil
|
||||||
|
sh.sdp = nil
|
||||||
|
} else {
|
||||||
|
delete(sh.readers, sc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// called after receiving a DESCRIBE request.
|
||||||
|
func (sh *serverHandler) OnDescribe(ctx *gortsplib.ServerHandlerOnDescribeCtx) (*base.Response, []byte, error) {
|
||||||
|
sh.mutex.Lock()
|
||||||
|
defer sh.mutex.Unlock()
|
||||||
|
|
||||||
// no one is publishing yet
|
// no one is publishing yet
|
||||||
if publisher == nil {
|
if sh.publisher == nil {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusNotFound,
|
StatusCode: base.StatusNotFound,
|
||||||
}, nil, nil
|
}, nil, nil
|
||||||
@@ -40,22 +56,22 @@ func handleConn(conn *gortsplib.ServerConn) {
|
|||||||
|
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, sdp, nil
|
}, sh.sdp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// called after receiving an ANNOUNCE request.
|
// called after receiving an ANNOUNCE request.
|
||||||
onAnnounce := func(ctx *gortsplib.ServerConnAnnounceCtx) (*base.Response, error) {
|
func (sh *serverHandler) OnAnnounce(ctx *gortsplib.ServerHandlerOnAnnounceCtx) (*base.Response, error) {
|
||||||
mutex.Lock()
|
sh.mutex.Lock()
|
||||||
defer mutex.Unlock()
|
defer sh.mutex.Unlock()
|
||||||
|
|
||||||
if publisher != nil {
|
if sh.publisher != nil {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusBadRequest,
|
StatusCode: base.StatusBadRequest,
|
||||||
}, fmt.Errorf("someone is already publishing")
|
}, fmt.Errorf("someone is already publishing")
|
||||||
}
|
}
|
||||||
|
|
||||||
publisher = conn
|
sh.publisher = ctx.Conn
|
||||||
sdp = ctx.Tracks.Write()
|
sh.sdp = ctx.Tracks.Write()
|
||||||
|
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
@@ -63,24 +79,24 @@ func handleConn(conn *gortsplib.ServerConn) {
|
|||||||
"Session": base.HeaderValue{"12345678"},
|
"Session": base.HeaderValue{"12345678"},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// called after receiving a SETUP request.
|
// called after receiving a SETUP request.
|
||||||
onSetup := func(ctx *gortsplib.ServerConnSetupCtx) (*base.Response, error) {
|
func (sh *serverHandler) OnSetup(ctx *gortsplib.ServerHandlerOnSetupCtx) (*base.Response, error) {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
Header: base.Header{
|
Header: base.Header{
|
||||||
"Session": base.HeaderValue{"12345678"},
|
"Session": base.HeaderValue{"12345678"},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// called after receiving a PLAY request.
|
// called after receiving a PLAY request.
|
||||||
onPlay := func(ctx *gortsplib.ServerConnPlayCtx) (*base.Response, error) {
|
func (sh *serverHandler) OnPlay(ctx *gortsplib.ServerHandlerOnPlayCtx) (*base.Response, error) {
|
||||||
mutex.Lock()
|
sh.mutex.Lock()
|
||||||
defer mutex.Unlock()
|
defer sh.mutex.Unlock()
|
||||||
|
|
||||||
readers[conn] = struct{}{}
|
sh.readers[ctx.Conn] = struct{}{}
|
||||||
|
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
@@ -88,14 +104,14 @@ func handleConn(conn *gortsplib.ServerConn) {
|
|||||||
"Session": base.HeaderValue{"12345678"},
|
"Session": base.HeaderValue{"12345678"},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// called after receiving a RECORD request.
|
// called after receiving a RECORD request.
|
||||||
onRecord := func(ctx *gortsplib.ServerConnRecordCtx) (*base.Response, error) {
|
func (sh *serverHandler) OnRecord(ctx *gortsplib.ServerHandlerOnRecordCtx) (*base.Response, error) {
|
||||||
mutex.Lock()
|
sh.mutex.Lock()
|
||||||
defer mutex.Unlock()
|
defer sh.mutex.Unlock()
|
||||||
|
|
||||||
if conn != publisher {
|
if ctx.Conn != sh.publisher {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusBadRequest,
|
StatusCode: base.StatusBadRequest,
|
||||||
}, fmt.Errorf("someone is already publishing")
|
}, fmt.Errorf("someone is already publishing")
|
||||||
@@ -107,40 +123,19 @@ func handleConn(conn *gortsplib.ServerConn) {
|
|||||||
"Session": base.HeaderValue{"12345678"},
|
"Session": base.HeaderValue{"12345678"},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// called after receiving a frame.
|
// called after receiving a frame.
|
||||||
onFrame := func(trackID int, typ gortsplib.StreamType, buf []byte) {
|
func (sh *serverHandler) OnFrame(ctx *gortsplib.ServerHandlerOnFrameCtx) {
|
||||||
mutex.Lock()
|
sh.mutex.Lock()
|
||||||
defer mutex.Unlock()
|
defer sh.mutex.Unlock()
|
||||||
|
|
||||||
// if we are the publisher, route frames to readers
|
// if we are the publisher, route frames to readers
|
||||||
if conn == publisher {
|
if ctx.Conn == sh.publisher {
|
||||||
for r := range readers {
|
for r := range sh.readers {
|
||||||
r.WriteFrame(trackID, typ, buf)
|
r.WriteFrame(ctx.TrackID, ctx.StreamType, ctx.Payload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
err := <-conn.Read(gortsplib.ServerConnReadHandlers{
|
|
||||||
OnDescribe: onDescribe,
|
|
||||||
OnAnnounce: onAnnounce,
|
|
||||||
OnSetup: onSetup,
|
|
||||||
OnPlay: onPlay,
|
|
||||||
OnRecord: onRecord,
|
|
||||||
OnFrame: onFrame,
|
|
||||||
})
|
|
||||||
log.Printf("client disconnected (%s)", err)
|
|
||||||
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
|
|
||||||
if conn == publisher {
|
|
||||||
publisher = nil
|
|
||||||
sdp = nil
|
|
||||||
} else {
|
|
||||||
delete(readers, conn)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -152,24 +147,12 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// create server
|
// configure server
|
||||||
s := &gortsplib.Server{
|
s := &gortsplib.Server{
|
||||||
|
Handler: &serverHandler{},
|
||||||
TLSConfig: &tls.Config{Certificates: []tls.Certificate{cert}},
|
TLSConfig: &tls.Config{Certificates: []tls.Certificate{cert}},
|
||||||
}
|
}
|
||||||
err = s.Serve(":8554")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("server is ready")
|
// start server and wait until a fatal error
|
||||||
|
panic(s.StartAndWait(":8554"))
|
||||||
// accept connections
|
|
||||||
for {
|
|
||||||
conn, err := s.Accept()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
go handleConn(conn)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -1,167 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/aler9/gortsplib"
|
|
||||||
"github.com/aler9/gortsplib/pkg/base"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This example shows how to
|
|
||||||
// 1. create a RTSP server which accepts plain connections
|
|
||||||
// 2. allow a single client to publish a stream with TCP or UDP
|
|
||||||
// 3. allow multiple clients to read that stream with TCP or UDP
|
|
||||||
|
|
||||||
var mutex sync.Mutex
|
|
||||||
var publisher *gortsplib.ServerConn
|
|
||||||
var readers = make(map[*gortsplib.ServerConn]struct{})
|
|
||||||
var sdp []byte
|
|
||||||
|
|
||||||
// this is called for each incoming connection
|
|
||||||
func handleConn(conn *gortsplib.ServerConn) {
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
log.Printf("client connected")
|
|
||||||
|
|
||||||
// called after receiving a DESCRIBE request.
|
|
||||||
onDescribe := func(ctx *gortsplib.ServerConnDescribeCtx) (*base.Response, []byte, error) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
|
|
||||||
// no one is publishing yet
|
|
||||||
if publisher == nil {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusNotFound,
|
|
||||||
}, nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, sdp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// called after receiving an ANNOUNCE request.
|
|
||||||
onAnnounce := func(ctx *gortsplib.ServerConnAnnounceCtx) (*base.Response, error) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
|
|
||||||
if publisher != nil {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusBadRequest,
|
|
||||||
}, fmt.Errorf("someone is already publishing")
|
|
||||||
}
|
|
||||||
|
|
||||||
publisher = conn
|
|
||||||
sdp = ctx.Tracks.Write()
|
|
||||||
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
Header: base.Header{
|
|
||||||
"Session": base.HeaderValue{"12345678"},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// called after receiving a SETUP request.
|
|
||||||
onSetup := func(ctx *gortsplib.ServerConnSetupCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
Header: base.Header{
|
|
||||||
"Session": base.HeaderValue{"12345678"},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// called after receiving a PLAY request.
|
|
||||||
onPlay := func(ctx *gortsplib.ServerConnPlayCtx) (*base.Response, error) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
|
|
||||||
readers[conn] = struct{}{}
|
|
||||||
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
Header: base.Header{
|
|
||||||
"Session": base.HeaderValue{"12345678"},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// called after receiving a RECORD request.
|
|
||||||
onRecord := func(ctx *gortsplib.ServerConnRecordCtx) (*base.Response, error) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
|
|
||||||
if conn != publisher {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusBadRequest,
|
|
||||||
}, fmt.Errorf("someone is already publishing")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
Header: base.Header{
|
|
||||||
"Session": base.HeaderValue{"12345678"},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// called after receiving a frame.
|
|
||||||
onFrame := func(trackID int, typ gortsplib.StreamType, buf []byte) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
|
|
||||||
// if we are the publisher, route frames to readers
|
|
||||||
if conn == publisher {
|
|
||||||
for r := range readers {
|
|
||||||
r.WriteFrame(trackID, typ, buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := <-conn.Read(gortsplib.ServerConnReadHandlers{
|
|
||||||
OnDescribe: onDescribe,
|
|
||||||
OnAnnounce: onAnnounce,
|
|
||||||
OnSetup: onSetup,
|
|
||||||
OnPlay: onPlay,
|
|
||||||
OnRecord: onRecord,
|
|
||||||
OnFrame: onFrame,
|
|
||||||
})
|
|
||||||
log.Printf("client disconnected (%s)", err)
|
|
||||||
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
|
|
||||||
if conn == publisher {
|
|
||||||
publisher = nil
|
|
||||||
sdp = nil
|
|
||||||
} else {
|
|
||||||
delete(readers, conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// create server
|
|
||||||
s := &gortsplib.Server{
|
|
||||||
UDPRTPAddress: ":8000",
|
|
||||||
UDPRTCPAddress: ":8001",
|
|
||||||
}
|
|
||||||
err := s.Serve(":8554")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("server is ready")
|
|
||||||
|
|
||||||
// accept connections
|
|
||||||
for {
|
|
||||||
conn, err := s.Accept()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
go handleConn(conn)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -11,27 +11,43 @@ import (
|
|||||||
|
|
||||||
// This example shows how to
|
// This example shows how to
|
||||||
// 1. create a RTSP server which accepts plain connections
|
// 1. create a RTSP server which accepts plain connections
|
||||||
// 2. allow a single client to publish a stream with TCP
|
// 2. allow a single client to publish a stream with TCP or UDP
|
||||||
// 3. allow multiple clients to read that stream with TCP
|
// 3. allow multiple clients to read that stream with TCP or UDP
|
||||||
|
|
||||||
var mutex sync.Mutex
|
type serverHandler struct {
|
||||||
var publisher *gortsplib.ServerConn
|
mutex sync.Mutex
|
||||||
var readers = make(map[*gortsplib.ServerConn]struct{})
|
publisher *gortsplib.ServerConn
|
||||||
var sdp []byte
|
readers map[*gortsplib.ServerConn]struct{}
|
||||||
|
sdp []byte
|
||||||
|
}
|
||||||
|
|
||||||
// this is called for each incoming connection
|
// called when a connection is opened.
|
||||||
func handleConn(conn *gortsplib.ServerConn) {
|
func (sh *serverHandler) OnConnOpen(sc *gortsplib.ServerConn) {
|
||||||
defer conn.Close()
|
log.Printf("conn opened")
|
||||||
|
}
|
||||||
|
|
||||||
log.Printf("client connected")
|
// called when a connection is closed.
|
||||||
|
func (sh *serverHandler) OnConnClose(sc *gortsplib.ServerConn, err error) {
|
||||||
|
log.Println("conn closed (%v)", err)
|
||||||
|
|
||||||
// called after receiving a DESCRIBE request.
|
sh.mutex.Lock()
|
||||||
onDescribe := func(ctx *gortsplib.ServerConnDescribeCtx) (*base.Response, []byte, error) {
|
defer sh.mutex.Unlock()
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
if sc == sh.publisher {
|
||||||
|
sh.publisher = nil
|
||||||
|
sh.sdp = nil
|
||||||
|
} else {
|
||||||
|
delete(sh.readers, sc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// called after receiving a DESCRIBE request.
|
||||||
|
func (sh *serverHandler) OnDescribe(ctx *gortsplib.ServerHandlerOnDescribeCtx) (*base.Response, []byte, error) {
|
||||||
|
sh.mutex.Lock()
|
||||||
|
defer sh.mutex.Unlock()
|
||||||
|
|
||||||
// no one is publishing yet
|
// no one is publishing yet
|
||||||
if publisher == nil {
|
if sh.publisher == nil {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusNotFound,
|
StatusCode: base.StatusNotFound,
|
||||||
}, nil, nil
|
}, nil, nil
|
||||||
@@ -39,22 +55,22 @@ func handleConn(conn *gortsplib.ServerConn) {
|
|||||||
|
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, sdp, nil
|
}, sh.sdp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// called after receiving an ANNOUNCE request.
|
// called after receiving an ANNOUNCE request.
|
||||||
onAnnounce := func(ctx *gortsplib.ServerConnAnnounceCtx) (*base.Response, error) {
|
func (sh *serverHandler) OnAnnounce(ctx *gortsplib.ServerHandlerOnAnnounceCtx) (*base.Response, error) {
|
||||||
mutex.Lock()
|
sh.mutex.Lock()
|
||||||
defer mutex.Unlock()
|
defer sh.mutex.Unlock()
|
||||||
|
|
||||||
if publisher != nil {
|
if sh.publisher != nil {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusBadRequest,
|
StatusCode: base.StatusBadRequest,
|
||||||
}, fmt.Errorf("someone is already publishing")
|
}, fmt.Errorf("someone is already publishing")
|
||||||
}
|
}
|
||||||
|
|
||||||
publisher = conn
|
sh.publisher = ctx.Conn
|
||||||
sdp = ctx.Tracks.Write()
|
sh.sdp = ctx.Tracks.Write()
|
||||||
|
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
@@ -62,24 +78,24 @@ func handleConn(conn *gortsplib.ServerConn) {
|
|||||||
"Session": base.HeaderValue{"12345678"},
|
"Session": base.HeaderValue{"12345678"},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// called after receiving a SETUP request.
|
// called after receiving a SETUP request.
|
||||||
onSetup := func(ctx *gortsplib.ServerConnSetupCtx) (*base.Response, error) {
|
func (sh *serverHandler) OnSetup(ctx *gortsplib.ServerHandlerOnSetupCtx) (*base.Response, error) {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
Header: base.Header{
|
Header: base.Header{
|
||||||
"Session": base.HeaderValue{"12345678"},
|
"Session": base.HeaderValue{"12345678"},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// called after receiving a PLAY request.
|
// called after receiving a PLAY request.
|
||||||
onPlay := func(ctx *gortsplib.ServerConnPlayCtx) (*base.Response, error) {
|
func (sh *serverHandler) OnPlay(ctx *gortsplib.ServerHandlerOnPlayCtx) (*base.Response, error) {
|
||||||
mutex.Lock()
|
sh.mutex.Lock()
|
||||||
defer mutex.Unlock()
|
defer sh.mutex.Unlock()
|
||||||
|
|
||||||
readers[conn] = struct{}{}
|
sh.readers[ctx.Conn] = struct{}{}
|
||||||
|
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
@@ -87,14 +103,14 @@ func handleConn(conn *gortsplib.ServerConn) {
|
|||||||
"Session": base.HeaderValue{"12345678"},
|
"Session": base.HeaderValue{"12345678"},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// called after receiving a RECORD request.
|
// called after receiving a RECORD request.
|
||||||
onRecord := func(ctx *gortsplib.ServerConnRecordCtx) (*base.Response, error) {
|
func (sh *serverHandler) OnRecord(ctx *gortsplib.ServerHandlerOnRecordCtx) (*base.Response, error) {
|
||||||
mutex.Lock()
|
sh.mutex.Lock()
|
||||||
defer mutex.Unlock()
|
defer sh.mutex.Unlock()
|
||||||
|
|
||||||
if conn != publisher {
|
if ctx.Conn != sh.publisher {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusBadRequest,
|
StatusCode: base.StatusBadRequest,
|
||||||
}, fmt.Errorf("someone is already publishing")
|
}, fmt.Errorf("someone is already publishing")
|
||||||
@@ -106,59 +122,29 @@ func handleConn(conn *gortsplib.ServerConn) {
|
|||||||
"Session": base.HeaderValue{"12345678"},
|
"Session": base.HeaderValue{"12345678"},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// called after receiving a frame.
|
// called after receiving a frame.
|
||||||
onFrame := func(trackID int, typ gortsplib.StreamType, buf []byte) {
|
func (sh *serverHandler) OnFrame(ctx *gortsplib.ServerHandlerOnFrameCtx) {
|
||||||
mutex.Lock()
|
sh.mutex.Lock()
|
||||||
defer mutex.Unlock()
|
defer sh.mutex.Unlock()
|
||||||
|
|
||||||
// if we are the publisher, route frames to readers
|
// if we are the publisher, route frames to readers
|
||||||
if conn == publisher {
|
if ctx.Conn == sh.publisher {
|
||||||
for r := range readers {
|
for r := range sh.readers {
|
||||||
r.WriteFrame(trackID, typ, buf)
|
r.WriteFrame(ctx.TrackID, ctx.StreamType, ctx.Payload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
err := <-conn.Read(gortsplib.ServerConnReadHandlers{
|
|
||||||
OnDescribe: onDescribe,
|
|
||||||
OnAnnounce: onAnnounce,
|
|
||||||
OnSetup: onSetup,
|
|
||||||
OnPlay: onPlay,
|
|
||||||
OnRecord: onRecord,
|
|
||||||
OnFrame: onFrame,
|
|
||||||
})
|
|
||||||
log.Printf("client disconnected (%s)", err)
|
|
||||||
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
|
|
||||||
if conn == publisher {
|
|
||||||
publisher = nil
|
|
||||||
sdp = nil
|
|
||||||
} else {
|
|
||||||
delete(readers, conn)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// create server
|
// configure server
|
||||||
s := &gortsplib.Server{}
|
s := &gortsplib.Server{
|
||||||
err := s.Serve(":8554")
|
Handler: &serverHandler{},
|
||||||
if err != nil {
|
UDPRTPAddress: ":8000",
|
||||||
panic(err)
|
UDPRTCPAddress: ":8001",
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("server is ready")
|
// start server and wait until a fatal error
|
||||||
|
panic(s.StartAndWait(":8554"))
|
||||||
// accept connections
|
|
||||||
for {
|
|
||||||
conn, err := s.Accept()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
go handleConn(conn)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
141
server.go
141
server.go
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -24,6 +25,9 @@ func extractPort(address string) (int, error) {
|
|||||||
|
|
||||||
// Server is a RTSP server.
|
// Server is a RTSP server.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
|
// an handler to handle requests.
|
||||||
|
Handler ServerHandler
|
||||||
|
|
||||||
// a TLS configuration to accept TLS (RTSPS) connections.
|
// a TLS configuration to accept TLS (RTSPS) connections.
|
||||||
TLSConfig *tls.Config
|
TLSConfig *tls.Config
|
||||||
|
|
||||||
@@ -65,10 +69,19 @@ type Server struct {
|
|||||||
tcpListener net.Listener
|
tcpListener net.Listener
|
||||||
udpRTPListener *serverUDPListener
|
udpRTPListener *serverUDPListener
|
||||||
udpRTCPListener *serverUDPListener
|
udpRTCPListener *serverUDPListener
|
||||||
|
conns map[*ServerConn]struct{}
|
||||||
|
exitError error
|
||||||
|
|
||||||
|
// in
|
||||||
|
connClose chan *ServerConn
|
||||||
|
terminate chan struct{}
|
||||||
|
|
||||||
|
// out
|
||||||
|
done chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve starts listening on the given address.
|
// Start starts listening on the given address.
|
||||||
func (s *Server) Serve(address string) error {
|
func (s *Server) Start(address string) error {
|
||||||
if s.ReadTimeout == 0 {
|
if s.ReadTimeout == 0 {
|
||||||
s.ReadTimeout = 10 * time.Second
|
s.ReadTimeout = 10 * time.Second
|
||||||
}
|
}
|
||||||
@@ -125,6 +138,7 @@ func (s *Server) Serve(address string) error {
|
|||||||
|
|
||||||
s.udpRTCPListener, err = newServerUDPListener(s, s.UDPRTCPAddress, StreamTypeRTCP)
|
s.udpRTCPListener, err = newServerUDPListener(s, s.UDPRTCPAddress, StreamTypeRTCP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
s.udpRTPListener.close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,33 +146,132 @@ func (s *Server) Serve(address string) error {
|
|||||||
var err error
|
var err error
|
||||||
s.tcpListener, err = s.Listen("tcp", address)
|
s.tcpListener, err = s.Listen("tcp", address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
s.udpRTPListener.close()
|
||||||
|
s.udpRTPListener.close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.terminate = make(chan struct{})
|
||||||
|
s.done = make(chan struct{})
|
||||||
|
|
||||||
|
go s.run()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accept accepts a connection.
|
func (s *Server) run() {
|
||||||
func (s *Server) Accept() (*ServerConn, error) {
|
s.conns = make(map[*ServerConn]struct{})
|
||||||
|
s.connClose = make(chan *ServerConn)
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
connNew := make(chan net.Conn)
|
||||||
|
acceptErr := make(chan error)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
acceptErr <- func() error {
|
||||||
|
for {
|
||||||
nconn, err := s.tcpListener.Accept()
|
nconn, err := s.tcpListener.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return newServerConn(s, nconn), nil
|
connNew <- nconn
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes all the server resources.
|
|
||||||
func (s *Server) Close() error {
|
|
||||||
s.tcpListener.Close()
|
|
||||||
|
|
||||||
if s.udpRTPListener != nil {
|
|
||||||
s.udpRTPListener.close()
|
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
|
}()
|
||||||
|
|
||||||
|
outer:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case err := <-acceptErr:
|
||||||
|
s.exitError = err
|
||||||
|
break outer
|
||||||
|
|
||||||
|
case nconn := <-connNew:
|
||||||
|
sc := newServerConn(s, &wg, nconn)
|
||||||
|
s.conns[sc] = struct{}{}
|
||||||
|
|
||||||
|
case sc := <-s.connClose:
|
||||||
|
if _, ok := s.conns[sc]; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s.doConnClose(sc)
|
||||||
|
|
||||||
|
case <-s.terminate:
|
||||||
|
break outer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case _, ok := <-acceptErr:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case nconn, ok := <-connNew:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nconn.Close()
|
||||||
|
|
||||||
|
case _, ok := <-s.connClose:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
if s.udpRTCPListener != nil {
|
if s.udpRTCPListener != nil {
|
||||||
s.udpRTCPListener.close()
|
s.udpRTCPListener.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.udpRTPListener != nil {
|
||||||
|
s.udpRTPListener.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
s.tcpListener.Close()
|
||||||
|
|
||||||
|
for sc := range s.conns {
|
||||||
|
s.doConnClose(sc)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
close(acceptErr)
|
||||||
|
close(connNew)
|
||||||
|
close(s.connClose)
|
||||||
|
close(s.done)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes all the server resources and waits for the server to exit.
|
||||||
|
func (s *Server) Close() error {
|
||||||
|
close(s.terminate)
|
||||||
|
<-s.done
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait waits until a fatal error.
|
||||||
|
func (s *Server) Wait() error {
|
||||||
|
<-s.done
|
||||||
|
return s.exitError
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartAndWait starts the server and waits until a fatal error.
|
||||||
|
func (s *Server) StartAndWait(address string) error {
|
||||||
|
err := s.Start(address)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) doConnClose(sc *ServerConn) {
|
||||||
|
delete(s.conns, sc)
|
||||||
|
close(sc.terminate)
|
||||||
|
}
|
||||||
|
@@ -13,201 +13,70 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/aler9/gortsplib/pkg/base"
|
"github.com/aler9/gortsplib/pkg/base"
|
||||||
"github.com/aler9/gortsplib/pkg/liberrors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type testServ struct {
|
type testServerHandler struct {
|
||||||
s *Server
|
onConnClose func(*ServerConn, error)
|
||||||
wg sync.WaitGroup
|
onDescribe func(*ServerHandlerOnDescribeCtx) (*base.Response, []byte, error)
|
||||||
mutex sync.Mutex
|
onAnnounce func(*ServerHandlerOnAnnounceCtx) (*base.Response, error)
|
||||||
publisher *ServerConn
|
onSetup func(*ServerHandlerOnSetupCtx) (*base.Response, error)
|
||||||
sdp []byte
|
onPlay func(*ServerHandlerOnPlayCtx) (*base.Response, error)
|
||||||
readers map[*ServerConn]struct{}
|
onRecord func(*ServerHandlerOnRecordCtx) (*base.Response, error)
|
||||||
|
onPause func(*ServerHandlerOnPauseCtx) (*base.Response, error)
|
||||||
|
onFrame func(*ServerHandlerOnFrameCtx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestServ(tlsConf *tls.Config) (*testServ, error) {
|
func (sh *testServerHandler) OnConnClose(sc *ServerConn, err error) {
|
||||||
s := &Server{}
|
if sh.onConnClose != nil {
|
||||||
if tlsConf != nil {
|
sh.onConnClose(sc, err)
|
||||||
s.TLSConfig = tlsConf
|
|
||||||
} else {
|
|
||||||
s.UDPRTPAddress = "127.0.0.1:8000"
|
|
||||||
s.UDPRTCPAddress = "127.0.0.1:8001"
|
|
||||||
}
|
|
||||||
|
|
||||||
err := s.Serve("127.0.0.1:8554")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ts := &testServ{
|
|
||||||
s: s,
|
|
||||||
readers: make(map[*ServerConn]struct{}),
|
|
||||||
}
|
|
||||||
|
|
||||||
ts.wg.Add(1)
|
|
||||||
go ts.run()
|
|
||||||
|
|
||||||
return ts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ts *testServ) close() {
|
|
||||||
ts.s.Close()
|
|
||||||
ts.wg.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ts *testServ) run() {
|
|
||||||
defer ts.wg.Done()
|
|
||||||
|
|
||||||
for {
|
|
||||||
conn, err := ts.s.Accept()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ts.wg.Add(1)
|
|
||||||
go ts.handleConn(conn)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts *testServ) handleConn(conn *ServerConn) {
|
func (sh *testServerHandler) OnDescribe(ctx *ServerHandlerOnDescribeCtx) (*base.Response, []byte, error) {
|
||||||
defer ts.wg.Done()
|
if sh.onDescribe != nil {
|
||||||
defer conn.Close()
|
return sh.onDescribe(ctx)
|
||||||
|
|
||||||
onDescribe := func(ctx *ServerConnDescribeCtx) (*base.Response, []byte, error) {
|
|
||||||
if ctx.Path != "teststream" {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusBadRequest,
|
|
||||||
}, nil, fmt.Errorf("invalid path (%s)", ctx.Req.URL)
|
|
||||||
}
|
}
|
||||||
|
return nil, nil, fmt.Errorf("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
ts.mutex.Lock()
|
func (sh *testServerHandler) OnAnnounce(ctx *ServerHandlerOnAnnounceCtx) (*base.Response, error) {
|
||||||
defer ts.mutex.Unlock()
|
if sh.onAnnounce != nil {
|
||||||
|
return sh.onAnnounce(ctx)
|
||||||
if ts.publisher == nil {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusNotFound,
|
|
||||||
}, nil, nil
|
|
||||||
}
|
}
|
||||||
|
return nil, fmt.Errorf("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
return &base.Response{
|
func (sh *testServerHandler) OnSetup(ctx *ServerHandlerOnSetupCtx) (*base.Response, error) {
|
||||||
StatusCode: base.StatusOK,
|
if sh.onSetup != nil {
|
||||||
}, ts.sdp, nil
|
return sh.onSetup(ctx)
|
||||||
}
|
}
|
||||||
|
return nil, fmt.Errorf("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
onAnnounce := func(ctx *ServerConnAnnounceCtx) (*base.Response, error) {
|
func (sh *testServerHandler) OnPlay(ctx *ServerHandlerOnPlayCtx) (*base.Response, error) {
|
||||||
if ctx.Path != "teststream" {
|
if sh.onPlay != nil {
|
||||||
return &base.Response{
|
return sh.onPlay(ctx)
|
||||||
StatusCode: base.StatusBadRequest,
|
|
||||||
}, fmt.Errorf("invalid path (%s)", ctx.Req.URL)
|
|
||||||
}
|
}
|
||||||
|
return nil, fmt.Errorf("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
ts.mutex.Lock()
|
func (sh *testServerHandler) OnRecord(ctx *ServerHandlerOnRecordCtx) (*base.Response, error) {
|
||||||
defer ts.mutex.Unlock()
|
if sh.onRecord != nil {
|
||||||
|
return sh.onRecord(ctx)
|
||||||
if ts.publisher != nil {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusBadRequest,
|
|
||||||
}, fmt.Errorf("someone is already publishing")
|
|
||||||
}
|
}
|
||||||
|
return nil, fmt.Errorf("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
ts.publisher = conn
|
func (sh *testServerHandler) OnPause(ctx *ServerHandlerOnPauseCtx) (*base.Response, error) {
|
||||||
ts.sdp = ctx.Tracks.Write()
|
if sh.onPause != nil {
|
||||||
|
return sh.onPause(ctx)
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
Header: base.Header{
|
|
||||||
"Session": base.HeaderValue{"12345678"},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
return nil, fmt.Errorf("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
onSetup := func(ctx *ServerConnSetupCtx) (*base.Response, error) {
|
func (sh *testServerHandler) OnFrame(ctx *ServerHandlerOnFrameCtx) {
|
||||||
if ctx.Path != "teststream" {
|
if sh.onFrame != nil {
|
||||||
return &base.Response{
|
sh.onFrame(ctx)
|
||||||
StatusCode: base.StatusBadRequest,
|
|
||||||
}, fmt.Errorf("invalid path (%s)", ctx.Req.URL)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
Header: base.Header{
|
|
||||||
"Session": base.HeaderValue{"12345678"},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
onPlay := func(ctx *ServerConnPlayCtx) (*base.Response, error) {
|
|
||||||
if ctx.Path != "teststream" {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusBadRequest,
|
|
||||||
}, fmt.Errorf("invalid path (%s)", ctx.Req.URL)
|
|
||||||
}
|
|
||||||
|
|
||||||
ts.mutex.Lock()
|
|
||||||
defer ts.mutex.Unlock()
|
|
||||||
|
|
||||||
ts.readers[conn] = struct{}{}
|
|
||||||
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
Header: base.Header{
|
|
||||||
"Session": base.HeaderValue{"12345678"},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
onRecord := func(ctx *ServerConnRecordCtx) (*base.Response, error) {
|
|
||||||
if ctx.Path != "teststream" {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusBadRequest,
|
|
||||||
}, fmt.Errorf("invalid path (%s)", ctx.Req.URL)
|
|
||||||
}
|
|
||||||
|
|
||||||
ts.mutex.Lock()
|
|
||||||
defer ts.mutex.Unlock()
|
|
||||||
|
|
||||||
if conn != ts.publisher {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusBadRequest,
|
|
||||||
}, fmt.Errorf("someone is already publishing")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
Header: base.Header{
|
|
||||||
"Session": base.HeaderValue{"12345678"},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
onFrame := func(trackID int, typ StreamType, buf []byte) {
|
|
||||||
ts.mutex.Lock()
|
|
||||||
defer ts.mutex.Unlock()
|
|
||||||
|
|
||||||
if conn == ts.publisher {
|
|
||||||
for r := range ts.readers {
|
|
||||||
r.WriteFrame(trackID, typ, buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<-conn.Read(ServerConnReadHandlers{
|
|
||||||
OnDescribe: onDescribe,
|
|
||||||
OnAnnounce: onAnnounce,
|
|
||||||
OnSetup: onSetup,
|
|
||||||
OnPlay: onPlay,
|
|
||||||
OnRecord: onRecord,
|
|
||||||
OnFrame: onFrame,
|
|
||||||
})
|
|
||||||
|
|
||||||
ts.mutex.Lock()
|
|
||||||
defer ts.mutex.Unlock()
|
|
||||||
|
|
||||||
if conn == ts.publisher {
|
|
||||||
ts.publisher = nil
|
|
||||||
ts.sdp = nil
|
|
||||||
} else {
|
|
||||||
delete(ts.readers, conn)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,22 +167,156 @@ func TestServerHighLevelPublishRead(t *testing.T) {
|
|||||||
|
|
||||||
t.Run(encryptedStr+"_"+ca.publisherSoft+"_"+ca.publisherProto+"_"+
|
t.Run(encryptedStr+"_"+ca.publisherSoft+"_"+ca.publisherProto+"_"+
|
||||||
ca.readerSoft+"_"+ca.readerProto, func(t *testing.T) {
|
ca.readerSoft+"_"+ca.readerProto, func(t *testing.T) {
|
||||||
|
|
||||||
|
var mutex sync.Mutex
|
||||||
|
var publisher *ServerConn
|
||||||
|
var sdp []byte
|
||||||
|
readers := make(map[*ServerConn]struct{})
|
||||||
|
|
||||||
|
s := &Server{
|
||||||
|
Handler: &testServerHandler{
|
||||||
|
onConnClose: func(sc *ServerConn, err error) {
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
|
||||||
|
if sc == publisher {
|
||||||
|
publisher = nil
|
||||||
|
sdp = nil
|
||||||
|
} else {
|
||||||
|
delete(readers, sc)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, []byte, error) {
|
||||||
|
if ctx.Path != "teststream" {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusBadRequest,
|
||||||
|
}, nil, fmt.Errorf("invalid path (%s)", ctx.Req.URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
|
||||||
|
if publisher == nil {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusNotFound,
|
||||||
|
}, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, sdp, nil
|
||||||
|
},
|
||||||
|
onAnnounce: func(ctx *ServerHandlerOnAnnounceCtx) (*base.Response, error) {
|
||||||
|
if ctx.Path != "teststream" {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusBadRequest,
|
||||||
|
}, fmt.Errorf("invalid path (%s)", ctx.Req.URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
|
||||||
|
if publisher != nil {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusBadRequest,
|
||||||
|
}, fmt.Errorf("someone is already publishing")
|
||||||
|
}
|
||||||
|
|
||||||
|
publisher = ctx.Conn
|
||||||
|
sdp = ctx.Tracks.Write()
|
||||||
|
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
Header: base.Header{
|
||||||
|
"Session": base.HeaderValue{"12345678"},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, error) {
|
||||||
|
if ctx.Path != "teststream" {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusBadRequest,
|
||||||
|
}, fmt.Errorf("invalid path (%s)", ctx.Req.URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
Header: base.Header{
|
||||||
|
"Session": base.HeaderValue{"12345678"},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
onPlay: func(ctx *ServerHandlerOnPlayCtx) (*base.Response, error) {
|
||||||
|
if ctx.Path != "teststream" {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusBadRequest,
|
||||||
|
}, fmt.Errorf("invalid path (%s)", ctx.Req.URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
|
||||||
|
readers[ctx.Conn] = struct{}{}
|
||||||
|
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
Header: base.Header{
|
||||||
|
"Session": base.HeaderValue{"12345678"},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
onRecord: func(ctx *ServerHandlerOnRecordCtx) (*base.Response, error) {
|
||||||
|
if ctx.Path != "teststream" {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusBadRequest,
|
||||||
|
}, fmt.Errorf("invalid path (%s)", ctx.Req.URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
|
||||||
|
if ctx.Conn != publisher {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusBadRequest,
|
||||||
|
}, fmt.Errorf("someone is already publishing")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
Header: base.Header{
|
||||||
|
"Session": base.HeaderValue{"12345678"},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
onFrame: func(ctx *ServerHandlerOnFrameCtx) {
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
|
||||||
|
if ctx.Conn == publisher {
|
||||||
|
for r := range readers {
|
||||||
|
r.WriteFrame(ctx.TrackID, ctx.StreamType, ctx.Payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
var proto string
|
var proto string
|
||||||
var tlsConf *tls.Config
|
|
||||||
if !ca.encrypted {
|
if !ca.encrypted {
|
||||||
proto = "rtsp"
|
proto = "rtsp"
|
||||||
tlsConf = nil
|
s.UDPRTPAddress = "127.0.0.1:8000"
|
||||||
|
s.UDPRTCPAddress = "127.0.0.1:8001"
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
proto = "rtsps"
|
proto = "rtsps"
|
||||||
cert, err := tls.X509KeyPair(serverCert, serverKey)
|
cert, err := tls.X509KeyPair(serverCert, serverKey)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
tlsConf = &tls.Config{Certificates: []tls.Certificate{cert}}
|
s.TLSConfig = &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||||
}
|
}
|
||||||
|
|
||||||
ts, err := newTestServ(tlsConf)
|
err := s.Start("127.0.0.1:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer ts.close()
|
defer s.Close()
|
||||||
|
|
||||||
switch ca.publisherSoft {
|
switch ca.publisherSoft {
|
||||||
case "ffmpeg":
|
case "ffmpeg":
|
||||||
@@ -374,7 +377,7 @@ func TestServerErrorWrongUDPPorts(t *testing.T) {
|
|||||||
UDPRTPAddress: "127.0.0.1:8006",
|
UDPRTPAddress: "127.0.0.1:8006",
|
||||||
UDPRTCPAddress: "127.0.0.1:8009",
|
UDPRTCPAddress: "127.0.0.1:8009",
|
||||||
}
|
}
|
||||||
err := s.Serve("127.0.0.1:8554")
|
err := s.Start("127.0.0.1:8554")
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -383,29 +386,17 @@ func TestServerErrorWrongUDPPorts(t *testing.T) {
|
|||||||
UDPRTPAddress: "127.0.0.1:8003",
|
UDPRTPAddress: "127.0.0.1:8003",
|
||||||
UDPRTCPAddress: "127.0.0.1:8004",
|
UDPRTCPAddress: "127.0.0.1:8004",
|
||||||
}
|
}
|
||||||
err := s.Serve("127.0.0.1:8554")
|
err := s.Start("127.0.0.1:8554")
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServerCSeq(t *testing.T) {
|
func TestServerCSeq(t *testing.T) {
|
||||||
s := &Server{}
|
s := &Server{}
|
||||||
err := s.Serve("127.0.0.1:8554")
|
err := s.Start("127.0.0.1:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer s.Close()
|
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()
|
|
||||||
|
|
||||||
<-conn.Read(ServerConnReadHandlers{})
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "localhost:8554")
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
@@ -429,24 +420,17 @@ func TestServerCSeq(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestServerErrorCSeqMissing(t *testing.T) {
|
func TestServerErrorCSeqMissing(t *testing.T) {
|
||||||
s := &Server{}
|
h := &testServerHandler{
|
||||||
err := s.Serve("127.0.0.1:8554")
|
onConnClose: func(sc *ServerConn, err error) {
|
||||||
|
require.Equal(t, "CSeq is missing", err.Error())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
s := &Server{Handler: h}
|
||||||
|
err := s.Start("127.0.0.1:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer s.Close()
|
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()
|
|
||||||
|
|
||||||
err = <-conn.Read(ServerConnReadHandlers{})
|
|
||||||
require.Equal(t, "CSeq is missing", err.Error())
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "localhost:8554")
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
@@ -467,24 +451,10 @@ func TestServerErrorCSeqMissing(t *testing.T) {
|
|||||||
|
|
||||||
func TestServerTeardownResponse(t *testing.T) {
|
func TestServerTeardownResponse(t *testing.T) {
|
||||||
s := &Server{}
|
s := &Server{}
|
||||||
err := s.Serve("127.0.0.1:8554")
|
err := s.Start("127.0.0.1:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer s.Close()
|
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()
|
|
||||||
|
|
||||||
err = <-conn.Read(ServerConnReadHandlers{})
|
|
||||||
_, ok := err.(liberrors.ErrServerTeardown)
|
|
||||||
require.Equal(t, true, ok)
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "localhost:8554")
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer conn.Close()
|
defer conn.Close()
|
568
serverconn.go
568
serverconn.go
@@ -7,6 +7,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -85,7 +86,20 @@ func setupGetTrackIDPathQuery(url *base.URL,
|
|||||||
return 0, "", "", fmt.Errorf("invalid track path (%s)", pathAndQuery)
|
return 0, "", "", fmt.Errorf("invalid track path (%s)", pathAndQuery)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerConnState is the state of the connection.
|
// ServerConnSetuppedTrack is a setupped track of a ServerConn.
|
||||||
|
type ServerConnSetuppedTrack struct {
|
||||||
|
udpRTPPort int
|
||||||
|
udpRTCPPort int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerConnAnnouncedTrack is an announced track of a ServerConn.
|
||||||
|
type ServerConnAnnouncedTrack struct {
|
||||||
|
track *Track
|
||||||
|
rtcpReceiver *rtcpreceiver.RTCPReceiver
|
||||||
|
udpLastFrameTime *int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerConnState is a state of a ServerConn.
|
||||||
type ServerConnState int
|
type ServerConnState int
|
||||||
|
|
||||||
// standard states.
|
// standard states.
|
||||||
@@ -114,142 +128,10 @@ func (s ServerConnState) String() string {
|
|||||||
return "unknown"
|
return "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerConnSetuppedTrack is a setupped track of a ServerConn.
|
|
||||||
type ServerConnSetuppedTrack struct {
|
|
||||||
udpRTPPort int
|
|
||||||
udpRTCPPort int
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerConnAnnouncedTrack is an announced track of a ServerConn.
|
|
||||||
type ServerConnAnnouncedTrack struct {
|
|
||||||
track *Track
|
|
||||||
rtcpReceiver *rtcpreceiver.RTCPReceiver
|
|
||||||
udpLastFrameTime *int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerConnOptionsCtx is the context of a OPTIONS request.
|
|
||||||
type ServerConnOptionsCtx struct {
|
|
||||||
Req *base.Request
|
|
||||||
Path string
|
|
||||||
Query string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerConnDescribeCtx is the context of a DESCRIBE request.
|
|
||||||
type ServerConnDescribeCtx struct {
|
|
||||||
Req *base.Request
|
|
||||||
Path string
|
|
||||||
Query string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerConnAnnounceCtx is the context of a ANNOUNCE request.
|
|
||||||
type ServerConnAnnounceCtx struct {
|
|
||||||
Req *base.Request
|
|
||||||
Path string
|
|
||||||
Query string
|
|
||||||
Tracks Tracks
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerConnSetupCtx is the context of a OPTIONS request.
|
|
||||||
type ServerConnSetupCtx struct {
|
|
||||||
Req *base.Request
|
|
||||||
Path string
|
|
||||||
Query string
|
|
||||||
TrackID int
|
|
||||||
Transport *headers.Transport
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerConnPlayCtx is the context of a PLAY request.
|
|
||||||
type ServerConnPlayCtx struct {
|
|
||||||
Req *base.Request
|
|
||||||
Path string
|
|
||||||
Query string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerConnRecordCtx is the context of a RECORD request.
|
|
||||||
type ServerConnRecordCtx struct {
|
|
||||||
Req *base.Request
|
|
||||||
Path string
|
|
||||||
Query string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerConnPauseCtx is the context of a PAUSE request.
|
|
||||||
type ServerConnPauseCtx struct {
|
|
||||||
Req *base.Request
|
|
||||||
Path string
|
|
||||||
Query string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerConnGetParameterCtx is the context of a GET_PARAMETER request.
|
|
||||||
type ServerConnGetParameterCtx struct {
|
|
||||||
Req *base.Request
|
|
||||||
Path string
|
|
||||||
Query string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerConnSetParameterCtx is the context of a SET_PARAMETER request.
|
|
||||||
type ServerConnSetParameterCtx struct {
|
|
||||||
Req *base.Request
|
|
||||||
Path string
|
|
||||||
Query string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerConnTeardownCtx is the context of a TEARDOWN request.
|
|
||||||
type ServerConnTeardownCtx struct {
|
|
||||||
Req *base.Request
|
|
||||||
Path string
|
|
||||||
Query string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerConnReadHandlers allows to set the handlers required by ServerConn.Read.
|
|
||||||
// all fields are optional.
|
|
||||||
type ServerConnReadHandlers struct {
|
|
||||||
// called after receiving any request.
|
|
||||||
OnRequest func(req *base.Request)
|
|
||||||
|
|
||||||
// called before sending any response.
|
|
||||||
OnResponse func(res *base.Response)
|
|
||||||
|
|
||||||
// called after receiving a OPTIONS request.
|
|
||||||
// if nil, it is generated automatically.
|
|
||||||
OnOptions func(ctx *ServerConnOptionsCtx) (*base.Response, error)
|
|
||||||
|
|
||||||
// called after receiving a DESCRIBE request.
|
|
||||||
// the 2nd return value is a SDP, that is inserted into the response.
|
|
||||||
OnDescribe func(ctx *ServerConnDescribeCtx) (*base.Response, []byte, error)
|
|
||||||
|
|
||||||
// called after receiving an ANNOUNCE request.
|
|
||||||
OnAnnounce func(ctx *ServerConnAnnounceCtx) (*base.Response, error)
|
|
||||||
|
|
||||||
// called after receiving a SETUP request.
|
|
||||||
OnSetup func(ctx *ServerConnSetupCtx) (*base.Response, error)
|
|
||||||
|
|
||||||
// called after receiving a PLAY request.
|
|
||||||
OnPlay func(ctx *ServerConnPlayCtx) (*base.Response, error)
|
|
||||||
|
|
||||||
// called after receiving a RECORD request.
|
|
||||||
OnRecord func(ctx *ServerConnRecordCtx) (*base.Response, error)
|
|
||||||
|
|
||||||
// called after receiving a PAUSE request.
|
|
||||||
OnPause func(ctx *ServerConnPauseCtx) (*base.Response, error)
|
|
||||||
|
|
||||||
// called after receiving a GET_PARAMETER request.
|
|
||||||
// if nil, it is generated automatically.
|
|
||||||
OnGetParameter func(ctx *ServerConnGetParameterCtx) (*base.Response, error)
|
|
||||||
|
|
||||||
// called after receiving a SET_PARAMETER request.
|
|
||||||
OnSetParameter func(ctx *ServerConnSetParameterCtx) (*base.Response, error)
|
|
||||||
|
|
||||||
// called after receiving a TEARDOWN request.
|
|
||||||
// if nil, it is generated automatically.
|
|
||||||
OnTeardown func(ctx *ServerConnTeardownCtx) (*base.Response, error)
|
|
||||||
|
|
||||||
// called after receiving a frame.
|
|
||||||
OnFrame func(trackID int, streamType StreamType, payload []byte)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerConn is a server-side RTSP connection.
|
// ServerConn is a server-side RTSP connection.
|
||||||
type ServerConn struct {
|
type ServerConn struct {
|
||||||
s *Server
|
s *Server
|
||||||
|
wg *sync.WaitGroup
|
||||||
nconn net.Conn
|
nconn net.Conn
|
||||||
br *bufio.Reader
|
br *bufio.Reader
|
||||||
bw *bufio.Writer
|
bw *bufio.Writer
|
||||||
@@ -267,9 +149,6 @@ type ServerConn struct {
|
|||||||
tcpFrameWriteBuffer *ringbuffer.RingBuffer
|
tcpFrameWriteBuffer *ringbuffer.RingBuffer
|
||||||
tcpBackgroundWriteDone chan struct{}
|
tcpBackgroundWriteDone chan struct{}
|
||||||
|
|
||||||
// read
|
|
||||||
readHandlers ServerConnReadHandlers
|
|
||||||
|
|
||||||
// publish
|
// publish
|
||||||
announcedTracks []ServerConnAnnouncedTrack
|
announcedTracks []ServerConnAnnouncedTrack
|
||||||
backgroundRecordTerminate chan struct{}
|
backgroundRecordTerminate chan struct{}
|
||||||
@@ -282,31 +161,20 @@ type ServerConn struct {
|
|||||||
|
|
||||||
func newServerConn(
|
func newServerConn(
|
||||||
s *Server,
|
s *Server,
|
||||||
|
wg *sync.WaitGroup,
|
||||||
nconn net.Conn) *ServerConn {
|
nconn net.Conn) *ServerConn {
|
||||||
conn := func() net.Conn {
|
|
||||||
if s.TLSConfig != nil {
|
|
||||||
return tls.Server(nconn, s.TLSConfig)
|
|
||||||
}
|
|
||||||
return nconn
|
|
||||||
}()
|
|
||||||
|
|
||||||
return &ServerConn{
|
sc := &ServerConn{
|
||||||
s: s,
|
s: s,
|
||||||
|
wg: wg,
|
||||||
nconn: nconn,
|
nconn: nconn,
|
||||||
br: bufio.NewReaderSize(conn, serverConnReadBufferSize),
|
|
||||||
bw: bufio.NewWriterSize(conn, serverConnWriteBufferSize),
|
|
||||||
// always instantiate to allow writing to it before Play()
|
|
||||||
tcpFrameWriteBuffer: ringbuffer.New(uint64(s.ReadBufferCount)),
|
|
||||||
tcpBackgroundWriteDone: make(chan struct{}),
|
|
||||||
terminate: make(chan struct{}),
|
terminate: make(chan struct{}),
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes all the connection resources.
|
wg.Add(1)
|
||||||
func (sc *ServerConn) Close() error {
|
go sc.run()
|
||||||
err := sc.nconn.Close()
|
|
||||||
close(sc.terminate)
|
return sc
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// State returns the state.
|
// State returns the state.
|
||||||
@@ -329,25 +197,17 @@ func (sc *ServerConn) AnnouncedTracks() []ServerConnAnnouncedTrack {
|
|||||||
return sc.announcedTracks
|
return sc.announcedTracks
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc *ServerConn) tcpBackgroundWrite() {
|
// NetConn returns the underlying net.Conn.
|
||||||
defer close(sc.tcpBackgroundWriteDone)
|
func (sc *ServerConn) NetConn() net.Conn {
|
||||||
|
return sc.nconn
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
func (sc *ServerConn) ip() net.IP {
|
||||||
what, ok := sc.tcpFrameWriteBuffer.Pull()
|
return sc.nconn.RemoteAddr().(*net.TCPAddr).IP
|
||||||
if !ok {
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch w := what.(type) {
|
func (sc *ServerConn) zone() string {
|
||||||
case *base.InterleavedFrame:
|
return sc.nconn.RemoteAddr().(*net.TCPAddr).Zone
|
||||||
sc.nconn.SetWriteDeadline(time.Now().Add(sc.s.WriteTimeout))
|
|
||||||
w.Write(sc.bw)
|
|
||||||
|
|
||||||
case *base.Response:
|
|
||||||
sc.nconn.SetWriteDeadline(time.Now().Add(sc.s.WriteTimeout))
|
|
||||||
w.Write(sc.bw)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc *ServerConn) checkState(allowed map[ServerConnState]struct{}) error {
|
func (sc *ServerConn) checkState(allowed map[ServerConnState]struct{}) error {
|
||||||
@@ -364,89 +224,46 @@ func (sc *ServerConn) checkState(allowed map[ServerConnState]struct{}) error {
|
|||||||
return liberrors.ErrServerWrongState{AllowedList: allowedList, State: sc.state}
|
return liberrors.ErrServerWrongState{AllowedList: allowedList, State: sc.state}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetConn returns the underlying net.Conn.
|
func (sc *ServerConn) run() {
|
||||||
func (sc *ServerConn) NetConn() net.Conn {
|
defer sc.wg.Done()
|
||||||
|
|
||||||
|
if h, ok := sc.s.Handler.(ServerHandlerOnConnOpen); ok {
|
||||||
|
h.OnConnOpen(sc)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn := func() net.Conn {
|
||||||
|
if sc.s.TLSConfig != nil {
|
||||||
|
return tls.Server(sc.nconn, sc.s.TLSConfig)
|
||||||
|
}
|
||||||
return sc.nconn
|
return sc.nconn
|
||||||
}
|
}()
|
||||||
|
|
||||||
func (sc *ServerConn) ip() net.IP {
|
sc.br = bufio.NewReaderSize(conn, serverConnReadBufferSize)
|
||||||
return sc.nconn.RemoteAddr().(*net.TCPAddr).IP
|
sc.bw = bufio.NewWriterSize(conn, serverConnWriteBufferSize)
|
||||||
}
|
|
||||||
|
|
||||||
func (sc *ServerConn) zone() string {
|
// instantiate always to allow writing to this conn before Play()
|
||||||
return sc.nconn.RemoteAddr().(*net.TCPAddr).Zone
|
sc.tcpFrameWriteBuffer = ringbuffer.New(uint64(sc.s.ReadBufferCount))
|
||||||
}
|
sc.tcpBackgroundWriteDone = make(chan struct{})
|
||||||
|
|
||||||
func (sc *ServerConn) frameModeEnable() {
|
readDone := make(chan error)
|
||||||
switch sc.state {
|
go func() {
|
||||||
case ServerConnStatePlay:
|
readDone <- sc.backgroundRead()
|
||||||
if *sc.setupProtocol == StreamProtocolTCP {
|
}()
|
||||||
sc.doEnableTCPFrame = true
|
|
||||||
} else {
|
var err error
|
||||||
// readers can send RTCP frames, they cannot sent RTP frames
|
select {
|
||||||
for trackID, track := range sc.setuppedTracks {
|
case err = <-readDone:
|
||||||
sc.s.udpRTCPListener.addClient(sc.ip(), track.udpRTCPPort, sc, trackID, false)
|
sc.nconn.Close()
|
||||||
}
|
sc.s.connClose <- sc
|
||||||
|
<-sc.terminate
|
||||||
|
|
||||||
|
case <-sc.terminate:
|
||||||
|
sc.nconn.Close()
|
||||||
|
err = <-readDone
|
||||||
}
|
}
|
||||||
|
|
||||||
case ServerConnStateRecord:
|
if h, ok := sc.s.Handler.(ServerHandlerOnConnClose); ok {
|
||||||
if *sc.setupProtocol == StreamProtocolTCP {
|
h.OnConnClose(sc, err)
|
||||||
sc.doEnableTCPFrame = true
|
|
||||||
sc.tcpFrameTimeout = true
|
|
||||||
|
|
||||||
} else {
|
|
||||||
for trackID, track := range sc.setuppedTracks {
|
|
||||||
sc.s.udpRTPListener.addClient(sc.ip(), track.udpRTPPort, sc, trackID, true)
|
|
||||||
sc.s.udpRTCPListener.addClient(sc.ip(), track.udpRTCPPort, sc, trackID, true)
|
|
||||||
|
|
||||||
// open the firewall by sending packets to the counterpart
|
|
||||||
sc.WriteFrame(trackID, StreamTypeRTP,
|
|
||||||
[]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
|
||||||
sc.WriteFrame(trackID, StreamTypeRTCP,
|
|
||||||
[]byte{0x80, 0xc9, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sc.backgroundRecordTerminate = make(chan struct{})
|
|
||||||
sc.backgroundRecordDone = make(chan struct{})
|
|
||||||
go sc.backgroundRecord()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sc *ServerConn) frameModeDisable() {
|
|
||||||
switch sc.state {
|
|
||||||
case ServerConnStatePlay:
|
|
||||||
if *sc.setupProtocol == StreamProtocolTCP {
|
|
||||||
sc.tcpFrameEnabled = false
|
|
||||||
sc.tcpFrameWriteBuffer.Close()
|
|
||||||
<-sc.tcpBackgroundWriteDone
|
|
||||||
sc.tcpFrameWriteBuffer.Reset()
|
|
||||||
|
|
||||||
} else {
|
|
||||||
for _, track := range sc.setuppedTracks {
|
|
||||||
sc.s.udpRTCPListener.removeClient(sc.ip(), track.udpRTCPPort)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case ServerConnStateRecord:
|
|
||||||
close(sc.backgroundRecordTerminate)
|
|
||||||
<-sc.backgroundRecordDone
|
|
||||||
|
|
||||||
if *sc.setupProtocol == StreamProtocolTCP {
|
|
||||||
sc.tcpFrameTimeout = false
|
|
||||||
sc.nconn.SetReadDeadline(time.Time{})
|
|
||||||
|
|
||||||
sc.tcpFrameEnabled = false
|
|
||||||
sc.tcpFrameWriteBuffer.Close()
|
|
||||||
<-sc.tcpBackgroundWriteDone
|
|
||||||
sc.tcpFrameWriteBuffer.Reset()
|
|
||||||
|
|
||||||
} else {
|
|
||||||
for _, track := range sc.setuppedTracks {
|
|
||||||
sc.s.udpRTPListener.removeClient(sc.ip(), track.udpRTPPort)
|
|
||||||
sc.s.udpRTCPListener.removeClient(sc.ip(), track.udpRTCPPort)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,13 +275,9 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
}, liberrors.ErrServerCSeqMissing{}
|
}, liberrors.ErrServerCSeqMissing{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if sc.readHandlers.OnRequest != nil {
|
|
||||||
sc.readHandlers.OnRequest(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch req.Method {
|
switch req.Method {
|
||||||
case base.Options:
|
case base.Options:
|
||||||
if sc.readHandlers.OnOptions != nil {
|
if h, ok := sc.s.Handler.(ServerHandlerOnOptions); ok {
|
||||||
pathAndQuery, ok := req.URL.RTSPPath()
|
pathAndQuery, ok := req.URL.RTSPPath()
|
||||||
if !ok {
|
if !ok {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
@@ -474,7 +287,8 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
|
|
||||||
path, query := base.PathSplitQuery(pathAndQuery)
|
path, query := base.PathSplitQuery(pathAndQuery)
|
||||||
|
|
||||||
return sc.readHandlers.OnOptions(&ServerConnOptionsCtx{
|
return h.OnOptions(&ServerHandlerOnOptionsCtx{
|
||||||
|
Conn: sc,
|
||||||
Req: req,
|
Req: req,
|
||||||
Path: path,
|
Path: path,
|
||||||
Query: query,
|
Query: query,
|
||||||
@@ -482,26 +296,26 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var methods []string
|
var methods []string
|
||||||
if sc.readHandlers.OnDescribe != nil {
|
if _, ok := sc.s.Handler.(ServerHandlerOnDescribe); ok {
|
||||||
methods = append(methods, string(base.Describe))
|
methods = append(methods, string(base.Describe))
|
||||||
}
|
}
|
||||||
if sc.readHandlers.OnAnnounce != nil {
|
if _, ok := sc.s.Handler.(ServerHandlerOnAnnounce); ok {
|
||||||
methods = append(methods, string(base.Announce))
|
methods = append(methods, string(base.Announce))
|
||||||
}
|
}
|
||||||
if sc.readHandlers.OnSetup != nil {
|
if _, ok := sc.s.Handler.(ServerHandlerOnSetup); ok {
|
||||||
methods = append(methods, string(base.Setup))
|
methods = append(methods, string(base.Setup))
|
||||||
}
|
}
|
||||||
if sc.readHandlers.OnPlay != nil {
|
if _, ok := sc.s.Handler.(ServerHandlerOnPlay); ok {
|
||||||
methods = append(methods, string(base.Play))
|
methods = append(methods, string(base.Play))
|
||||||
}
|
}
|
||||||
if sc.readHandlers.OnRecord != nil {
|
if _, ok := sc.s.Handler.(ServerHandlerOnRecord); ok {
|
||||||
methods = append(methods, string(base.Record))
|
methods = append(methods, string(base.Record))
|
||||||
}
|
}
|
||||||
if sc.readHandlers.OnPause != nil {
|
if _, ok := sc.s.Handler.(ServerHandlerOnPause); ok {
|
||||||
methods = append(methods, string(base.Pause))
|
methods = append(methods, string(base.Pause))
|
||||||
}
|
}
|
||||||
methods = append(methods, string(base.GetParameter))
|
methods = append(methods, string(base.GetParameter))
|
||||||
if sc.readHandlers.OnSetParameter != nil {
|
if _, ok := sc.s.Handler.(ServerHandlerOnSetParameter); ok {
|
||||||
methods = append(methods, string(base.SetParameter))
|
methods = append(methods, string(base.SetParameter))
|
||||||
}
|
}
|
||||||
methods = append(methods, string(base.Teardown))
|
methods = append(methods, string(base.Teardown))
|
||||||
@@ -514,7 +328,7 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case base.Describe:
|
case base.Describe:
|
||||||
if sc.readHandlers.OnDescribe != nil {
|
if h, ok := sc.s.Handler.(ServerHandlerOnDescribe); ok {
|
||||||
err := sc.checkState(map[ServerConnState]struct{}{
|
err := sc.checkState(map[ServerConnState]struct{}{
|
||||||
ServerConnStateInitial: {},
|
ServerConnStateInitial: {},
|
||||||
})
|
})
|
||||||
@@ -533,7 +347,8 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
|
|
||||||
path, query := base.PathSplitQuery(pathAndQuery)
|
path, query := base.PathSplitQuery(pathAndQuery)
|
||||||
|
|
||||||
res, sdp, err := sc.readHandlers.OnDescribe(&ServerConnDescribeCtx{
|
res, sdp, err := h.OnDescribe(&ServerHandlerOnDescribeCtx{
|
||||||
|
Conn: sc,
|
||||||
Req: req,
|
Req: req,
|
||||||
Path: path,
|
Path: path,
|
||||||
Query: query,
|
Query: query,
|
||||||
@@ -553,7 +368,7 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case base.Announce:
|
case base.Announce:
|
||||||
if sc.readHandlers.OnAnnounce != nil {
|
if h, ok := sc.s.Handler.(ServerHandlerOnAnnounce); ok {
|
||||||
err := sc.checkState(map[ServerConnState]struct{}{
|
err := sc.checkState(map[ServerConnState]struct{}{
|
||||||
ServerConnStateInitial: {},
|
ServerConnStateInitial: {},
|
||||||
})
|
})
|
||||||
@@ -621,7 +436,8 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := sc.readHandlers.OnAnnounce(&ServerConnAnnounceCtx{
|
res, err := h.OnAnnounce(&ServerHandlerOnAnnounceCtx{
|
||||||
|
Conn: sc,
|
||||||
Req: req,
|
Req: req,
|
||||||
Path: path,
|
Path: path,
|
||||||
Query: query,
|
Query: query,
|
||||||
@@ -650,7 +466,7 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case base.Setup:
|
case base.Setup:
|
||||||
if sc.readHandlers.OnSetup != nil {
|
if h, ok := sc.s.Handler.(ServerHandlerOnSetup); ok {
|
||||||
err := sc.checkState(map[ServerConnState]struct{}{
|
err := sc.checkState(map[ServerConnState]struct{}{
|
||||||
ServerConnStateInitial: {},
|
ServerConnStateInitial: {},
|
||||||
ServerConnStatePrePlay: {},
|
ServerConnStatePrePlay: {},
|
||||||
@@ -741,7 +557,8 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
}, liberrors.ErrServerTracksDifferentProtocols{}
|
}, liberrors.ErrServerTracksDifferentProtocols{}
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := sc.readHandlers.OnSetup(&ServerConnSetupCtx{
|
res, err := h.OnSetup(&ServerHandlerOnSetupCtx{
|
||||||
|
Conn: sc,
|
||||||
Req: req,
|
Req: req,
|
||||||
Path: path,
|
Path: path,
|
||||||
Query: query,
|
Query: query,
|
||||||
@@ -810,7 +627,7 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case base.Play:
|
case base.Play:
|
||||||
if sc.readHandlers.OnPlay != nil {
|
if h, ok := sc.s.Handler.(ServerHandlerOnPlay); ok {
|
||||||
// play can be sent twice, allow calling it even if we're already playing
|
// play can be sent twice, allow calling it even if we're already playing
|
||||||
err := sc.checkState(map[ServerConnState]struct{}{
|
err := sc.checkState(map[ServerConnState]struct{}{
|
||||||
ServerConnStatePrePlay: {},
|
ServerConnStatePrePlay: {},
|
||||||
@@ -840,7 +657,8 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
|
|
||||||
path, query := base.PathSplitQuery(pathAndQuery)
|
path, query := base.PathSplitQuery(pathAndQuery)
|
||||||
|
|
||||||
res, err := sc.readHandlers.OnPlay(&ServerConnPlayCtx{
|
res, err := h.OnPlay(&ServerHandlerOnPlayCtx{
|
||||||
|
Conn: sc,
|
||||||
Req: req,
|
Req: req,
|
||||||
Path: path,
|
Path: path,
|
||||||
Query: query,
|
Query: query,
|
||||||
@@ -855,7 +673,7 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case base.Record:
|
case base.Record:
|
||||||
if sc.readHandlers.OnRecord != nil {
|
if h, ok := sc.s.Handler.(ServerHandlerOnRecord); ok {
|
||||||
err := sc.checkState(map[ServerConnState]struct{}{
|
err := sc.checkState(map[ServerConnState]struct{}{
|
||||||
ServerConnStatePreRecord: {},
|
ServerConnStatePreRecord: {},
|
||||||
})
|
})
|
||||||
@@ -889,7 +707,8 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
|
|
||||||
path, query := base.PathSplitQuery(pathAndQuery)
|
path, query := base.PathSplitQuery(pathAndQuery)
|
||||||
|
|
||||||
res, err := sc.readHandlers.OnRecord(&ServerConnRecordCtx{
|
res, err := h.OnRecord(&ServerHandlerOnRecordCtx{
|
||||||
|
Conn: sc,
|
||||||
Req: req,
|
Req: req,
|
||||||
Path: path,
|
Path: path,
|
||||||
Query: query,
|
Query: query,
|
||||||
@@ -904,7 +723,7 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case base.Pause:
|
case base.Pause:
|
||||||
if sc.readHandlers.OnPause != nil {
|
if h, ok := sc.s.Handler.(ServerHandlerOnPause); ok {
|
||||||
err := sc.checkState(map[ServerConnState]struct{}{
|
err := sc.checkState(map[ServerConnState]struct{}{
|
||||||
ServerConnStatePrePlay: {},
|
ServerConnStatePrePlay: {},
|
||||||
ServerConnStatePlay: {},
|
ServerConnStatePlay: {},
|
||||||
@@ -929,7 +748,8 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
|
|
||||||
path, query := base.PathSplitQuery(pathAndQuery)
|
path, query := base.PathSplitQuery(pathAndQuery)
|
||||||
|
|
||||||
res, err := sc.readHandlers.OnPause(&ServerConnPauseCtx{
|
res, err := h.OnPause(&ServerHandlerOnPauseCtx{
|
||||||
|
Conn: sc,
|
||||||
Req: req,
|
Req: req,
|
||||||
Path: path,
|
Path: path,
|
||||||
Query: query,
|
Query: query,
|
||||||
@@ -951,7 +771,7 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case base.GetParameter:
|
case base.GetParameter:
|
||||||
if sc.readHandlers.OnGetParameter != nil {
|
if h, ok := sc.s.Handler.(ServerHandlerOnGetParameter); ok {
|
||||||
pathAndQuery, ok := req.URL.RTSPPath()
|
pathAndQuery, ok := req.URL.RTSPPath()
|
||||||
if !ok {
|
if !ok {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
@@ -961,7 +781,8 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
|
|
||||||
path, query := base.PathSplitQuery(pathAndQuery)
|
path, query := base.PathSplitQuery(pathAndQuery)
|
||||||
|
|
||||||
return sc.readHandlers.OnGetParameter(&ServerConnGetParameterCtx{
|
return h.OnGetParameter(&ServerHandlerOnGetParameterCtx{
|
||||||
|
Conn: sc,
|
||||||
Req: req,
|
Req: req,
|
||||||
Path: path,
|
Path: path,
|
||||||
Query: query,
|
Query: query,
|
||||||
@@ -978,7 +799,7 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case base.SetParameter:
|
case base.SetParameter:
|
||||||
if sc.readHandlers.OnSetParameter != nil {
|
if h, ok := sc.s.Handler.(ServerHandlerOnSetParameter); ok {
|
||||||
pathAndQuery, ok := req.URL.RTSPPath()
|
pathAndQuery, ok := req.URL.RTSPPath()
|
||||||
if !ok {
|
if !ok {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
@@ -988,7 +809,8 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
|
|
||||||
path, query := base.PathSplitQuery(pathAndQuery)
|
path, query := base.PathSplitQuery(pathAndQuery)
|
||||||
|
|
||||||
return sc.readHandlers.OnSetParameter(&ServerConnSetParameterCtx{
|
return h.OnSetParameter(&ServerHandlerOnSetParameterCtx{
|
||||||
|
Conn: sc,
|
||||||
Req: req,
|
Req: req,
|
||||||
Path: path,
|
Path: path,
|
||||||
Query: query,
|
Query: query,
|
||||||
@@ -996,7 +818,7 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case base.Teardown:
|
case base.Teardown:
|
||||||
if sc.readHandlers.OnTeardown != nil {
|
if h, ok := sc.s.Handler.(ServerHandlerOnTeardown); ok {
|
||||||
pathAndQuery, ok := req.URL.RTSPPath()
|
pathAndQuery, ok := req.URL.RTSPPath()
|
||||||
if !ok {
|
if !ok {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
@@ -1006,7 +828,8 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
|
|
||||||
path, query := base.PathSplitQuery(pathAndQuery)
|
path, query := base.PathSplitQuery(pathAndQuery)
|
||||||
|
|
||||||
return sc.readHandlers.OnTeardown(&ServerConnTeardownCtx{
|
return h.OnTeardown(&ServerHandlerOnTeardownCtx{
|
||||||
|
Conn: sc,
|
||||||
Req: req,
|
Req: req,
|
||||||
Path: path,
|
Path: path,
|
||||||
Query: query,
|
Query: query,
|
||||||
@@ -1024,6 +847,10 @@ func (sc *ServerConn) handleRequest(req *base.Request) (*base.Response, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (sc *ServerConn) handleRequestOuter(req *base.Request) error {
|
func (sc *ServerConn) handleRequestOuter(req *base.Request) error {
|
||||||
|
if h, ok := sc.s.Handler.(ServerHandlerOnRequest); ok {
|
||||||
|
h.OnRequest(req)
|
||||||
|
}
|
||||||
|
|
||||||
res, err := sc.handleRequest(req)
|
res, err := sc.handleRequest(req)
|
||||||
|
|
||||||
if res.Header == nil {
|
if res.Header == nil {
|
||||||
@@ -1038,8 +865,8 @@ func (sc *ServerConn) handleRequestOuter(req *base.Request) error {
|
|||||||
// add server
|
// add server
|
||||||
res.Header["Server"] = base.HeaderValue{"gortsplib"}
|
res.Header["Server"] = base.HeaderValue{"gortsplib"}
|
||||||
|
|
||||||
if sc.readHandlers.OnResponse != nil {
|
if h, ok := sc.s.Handler.(ServerHandlerOnResponse); ok {
|
||||||
sc.readHandlers.OnResponse(res)
|
h.OnResponse(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
@@ -1102,7 +929,15 @@ func (sc *ServerConn) backgroundRead() error {
|
|||||||
sc.announcedTracks[frame.TrackID].rtcpReceiver.ProcessFrame(time.Now(),
|
sc.announcedTracks[frame.TrackID].rtcpReceiver.ProcessFrame(time.Now(),
|
||||||
frame.StreamType, frame.Payload)
|
frame.StreamType, frame.Payload)
|
||||||
}
|
}
|
||||||
sc.readHandlers.OnFrame(frame.TrackID, frame.StreamType, frame.Payload)
|
|
||||||
|
if h, ok := sc.s.Handler.(ServerHandlerOnFrame); ok {
|
||||||
|
h.OnFrame(&ServerHandlerOnFrameCtx{
|
||||||
|
Conn: sc,
|
||||||
|
TrackID: frame.TrackID,
|
||||||
|
StreamType: frame.StreamType,
|
||||||
|
Payload: frame.Payload,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case *base.Request:
|
case *base.Request:
|
||||||
@@ -1129,50 +964,6 @@ func (sc *ServerConn) backgroundRead() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read starts reading requests and frames.
|
|
||||||
// it returns a channel that is written when the reading stops.
|
|
||||||
func (sc *ServerConn) Read(readHandlers ServerConnReadHandlers) chan error {
|
|
||||||
// channel is buffered, since listening to it is not mandatory
|
|
||||||
done := make(chan error, 1)
|
|
||||||
|
|
||||||
sc.readHandlers = readHandlers
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
done <- sc.backgroundRead()
|
|
||||||
}()
|
|
||||||
|
|
||||||
return done
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteFrame writes a frame.
|
|
||||||
func (sc *ServerConn) WriteFrame(trackID int, streamType StreamType, payload []byte) {
|
|
||||||
if *sc.setupProtocol == StreamProtocolUDP {
|
|
||||||
track := sc.setuppedTracks[trackID]
|
|
||||||
|
|
||||||
if streamType == StreamTypeRTP {
|
|
||||||
sc.s.udpRTPListener.write(payload, &net.UDPAddr{
|
|
||||||
IP: sc.ip(),
|
|
||||||
Zone: sc.zone(),
|
|
||||||
Port: track.udpRTPPort,
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sc.s.udpRTCPListener.write(payload, &net.UDPAddr{
|
|
||||||
IP: sc.ip(),
|
|
||||||
Zone: sc.zone(),
|
|
||||||
Port: track.udpRTCPPort,
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sc.tcpFrameWriteBuffer.Push(&base.InterleavedFrame{
|
|
||||||
TrackID: trackID,
|
|
||||||
StreamType: streamType,
|
|
||||||
Payload: payload,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sc *ServerConn) backgroundRecord() {
|
func (sc *ServerConn) backgroundRecord() {
|
||||||
defer close(sc.backgroundRecordDone)
|
defer close(sc.backgroundRecordDone)
|
||||||
|
|
||||||
@@ -1217,3 +1008,126 @@ func (sc *ServerConn) backgroundRecord() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sc *ServerConn) tcpBackgroundWrite() {
|
||||||
|
defer close(sc.tcpBackgroundWriteDone)
|
||||||
|
|
||||||
|
for {
|
||||||
|
what, ok := sc.tcpFrameWriteBuffer.Pull()
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch w := what.(type) {
|
||||||
|
case *base.InterleavedFrame:
|
||||||
|
sc.nconn.SetWriteDeadline(time.Now().Add(sc.s.WriteTimeout))
|
||||||
|
w.Write(sc.bw)
|
||||||
|
|
||||||
|
case *base.Response:
|
||||||
|
sc.nconn.SetWriteDeadline(time.Now().Add(sc.s.WriteTimeout))
|
||||||
|
w.Write(sc.bw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *ServerConn) frameModeEnable() {
|
||||||
|
switch sc.state {
|
||||||
|
case ServerConnStatePlay:
|
||||||
|
if *sc.setupProtocol == StreamProtocolTCP {
|
||||||
|
sc.doEnableTCPFrame = true
|
||||||
|
} else {
|
||||||
|
// readers can send RTCP frames, they cannot sent RTP frames
|
||||||
|
for trackID, track := range sc.setuppedTracks {
|
||||||
|
sc.s.udpRTCPListener.addClient(sc.ip(), track.udpRTCPPort, sc, trackID, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case ServerConnStateRecord:
|
||||||
|
if *sc.setupProtocol == StreamProtocolTCP {
|
||||||
|
sc.doEnableTCPFrame = true
|
||||||
|
sc.tcpFrameTimeout = true
|
||||||
|
|
||||||
|
} else {
|
||||||
|
for trackID, track := range sc.setuppedTracks {
|
||||||
|
sc.s.udpRTPListener.addClient(sc.ip(), track.udpRTPPort, sc, trackID, true)
|
||||||
|
sc.s.udpRTCPListener.addClient(sc.ip(), track.udpRTCPPort, sc, trackID, true)
|
||||||
|
|
||||||
|
// open the firewall by sending packets to the counterpart
|
||||||
|
sc.WriteFrame(trackID, StreamTypeRTP,
|
||||||
|
[]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||||
|
sc.WriteFrame(trackID, StreamTypeRTCP,
|
||||||
|
[]byte{0x80, 0xc9, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sc.backgroundRecordTerminate = make(chan struct{})
|
||||||
|
sc.backgroundRecordDone = make(chan struct{})
|
||||||
|
go sc.backgroundRecord()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *ServerConn) frameModeDisable() {
|
||||||
|
switch sc.state {
|
||||||
|
case ServerConnStatePlay:
|
||||||
|
if *sc.setupProtocol == StreamProtocolTCP {
|
||||||
|
sc.tcpFrameEnabled = false
|
||||||
|
sc.tcpFrameWriteBuffer.Close()
|
||||||
|
<-sc.tcpBackgroundWriteDone
|
||||||
|
sc.tcpFrameWriteBuffer.Reset()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
for _, track := range sc.setuppedTracks {
|
||||||
|
sc.s.udpRTCPListener.removeClient(sc.ip(), track.udpRTCPPort)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case ServerConnStateRecord:
|
||||||
|
close(sc.backgroundRecordTerminate)
|
||||||
|
<-sc.backgroundRecordDone
|
||||||
|
|
||||||
|
if *sc.setupProtocol == StreamProtocolTCP {
|
||||||
|
sc.tcpFrameTimeout = false
|
||||||
|
sc.nconn.SetReadDeadline(time.Time{})
|
||||||
|
|
||||||
|
sc.tcpFrameEnabled = false
|
||||||
|
sc.tcpFrameWriteBuffer.Close()
|
||||||
|
<-sc.tcpBackgroundWriteDone
|
||||||
|
sc.tcpFrameWriteBuffer.Reset()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
for _, track := range sc.setuppedTracks {
|
||||||
|
sc.s.udpRTPListener.removeClient(sc.ip(), track.udpRTPPort)
|
||||||
|
sc.s.udpRTCPListener.removeClient(sc.ip(), track.udpRTCPPort)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteFrame writes a frame.
|
||||||
|
func (sc *ServerConn) WriteFrame(trackID int, streamType StreamType, payload []byte) {
|
||||||
|
if *sc.setupProtocol == StreamProtocolUDP {
|
||||||
|
track := sc.setuppedTracks[trackID]
|
||||||
|
|
||||||
|
if streamType == StreamTypeRTP {
|
||||||
|
sc.s.udpRTPListener.write(payload, &net.UDPAddr{
|
||||||
|
IP: sc.ip(),
|
||||||
|
Zone: sc.zone(),
|
||||||
|
Port: track.udpRTPPort,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sc.s.udpRTCPListener.write(payload, &net.UDPAddr{
|
||||||
|
IP: sc.ip(),
|
||||||
|
Zone: sc.zone(),
|
||||||
|
Port: track.udpRTCPPort,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sc.tcpFrameWriteBuffer.Push(&base.InterleavedFrame{
|
||||||
|
TrackID: trackID,
|
||||||
|
StreamType: streamType,
|
||||||
|
Payload: payload,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
183
serverhandler.go
Normal file
183
serverhandler.go
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
package gortsplib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aler9/gortsplib/pkg/base"
|
||||||
|
"github.com/aler9/gortsplib/pkg/headers"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServerHandler is the interface implemented by all the server handlers.
|
||||||
|
type ServerHandler interface {
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnConnOpen can be implemented by a ServerHandler.
|
||||||
|
type ServerHandlerOnConnOpen interface {
|
||||||
|
OnConnOpen(*ServerConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnConnClose can be implemented by a ServerHandler.
|
||||||
|
type ServerHandlerOnConnClose interface {
|
||||||
|
OnConnClose(*ServerConn, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnRequest can be implemented by a ServerHandler.
|
||||||
|
type ServerHandlerOnRequest interface {
|
||||||
|
OnRequest(*base.Request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnResponse can be implemented by a ServerHandler.
|
||||||
|
type ServerHandlerOnResponse interface {
|
||||||
|
OnResponse(*base.Response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnOptionsCtx is the context of an OPTIONS request.
|
||||||
|
type ServerHandlerOnOptionsCtx struct {
|
||||||
|
Conn *ServerConn
|
||||||
|
Req *base.Request
|
||||||
|
Path string
|
||||||
|
Query string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnOptions can be implemented by a ServerHandler.
|
||||||
|
type ServerHandlerOnOptions interface {
|
||||||
|
OnOptions(*ServerHandlerOnOptionsCtx) (*base.Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnDescribeCtx is the context of a DESCRIBE request.
|
||||||
|
type ServerHandlerOnDescribeCtx struct {
|
||||||
|
Conn *ServerConn
|
||||||
|
Req *base.Request
|
||||||
|
Path string
|
||||||
|
Query string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnDescribe can be implemented by a ServerHandler.
|
||||||
|
type ServerHandlerOnDescribe interface {
|
||||||
|
OnDescribe(*ServerHandlerOnDescribeCtx) (*base.Response, []byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnAnnounceCtx is the context of an ANNOUNCE request.
|
||||||
|
type ServerHandlerOnAnnounceCtx struct {
|
||||||
|
Conn *ServerConn
|
||||||
|
// Session *ServerSession
|
||||||
|
Req *base.Request
|
||||||
|
Path string
|
||||||
|
Query string
|
||||||
|
Tracks Tracks
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnAnnounce can be implemented by a ServerHandler.
|
||||||
|
type ServerHandlerOnAnnounce interface {
|
||||||
|
OnAnnounce(*ServerHandlerOnAnnounceCtx) (*base.Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnSetupCtx is the context of a OPTIONS request.
|
||||||
|
type ServerHandlerOnSetupCtx struct {
|
||||||
|
Conn *ServerConn
|
||||||
|
Session *ServerSession
|
||||||
|
Req *base.Request
|
||||||
|
Path string
|
||||||
|
Query string
|
||||||
|
TrackID int
|
||||||
|
Transport *headers.Transport
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnSetup can be implemented by a ServerHandler.
|
||||||
|
type ServerHandlerOnSetup interface {
|
||||||
|
OnSetup(*ServerHandlerOnSetupCtx) (*base.Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnPlayCtx is the context of a PLAY request.
|
||||||
|
type ServerHandlerOnPlayCtx struct {
|
||||||
|
Conn *ServerConn
|
||||||
|
// Session *ServerSession
|
||||||
|
Req *base.Request
|
||||||
|
Path string
|
||||||
|
Query string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnPlay can be implemented by a ServerHandler.
|
||||||
|
type ServerHandlerOnPlay interface {
|
||||||
|
OnPlay(*ServerHandlerOnPlayCtx) (*base.Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnRecordCtx is the context of a RECORD request.
|
||||||
|
type ServerHandlerOnRecordCtx struct {
|
||||||
|
Conn *ServerConn
|
||||||
|
// Session *ServerSession
|
||||||
|
Req *base.Request
|
||||||
|
Path string
|
||||||
|
Query string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnRecord can be implemented by a ServerHandler.
|
||||||
|
type ServerHandlerOnRecord interface {
|
||||||
|
OnRecord(*ServerHandlerOnRecordCtx) (*base.Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnPauseCtx is the context of a PAUSE request.
|
||||||
|
type ServerHandlerOnPauseCtx struct {
|
||||||
|
Conn *ServerConn
|
||||||
|
// Session *ServerSession
|
||||||
|
Req *base.Request
|
||||||
|
Path string
|
||||||
|
Query string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnPause can be implemented by a ServerHandler.
|
||||||
|
type ServerHandlerOnPause interface {
|
||||||
|
OnPause(*ServerHandlerOnPauseCtx) (*base.Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnGetParameterCtx is the context of a GET_PARAMETER request.
|
||||||
|
type ServerHandlerOnGetParameterCtx struct {
|
||||||
|
Conn *ServerConn
|
||||||
|
Req *base.Request
|
||||||
|
Path string
|
||||||
|
Query string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnGetParameter can be implemented by a ServerHandler.
|
||||||
|
type ServerHandlerOnGetParameter interface {
|
||||||
|
OnGetParameter(*ServerHandlerOnGetParameterCtx) (*base.Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnSetParameterCtx is the context of a SET_PARAMETER request.
|
||||||
|
type ServerHandlerOnSetParameterCtx struct {
|
||||||
|
Conn *ServerConn
|
||||||
|
Req *base.Request
|
||||||
|
Path string
|
||||||
|
Query string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnSetParameter can be implemented by a ServerHandler.
|
||||||
|
type ServerHandlerOnSetParameter interface {
|
||||||
|
OnSetParameter(*ServerHandlerOnSetParameterCtx) (*base.Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnTeardownCtx is the context of a TEARDOWN request.
|
||||||
|
type ServerHandlerOnTeardownCtx struct {
|
||||||
|
Conn *ServerConn
|
||||||
|
// Session *ServerSession
|
||||||
|
Req *base.Request
|
||||||
|
Path string
|
||||||
|
Query string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnTeardown can be implemented by a ServerHandler.
|
||||||
|
type ServerHandlerOnTeardown interface {
|
||||||
|
OnTeardown(*ServerHandlerOnTeardownCtx) (*base.Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnFrameCtx is the context of a frame request.
|
||||||
|
type ServerHandlerOnFrameCtx struct {
|
||||||
|
Conn *ServerConn
|
||||||
|
// Session *ServerSession
|
||||||
|
TrackID int
|
||||||
|
StreamType StreamType
|
||||||
|
Payload []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandlerOnFrame can be implemented by a ServerHandler.
|
||||||
|
type ServerHandlerOnFrame interface {
|
||||||
|
OnFrame(*ServerHandlerOnFrameCtx)
|
||||||
|
}
|
@@ -70,45 +70,30 @@ func TestServerPublishSetupPath(t *testing.T) {
|
|||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(ca.name, func(t *testing.T) {
|
t.Run(ca.name, func(t *testing.T) {
|
||||||
type pathTrackIDPair struct {
|
setupDone := make(chan struct{})
|
||||||
path string
|
|
||||||
trackID int
|
|
||||||
}
|
|
||||||
setupDone := make(chan pathTrackIDPair)
|
|
||||||
|
|
||||||
s := &Server{}
|
s := &Server{
|
||||||
err := s.Serve("127.0.0.1:8554")
|
Handler: &testServerHandler{
|
||||||
|
onAnnounce: func(ctx *ServerHandlerOnAnnounceCtx) (*base.Response, error) {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, error) {
|
||||||
|
require.Equal(t, ca.path, ctx.Path)
|
||||||
|
require.Equal(t, ca.trackID, ctx.TrackID)
|
||||||
|
close(setupDone)
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.Start("127.0.0.1:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer s.Close()
|
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()
|
|
||||||
|
|
||||||
onAnnounce := func(ctx *ServerConnAnnounceCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
onSetup := func(ctx *ServerConnSetupCtx) (*base.Response, error) {
|
|
||||||
setupDone <- pathTrackIDPair{ctx.Path, ctx.TrackID}
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
<-conn.Read(ServerConnReadHandlers{
|
|
||||||
OnAnnounce: onAnnounce,
|
|
||||||
OnSetup: onSetup,
|
|
||||||
})
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "localhost:8554")
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
@@ -178,9 +163,7 @@ func TestServerPublishSetupPath(t *testing.T) {
|
|||||||
}.Write(bconn.Writer)
|
}.Write(bconn.Writer)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
pair := <-setupDone
|
<-setupDone
|
||||||
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)
|
||||||
@@ -192,39 +175,28 @@ func TestServerPublishSetupPath(t *testing.T) {
|
|||||||
func TestServerPublishSetupErrorDifferentPaths(t *testing.T) {
|
func TestServerPublishSetupErrorDifferentPaths(t *testing.T) {
|
||||||
serverErr := make(chan error)
|
serverErr := make(chan error)
|
||||||
|
|
||||||
s := &Server{}
|
s := &Server{
|
||||||
err := s.Serve("127.0.0.1:8554")
|
Handler: &testServerHandler{
|
||||||
|
onConnClose: func(sc *ServerConn, err error) {
|
||||||
|
serverErr <- err
|
||||||
|
},
|
||||||
|
onAnnounce: func(ctx *ServerHandlerOnAnnounceCtx) (*base.Response, error) {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, error) {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.Start("127.0.0.1:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer s.Close()
|
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()
|
|
||||||
|
|
||||||
onAnnounce := func(ctx *ServerConnAnnounceCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
onSetup := func(ctx *ServerConnSetupCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err = <-conn.Read(ServerConnReadHandlers{
|
|
||||||
OnAnnounce: onAnnounce,
|
|
||||||
OnSetup: onSetup,
|
|
||||||
})
|
|
||||||
serverErr <- err
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "localhost:8554")
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
@@ -291,39 +263,28 @@ func TestServerPublishSetupErrorDifferentPaths(t *testing.T) {
|
|||||||
func TestServerPublishSetupErrorTrackTwice(t *testing.T) {
|
func TestServerPublishSetupErrorTrackTwice(t *testing.T) {
|
||||||
serverErr := make(chan error)
|
serverErr := make(chan error)
|
||||||
|
|
||||||
s := &Server{}
|
s := &Server{
|
||||||
err := s.Serve("127.0.0.1:8554")
|
Handler: &testServerHandler{
|
||||||
|
onConnClose: func(sc *ServerConn, err error) {
|
||||||
|
serverErr <- err
|
||||||
|
},
|
||||||
|
onAnnounce: func(ctx *ServerHandlerOnAnnounceCtx) (*base.Response, error) {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, error) {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.Start("127.0.0.1:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer s.Close()
|
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()
|
|
||||||
|
|
||||||
onAnnounce := func(ctx *ServerConnAnnounceCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
onSetup := func(ctx *ServerConnSetupCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err = <-conn.Read(ServerConnReadHandlers{
|
|
||||||
OnAnnounce: onAnnounce,
|
|
||||||
OnSetup: onSetup,
|
|
||||||
})
|
|
||||||
serverErr <- err
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "localhost:8554")
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
@@ -404,46 +365,33 @@ func TestServerPublishSetupErrorTrackTwice(t *testing.T) {
|
|||||||
func TestServerPublishRecordErrorPartialTracks(t *testing.T) {
|
func TestServerPublishRecordErrorPartialTracks(t *testing.T) {
|
||||||
serverErr := make(chan error)
|
serverErr := make(chan error)
|
||||||
|
|
||||||
s := &Server{}
|
s := &Server{
|
||||||
err := s.Serve("127.0.0.1:8554")
|
Handler: &testServerHandler{
|
||||||
|
onConnClose: func(sc *ServerConn, err error) {
|
||||||
|
serverErr <- err
|
||||||
|
},
|
||||||
|
onAnnounce: func(ctx *ServerHandlerOnAnnounceCtx) (*base.Response, error) {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, error) {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
onRecord: func(ctx *ServerHandlerOnRecordCtx) (*base.Response, error) {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.Start("127.0.0.1:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer s.Close()
|
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()
|
|
||||||
|
|
||||||
onAnnounce := func(ctx *ServerConnAnnounceCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
onSetup := func(ctx *ServerConnSetupCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
onRecord := func(ctx *ServerConnRecordCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err = <-conn.Read(ServerConnReadHandlers{
|
|
||||||
OnAnnounce: onAnnounce,
|
|
||||||
OnSetup: onSetup,
|
|
||||||
OnRecord: onRecord,
|
|
||||||
})
|
|
||||||
serverErr <- err
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "localhost:8554")
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
@@ -529,67 +477,50 @@ func TestServerPublish(t *testing.T) {
|
|||||||
"tcp",
|
"tcp",
|
||||||
} {
|
} {
|
||||||
t.Run(proto, func(t *testing.T) {
|
t.Run(proto, func(t *testing.T) {
|
||||||
s := &Server{}
|
rtpReceived := uint64(0)
|
||||||
|
|
||||||
|
s := &Server{
|
||||||
|
Handler: &testServerHandler{
|
||||||
|
onAnnounce: func(ctx *ServerHandlerOnAnnounceCtx) (*base.Response, error) {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, error) {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
onRecord: func(ctx *ServerHandlerOnRecordCtx) (*base.Response, error) {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
onFrame: func(ctx *ServerHandlerOnFrameCtx) {
|
||||||
|
if atomic.SwapUint64(&rtpReceived, 1) == 0 {
|
||||||
|
require.Equal(t, 0, ctx.TrackID)
|
||||||
|
require.Equal(t, StreamTypeRTP, ctx.StreamType)
|
||||||
|
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, ctx.Payload)
|
||||||
|
} else {
|
||||||
|
require.Equal(t, 0, ctx.TrackID)
|
||||||
|
require.Equal(t, StreamTypeRTCP, ctx.StreamType)
|
||||||
|
require.Equal(t, []byte{0x05, 0x06, 0x07, 0x08}, ctx.Payload)
|
||||||
|
|
||||||
|
ctx.Conn.WriteFrame(0, StreamTypeRTCP, []byte{0x09, 0x0A, 0x0B, 0x0C})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
if proto == "udp" {
|
if proto == "udp" {
|
||||||
s.UDPRTPAddress = "127.0.0.1:8000"
|
s.UDPRTPAddress = "127.0.0.1:8000"
|
||||||
s.UDPRTCPAddress = "127.0.0.1:8001"
|
s.UDPRTCPAddress = "127.0.0.1:8001"
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.Serve("127.0.0.1:8554")
|
err := s.Start("127.0.0.1:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer s.Close()
|
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()
|
|
||||||
|
|
||||||
onAnnounce := func(ctx *ServerConnAnnounceCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
onSetup := func(ctx *ServerConnSetupCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
onRecord := func(ctx *ServerConnRecordCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
rtpReceived := uint64(0)
|
|
||||||
onFrame := func(trackID int, typ StreamType, buf []byte) {
|
|
||||||
if atomic.SwapUint64(&rtpReceived, 1) == 0 {
|
|
||||||
require.Equal(t, 0, trackID)
|
|
||||||
require.Equal(t, StreamTypeRTP, typ)
|
|
||||||
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, buf)
|
|
||||||
} else {
|
|
||||||
require.Equal(t, 0, trackID)
|
|
||||||
require.Equal(t, StreamTypeRTCP, typ)
|
|
||||||
require.Equal(t, []byte{0x05, 0x06, 0x07, 0x08}, buf)
|
|
||||||
|
|
||||||
conn.WriteFrame(0, StreamTypeRTCP, []byte{0x09, 0x0A, 0x0B, 0x0C})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<-conn.Read(ServerConnReadHandlers{
|
|
||||||
OnAnnounce: onAnnounce,
|
|
||||||
OnSetup: onSetup,
|
|
||||||
OnRecord: onRecord,
|
|
||||||
OnFrame: onFrame,
|
|
||||||
})
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "localhost:8554")
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
@@ -742,53 +673,34 @@ func TestServerPublish(t *testing.T) {
|
|||||||
|
|
||||||
func TestServerPublishErrorWrongProtocol(t *testing.T) {
|
func TestServerPublishErrorWrongProtocol(t *testing.T) {
|
||||||
s := &Server{
|
s := &Server{
|
||||||
|
Handler: &testServerHandler{
|
||||||
|
onAnnounce: func(ctx *ServerHandlerOnAnnounceCtx) (*base.Response, error) {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, error) {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
onRecord: func(ctx *ServerHandlerOnRecordCtx) (*base.Response, error) {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
onFrame: func(ctx *ServerHandlerOnFrameCtx) {
|
||||||
|
t.Error("should not happen")
|
||||||
|
},
|
||||||
|
},
|
||||||
UDPRTPAddress: "127.0.0.1:8000",
|
UDPRTPAddress: "127.0.0.1:8000",
|
||||||
UDPRTCPAddress: "127.0.0.1:8001",
|
UDPRTCPAddress: "127.0.0.1:8001",
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.Serve("127.0.0.1:8554")
|
err := s.Start("127.0.0.1:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer s.Close()
|
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()
|
|
||||||
|
|
||||||
onAnnounce := func(ctx *ServerConnAnnounceCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
onSetup := func(ctx *ServerConnSetupCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
onRecord := func(ctx *ServerConnRecordCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
onFrame := func(trackID int, typ StreamType, buf []byte) {
|
|
||||||
t.Error("should not happen")
|
|
||||||
}
|
|
||||||
|
|
||||||
<-conn.Read(ServerConnReadHandlers{
|
|
||||||
OnAnnounce: onAnnounce,
|
|
||||||
OnSetup: onSetup,
|
|
||||||
OnRecord: onRecord,
|
|
||||||
OnFrame: onFrame,
|
|
||||||
})
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "localhost:8554")
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
@@ -875,51 +787,30 @@ func TestServerPublishErrorWrongProtocol(t *testing.T) {
|
|||||||
|
|
||||||
func TestServerPublishRTCPReport(t *testing.T) {
|
func TestServerPublishRTCPReport(t *testing.T) {
|
||||||
s := &Server{
|
s := &Server{
|
||||||
|
Handler: &testServerHandler{
|
||||||
|
onAnnounce: func(ctx *ServerHandlerOnAnnounceCtx) (*base.Response, error) {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, error) {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
onRecord: func(ctx *ServerHandlerOnRecordCtx) (*base.Response, error) {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
receiverReportPeriod: 1 * time.Second,
|
receiverReportPeriod: 1 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.Serve("127.0.0.1:8554")
|
err := s.Start("127.0.0.1:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer s.Close()
|
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()
|
|
||||||
|
|
||||||
onAnnounce := func(ctx *ServerConnAnnounceCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
onSetup := func(ctx *ServerConnSetupCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
onRecord := func(ctx *ServerConnRecordCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
onFrame := func(trackID int, typ StreamType, buf []byte) {
|
|
||||||
}
|
|
||||||
|
|
||||||
<-conn.Read(ServerConnReadHandlers{
|
|
||||||
OnAnnounce: onAnnounce,
|
|
||||||
OnSetup: onSetup,
|
|
||||||
OnRecord: onRecord,
|
|
||||||
OnFrame: onFrame,
|
|
||||||
})
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "localhost:8554")
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
@@ -1053,6 +944,31 @@ func TestServerPublishErrorTimeout(t *testing.T) {
|
|||||||
errDone := make(chan struct{})
|
errDone := make(chan struct{})
|
||||||
|
|
||||||
s := &Server{
|
s := &Server{
|
||||||
|
Handler: &testServerHandler{
|
||||||
|
onConnClose: func(sc *ServerConn, err error) {
|
||||||
|
if proto == "udp" {
|
||||||
|
require.Equal(t, "no UDP packets received (maybe there's a firewall/NAT in between)", err.Error())
|
||||||
|
} else {
|
||||||
|
require.True(t, strings.HasSuffix(err.Error(), "i/o timeout"))
|
||||||
|
}
|
||||||
|
close(errDone)
|
||||||
|
},
|
||||||
|
onAnnounce: func(ctx *ServerHandlerOnAnnounceCtx) (*base.Response, error) {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, error) {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
onRecord: func(ctx *ServerHandlerOnRecordCtx) (*base.Response, error) {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
ReadTimeout: 1 * time.Second,
|
ReadTimeout: 1 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1061,56 +977,10 @@ func TestServerPublishErrorTimeout(t *testing.T) {
|
|||||||
s.UDPRTCPAddress = "127.0.0.1:8001"
|
s.UDPRTCPAddress = "127.0.0.1:8001"
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.Serve("127.0.0.1:8554")
|
err := s.Start("127.0.0.1:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer s.Close()
|
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()
|
|
||||||
|
|
||||||
onAnnounce := func(ctx *ServerConnAnnounceCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
onSetup := func(ctx *ServerConnSetupCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
onRecord := func(ctx *ServerConnRecordCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
onFrame := func(trackID int, typ StreamType, buf []byte) {
|
|
||||||
}
|
|
||||||
|
|
||||||
err = <-conn.Read(ServerConnReadHandlers{
|
|
||||||
OnAnnounce: onAnnounce,
|
|
||||||
OnSetup: onSetup,
|
|
||||||
OnRecord: onRecord,
|
|
||||||
OnFrame: onFrame,
|
|
||||||
})
|
|
||||||
|
|
||||||
if proto == "udp" {
|
|
||||||
require.Equal(t, "no UDP packets received (maybe there's a firewall/NAT in between)", err.Error())
|
|
||||||
} else {
|
|
||||||
require.True(t, strings.HasSuffix(err.Error(), "i/o timeout"))
|
|
||||||
}
|
|
||||||
|
|
||||||
close(errDone)
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "localhost:8554")
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer conn.Close()
|
defer conn.Close()
|
@@ -58,37 +58,24 @@ func TestServerReadSetupPath(t *testing.T) {
|
|||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(ca.name, func(t *testing.T) {
|
t.Run(ca.name, func(t *testing.T) {
|
||||||
type pathTrackIDPair struct {
|
setupDone := make(chan struct{})
|
||||||
path string
|
|
||||||
trackID int
|
|
||||||
}
|
|
||||||
setupDone := make(chan pathTrackIDPair)
|
|
||||||
|
|
||||||
s := &Server{}
|
s := &Server{
|
||||||
err := s.Serve("127.0.0.1:8554")
|
Handler: &testServerHandler{
|
||||||
require.NoError(t, err)
|
onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, error) {
|
||||||
defer s.Close()
|
require.Equal(t, ca.path, ctx.Path)
|
||||||
|
require.Equal(t, ca.trackID, ctx.TrackID)
|
||||||
serverDone := make(chan struct{})
|
close(setupDone)
|
||||||
defer func() { <-serverDone }()
|
|
||||||
go func() {
|
|
||||||
defer close(serverDone)
|
|
||||||
|
|
||||||
conn, err := s.Accept()
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
onSetup := func(ctx *ServerConnSetupCtx) (*base.Response, error) {
|
|
||||||
setupDone <- pathTrackIDPair{ctx.Path, ctx.TrackID}
|
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
<-conn.Read(ServerConnReadHandlers{
|
err := s.Start("127.0.0.1:8554")
|
||||||
OnSetup: onSetup,
|
require.NoError(t, err)
|
||||||
})
|
defer s.Close()
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "localhost:8554")
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -118,9 +105,7 @@ func TestServerReadSetupPath(t *testing.T) {
|
|||||||
}.Write(bconn.Writer)
|
}.Write(bconn.Writer)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
pair := <-setupDone
|
<-setupDone
|
||||||
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)
|
||||||
@@ -133,31 +118,22 @@ func TestServerReadSetupPath(t *testing.T) {
|
|||||||
func TestServerReadSetupErrorDifferentPaths(t *testing.T) {
|
func TestServerReadSetupErrorDifferentPaths(t *testing.T) {
|
||||||
serverErr := make(chan error)
|
serverErr := make(chan error)
|
||||||
|
|
||||||
s := &Server{}
|
s := &Server{
|
||||||
err := s.Serve("127.0.0.1:8554")
|
Handler: &testServerHandler{
|
||||||
require.NoError(t, err)
|
onConnClose: func(sc *ServerConn, err error) {
|
||||||
defer s.Close()
|
serverErr <- err
|
||||||
|
},
|
||||||
serverDone := make(chan struct{})
|
onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, error) {
|
||||||
defer func() { <-serverDone }()
|
|
||||||
go func() {
|
|
||||||
defer close(serverDone)
|
|
||||||
|
|
||||||
conn, err := s.Accept()
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
onSetup := func(ctx *ServerConnSetupCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
err = <-conn.Read(ServerConnReadHandlers{
|
err := s.Start("127.0.0.1:8554")
|
||||||
OnSetup: onSetup,
|
require.NoError(t, err)
|
||||||
})
|
defer s.Close()
|
||||||
serverErr <- err
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "localhost:8554")
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -215,31 +191,22 @@ func TestServerReadSetupErrorDifferentPaths(t *testing.T) {
|
|||||||
func TestServerReadSetupErrorTrackTwice(t *testing.T) {
|
func TestServerReadSetupErrorTrackTwice(t *testing.T) {
|
||||||
serverErr := make(chan error)
|
serverErr := make(chan error)
|
||||||
|
|
||||||
s := &Server{}
|
s := &Server{
|
||||||
err := s.Serve("127.0.0.1:8554")
|
Handler: &testServerHandler{
|
||||||
require.NoError(t, err)
|
onConnClose: func(sc *ServerConn, err error) {
|
||||||
defer s.Close()
|
serverErr <- err
|
||||||
|
},
|
||||||
serverDone := make(chan struct{})
|
onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, error) {
|
||||||
defer func() { <-serverDone }()
|
|
||||||
go func() {
|
|
||||||
defer close(serverDone)
|
|
||||||
|
|
||||||
conn, err := s.Accept()
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
onSetup := func(ctx *ServerConnSetupCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
err = <-conn.Read(ServerConnReadHandlers{
|
err := s.Start("127.0.0.1:8554")
|
||||||
OnSetup: onSetup,
|
require.NoError(t, err)
|
||||||
})
|
defer s.Close()
|
||||||
serverErr <- err
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "localhost:8554")
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -303,52 +270,35 @@ func TestServerRead(t *testing.T) {
|
|||||||
framesReceived := make(chan struct{})
|
framesReceived := make(chan struct{})
|
||||||
|
|
||||||
s := &Server{
|
s := &Server{
|
||||||
|
Handler: &testServerHandler{
|
||||||
|
onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, error) {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
onPlay: func(ctx *ServerHandlerOnPlayCtx) (*base.Response, error) {
|
||||||
|
ctx.Conn.WriteFrame(0, StreamTypeRTP, []byte{0x01, 0x02, 0x03, 0x04})
|
||||||
|
ctx.Conn.WriteFrame(0, StreamTypeRTCP, []byte{0x05, 0x06, 0x07, 0x08})
|
||||||
|
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusOK,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
onFrame: func(ctx *ServerHandlerOnFrameCtx) {
|
||||||
|
require.Equal(t, 0, ctx.TrackID)
|
||||||
|
require.Equal(t, StreamTypeRTCP, ctx.StreamType)
|
||||||
|
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, ctx.Payload)
|
||||||
|
close(framesReceived)
|
||||||
|
},
|
||||||
|
},
|
||||||
UDPRTPAddress: "127.0.0.1:8000",
|
UDPRTPAddress: "127.0.0.1:8000",
|
||||||
UDPRTCPAddress: "127.0.0.1:8001",
|
UDPRTCPAddress: "127.0.0.1:8001",
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.Serve("127.0.0.1:8554")
|
err := s.Start("127.0.0.1:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer s.Close()
|
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(ctx *ServerConnSetupCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
onPlay := func(ctx *ServerConnPlayCtx) (*base.Response, error) {
|
|
||||||
conn.WriteFrame(0, StreamTypeRTP, []byte{0x01, 0x02, 0x03, 0x04})
|
|
||||||
conn.WriteFrame(0, StreamTypeRTCP, []byte{0x05, 0x06, 0x07, 0x08})
|
|
||||||
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusOK,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
onFrame := func(trackID int, typ StreamType, buf []byte) {
|
|
||||||
require.Equal(t, 0, trackID)
|
|
||||||
require.Equal(t, StreamTypeRTCP, typ)
|
|
||||||
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, buf)
|
|
||||||
close(framesReceived)
|
|
||||||
}
|
|
||||||
|
|
||||||
<-conn.Read(ServerConnReadHandlers{
|
|
||||||
OnSetup: onSetup,
|
|
||||||
OnPlay: onPlay,
|
|
||||||
OnFrame: onFrame,
|
|
||||||
})
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "localhost:8554")
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
@@ -467,36 +417,25 @@ func TestServerRead(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestServerReadTCPResponseBeforeFrames(t *testing.T) {
|
func TestServerReadTCPResponseBeforeFrames(t *testing.T) {
|
||||||
s := &Server{}
|
|
||||||
err := s.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()
|
|
||||||
|
|
||||||
writerDone := make(chan struct{})
|
writerDone := make(chan struct{})
|
||||||
defer func() { <-writerDone }()
|
|
||||||
writerTerminate := make(chan struct{})
|
writerTerminate := make(chan struct{})
|
||||||
defer close(writerTerminate)
|
|
||||||
|
|
||||||
onSetup := func(ctx *ServerConnSetupCtx) (*base.Response, error) {
|
s := &Server{
|
||||||
|
Handler: &testServerHandler{
|
||||||
|
onConnClose: func(sc *ServerConn, err error) {
|
||||||
|
close(writerTerminate)
|
||||||
|
<-writerDone
|
||||||
|
},
|
||||||
|
onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, error) {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
},
|
||||||
|
onPlay: func(ctx *ServerHandlerOnPlayCtx) (*base.Response, error) {
|
||||||
onPlay := func(ctx *ServerConnPlayCtx) (*base.Response, error) {
|
|
||||||
go func() {
|
go func() {
|
||||||
defer close(writerDone)
|
defer close(writerDone)
|
||||||
|
|
||||||
conn.WriteFrame(0, StreamTypeRTP, []byte("\x00\x00\x00\x00"))
|
ctx.Conn.WriteFrame(0, StreamTypeRTP, []byte("\x00\x00\x00\x00"))
|
||||||
|
|
||||||
t := time.NewTicker(50 * time.Millisecond)
|
t := time.NewTicker(50 * time.Millisecond)
|
||||||
defer t.Stop()
|
defer t.Stop()
|
||||||
@@ -504,7 +443,7 @@ func TestServerReadTCPResponseBeforeFrames(t *testing.T) {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-t.C:
|
case <-t.C:
|
||||||
conn.WriteFrame(0, StreamTypeRTP, []byte("\x00\x00\x00\x00"))
|
ctx.Conn.WriteFrame(0, StreamTypeRTP, []byte("\x00\x00\x00\x00"))
|
||||||
case <-writerTerminate:
|
case <-writerTerminate:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -516,13 +455,13 @@ func TestServerReadTCPResponseBeforeFrames(t *testing.T) {
|
|||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
<-conn.Read(ServerConnReadHandlers{
|
err := s.Start("127.0.0.1:8554")
|
||||||
OnSetup: onSetup,
|
require.NoError(t, err)
|
||||||
OnPlay: onPlay,
|
defer s.Close()
|
||||||
})
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "localhost:8554")
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -575,33 +514,22 @@ func TestServerReadTCPResponseBeforeFrames(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestServerReadPlayPlay(t *testing.T) {
|
func TestServerReadPlayPlay(t *testing.T) {
|
||||||
s := &Server{}
|
|
||||||
err := s.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()
|
|
||||||
|
|
||||||
writerDone := make(chan struct{})
|
|
||||||
defer func() { <-writerDone }()
|
|
||||||
writerTerminate := make(chan struct{})
|
writerTerminate := make(chan struct{})
|
||||||
defer close(writerTerminate)
|
writerDone := make(chan struct{})
|
||||||
|
|
||||||
onSetup := func(ctx *ServerConnSetupCtx) (*base.Response, error) {
|
s := &Server{
|
||||||
|
Handler: &testServerHandler{
|
||||||
|
onConnClose: func(sc *ServerConn, err error) {
|
||||||
|
close(writerTerminate)
|
||||||
|
<-writerDone
|
||||||
|
},
|
||||||
|
onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, error) {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
},
|
||||||
|
onPlay: func(ctx *ServerHandlerOnPlayCtx) (*base.Response, error) {
|
||||||
onPlay := func(ctx *ServerConnPlayCtx) (*base.Response, error) {
|
if ctx.Conn.State() != ServerConnStatePlay {
|
||||||
if conn.State() != ServerConnStatePlay {
|
|
||||||
go func() {
|
go func() {
|
||||||
defer close(writerDone)
|
defer close(writerDone)
|
||||||
|
|
||||||
@@ -611,7 +539,7 @@ func TestServerReadPlayPlay(t *testing.T) {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-t.C:
|
case <-t.C:
|
||||||
conn.WriteFrame(0, StreamTypeRTP, []byte("\x00\x00\x00\x00"))
|
ctx.Conn.WriteFrame(0, StreamTypeRTP, []byte("\x00\x00\x00\x00"))
|
||||||
case <-writerTerminate:
|
case <-writerTerminate:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -622,13 +550,13 @@ func TestServerReadPlayPlay(t *testing.T) {
|
|||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
<-conn.Read(ServerConnReadHandlers{
|
err := s.Start("127.0.0.1:8554")
|
||||||
OnSetup: onSetup,
|
require.NoError(t, err)
|
||||||
OnPlay: onPlay,
|
defer s.Close()
|
||||||
})
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "localhost:8554")
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -690,33 +618,22 @@ func TestServerReadPlayPlay(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestServerReadPlayPausePlay(t *testing.T) {
|
func TestServerReadPlayPausePlay(t *testing.T) {
|
||||||
s := &Server{}
|
|
||||||
err := s.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()
|
|
||||||
|
|
||||||
writerStarted := false
|
writerStarted := false
|
||||||
writerDone := make(chan struct{})
|
writerDone := make(chan struct{})
|
||||||
defer func() { <-writerDone }()
|
|
||||||
writerTerminate := make(chan struct{})
|
writerTerminate := make(chan struct{})
|
||||||
defer close(writerTerminate)
|
|
||||||
|
|
||||||
onSetup := func(ctx *ServerConnSetupCtx) (*base.Response, error) {
|
s := &Server{
|
||||||
|
Handler: &testServerHandler{
|
||||||
|
onConnClose: func(sc *ServerConn, err error) {
|
||||||
|
close(writerTerminate)
|
||||||
|
<-writerDone
|
||||||
|
},
|
||||||
|
onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, error) {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
},
|
||||||
|
onPlay: func(ctx *ServerHandlerOnPlayCtx) (*base.Response, error) {
|
||||||
onPlay := func(ctx *ServerConnPlayCtx) (*base.Response, error) {
|
|
||||||
if !writerStarted {
|
if !writerStarted {
|
||||||
writerStarted = true
|
writerStarted = true
|
||||||
go func() {
|
go func() {
|
||||||
@@ -728,7 +645,7 @@ func TestServerReadPlayPausePlay(t *testing.T) {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-t.C:
|
case <-t.C:
|
||||||
conn.WriteFrame(0, StreamTypeRTP, []byte("\x00\x00\x00\x00"))
|
ctx.Conn.WriteFrame(0, StreamTypeRTP, []byte("\x00\x00\x00\x00"))
|
||||||
case <-writerTerminate:
|
case <-writerTerminate:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -739,20 +656,18 @@ func TestServerReadPlayPausePlay(t *testing.T) {
|
|||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
},
|
||||||
|
onPause: func(ctx *ServerHandlerOnPauseCtx) (*base.Response, error) {
|
||||||
onPause := func(ctx *ServerConnPauseCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
<-conn.Read(ServerConnReadHandlers{
|
err := s.Start("127.0.0.1:8554")
|
||||||
OnSetup: onSetup,
|
require.NoError(t, err)
|
||||||
OnPlay: onPlay,
|
defer s.Close()
|
||||||
OnPause: onPause,
|
|
||||||
})
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "localhost:8554")
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -832,32 +747,21 @@ func TestServerReadPlayPausePlay(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestServerReadPlayPausePause(t *testing.T) {
|
func TestServerReadPlayPausePause(t *testing.T) {
|
||||||
s := &Server{}
|
|
||||||
err := s.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()
|
|
||||||
|
|
||||||
writerDone := make(chan struct{})
|
writerDone := make(chan struct{})
|
||||||
defer func() { <-writerDone }()
|
|
||||||
writerTerminate := make(chan struct{})
|
writerTerminate := make(chan struct{})
|
||||||
defer close(writerTerminate)
|
|
||||||
|
|
||||||
onSetup := func(ctx *ServerConnSetupCtx) (*base.Response, error) {
|
s := &Server{
|
||||||
|
Handler: &testServerHandler{
|
||||||
|
onConnClose: func(sc *ServerConn, err error) {
|
||||||
|
close(writerTerminate)
|
||||||
|
<-writerDone
|
||||||
|
},
|
||||||
|
onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, error) {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
},
|
||||||
|
onPlay: func(ctx *ServerHandlerOnPlayCtx) (*base.Response, error) {
|
||||||
onPlay := func(ctx *ServerConnPlayCtx) (*base.Response, error) {
|
|
||||||
go func() {
|
go func() {
|
||||||
defer close(writerDone)
|
defer close(writerDone)
|
||||||
|
|
||||||
@@ -867,7 +771,7 @@ func TestServerReadPlayPausePause(t *testing.T) {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-t.C:
|
case <-t.C:
|
||||||
conn.WriteFrame(0, StreamTypeRTP, []byte("\x00\x00\x00\x00"))
|
ctx.Conn.WriteFrame(0, StreamTypeRTP, []byte("\x00\x00\x00\x00"))
|
||||||
case <-writerTerminate:
|
case <-writerTerminate:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -877,20 +781,18 @@ func TestServerReadPlayPausePause(t *testing.T) {
|
|||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
},
|
||||||
|
onPause: func(ctx *ServerHandlerOnPauseCtx) (*base.Response, error) {
|
||||||
onPause := func(ctx *ServerConnPauseCtx) (*base.Response, error) {
|
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
<-conn.Read(ServerConnReadHandlers{
|
err := s.Start("127.0.0.1:8554")
|
||||||
OnSetup: onSetup,
|
require.NoError(t, err)
|
||||||
OnPlay: onPlay,
|
defer s.Close()
|
||||||
OnPause: onPause,
|
|
||||||
})
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := net.Dial("tcp", "localhost:8554")
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
5
serversession.go
Normal file
5
serversession.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package gortsplib
|
||||||
|
|
||||||
|
// ServerSession is a server-side RTSP session.
|
||||||
|
type ServerSession struct {
|
||||||
|
}
|
@@ -42,6 +42,7 @@ func (p *clientAddr) fill(ip net.IP, port int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type serverUDPListener struct {
|
type serverUDPListener struct {
|
||||||
|
s *Server
|
||||||
pc *net.UDPConn
|
pc *net.UDPConn
|
||||||
streamType StreamType
|
streamType StreamType
|
||||||
writeTimeout time.Duration
|
writeTimeout time.Duration
|
||||||
@@ -71,6 +72,7 @@ func newServerUDPListener(
|
|||||||
}
|
}
|
||||||
|
|
||||||
u := &serverUDPListener{
|
u := &serverUDPListener{
|
||||||
|
s: s,
|
||||||
pc: pc,
|
pc: pc,
|
||||||
clients: make(map[clientAddr]*clientData),
|
clients: make(map[clientAddr]*clientData),
|
||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
@@ -125,7 +127,14 @@ func (u *serverUDPListener) run() {
|
|||||||
clientData.sc.announcedTracks[clientData.trackID].rtcpReceiver.ProcessFrame(now, u.streamType, buf[:n])
|
clientData.sc.announcedTracks[clientData.trackID].rtcpReceiver.ProcessFrame(now, u.streamType, buf[:n])
|
||||||
}
|
}
|
||||||
|
|
||||||
clientData.sc.readHandlers.OnFrame(clientData.trackID, u.streamType, buf[:n])
|
if h, ok := u.s.Handler.(ServerHandlerOnFrame); ok {
|
||||||
|
h.OnFrame(&ServerHandlerOnFrameCtx{
|
||||||
|
Conn: clientData.sc,
|
||||||
|
TrackID: clientData.trackID,
|
||||||
|
StreamType: u.streamType,
|
||||||
|
Payload: buf[:n],
|
||||||
|
})
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
Reference in New Issue
Block a user