mirror of
https://github.com/aler9/gortsplib
synced 2025-10-11 10:00:25 +08:00
server: use one IP per stream; expose multicast parameters in configuration
This commit is contained in:
@@ -272,7 +272,7 @@ func TestClientRead(t *testing.T) {
|
|||||||
v := base.StreamDeliveryMulticast
|
v := base.StreamDeliveryMulticast
|
||||||
th.Delivery = &v
|
th.Delivery = &v
|
||||||
th.Protocol = base.StreamProtocolUDP
|
th.Protocol = base.StreamProtocolUDP
|
||||||
v2 := multicastIP.String()
|
v2 := "224.1.0.1"
|
||||||
th.Destination = &v2
|
th.Destination = &v2
|
||||||
th.Ports = &[2]int{25000, 25001}
|
th.Ports = &[2]int{25000, 25001}
|
||||||
|
|
||||||
@@ -286,7 +286,7 @@ func TestClientRead(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for _, intf := range intfs {
|
for _, intf := range intfs {
|
||||||
err := p.JoinGroup(&intf, &net.UDPAddr{IP: multicastIP})
|
err := p.JoinGroup(&intf, &net.UDPAddr{IP: net.ParseIP("224.1.0.1")})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,7 +300,7 @@ func TestClientRead(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for _, intf := range intfs {
|
for _, intf := range intfs {
|
||||||
err := p.JoinGroup(&intf, &net.UDPAddr{IP: multicastIP})
|
err := p.JoinGroup(&intf, &net.UDPAddr{IP: net.ParseIP("224.1.0.1")})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,7 +341,7 @@ func TestClientRead(t *testing.T) {
|
|||||||
case "multicast":
|
case "multicast":
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
l1.WriteTo([]byte{0x01, 0x02, 0x03, 0x04}, &net.UDPAddr{
|
l1.WriteTo([]byte{0x01, 0x02, 0x03, 0x04}, &net.UDPAddr{
|
||||||
IP: multicastIP,
|
IP: net.ParseIP("224.1.0.1"),
|
||||||
Port: 25000,
|
Port: 25000,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -137,9 +137,12 @@ func (sh *serverHandler) OnFrame(ctx *gortsplib.ServerHandlerOnFrameCtx) {
|
|||||||
func main() {
|
func main() {
|
||||||
// configure server
|
// configure server
|
||||||
s := &gortsplib.Server{
|
s := &gortsplib.Server{
|
||||||
Handler: &serverHandler{},
|
Handler: &serverHandler{},
|
||||||
UDPRTPAddress: ":8000",
|
UDPRTPAddress: ":8000",
|
||||||
UDPRTCPAddress: ":8001",
|
UDPRTCPAddress: ":8001",
|
||||||
|
MulticastIPRange: "244.1.0.0/16",
|
||||||
|
MulticastRTPPort: 8002,
|
||||||
|
MulticastRTCPPort: 8003,
|
||||||
}
|
}
|
||||||
|
|
||||||
// start server and wait until a fatal error
|
// start server and wait until a fatal error
|
||||||
|
124
server.go
124
server.go
@@ -45,18 +45,22 @@ func newSessionID(sessions map[string]*ServerSession) (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type requestRes struct {
|
type sessionRequestRes struct {
|
||||||
ss *ServerSession
|
ss *ServerSession
|
||||||
res *base.Response
|
res *base.Response
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
type request struct {
|
type sessionRequestReq struct {
|
||||||
sc *ServerConn
|
sc *ServerConn
|
||||||
req *base.Request
|
req *base.Request
|
||||||
id string
|
id string
|
||||||
create bool
|
create bool
|
||||||
res chan requestRes
|
res chan sessionRequestRes
|
||||||
|
}
|
||||||
|
|
||||||
|
type streamMulticastIPReq struct {
|
||||||
|
res chan net.IP
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server is a RTSP server.
|
// Server is a RTSP server.
|
||||||
@@ -68,7 +72,7 @@ type Server struct {
|
|||||||
Handler ServerHandler
|
Handler ServerHandler
|
||||||
|
|
||||||
//
|
//
|
||||||
// connection
|
// RTSP parameters
|
||||||
//
|
//
|
||||||
// timeout of read operations.
|
// timeout of read operations.
|
||||||
// It defaults to 10 seconds
|
// It defaults to 10 seconds
|
||||||
@@ -78,16 +82,21 @@ type Server struct {
|
|||||||
WriteTimeout time.Duration
|
WriteTimeout time.Duration
|
||||||
// a TLS configuration to accept TLS (RTSPS) connections.
|
// a TLS configuration to accept TLS (RTSPS) connections.
|
||||||
TLSConfig *tls.Config
|
TLSConfig *tls.Config
|
||||||
// a port to send and receive UDP/RTP packets.
|
// a port to send and receive RTP packets with UDP.
|
||||||
// If UDPRTPAddress and UDPRTCPAddress are != "", the server can accept and send UDP streams.
|
// If UDPRTPAddress and UDPRTCPAddress are filled, the server can read and write UDP streams.
|
||||||
UDPRTPAddress string
|
UDPRTPAddress string
|
||||||
// a port to send and receive UDP/RTCP packets.
|
// a port to send and receive RTCP packets with UDP.
|
||||||
// If UDPRTPAddress and UDPRTCPAddress are != "", the server can accept and send UDP streams.
|
// If UDPRTPAddress and UDPRTCPAddress are filled, the server can read and write UDP streams.
|
||||||
UDPRTCPAddress string
|
UDPRTCPAddress string
|
||||||
|
// a range of multicast IPs to use.
|
||||||
//
|
// If MulticastIPRange, MulticastRTPPort, MulticastRTCPPort are filled, the server can read and write UDP-multicast streams.
|
||||||
// reading / writing
|
MulticastIPRange string
|
||||||
//
|
// a port to send RTP packets with UDP-multicast.
|
||||||
|
// If MulticastIPRange, MulticastRTPPort, MulticastRTCPPort are filled, the server can read and write UDP-multicast streams.
|
||||||
|
MulticastRTPPort uint
|
||||||
|
// a port to send RTCP packets with UDP-multicast.
|
||||||
|
// If MulticastIPRange, MulticastRTPPort, MulticastRTCPPort are filled, the server can read and write UDP-multicast streams.
|
||||||
|
MulticastRTCPPort uint
|
||||||
// read buffer count.
|
// read buffer count.
|
||||||
// If greater than 1, allows to pass buffers to routines different than the one
|
// If greater than 1, allows to pass buffers to routines different than the one
|
||||||
// that is reading frames.
|
// that is reading frames.
|
||||||
@@ -120,6 +129,8 @@ type Server struct {
|
|||||||
ctx context.Context
|
ctx context.Context
|
||||||
ctxCancel func()
|
ctxCancel func()
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
|
multicastNet *net.IPNet
|
||||||
|
multicastNextIP net.IP
|
||||||
tcpListener net.Listener
|
tcpListener net.Listener
|
||||||
udpRTPListener *serverUDPListener
|
udpRTPListener *serverUDPListener
|
||||||
udpRTCPListener *serverUDPListener
|
udpRTCPListener *serverUDPListener
|
||||||
@@ -129,24 +140,23 @@ type Server struct {
|
|||||||
streams map[*ServerStream]struct{}
|
streams map[*ServerStream]struct{}
|
||||||
|
|
||||||
// in
|
// in
|
||||||
connClose chan *ServerConn
|
connClose chan *ServerConn
|
||||||
sessionRequest chan request
|
sessionRequest chan sessionRequestReq
|
||||||
sessionClose chan *ServerSession
|
sessionClose chan *ServerSession
|
||||||
streamAdd chan *ServerStream
|
streamAdd chan *ServerStream
|
||||||
streamRemove chan *ServerStream
|
streamRemove chan *ServerStream
|
||||||
|
streamMulticastIP chan streamMulticastIPReq
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts listening on the given address.
|
// Start starts listening on the given address.
|
||||||
func (s *Server) Start(address string) error {
|
func (s *Server) Start(address string) error {
|
||||||
// connection
|
// RTSP parameters
|
||||||
if s.ReadTimeout == 0 {
|
if s.ReadTimeout == 0 {
|
||||||
s.ReadTimeout = 10 * time.Second
|
s.ReadTimeout = 10 * time.Second
|
||||||
}
|
}
|
||||||
if s.WriteTimeout == 0 {
|
if s.WriteTimeout == 0 {
|
||||||
s.WriteTimeout = 10 * time.Second
|
s.WriteTimeout = 10 * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
// reading / writing
|
|
||||||
if s.ReadBufferCount == 0 {
|
if s.ReadBufferCount == 0 {
|
||||||
s.ReadBufferCount = 512
|
s.ReadBufferCount = 512
|
||||||
}
|
}
|
||||||
@@ -210,11 +220,63 @@ func (s *Server) Start(address string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.MulticastIPRange != "" && (s.MulticastRTPPort == 0 || s.MulticastRTCPPort == 0) ||
|
||||||
|
(s.MulticastRTPPort != 0 && (s.MulticastRTCPPort == 0 || s.MulticastIPRange == "")) ||
|
||||||
|
s.MulticastRTCPPort != 0 && (s.MulticastRTPPort == 0 || s.MulticastIPRange == "") {
|
||||||
|
if s.udpRTPListener != nil {
|
||||||
|
s.udpRTPListener.close()
|
||||||
|
}
|
||||||
|
if s.udpRTCPListener != nil {
|
||||||
|
s.udpRTCPListener.close()
|
||||||
|
}
|
||||||
|
return fmt.Errorf("MulticastIPRange, MulticastRTPPort and MulticastRTCPPort must be used together")
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.MulticastIPRange != "" {
|
||||||
|
if (s.MulticastRTPPort % 2) != 0 {
|
||||||
|
if s.udpRTPListener != nil {
|
||||||
|
s.udpRTPListener.close()
|
||||||
|
}
|
||||||
|
if s.udpRTCPListener != nil {
|
||||||
|
s.udpRTCPListener.close()
|
||||||
|
}
|
||||||
|
return fmt.Errorf("RTP port must be even")
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.MulticastRTCPPort != (s.MulticastRTPPort + 1) {
|
||||||
|
if s.udpRTPListener != nil {
|
||||||
|
s.udpRTPListener.close()
|
||||||
|
}
|
||||||
|
if s.udpRTCPListener != nil {
|
||||||
|
s.udpRTCPListener.close()
|
||||||
|
}
|
||||||
|
return fmt.Errorf("RTCP and RTP ports must be consecutive")
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
_, s.multicastNet, err = net.ParseCIDR(s.MulticastIPRange)
|
||||||
|
if err != nil {
|
||||||
|
if s.udpRTPListener != nil {
|
||||||
|
s.udpRTPListener.close()
|
||||||
|
}
|
||||||
|
if s.udpRTCPListener != nil {
|
||||||
|
s.udpRTCPListener.close()
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.multicastNextIP = s.multicastNet.IP
|
||||||
|
}
|
||||||
|
|
||||||
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()
|
if s.udpRTPListener != nil {
|
||||||
s.udpRTPListener.close()
|
s.udpRTPListener.close()
|
||||||
|
}
|
||||||
|
if s.udpRTCPListener != nil {
|
||||||
|
s.udpRTCPListener.close()
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,10 +308,11 @@ func (s *Server) run() {
|
|||||||
s.conns = make(map[*ServerConn]struct{})
|
s.conns = make(map[*ServerConn]struct{})
|
||||||
s.streams = make(map[*ServerStream]struct{})
|
s.streams = make(map[*ServerStream]struct{})
|
||||||
s.connClose = make(chan *ServerConn)
|
s.connClose = make(chan *ServerConn)
|
||||||
s.sessionRequest = make(chan request)
|
s.sessionRequest = make(chan sessionRequestReq)
|
||||||
s.sessionClose = make(chan *ServerSession)
|
s.sessionClose = make(chan *ServerSession)
|
||||||
s.streamAdd = make(chan *ServerStream)
|
s.streamAdd = make(chan *ServerStream)
|
||||||
s.streamRemove = make(chan *ServerStream)
|
s.streamRemove = make(chan *ServerStream)
|
||||||
|
s.streamMulticastIP = make(chan streamMulticastIPReq)
|
||||||
|
|
||||||
s.wg.Add(1)
|
s.wg.Add(1)
|
||||||
connNew := make(chan net.Conn)
|
connNew := make(chan net.Conn)
|
||||||
@@ -300,7 +363,7 @@ outer:
|
|||||||
ss.request <- req
|
ss.request <- req
|
||||||
} else {
|
} else {
|
||||||
if !req.create {
|
if !req.create {
|
||||||
req.res <- requestRes{
|
req.res <- sessionRequestRes{
|
||||||
res: &base.Response{
|
res: &base.Response{
|
||||||
StatusCode: base.StatusBadRequest,
|
StatusCode: base.StatusBadRequest,
|
||||||
},
|
},
|
||||||
@@ -311,7 +374,7 @@ outer:
|
|||||||
|
|
||||||
id, err := newSessionID(s.sessions)
|
id, err := newSessionID(s.sessions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
req.res <- requestRes{
|
req.res <- sessionRequestRes{
|
||||||
res: &base.Response{
|
res: &base.Response{
|
||||||
StatusCode: base.StatusBadRequest,
|
StatusCode: base.StatusBadRequest,
|
||||||
},
|
},
|
||||||
@@ -326,7 +389,7 @@ outer:
|
|||||||
select {
|
select {
|
||||||
case ss.request <- req:
|
case ss.request <- req:
|
||||||
case <-ss.ctx.Done():
|
case <-ss.ctx.Done():
|
||||||
req.res <- requestRes{
|
req.res <- sessionRequestRes{
|
||||||
res: &base.Response{
|
res: &base.Response{
|
||||||
StatusCode: base.StatusBadRequest,
|
StatusCode: base.StatusBadRequest,
|
||||||
},
|
},
|
||||||
@@ -348,6 +411,15 @@ outer:
|
|||||||
case st := <-s.streamRemove:
|
case st := <-s.streamRemove:
|
||||||
delete(s.streams, st)
|
delete(s.streams, st)
|
||||||
|
|
||||||
|
case req := <-s.streamMulticastIP:
|
||||||
|
ip32 := binary.BigEndian.Uint32(s.multicastNextIP)
|
||||||
|
mask := binary.BigEndian.Uint32(s.multicastNet.Mask)
|
||||||
|
ip32 = (ip32 & mask) | ((ip32 + 1) & ^mask)
|
||||||
|
ip := make(net.IP, 4)
|
||||||
|
binary.BigEndian.PutUint32(ip, ip32)
|
||||||
|
s.multicastNextIP = ip
|
||||||
|
req.res <- ip
|
||||||
|
|
||||||
case <-s.ctx.Done():
|
case <-s.ctx.Done():
|
||||||
break outer
|
break outer
|
||||||
}
|
}
|
||||||
|
@@ -357,10 +357,15 @@ func TestServerRead(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch proto {
|
switch proto {
|
||||||
case "udp", "multicast":
|
case "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"
|
||||||
|
|
||||||
|
case "multicast":
|
||||||
|
s.MulticastIPRange = "224.1.0.0/16"
|
||||||
|
s.MulticastRTPPort = 8000
|
||||||
|
s.MulticastRTCPPort = 8001
|
||||||
|
|
||||||
case "tls":
|
case "tls":
|
||||||
cert, err := tls.X509KeyPair(serverCert, serverKey)
|
cert, err := tls.X509KeyPair(serverCert, serverKey)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -451,7 +456,7 @@ func TestServerRead(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for _, intf := range intfs {
|
for _, intf := range intfs {
|
||||||
err := p.JoinGroup(&intf, &net.UDPAddr{IP: multicastIP})
|
err := p.JoinGroup(&intf, &net.UDPAddr{IP: net.ParseIP(*th.Destination)})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,7 +470,7 @@ func TestServerRead(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for _, intf := range intfs {
|
for _, intf := range intfs {
|
||||||
err := p.JoinGroup(&intf, &net.UDPAddr{IP: multicastIP})
|
err := p.JoinGroup(&intf, &net.UDPAddr{IP: net.ParseIP(*th.Destination)})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -527,7 +532,7 @@ func TestServerRead(t *testing.T) {
|
|||||||
|
|
||||||
case "multicast":
|
case "multicast":
|
||||||
l2.WriteTo([]byte{0x01, 0x02, 0x03, 0x04}, &net.UDPAddr{
|
l2.WriteTo([]byte{0x01, 0x02, 0x03, 0x04}, &net.UDPAddr{
|
||||||
IP: multicastIP,
|
IP: net.ParseIP(*th.Destination),
|
||||||
Port: th.Ports[1],
|
Port: th.Ports[1],
|
||||||
})
|
})
|
||||||
<-framesReceived
|
<-framesReceived
|
||||||
|
@@ -389,6 +389,9 @@ func TestServerHighLevelPublishRead(t *testing.T) {
|
|||||||
proto = "rtsp"
|
proto = "rtsp"
|
||||||
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"
|
||||||
|
s.MulticastIPRange = "224.1.0.0/16"
|
||||||
|
s.MulticastRTPPort = 8002
|
||||||
|
s.MulticastRTCPPort = 8003
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.Start("localhost:8554")
|
err := s.Start("localhost:8554")
|
||||||
|
@@ -543,8 +543,8 @@ func (sc *ServerConn) handleRequestInSession(
|
|||||||
// if the session is already linked to this conn, communicate directly with it
|
// if the session is already linked to this conn, communicate directly with it
|
||||||
if sxID != "" {
|
if sxID != "" {
|
||||||
if ss, ok := sc.sessions[sxID]; ok {
|
if ss, ok := sc.sessions[sxID]; ok {
|
||||||
cres := make(chan requestRes)
|
cres := make(chan sessionRequestRes)
|
||||||
sreq := request{
|
sreq := sessionRequestReq{
|
||||||
sc: sc,
|
sc: sc,
|
||||||
req: req,
|
req: req,
|
||||||
id: sxID,
|
id: sxID,
|
||||||
@@ -566,8 +566,8 @@ func (sc *ServerConn) handleRequestInSession(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// otherwise, pass through Server
|
// otherwise, pass through Server
|
||||||
cres := make(chan requestRes)
|
cres := make(chan sessionRequestRes)
|
||||||
sreq := request{
|
sreq := sessionRequestReq{
|
||||||
sc: sc,
|
sc: sc,
|
||||||
req: req,
|
req: req,
|
||||||
id: sxID,
|
id: sxID,
|
||||||
|
@@ -137,7 +137,7 @@ type ServerSession struct {
|
|||||||
udpLastFrameTime *int64 // publish, udp
|
udpLastFrameTime *int64 // publish, udp
|
||||||
|
|
||||||
// in
|
// in
|
||||||
request chan request
|
request chan sessionRequestReq
|
||||||
connRemove chan *ServerConn
|
connRemove chan *ServerConn
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +156,7 @@ func newServerSession(
|
|||||||
ctxCancel: ctxCancel,
|
ctxCancel: ctxCancel,
|
||||||
conns: make(map[*ServerConn]struct{}),
|
conns: make(map[*ServerConn]struct{}),
|
||||||
lastRequestTime: time.Now(),
|
lastRequestTime: time.Now(),
|
||||||
request: make(chan request),
|
request: make(chan sessionRequestReq),
|
||||||
connRemove: make(chan *ServerConn),
|
connRemove: make(chan *ServerConn),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,11 +253,11 @@ func (ss *ServerSession) run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := err.(liberrors.ErrServerSessionTeardown); ok {
|
if _, ok := err.(liberrors.ErrServerSessionTeardown); ok {
|
||||||
req.res <- requestRes{res: res, err: nil}
|
req.res <- sessionRequestRes{res: res, err: nil}
|
||||||
return liberrors.ErrServerSessionTeardown{}
|
return liberrors.ErrServerSessionTeardown{}
|
||||||
}
|
}
|
||||||
|
|
||||||
req.res <- requestRes{
|
req.res <- sessionRequestRes{
|
||||||
res: res,
|
res: res,
|
||||||
err: err,
|
err: err,
|
||||||
ss: ss,
|
ss: ss,
|
||||||
@@ -327,7 +327,8 @@ func (ss *ServerSession) run() {
|
|||||||
case ServerSessionStatePlay:
|
case ServerSessionStatePlay:
|
||||||
ss.setuppedStream.readerSetInactive(ss)
|
ss.setuppedStream.readerSetInactive(ss)
|
||||||
|
|
||||||
if *ss.setuppedProtocol == base.StreamProtocolUDP {
|
if *ss.setuppedProtocol == base.StreamProtocolUDP &&
|
||||||
|
*ss.setuppedDelivery == base.StreamDeliveryUnicast {
|
||||||
ss.s.udpRTCPListener.removeClient(ss)
|
ss.s.udpRTCPListener.removeClient(ss)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -572,18 +573,23 @@ func (ss *ServerSession) handleRequest(sc *ServerConn, req *base.Request) (*base
|
|||||||
}
|
}
|
||||||
|
|
||||||
if inTH.Protocol == base.StreamProtocolUDP {
|
if inTH.Protocol == base.StreamProtocolUDP {
|
||||||
if ss.s.udpRTPListener == nil {
|
if delivery == base.StreamDeliveryUnicast {
|
||||||
|
if ss.s.udpRTPListener == nil {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusUnsupportedTransport,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if inTH.ClientPorts == nil {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusBadRequest,
|
||||||
|
}, liberrors.ErrServerTransportHeaderNoClientPorts{}
|
||||||
|
}
|
||||||
|
} else if ss.s.MulticastIPRange == "" {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusUnsupportedTransport,
|
StatusCode: base.StatusUnsupportedTransport,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if delivery == base.StreamDeliveryUnicast && inTH.ClientPorts == nil {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusBadRequest,
|
|
||||||
}, liberrors.ErrServerTransportHeaderNoClientPorts{}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if delivery == base.StreamDeliveryMulticast {
|
if delivery == base.StreamDeliveryMulticast {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
@@ -626,16 +632,22 @@ func (ss *ServerSession) handleRequest(sc *ServerConn, req *base.Request) (*base
|
|||||||
})
|
})
|
||||||
|
|
||||||
if res.StatusCode == base.StatusOK {
|
if res.StatusCode == base.StatusOK {
|
||||||
th := headers.Transport{}
|
|
||||||
|
|
||||||
if ss.state == ServerSessionStateInitial {
|
if ss.state == ServerSessionStateInitial {
|
||||||
|
err := stream.readerAdd(ss, delivery == base.StreamDeliveryMulticast)
|
||||||
|
if err != nil {
|
||||||
|
return &base.Response{
|
||||||
|
StatusCode: base.StatusBadRequest,
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
ss.state = ServerSessionStatePrePlay
|
ss.state = ServerSessionStatePrePlay
|
||||||
ss.setuppedPath = &path
|
ss.setuppedPath = &path
|
||||||
ss.setuppedQuery = &query
|
ss.setuppedQuery = &query
|
||||||
ss.setuppedStream = stream
|
ss.setuppedStream = stream
|
||||||
stream.readerAdd(ss, delivery == base.StreamDeliveryMulticast)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
th := headers.Transport{}
|
||||||
|
|
||||||
if ss.state == ServerSessionStatePrePlay {
|
if ss.state == ServerSessionStatePrePlay {
|
||||||
ssrc := stream.ssrc(trackID)
|
ssrc := stream.ssrc(trackID)
|
||||||
if ssrc != 0 {
|
if ssrc != 0 {
|
||||||
@@ -663,7 +675,7 @@ func (ss *ServerSession) handleRequest(sc *ServerConn, req *base.Request) (*base
|
|||||||
th.Delivery = &de
|
th.Delivery = &de
|
||||||
v := uint(127)
|
v := uint(127)
|
||||||
th.TTL = &v
|
th.TTL = &v
|
||||||
d := multicastIP.String()
|
d := stream.multicastListeners[trackID].rtpListener.ip().String()
|
||||||
th.Destination = &d
|
th.Destination = &d
|
||||||
th.Ports = &[2]int{
|
th.Ports = &[2]int{
|
||||||
stream.multicastListeners[trackID].rtpListener.port(),
|
stream.multicastListeners[trackID].rtpListener.port(),
|
||||||
|
@@ -108,7 +108,7 @@ func (st *ServerStream) lastSequenceNumber(trackID int) uint16 {
|
|||||||
return uint16(atomic.LoadUint32(&st.trackInfos[trackID].lastSequenceNumber))
|
return uint16(atomic.LoadUint32(&st.trackInfos[trackID].lastSequenceNumber))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *ServerStream) readerAdd(ss *ServerSession, isMulticast bool) {
|
func (st *ServerStream) readerAdd(ss *ServerSession, isMulticast bool) error {
|
||||||
st.mutex.Lock()
|
st.mutex.Lock()
|
||||||
defer st.mutex.Unlock()
|
defer st.mutex.Unlock()
|
||||||
|
|
||||||
@@ -123,18 +123,31 @@ func (st *ServerStream) readerAdd(ss *ServerSession, isMulticast bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !isMulticast || st.multicastListeners != nil {
|
if !isMulticast || st.multicastListeners != nil {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
st.multicastListeners = make([]*listenerPair, len(st.tracks))
|
st.multicastListeners = make([]*listenerPair, len(st.tracks))
|
||||||
|
|
||||||
for i := range st.tracks {
|
for i := range st.tracks {
|
||||||
rtpListener, rtcpListener := newServerUDPListenerMulticastPair(st.s)
|
rtpListener, rtcpListener, err := newServerUDPListenerMulticastPair(st.s)
|
||||||
|
if err != nil {
|
||||||
|
for _, l := range st.multicastListeners {
|
||||||
|
if l != nil {
|
||||||
|
l.rtpListener.close()
|
||||||
|
l.rtcpListener.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
st.multicastListeners = nil
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
st.multicastListeners[i] = &listenerPair{
|
st.multicastListeners[i] = &listenerPair{
|
||||||
rtpListener: rtpListener,
|
rtpListener: rtpListener,
|
||||||
rtcpListener: rtcpListener,
|
rtcpListener: rtcpListener,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *ServerStream) readerRemove(ss *ServerSession) {
|
func (st *ServerStream) readerRemove(ss *ServerSession) {
|
||||||
@@ -207,13 +220,13 @@ func (st *ServerStream) WriteFrame(trackID int, streamType StreamType, payload [
|
|||||||
if st.multicastListeners != nil {
|
if st.multicastListeners != nil {
|
||||||
if streamType == StreamTypeRTP {
|
if streamType == StreamTypeRTP {
|
||||||
st.multicastListeners[trackID].rtpListener.write(payload, &net.UDPAddr{
|
st.multicastListeners[trackID].rtpListener.write(payload, &net.UDPAddr{
|
||||||
IP: multicastIP,
|
IP: st.multicastListeners[trackID].rtpListener.ip(),
|
||||||
Zone: "",
|
Zone: "",
|
||||||
Port: st.multicastListeners[trackID].rtpListener.port(),
|
Port: st.multicastListeners[trackID].rtpListener.port(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
st.multicastListeners[trackID].rtcpListener.write(payload, &net.UDPAddr{
|
st.multicastListeners[trackID].rtcpListener.write(payload, &net.UDPAddr{
|
||||||
IP: multicastIP,
|
IP: st.multicastListeners[trackID].rtpListener.ip(),
|
||||||
Zone: "",
|
Zone: "",
|
||||||
Port: st.multicastListeners[trackID].rtcpListener.port(),
|
Port: st.multicastListeners[trackID].rtcpListener.port(),
|
||||||
})
|
})
|
||||||
|
@@ -2,7 +2,7 @@ package gortsplib
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"math/rand"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -19,8 +19,6 @@ const (
|
|||||||
serverConnUDPListenerKernelReadBufferSize = 0x80000 // same as gstreamer's rtspsrc
|
serverConnUDPListenerKernelReadBufferSize = 0x80000 // same as gstreamer's rtspsrc
|
||||||
)
|
)
|
||||||
|
|
||||||
var multicastIP = net.ParseIP("239.0.0.0")
|
|
||||||
|
|
||||||
type bufAddrPair struct {
|
type bufAddrPair struct {
|
||||||
buf []byte
|
buf []byte
|
||||||
addr *net.UDPAddr
|
addr *net.UDPAddr
|
||||||
@@ -55,6 +53,7 @@ type serverUDPListener struct {
|
|||||||
ctxCancel func()
|
ctxCancel func()
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
pc *net.UDPConn
|
pc *net.UDPConn
|
||||||
|
listenIP net.IP
|
||||||
streamType StreamType
|
streamType StreamType
|
||||||
writeTimeout time.Duration
|
writeTimeout time.Duration
|
||||||
readBuf *multibuffer.MultiBuffer
|
readBuf *multibuffer.MultiBuffer
|
||||||
@@ -63,25 +62,29 @@ type serverUDPListener struct {
|
|||||||
ringBuffer *ringbuffer.RingBuffer
|
ringBuffer *ringbuffer.RingBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
func newServerUDPListenerMulticastPair(s *Server) (*serverUDPListener, *serverUDPListener) {
|
func newServerUDPListenerMulticastPair(s *Server) (*serverUDPListener, *serverUDPListener, error) {
|
||||||
// choose two consecutive ports in range 65535-10000
|
res := make(chan net.IP)
|
||||||
// rtp must be even and rtcp odd
|
select {
|
||||||
for {
|
case s.streamMulticastIP <- streamMulticastIPReq{res: res}:
|
||||||
rtpPort := (rand.Intn((65535-10000)/2) * 2) + 10000
|
case <-s.ctx.Done():
|
||||||
rtpListener, err := newServerUDPListener(s, true, multicastIP.String()+":"+strconv.FormatInt(int64(rtpPort), 10), StreamTypeRTP)
|
return nil, nil, fmt.Errorf("terminated")
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
rtcpPort := rtpPort + 1
|
|
||||||
rtcpListener, err := newServerUDPListener(s, true, multicastIP.String()+":"+strconv.FormatInt(int64(rtcpPort), 10), StreamTypeRTCP)
|
|
||||||
if err != nil {
|
|
||||||
rtpListener.close()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return rtpListener, rtcpListener
|
|
||||||
}
|
}
|
||||||
|
ip := <-res
|
||||||
|
|
||||||
|
rtpListener, err := newServerUDPListener(s, true,
|
||||||
|
ip.String()+":"+strconv.FormatInt(int64(s.MulticastRTPPort), 10), StreamTypeRTP)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rtcpListener, err := newServerUDPListener(s, true,
|
||||||
|
ip.String()+":"+strconv.FormatInt(int64(s.MulticastRTCPPort), 10), StreamTypeRTCP)
|
||||||
|
if err != nil {
|
||||||
|
rtpListener.close()
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtpListener, rtcpListener, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newServerUDPListener(
|
func newServerUDPListener(
|
||||||
@@ -90,6 +93,7 @@ func newServerUDPListener(
|
|||||||
address string,
|
address string,
|
||||||
streamType StreamType) (*serverUDPListener, error) {
|
streamType StreamType) (*serverUDPListener, error) {
|
||||||
var pc *net.UDPConn
|
var pc *net.UDPConn
|
||||||
|
var listenIP net.IP
|
||||||
if multicast {
|
if multicast {
|
||||||
host, port, err := net.SplitHostPort(address)
|
host, port, err := net.SplitHostPort(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -113,8 +117,10 @@ func newServerUDPListener(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
listenIP = net.ParseIP(host)
|
||||||
|
|
||||||
for _, intf := range intfs {
|
for _, intf := range intfs {
|
||||||
err := p.JoinGroup(&intf, &net.UDPAddr{IP: net.ParseIP(host)})
|
err := p.JoinGroup(&intf, &net.UDPAddr{IP: listenIP})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -126,7 +132,9 @@ func newServerUDPListener(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pc = tmp.(*net.UDPConn)
|
pc = tmp.(*net.UDPConn)
|
||||||
|
listenIP = tmp.LocalAddr().(*net.UDPAddr).IP
|
||||||
}
|
}
|
||||||
|
|
||||||
err := pc.SetReadBuffer(serverConnUDPListenerKernelReadBufferSize)
|
err := pc.SetReadBuffer(serverConnUDPListenerKernelReadBufferSize)
|
||||||
@@ -141,6 +149,7 @@ func newServerUDPListener(
|
|||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
ctxCancel: ctxCancel,
|
ctxCancel: ctxCancel,
|
||||||
pc: pc,
|
pc: pc,
|
||||||
|
listenIP: listenIP,
|
||||||
clients: make(map[clientAddr]*clientData),
|
clients: make(map[clientAddr]*clientData),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,6 +169,14 @@ func (u *serverUDPListener) close() {
|
|||||||
u.wg.Wait()
|
u.wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *serverUDPListener) ip() net.IP {
|
||||||
|
return u.listenIP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *serverUDPListener) port() int {
|
||||||
|
return u.pc.LocalAddr().(*net.UDPAddr).Port
|
||||||
|
}
|
||||||
|
|
||||||
func (u *serverUDPListener) run() {
|
func (u *serverUDPListener) run() {
|
||||||
defer u.wg.Done()
|
defer u.wg.Done()
|
||||||
|
|
||||||
@@ -225,10 +242,6 @@ func (u *serverUDPListener) run() {
|
|||||||
u.ringBuffer.Close()
|
u.ringBuffer.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *serverUDPListener) port() int {
|
|
||||||
return u.pc.LocalAddr().(*net.UDPAddr).Port
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *serverUDPListener) write(buf []byte, addr *net.UDPAddr) {
|
func (u *serverUDPListener) write(buf []byte, addr *net.UDPAddr) {
|
||||||
u.ringBuffer.Push(bufAddrPair{buf, addr})
|
u.ringBuffer.Push(bufAddrPair{buf, addr})
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user