mirror of
https://github.com/aler9/gortsplib
synced 2025-09-27 11:32:08 +08:00
make most methods thread safe (#882)
Client: Stats ServerConn: Session, Stats ServerSession: State, Stats, Medias, Path, Query, Stream, SetuppedSecure, SetuppedTransport, AnnouncedDescription
This commit is contained in:
@@ -543,6 +543,7 @@ type Client struct {
|
|||||||
|
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
ctxCancel func()
|
ctxCancel func()
|
||||||
|
propsMutex sync.RWMutex
|
||||||
state clientState
|
state clientState
|
||||||
nconn net.Conn
|
nconn net.Conn
|
||||||
conn *conn.Conn
|
conn *conn.Conn
|
||||||
@@ -2023,16 +2024,19 @@ func (c *Client) doSetup(
|
|||||||
udpRTPListener = nil
|
udpRTPListener = nil
|
||||||
udpRTCPListener = nil
|
udpRTCPListener = nil
|
||||||
|
|
||||||
|
c.propsMutex.Lock()
|
||||||
|
|
||||||
if c.setuppedMedias == nil {
|
if c.setuppedMedias == nil {
|
||||||
c.setuppedMedias = make(map[*description.Media]*clientMedia)
|
c.setuppedMedias = make(map[*description.Media]*clientMedia)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.setuppedMedias[medi] = cm
|
c.setuppedMedias[medi] = cm
|
||||||
|
|
||||||
c.baseURL = baseURL
|
c.baseURL = baseURL
|
||||||
c.setuppedTransport = &transport
|
c.setuppedTransport = &transport
|
||||||
c.setuppedProfile = th.Profile
|
c.setuppedProfile = th.Profile
|
||||||
|
|
||||||
|
c.propsMutex.Unlock()
|
||||||
|
|
||||||
if medi.IsBackChannel {
|
if medi.IsBackChannel {
|
||||||
c.backChannelSetupped = true
|
c.backChannelSetupped = true
|
||||||
} else {
|
} else {
|
||||||
@@ -2426,6 +2430,9 @@ func (c *Client) PacketNTP(medi *description.Media, pkt *rtp.Packet) (time.Time,
|
|||||||
|
|
||||||
// Stats returns client statistics.
|
// Stats returns client statistics.
|
||||||
func (c *Client) Stats() *ClientStats {
|
func (c *Client) Stats() *ClientStats {
|
||||||
|
c.propsMutex.RLock()
|
||||||
|
defer c.propsMutex.RUnlock()
|
||||||
|
|
||||||
mediaStats := func() map[*description.Media]StatsSessionMedia { //nolint:dupl
|
mediaStats := func() map[*description.Media]StatsSessionMedia { //nolint:dupl
|
||||||
ret := make(map[*description.Media]StatsSessionMedia, len(c.setuppedMedias))
|
ret := make(map[*description.Media]StatsSessionMedia, len(c.setuppedMedias))
|
||||||
|
|
||||||
|
@@ -125,21 +125,11 @@ func (cm *clientMedia) initialize() {
|
|||||||
f.initialize()
|
f.initialize()
|
||||||
cm.formats[forma.PayloadType()] = f
|
cm.formats[forma.PayloadType()] = f
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func (cm *clientMedia) close() {
|
|
||||||
cm.stop()
|
|
||||||
|
|
||||||
for _, ct := range cm.formats {
|
|
||||||
ct.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cm *clientMedia) start() {
|
|
||||||
if cm.udpRTPListener != nil {
|
if cm.udpRTPListener != nil {
|
||||||
cm.writePacketRTCPInQueue = cm.writePacketRTCPInQueueUDP
|
cm.writePacketRTCPInQueue = cm.writePacketRTCPInQueueUDP
|
||||||
|
|
||||||
if cm.c.state == clientStateRecord || cm.media.IsBackChannel {
|
if cm.c.state == clientStatePreRecord || cm.media.IsBackChannel {
|
||||||
cm.udpRTPListener.readFunc = cm.readPacketRTPUDPRecord
|
cm.udpRTPListener.readFunc = cm.readPacketRTPUDPRecord
|
||||||
cm.udpRTCPListener.readFunc = cm.readPacketRTCPUDPRecord
|
cm.udpRTCPListener.readFunc = cm.readPacketRTCPUDPRecord
|
||||||
} else {
|
} else {
|
||||||
@@ -153,7 +143,7 @@ func (cm *clientMedia) start() {
|
|||||||
cm.c.tcpCallbackByChannel = make(map[int]readFunc)
|
cm.c.tcpCallbackByChannel = make(map[int]readFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cm.c.state == clientStateRecord || cm.media.IsBackChannel {
|
if cm.c.state == clientStatePreRecord || cm.media.IsBackChannel {
|
||||||
cm.c.tcpCallbackByChannel[cm.tcpChannel] = cm.readPacketRTPTCPRecord
|
cm.c.tcpCallbackByChannel[cm.tcpChannel] = cm.readPacketRTPTCPRecord
|
||||||
cm.c.tcpCallbackByChannel[cm.tcpChannel+1] = cm.readPacketRTCPTCPRecord
|
cm.c.tcpCallbackByChannel[cm.tcpChannel+1] = cm.readPacketRTCPTCPRecord
|
||||||
} else {
|
} else {
|
||||||
@@ -161,7 +151,17 @@ func (cm *clientMedia) start() {
|
|||||||
cm.c.tcpCallbackByChannel[cm.tcpChannel+1] = cm.readPacketRTCPTCPPlay
|
cm.c.tcpCallbackByChannel[cm.tcpChannel+1] = cm.readPacketRTCPTCPPlay
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *clientMedia) close() {
|
||||||
|
cm.stop()
|
||||||
|
|
||||||
|
for _, ct := range cm.formats {
|
||||||
|
ct.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *clientMedia) start() {
|
||||||
if cm.udpRTPListener != nil {
|
if cm.udpRTPListener != nil {
|
||||||
cm.udpRTPListener.start()
|
cm.udpRTPListener.start()
|
||||||
cm.udpRTCPListener.start()
|
cm.udpRTCPListener.start()
|
||||||
|
@@ -640,6 +640,11 @@ func TestClientPlay(t *testing.T) {
|
|||||||
sd, _, err := c.Describe(u)
|
sd, _, err := c.Describe(u)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// test that properties can be accessed in parallel
|
||||||
|
go func() {
|
||||||
|
c.Stats()
|
||||||
|
}()
|
||||||
|
|
||||||
err = c.SetupAll(sd.BaseURL, sd.Medias)
|
err = c.SetupAll(sd.BaseURL, sd.Medias)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@@ -108,9 +108,11 @@ func (u *clientUDPListener) start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *clientUDPListener) stop() {
|
func (u *clientUDPListener) stop() {
|
||||||
u.pc.SetReadDeadline(time.Now())
|
if u.running {
|
||||||
<-u.done
|
u.pc.SetReadDeadline(time.Now())
|
||||||
u.running = false
|
<-u.done
|
||||||
|
u.running = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *clientUDPListener) run() {
|
func (u *clientUDPListener) run() {
|
||||||
|
@@ -9,6 +9,7 @@ import (
|
|||||||
gourl "net/url"
|
gourl "net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/auth"
|
"github.com/bluenviron/gortsplib/v4/pkg/auth"
|
||||||
@@ -199,6 +200,7 @@ type ServerConn struct {
|
|||||||
|
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
ctxCancel func()
|
ctxCancel func()
|
||||||
|
propsMutex sync.RWMutex
|
||||||
userData interface{}
|
userData interface{}
|
||||||
remoteAddr *net.TCPAddr
|
remoteAddr *net.TCPAddr
|
||||||
bc *bytecounter.ByteCounter
|
bc *bytecounter.ByteCounter
|
||||||
@@ -268,6 +270,9 @@ func (sc *ServerConn) UserData() interface{} {
|
|||||||
|
|
||||||
// Session returns the associated session.
|
// Session returns the associated session.
|
||||||
func (sc *ServerConn) Session() *ServerSession {
|
func (sc *ServerConn) Session() *ServerSession {
|
||||||
|
sc.propsMutex.RLock()
|
||||||
|
defer sc.propsMutex.RUnlock()
|
||||||
|
|
||||||
return sc.session
|
return sc.session
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -658,7 +663,11 @@ func (sc *ServerConn) handleRequestInSession(
|
|||||||
}
|
}
|
||||||
|
|
||||||
res, session, err := sc.s.handleRequest(sreq)
|
res, session, err := sc.s.handleRequest(sreq)
|
||||||
|
|
||||||
|
sc.propsMutex.Lock()
|
||||||
sc.session = session
|
sc.session = session
|
||||||
|
sc.propsMutex.Unlock()
|
||||||
|
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -665,7 +665,21 @@ func TestServerPlay(t *testing.T) {
|
|||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, stream, nil
|
}, stream, nil
|
||||||
},
|
},
|
||||||
onSetup: func(_ *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) {
|
onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) {
|
||||||
|
// test that properties can be accessed in parallel
|
||||||
|
go func() {
|
||||||
|
ctx.Conn.Session()
|
||||||
|
ctx.Conn.Stats()
|
||||||
|
ctx.Session.State()
|
||||||
|
ctx.Session.Stats()
|
||||||
|
ctx.Session.Medias()
|
||||||
|
ctx.Session.Path()
|
||||||
|
ctx.Session.Query()
|
||||||
|
ctx.Session.Stream()
|
||||||
|
ctx.Session.SetuppedTransport()
|
||||||
|
ctx.Session.SetuppedSecure()
|
||||||
|
}()
|
||||||
|
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, stream, nil
|
}, stream, nil
|
||||||
@@ -1726,7 +1740,13 @@ func TestServerPlayPause(t *testing.T) {
|
|||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
onPause: func(_ *ServerHandlerOnPauseCtx) (*base.Response, error) {
|
onPause: func(ctx *ServerHandlerOnPauseCtx) (*base.Response, error) {
|
||||||
|
// test that properties can be accessed in parallel
|
||||||
|
go func() {
|
||||||
|
ctx.Session.State()
|
||||||
|
ctx.Session.Stats()
|
||||||
|
}()
|
||||||
|
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
|
@@ -602,7 +602,14 @@ func TestServerRecord(t *testing.T) {
|
|||||||
|
|
||||||
close(sessionClosed)
|
close(sessionClosed)
|
||||||
},
|
},
|
||||||
onAnnounce: func(_ *ServerHandlerOnAnnounceCtx) (*base.Response, error) {
|
onAnnounce: func(ctx *ServerHandlerOnAnnounceCtx) (*base.Response, error) {
|
||||||
|
// test that properties can be accessed in parallel
|
||||||
|
go func() {
|
||||||
|
ctx.Session.State()
|
||||||
|
ctx.Session.Stats()
|
||||||
|
ctx.Session.AnnouncedDescription()
|
||||||
|
}()
|
||||||
|
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -1732,7 +1739,13 @@ func TestServerRecordPausePause(t *testing.T) {
|
|||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
onPause: func(_ *ServerHandlerOnPauseCtx) (*base.Response, error) {
|
onPause: func(ctx *ServerHandlerOnPauseCtx) (*base.Response, error) {
|
||||||
|
// test that properties can be accessed in parallel
|
||||||
|
go func() {
|
||||||
|
ctx.Session.State()
|
||||||
|
ctx.Session.Stats()
|
||||||
|
}()
|
||||||
|
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusOK,
|
StatusCode: base.StatusOK,
|
||||||
}, nil
|
}, nil
|
||||||
|
@@ -428,8 +428,9 @@ type ServerSession struct {
|
|||||||
secretID string // must not be shared, allows to take ownership of the session
|
secretID string // must not be shared, allows to take ownership of the session
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
ctxCancel func()
|
ctxCancel func()
|
||||||
userData interface{}
|
propsMutex sync.RWMutex
|
||||||
conns map[*ServerConn]struct{}
|
conns map[*ServerConn]struct{}
|
||||||
|
userData interface{}
|
||||||
state ServerSessionState
|
state ServerSessionState
|
||||||
setuppedMedias map[*description.Media]*serverSessionMedia
|
setuppedMedias map[*description.Media]*serverSessionMedia
|
||||||
setuppedMediasOrdered []*serverSessionMedia
|
setuppedMediasOrdered []*serverSessionMedia
|
||||||
@@ -506,11 +507,17 @@ func (ss *ServerSession) BytesSent() uint64 {
|
|||||||
|
|
||||||
// State returns the state of the session.
|
// State returns the state of the session.
|
||||||
func (ss *ServerSession) State() ServerSessionState {
|
func (ss *ServerSession) State() ServerSessionState {
|
||||||
|
ss.propsMutex.RLock()
|
||||||
|
defer ss.propsMutex.RUnlock()
|
||||||
|
|
||||||
return ss.state
|
return ss.state
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetuppedTransport returns the transport negotiated during SETUP.
|
// SetuppedTransport returns the transport negotiated during SETUP.
|
||||||
func (ss *ServerSession) SetuppedTransport() *Transport {
|
func (ss *ServerSession) SetuppedTransport() *Transport {
|
||||||
|
ss.propsMutex.RLock()
|
||||||
|
defer ss.propsMutex.RUnlock()
|
||||||
|
|
||||||
return ss.setuppedTransport
|
return ss.setuppedTransport
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -518,47 +525,62 @@ func (ss *ServerSession) SetuppedTransport() *Transport {
|
|||||||
// If this is false, it does not mean that the stream is not secure, since
|
// If this is false, it does not mean that the stream is not secure, since
|
||||||
// there are some combinations that are secure nonetheless, like RTSPS+TCP+unsecure.
|
// there are some combinations that are secure nonetheless, like RTSPS+TCP+unsecure.
|
||||||
func (ss *ServerSession) SetuppedSecure() bool {
|
func (ss *ServerSession) SetuppedSecure() bool {
|
||||||
|
ss.propsMutex.RLock()
|
||||||
|
defer ss.propsMutex.RUnlock()
|
||||||
|
|
||||||
return isSecure(ss.setuppedProfile)
|
return isSecure(ss.setuppedProfile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetuppedStream returns the stream associated with the session.
|
// SetuppedStream returns the stream associated with the session.
|
||||||
//
|
//
|
||||||
// Deprecated: replaced by Stream
|
// Deprecated: replaced by Stream.
|
||||||
func (ss *ServerSession) SetuppedStream() *ServerStream {
|
func (ss *ServerSession) SetuppedStream() *ServerStream {
|
||||||
return ss.Stream()
|
return ss.Stream()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stream returns the stream associated with the session.
|
// Stream returns the stream associated with the session.
|
||||||
func (ss *ServerSession) Stream() *ServerStream {
|
func (ss *ServerSession) Stream() *ServerStream {
|
||||||
|
ss.propsMutex.RLock()
|
||||||
|
defer ss.propsMutex.RUnlock()
|
||||||
|
|
||||||
return ss.setuppedStream
|
return ss.setuppedStream
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetuppedPath returns the path sent during SETUP or ANNOUNCE.
|
// SetuppedPath returns the path sent during SETUP or ANNOUNCE.
|
||||||
//
|
//
|
||||||
// Deprecated: replaced by Path
|
// Deprecated: replaced by Path.
|
||||||
func (ss *ServerSession) SetuppedPath() string {
|
func (ss *ServerSession) SetuppedPath() string {
|
||||||
return ss.Path()
|
return ss.Path()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path returns the path sent during SETUP or ANNOUNCE.
|
// Path returns the path sent during SETUP or ANNOUNCE.
|
||||||
func (ss *ServerSession) Path() string {
|
func (ss *ServerSession) Path() string {
|
||||||
|
ss.propsMutex.RLock()
|
||||||
|
defer ss.propsMutex.RUnlock()
|
||||||
|
|
||||||
return ss.setuppedPath
|
return ss.setuppedPath
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetuppedQuery returns the query sent during SETUP or ANNOUNCE.
|
// SetuppedQuery returns the query sent during SETUP or ANNOUNCE.
|
||||||
//
|
//
|
||||||
// Deprecated: replaced by Medias.
|
// Deprecated: replaced by Query.
|
||||||
func (ss *ServerSession) SetuppedQuery() string {
|
func (ss *ServerSession) SetuppedQuery() string {
|
||||||
return ss.Query()
|
return ss.Query()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query returns the query sent during SETUP or ANNOUNCE.
|
// Query returns the query sent during SETUP or ANNOUNCE.
|
||||||
func (ss *ServerSession) Query() string {
|
func (ss *ServerSession) Query() string {
|
||||||
|
ss.propsMutex.RLock()
|
||||||
|
defer ss.propsMutex.RUnlock()
|
||||||
|
|
||||||
return ss.setuppedQuery
|
return ss.setuppedQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
// AnnouncedDescription returns the announced stream description.
|
// AnnouncedDescription returns the announced stream description.
|
||||||
func (ss *ServerSession) AnnouncedDescription() *description.Session {
|
func (ss *ServerSession) AnnouncedDescription() *description.Session {
|
||||||
|
ss.propsMutex.RLock()
|
||||||
|
defer ss.propsMutex.RUnlock()
|
||||||
|
|
||||||
return ss.announcedDesc
|
return ss.announcedDesc
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -571,6 +593,9 @@ func (ss *ServerSession) SetuppedMedias() []*description.Media {
|
|||||||
|
|
||||||
// Medias returns setupped medias.
|
// Medias returns setupped medias.
|
||||||
func (ss *ServerSession) Medias() []*description.Media {
|
func (ss *ServerSession) Medias() []*description.Media {
|
||||||
|
ss.propsMutex.RLock()
|
||||||
|
defer ss.propsMutex.RUnlock()
|
||||||
|
|
||||||
ret := make([]*description.Media, len(ss.setuppedMedias))
|
ret := make([]*description.Media, len(ss.setuppedMedias))
|
||||||
for i, sm := range ss.setuppedMediasOrdered {
|
for i, sm := range ss.setuppedMediasOrdered {
|
||||||
ret[i] = sm.media
|
ret[i] = sm.media
|
||||||
@@ -590,6 +615,9 @@ func (ss *ServerSession) UserData() interface{} {
|
|||||||
|
|
||||||
// Stats returns server session statistics.
|
// Stats returns server session statistics.
|
||||||
func (ss *ServerSession) Stats() *StatsSession {
|
func (ss *ServerSession) Stats() *StatsSession {
|
||||||
|
ss.propsMutex.RLock()
|
||||||
|
defer ss.propsMutex.RUnlock()
|
||||||
|
|
||||||
mediaStats := func() map[*description.Media]StatsSessionMedia { //nolint:dupl
|
mediaStats := func() map[*description.Media]StatsSessionMedia { //nolint:dupl
|
||||||
ret := make(map[*description.Media]StatsSessionMedia, len(ss.setuppedMedias))
|
ret := make(map[*description.Media]StatsSessionMedia, len(ss.setuppedMedias))
|
||||||
|
|
||||||
@@ -856,10 +884,14 @@ func (ss *ServerSession) run() {
|
|||||||
ss.setuppedStream.readerRemove(ss)
|
ss.setuppedStream.readerRemove(ss)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ss.propsMutex.Lock()
|
||||||
|
|
||||||
for _, sm := range ss.setuppedMedias {
|
for _, sm := range ss.setuppedMedias {
|
||||||
sm.close()
|
sm.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ss.propsMutex.Unlock()
|
||||||
|
|
||||||
if ss.writer != nil {
|
if ss.writer != nil {
|
||||||
ss.destroyWriter()
|
ss.destroyWriter()
|
||||||
}
|
}
|
||||||
@@ -1094,10 +1126,12 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
|
|||||||
})
|
})
|
||||||
|
|
||||||
if res.StatusCode == base.StatusOK {
|
if res.StatusCode == base.StatusOK {
|
||||||
|
ss.propsMutex.Lock()
|
||||||
ss.state = ServerSessionStatePreRecord
|
ss.state = ServerSessionStatePreRecord
|
||||||
ss.setuppedPath = path
|
ss.setuppedPath = path
|
||||||
ss.setuppedQuery = query
|
ss.setuppedQuery = query
|
||||||
ss.announcedDesc = &desc
|
ss.announcedDesc = &desc
|
||||||
|
ss.propsMutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, err
|
return res, err
|
||||||
@@ -1272,6 +1306,12 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
|
|||||||
panic("stream cannot be nil when StatusCode is StatusOK")
|
panic("stream cannot be nil when StatusCode is StatusOK")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ss.state == ServerSessionStatePrePlay {
|
||||||
|
if stream != ss.setuppedStream {
|
||||||
|
panic("stream cannot be different than the one returned in previous OnSetup call")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
medi = findMediaByTrackID(stream.Desc.Medias, trackID)
|
medi = findMediaByTrackID(stream.Desc.Medias, trackID)
|
||||||
default: // record
|
default: // record
|
||||||
medi = findMediaByURL(ss.announcedDesc.Medias, path, query, req.URL)
|
medi = findMediaByURL(ss.announcedDesc.Medias, path, query, req.URL)
|
||||||
@@ -1289,34 +1329,23 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
|
|||||||
}, liberrors.ErrServerMediaAlreadySetup{}
|
}, liberrors.ErrServerMediaAlreadySetup{}
|
||||||
}
|
}
|
||||||
|
|
||||||
ss.setuppedTransport = &transport
|
|
||||||
ss.setuppedProfile = inTH.Profile
|
|
||||||
|
|
||||||
if ss.state == ServerSessionStateInitial {
|
if ss.state == ServerSessionStateInitial {
|
||||||
err = stream.readerAdd(ss,
|
err = stream.readerAdd(ss,
|
||||||
inTH.ClientPorts,
|
inTH.ClientPorts,
|
||||||
|
transport,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusBadRequest,
|
StatusCode: base.StatusBadRequest,
|
||||||
}, err
|
}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ss.state = ServerSessionStatePrePlay
|
|
||||||
ss.setuppedPath = path
|
|
||||||
ss.setuppedQuery = query
|
|
||||||
ss.setuppedStream = stream
|
|
||||||
}
|
}
|
||||||
|
|
||||||
th := headers.Transport{
|
th := headers.Transport{
|
||||||
Profile: inTH.Profile,
|
Profile: inTH.Profile,
|
||||||
}
|
}
|
||||||
|
|
||||||
if ss.state == ServerSessionStatePrePlay {
|
if ss.state == ServerSessionStateInitial || ss.state == ServerSessionStatePrePlay {
|
||||||
if stream != ss.setuppedStream {
|
|
||||||
panic("stream cannot be different than the one returned in previous OnSetup call")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill SSRC if there is a single SSRC only
|
// Fill SSRC if there is a single SSRC only
|
||||||
// since the Transport header does not support multiple SSRCs.
|
// since the Transport header does not support multiple SSRCs.
|
||||||
if len(stream.medias[medi].formats) == 1 {
|
if len(stream.medias[medi].formats) == 1 {
|
||||||
@@ -1343,7 +1372,7 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
localSSRCs = make(map[uint8]uint32)
|
localSSRCs = make(map[uint8]uint32)
|
||||||
for forma, data := range ss.setuppedStream.medias[medi].formats {
|
for forma, data := range stream.medias[medi].formats {
|
||||||
localSSRCs[forma] = data.localSSRC
|
localSSRCs[forma] = data.localSSRC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1371,7 +1400,7 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
|
|||||||
}, err
|
}, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
srtpOutCtx = ss.setuppedStream.medias[medi].srtpOutCtx
|
srtpOutCtx = stream.medias[medi].srtpOutCtx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1429,6 +1458,11 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
|
|||||||
th.InterleavedIDs = &[2]int{tcpChannel, tcpChannel + 1}
|
th.InterleavedIDs = &[2]int{tcpChannel, tcpChannel + 1}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ss.propsMutex.Lock()
|
||||||
|
|
||||||
|
ss.setuppedTransport = &transport
|
||||||
|
ss.setuppedProfile = inTH.Profile
|
||||||
|
|
||||||
sm := &serverSessionMedia{
|
sm := &serverSessionMedia{
|
||||||
ss: ss,
|
ss: ss,
|
||||||
media: medi,
|
media: medi,
|
||||||
@@ -1447,10 +1481,18 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
|
|||||||
if ss.setuppedMedias == nil {
|
if ss.setuppedMedias == nil {
|
||||||
ss.setuppedMedias = make(map[*description.Media]*serverSessionMedia)
|
ss.setuppedMedias = make(map[*description.Media]*serverSessionMedia)
|
||||||
}
|
}
|
||||||
|
|
||||||
ss.setuppedMedias[medi] = sm
|
ss.setuppedMedias[medi] = sm
|
||||||
ss.setuppedMediasOrdered = append(ss.setuppedMediasOrdered, sm)
|
ss.setuppedMediasOrdered = append(ss.setuppedMediasOrdered, sm)
|
||||||
|
|
||||||
|
if ss.state == ServerSessionStateInitial {
|
||||||
|
ss.state = ServerSessionStatePrePlay
|
||||||
|
ss.setuppedPath = path
|
||||||
|
ss.setuppedQuery = query
|
||||||
|
ss.setuppedStream = stream
|
||||||
|
}
|
||||||
|
|
||||||
|
ss.propsMutex.Unlock()
|
||||||
|
|
||||||
res.Header["Transport"] = th.Marshal()
|
res.Header["Transport"] = th.Marshal()
|
||||||
|
|
||||||
if isSecure(inTH.Profile) {
|
if isSecure(inTH.Profile) {
|
||||||
@@ -1514,7 +1556,9 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
|
|||||||
|
|
||||||
if res.StatusCode == base.StatusOK {
|
if res.StatusCode == base.StatusOK {
|
||||||
if ss.state != ServerSessionStatePlay {
|
if ss.state != ServerSessionStatePlay {
|
||||||
|
ss.propsMutex.Lock()
|
||||||
ss.state = ServerSessionStatePlay
|
ss.state = ServerSessionStatePlay
|
||||||
|
ss.propsMutex.Unlock()
|
||||||
|
|
||||||
v := ss.s.timeNow().Unix()
|
v := ss.s.timeNow().Unix()
|
||||||
ss.udpLastPacketTime = &v
|
ss.udpLastPacketTime = &v
|
||||||
@@ -1685,7 +1729,9 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
|
|||||||
|
|
||||||
switch ss.state {
|
switch ss.state {
|
||||||
case ServerSessionStatePlay:
|
case ServerSessionStatePlay:
|
||||||
|
ss.propsMutex.Lock()
|
||||||
ss.state = ServerSessionStatePrePlay
|
ss.state = ServerSessionStatePrePlay
|
||||||
|
ss.propsMutex.Unlock()
|
||||||
|
|
||||||
switch *ss.setuppedTransport {
|
switch *ss.setuppedTransport {
|
||||||
case TransportUDP:
|
case TransportUDP:
|
||||||
@@ -1709,7 +1755,9 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
|
|||||||
ss.tcpConn = nil
|
ss.tcpConn = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ss.propsMutex.Lock()
|
||||||
ss.state = ServerSessionStatePreRecord
|
ss.state = ServerSessionStatePreRecord
|
||||||
|
ss.propsMutex.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -57,6 +57,26 @@ func (sm *serverSessionMedia) initialize() {
|
|||||||
f.initialize()
|
f.initialize()
|
||||||
sm.formats[forma.PayloadType()] = f
|
sm.formats[forma.PayloadType()] = f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch *sm.ss.setuppedTransport {
|
||||||
|
case TransportUDP, TransportUDPMulticast:
|
||||||
|
sm.writePacketRTCPInQueue = sm.writePacketRTCPInQueueUDP
|
||||||
|
|
||||||
|
case TransportTCP:
|
||||||
|
sm.writePacketRTCPInQueue = sm.writePacketRTCPInQueueTCP
|
||||||
|
|
||||||
|
if sm.ss.tcpCallbackByChannel == nil {
|
||||||
|
sm.ss.tcpCallbackByChannel = make(map[int]readFunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sm.ss.state == ServerSessionStateInitial || sm.ss.state == ServerSessionStatePrePlay {
|
||||||
|
sm.ss.tcpCallbackByChannel[sm.tcpChannel] = sm.readPacketRTPTCPPlay
|
||||||
|
sm.ss.tcpCallbackByChannel[sm.tcpChannel+1] = sm.readPacketRTCPTCPPlay
|
||||||
|
} else {
|
||||||
|
sm.ss.tcpCallbackByChannel[sm.tcpChannel] = sm.readPacketRTPTCPRecord
|
||||||
|
sm.ss.tcpCallbackByChannel[sm.tcpChannel+1] = sm.readPacketRTCPTCPRecord
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *serverSessionMedia) close() {
|
func (sm *serverSessionMedia) close() {
|
||||||
@@ -70,8 +90,6 @@ func (sm *serverSessionMedia) close() {
|
|||||||
func (sm *serverSessionMedia) start() error {
|
func (sm *serverSessionMedia) start() error {
|
||||||
switch *sm.ss.setuppedTransport {
|
switch *sm.ss.setuppedTransport {
|
||||||
case TransportUDP, TransportUDPMulticast:
|
case TransportUDP, TransportUDPMulticast:
|
||||||
sm.writePacketRTCPInQueue = sm.writePacketRTCPInQueueUDP
|
|
||||||
|
|
||||||
if *sm.ss.setuppedTransport == TransportUDP {
|
if *sm.ss.setuppedTransport == TransportUDP {
|
||||||
if sm.ss.state == ServerSessionStatePlay {
|
if sm.ss.state == ServerSessionStatePlay {
|
||||||
if sm.media.IsBackChannel {
|
if sm.media.IsBackChannel {
|
||||||
@@ -112,21 +130,6 @@ func (sm *serverSessionMedia) start() error {
|
|||||||
sm.ss.s.udpRTCPListener.addClient(sm.ss.author.ip(), sm.udpRTCPReadPort, sm.readPacketRTCPUDPRecord)
|
sm.ss.s.udpRTCPListener.addClient(sm.ss.author.ip(), sm.udpRTCPReadPort, sm.readPacketRTCPUDPRecord)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case TransportTCP:
|
|
||||||
sm.writePacketRTCPInQueue = sm.writePacketRTCPInQueueTCP
|
|
||||||
|
|
||||||
if sm.ss.tcpCallbackByChannel == nil {
|
|
||||||
sm.ss.tcpCallbackByChannel = make(map[int]readFunc)
|
|
||||||
}
|
|
||||||
|
|
||||||
if sm.ss.state == ServerSessionStatePlay {
|
|
||||||
sm.ss.tcpCallbackByChannel[sm.tcpChannel] = sm.readPacketRTPTCPPlay
|
|
||||||
sm.ss.tcpCallbackByChannel[sm.tcpChannel+1] = sm.readPacketRTCPTCPPlay
|
|
||||||
} else {
|
|
||||||
sm.ss.tcpCallbackByChannel[sm.tcpChannel] = sm.readPacketRTPTCPRecord
|
|
||||||
sm.ss.tcpCallbackByChannel[sm.tcpChannel+1] = sm.readPacketRTCPTCPRecord
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@@ -211,6 +211,7 @@ func (st *ServerStream) Stats() *ServerStreamStats {
|
|||||||
func (st *ServerStream) readerAdd(
|
func (st *ServerStream) readerAdd(
|
||||||
ss *ServerSession,
|
ss *ServerSession,
|
||||||
clientPorts *[2]int,
|
clientPorts *[2]int,
|
||||||
|
protocol Transport,
|
||||||
) error {
|
) error {
|
||||||
st.mutex.Lock()
|
st.mutex.Lock()
|
||||||
defer st.mutex.Unlock()
|
defer st.mutex.Unlock()
|
||||||
@@ -219,11 +220,11 @@ func (st *ServerStream) readerAdd(
|
|||||||
return liberrors.ErrServerStreamClosed{}
|
return liberrors.ErrServerStreamClosed{}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch *ss.setuppedTransport {
|
switch protocol {
|
||||||
case TransportUDP:
|
case TransportUDP:
|
||||||
// check whether UDP ports and IP are already assigned to another reader
|
// check whether UDP ports and IP are already assigned to another reader
|
||||||
for r := range st.readers {
|
for r := range st.readers {
|
||||||
if *r.setuppedTransport == TransportUDP &&
|
if protocol == TransportUDP &&
|
||||||
r.author.ip().Equal(ss.author.ip()) &&
|
r.author.ip().Equal(ss.author.ip()) &&
|
||||||
r.author.zone() == ss.author.zone() {
|
r.author.zone() == ss.author.zone() {
|
||||||
for _, rt := range r.setuppedMedias {
|
for _, rt := range r.setuppedMedias {
|
||||||
|
Reference in New Issue
Block a user