expose transport details (#850)

add Client.Transport, ServerConn.Transport ServerSession.Transport
This commit is contained in:
Alessandro Ros
2025-09-06 16:28:40 +02:00
committed by GitHub
parent 3c2625c7cf
commit c466c342ba
20 changed files with 241 additions and 137 deletions

View File

@@ -468,7 +468,7 @@ type Client struct {
// transport protocol (UDP, Multicast or TCP). // transport protocol (UDP, Multicast or TCP).
// If nil, it is chosen automatically (first UDP, then, if it fails, TCP). // If nil, it is chosen automatically (first UDP, then, if it fails, TCP).
// It defaults to nil. // It defaults to nil.
Transport *Transport Transport *TransportProtocol
// If the client is reading with UDP, it must receive // If the client is reading with UDP, it must receive
// at least a packet within this timeout, otherwise it switches to TCP. // at least a packet within this timeout, otherwise it switches to TCP.
// It defaults to 3 seconds. // It defaults to 3 seconds.
@@ -556,8 +556,7 @@ type Client struct {
lastDescribeDesc *description.Session lastDescribeDesc *description.Session
baseURL *base.URL baseURL *base.URL
announceData map[*description.Media]*clientAnnounceDataMedia // record announceData map[*description.Media]*clientAnnounceDataMedia // record
setuppedTransport *Transport setuppedTransport *SessionTransport
setuppedProfile headers.TransportProfile
backChannelSetupped bool backChannelSetupped bool
stdChannelSetupped bool stdChannelSetupped bool
setuppedMedias map[*description.Media]*clientMedia setuppedMedias map[*description.Media]*clientMedia
@@ -1047,8 +1046,9 @@ func (c *Client) trySwitchingProtocol() error {
c.reset() c.reset()
v := TransportTCP c.setuppedTransport = &SessionTransport{
c.setuppedTransport = &v Protocol: TransportTCP,
}
// some Hikvision cameras require a describe before a setup // some Hikvision cameras require a describe before a setup
_, _, err := c.doDescribe(c.lastDescribeURL) _, _, err := c.doDescribe(c.lastDescribeURL)
@@ -1084,18 +1084,18 @@ func (c *Client) startTransportRoutines() {
cm.start() cm.start()
} }
if *c.setuppedTransport == TransportTCP { if c.setuppedTransport.Protocol == TransportTCP {
c.tcpFrame = &base.InterleavedFrame{} c.tcpFrame = &base.InterleavedFrame{}
c.tcpBuffer = make([]byte, c.MaxPacketSize+4) c.tcpBuffer = make([]byte, c.MaxPacketSize+4)
} }
// always enable keepalives unless we are recording with TCP // always enable keepalives unless we are recording with TCP
if c.state == clientStatePlay || *c.setuppedTransport != TransportTCP { if c.state == clientStatePlay || c.setuppedTransport.Protocol != TransportTCP {
c.keepAliveTimer = time.NewTimer(c.keepAlivePeriod) c.keepAliveTimer = time.NewTimer(c.keepAlivePeriod)
} }
if c.state == clientStatePlay && c.stdChannelSetupped { if c.state == clientStatePlay && c.stdChannelSetupped {
switch *c.setuppedTransport { switch c.setuppedTransport.Protocol {
case TransportUDP: case TransportUDP:
c.checkTimeoutTimer = time.NewTimer(c.InitialUDPReadTimeout) c.checkTimeoutTimer = time.NewTimer(c.InitialUDPReadTimeout)
c.checkTimeoutInitial = true c.checkTimeoutInitial = true
@@ -1110,7 +1110,7 @@ func (c *Client) startTransportRoutines() {
} }
} }
if *c.setuppedTransport == TransportTCP { if c.setuppedTransport.Protocol == TransportTCP {
c.reader.setAllowInterleavedFrames(true) c.reader.setAllowInterleavedFrames(true)
} }
} }
@@ -1325,8 +1325,8 @@ func (c *Client) isInTCPTimeout() bool {
} }
func (c *Client) doCheckTimeout() error { func (c *Client) doCheckTimeout() error {
if *c.setuppedTransport == TransportUDP || if c.setuppedTransport.Protocol == TransportUDP ||
*c.setuppedTransport == TransportUDPMulticast { c.setuppedTransport.Protocol == TransportUDPMulticast {
if c.checkTimeoutInitial && !c.backChannelSetupped && c.Transport == nil { if c.checkTimeoutInitial && !c.backChannelSetupped && c.Transport == nil {
c.checkTimeoutInitial = false c.checkTimeoutInitial = false
@@ -1626,17 +1626,17 @@ func (c *Client) doSetup(
th.Mode = &v th.Mode = &v
} }
var transport Transport var protocol TransportProtocol
switch { switch {
// use transport from previous SETUP calls // use transport from previous SETUP calls
case c.setuppedTransport != nil: case c.setuppedTransport != nil:
transport = *c.setuppedTransport protocol = c.setuppedTransport.Protocol
th.Profile = c.setuppedProfile th.Profile = c.setuppedTransport.Profile
// use transport from config, secure flag from server // use transport from config, secure flag from server
case c.Transport != nil: case c.Transport != nil:
transport = *c.Transport protocol = *c.Transport
if isSecure(medi.Profile) && c.Scheme == "rtsps" { if isSecure(medi.Profile) && c.Scheme == "rtsps" {
th.Profile = headers.TransportProfileSAVP th.Profile = headers.TransportProfileSAVP
} else { } else {
@@ -1654,9 +1654,9 @@ func (c *Client) doSetup(
} }
if th.Profile == headers.TransportProfileSAVP || c.Scheme == "rtsp" { if th.Profile == headers.TransportProfileSAVP || c.Scheme == "rtsp" {
transport = TransportUDP protocol = TransportUDP
} else { } else {
transport = TransportTCP protocol = TransportTCP
} }
} }
@@ -1692,7 +1692,7 @@ func (c *Client) doSetup(
} }
}() }()
switch transport { switch protocol {
case TransportUDP, TransportUDPMulticast: case TransportUDP, TransportUDPMulticast:
if c.Scheme == "rtsps" && !isSecure(th.Profile) { if c.Scheme == "rtsps" && !isSecure(th.Profile) {
return nil, fmt.Errorf("unable to setup secure UDP") return nil, fmt.Errorf("unable to setup secure UDP")
@@ -1700,7 +1700,7 @@ func (c *Client) doSetup(
th.Protocol = headers.TransportProtocolUDP th.Protocol = headers.TransportProtocolUDP
if transport == TransportUDP { if protocol == TransportUDP {
if (rtpPort == 0 && rtcpPort != 0) || if (rtpPort == 0 && rtcpPort != 0) ||
(rtpPort != 0 && rtcpPort == 0) { (rtpPort != 0 && rtcpPort == 0) {
return nil, liberrors.ErrClientUDPPortsZero{} return nil, liberrors.ErrClientUDPPortsZero{}
@@ -1808,9 +1808,10 @@ func (c *Client) doSetup(
if res.StatusCode == base.StatusUnsupportedTransport && if res.StatusCode == base.StatusUnsupportedTransport &&
c.setuppedTransport == nil && c.Transport == nil { c.setuppedTransport == nil && c.Transport == nil {
c.OnTransportSwitch(liberrors.ErrClientSwitchToTCP2{}) c.OnTransportSwitch(liberrors.ErrClientSwitchToTCP2{})
v := TransportTCP c.setuppedTransport = &SessionTransport{
c.setuppedTransport = &v Protocol: TransportTCP,
c.setuppedProfile = th.Profile Profile: th.Profile,
}
return c.doSetup(baseURL, medi, 0, 0) return c.doSetup(baseURL, medi, 0, 0)
} }
@@ -1824,7 +1825,7 @@ func (c *Client) doSetup(
return nil, liberrors.ErrClientTransportHeaderInvalid{Err: err} return nil, liberrors.ErrClientTransportHeaderInvalid{Err: err}
} }
switch transport { switch protocol {
case TransportUDP, TransportUDPMulticast: case TransportUDP, TransportUDPMulticast:
if thRes.Protocol == headers.TransportProtocolTCP { if thRes.Protocol == headers.TransportProtocolTCP {
// switch transport automatically // switch transport automatically
@@ -1835,9 +1836,10 @@ func (c *Client) doSetup(
c.reset() c.reset()
v := TransportTCP c.setuppedTransport = &SessionTransport{
c.setuppedTransport = &v Protocol: TransportTCP,
c.setuppedProfile = th.Profile Profile: th.Profile,
}
// some Hikvision cameras require a describe before a setup // some Hikvision cameras require a describe before a setup
_, _, err = c.doDescribe(c.lastDescribeURL) _, _, err = c.doDescribe(c.lastDescribeURL)
@@ -1852,7 +1854,7 @@ func (c *Client) doSetup(
} }
} }
switch transport { switch protocol {
case TransportUDP: case TransportUDP:
if thRes.Delivery != nil && *thRes.Delivery != headers.TransportDeliveryUnicast { if thRes.Delivery != nil && *thRes.Delivery != headers.TransportDeliveryUnicast {
return nil, liberrors.ErrClientTransportHeaderInvalidDelivery{} return nil, liberrors.ErrClientTransportHeaderInvalidDelivery{}
@@ -2032,8 +2034,10 @@ func (c *Client) doSetup(
c.setuppedMedias[medi] = cm c.setuppedMedias[medi] = cm
c.baseURL = baseURL c.baseURL = baseURL
c.setuppedTransport = &transport c.setuppedTransport = &SessionTransport{
c.setuppedProfile = th.Profile Protocol: protocol,
Profile: th.Profile,
}
c.propsMutex.Unlock() c.propsMutex.Unlock()
@@ -2136,7 +2140,7 @@ func (c *Client) doPlay(ra *headers.Range) (*base.Response, error) {
// when protocol is UDP, // when protocol is UDP,
// open the firewall by sending empty packets to the remote part. // open the firewall by sending empty packets to the remote part.
// do this before sending the PLAY request. // do this before sending the PLAY request.
if *c.setuppedTransport == TransportUDP { if c.setuppedTransport.Protocol == TransportUDP {
for _, cm := range c.setuppedMedias { for _, cm := range c.setuppedMedias {
if !cm.media.IsBackChannel && cm.udpRTPListener.writeAddr != nil { if !cm.media.IsBackChannel && cm.udpRTPListener.writeAddr != nil {
buf, _ := (&rtp.Packet{Header: rtp.Header{Version: 2}}).Marshal() buf, _ := (&rtp.Packet{Header: rtp.Header{Version: 2}}).Marshal()
@@ -2428,6 +2432,17 @@ func (c *Client) PacketNTP(medi *description.Media, pkt *rtp.Packet) (time.Time,
return ct.rtpReceiver.PacketNTP(pkt.Timestamp) return ct.rtpReceiver.PacketNTP(pkt.Timestamp)
} }
// Transport2 returns transport details.
func (c *Client) Transport2() *ClientTransport {
c.propsMutex.RLock()
defer c.propsMutex.RUnlock()
return &ClientTransport{
Conn: ConnTransport{},
Session: c.setuppedTransport,
}
}
// Stats returns client statistics. // Stats returns client statistics.
func (c *Client) Stats() *ClientStats { func (c *Client) Stats() *ClientStats {
c.propsMutex.RLock() c.propsMutex.RLock()
@@ -2437,15 +2452,15 @@ func (c *Client) Stats() *ClientStats {
ret := make(map[*description.Media]StatsSessionMedia, len(c.setuppedMedias)) ret := make(map[*description.Media]StatsSessionMedia, len(c.setuppedMedias))
for med, sm := range c.setuppedMedias { for med, sm := range c.setuppedMedias {
ret[med] = StatsSessionMedia{ ret[med] = SessionStatsMedia{
BytesReceived: atomic.LoadUint64(sm.bytesReceived), BytesReceived: atomic.LoadUint64(sm.bytesReceived),
BytesSent: atomic.LoadUint64(sm.bytesSent), BytesSent: atomic.LoadUint64(sm.bytesSent),
RTPPacketsInError: atomic.LoadUint64(sm.rtpPacketsInError), RTPPacketsInError: atomic.LoadUint64(sm.rtpPacketsInError),
RTCPPacketsReceived: atomic.LoadUint64(sm.rtcpPacketsReceived), RTCPPacketsReceived: atomic.LoadUint64(sm.rtcpPacketsReceived),
RTCPPacketsSent: atomic.LoadUint64(sm.rtcpPacketsSent), RTCPPacketsSent: atomic.LoadUint64(sm.rtcpPacketsSent),
RTCPPacketsInError: atomic.LoadUint64(sm.rtcpPacketsInError), RTCPPacketsInError: atomic.LoadUint64(sm.rtcpPacketsInError),
Formats: func() map[format.Format]StatsSessionFormat { Formats: func() map[format.Format]SessionStatsFormat {
ret := make(map[format.Format]StatsSessionFormat, len(sm.formats)) ret := make(map[format.Format]SessionStatsFormat, len(sm.formats))
for _, fo := range sm.formats { for _, fo := range sm.formats {
recvStats := func() *rtpreceiver.Stats { recvStats := func() *rtpreceiver.Stats {
@@ -2461,7 +2476,7 @@ func (c *Client) Stats() *ClientStats {
return nil return nil
}() }()
ret[fo.format] = StatsSessionFormat{ //nolint:dupl ret[fo.format] = SessionStatsFormat{ //nolint:dupl
RTPPacketsReceived: atomic.LoadUint64(fo.rtpPacketsReceived), RTPPacketsReceived: atomic.LoadUint64(fo.rtpPacketsReceived),
RTPPacketsSent: atomic.LoadUint64(fo.rtpPacketsSent), RTPPacketsSent: atomic.LoadUint64(fo.rtpPacketsSent),
RTPPacketsLost: atomic.LoadUint64(fo.rtpPacketsLost), RTPPacketsLost: atomic.LoadUint64(fo.rtpPacketsLost),
@@ -2517,11 +2532,11 @@ func (c *Client) Stats() *ClientStats {
}() }()
return &ClientStats{ return &ClientStats{
Conn: StatsConn{ Conn: ConnStats{
BytesReceived: atomic.LoadUint64(c.bytesReceived), BytesReceived: atomic.LoadUint64(c.bytesReceived),
BytesSent: atomic.LoadUint64(c.bytesSent), BytesSent: atomic.LoadUint64(c.bytesSent),
}, },
Session: StatsSession{ //nolint:dupl Session: SessionStats{ //nolint:dupl
BytesReceived: func() uint64 { BytesReceived: func() uint64 {
v := uint64(0) v := uint64(0)
for _, ms := range mediaStats { for _, ms := range mediaStats {

View File

@@ -36,7 +36,7 @@ func deliveryPtr(v headers.TransportDelivery) *headers.TransportDelivery {
return &v return &v
} }
func transportPtr(v Transport) *Transport { func transportPtr(v TransportProtocol) *TransportProtocol {
return &v return &v
} }
@@ -616,7 +616,7 @@ func TestClientPlay(t *testing.T) {
Scheme: u.Scheme, Scheme: u.Scheme,
Host: u.Host, Host: u.Host,
TLSConfig: &tls.Config{InsecureSkipVerify: true}, TLSConfig: &tls.Config{InsecureSkipVerify: true},
Transport: func() *Transport { Transport: func() *TransportProtocol {
switch ca.transport { switch ca.transport {
case "udp": case "udp":
v := TransportUDP v := TransportUDP
@@ -643,6 +643,7 @@ func TestClientPlay(t *testing.T) {
// test that properties can be accessed in parallel // test that properties can be accessed in parallel
go func() { go func() {
c.Stats() c.Stats()
c.Transport2()
}() }()
err = c.SetupAll(sd.BaseURL, sd.Medias) err = c.SetupAll(sd.BaseURL, sd.Medias)
@@ -661,23 +662,23 @@ func TestClientPlay(t *testing.T) {
s := c.Stats() s := c.Stats()
require.Equal(t, &ClientStats{ require.Equal(t, &ClientStats{
Conn: StatsConn{ Conn: ConnStats{
BytesReceived: s.Conn.BytesReceived, BytesReceived: s.Conn.BytesReceived,
BytesSent: s.Conn.BytesSent, BytesSent: s.Conn.BytesSent,
}, },
Session: StatsSession{ Session: SessionStats{
BytesReceived: s.Session.BytesReceived, BytesReceived: s.Session.BytesReceived,
BytesSent: s.Session.BytesSent, BytesSent: s.Session.BytesSent,
RTPPacketsReceived: s.Session.RTPPacketsReceived, RTPPacketsReceived: s.Session.RTPPacketsReceived,
RTCPPacketsReceived: s.Session.RTCPPacketsReceived, RTCPPacketsReceived: s.Session.RTCPPacketsReceived,
RTCPPacketsSent: s.Session.RTCPPacketsSent, RTCPPacketsSent: s.Session.RTCPPacketsSent,
Medias: map[*description.Media]StatsSessionMedia{ Medias: map[*description.Media]SessionStatsMedia{
sd.Medias[0]: { //nolint:dupl sd.Medias[0]: { //nolint:dupl
BytesReceived: s.Session.Medias[sd.Medias[0]].BytesReceived, BytesReceived: s.Session.Medias[sd.Medias[0]].BytesReceived,
BytesSent: s.Session.Medias[sd.Medias[0]].BytesSent, BytesSent: s.Session.Medias[sd.Medias[0]].BytesSent,
RTCPPacketsReceived: s.Session.Medias[sd.Medias[0]].RTCPPacketsReceived, RTCPPacketsReceived: s.Session.Medias[sd.Medias[0]].RTCPPacketsReceived,
RTCPPacketsSent: s.Session.Medias[sd.Medias[0]].RTCPPacketsSent, RTCPPacketsSent: s.Session.Medias[sd.Medias[0]].RTCPPacketsSent,
Formats: map[format.Format]StatsSessionFormat{ Formats: map[format.Format]SessionStatsFormat{
sd.Medias[0].Formats[0]: { sd.Medias[0].Formats[0]: {
RTPPacketsReceived: s.Session.Medias[sd.Medias[0]].Formats[sd.Medias[0].Formats[0]].RTPPacketsReceived, RTPPacketsReceived: s.Session.Medias[sd.Medias[0]].Formats[sd.Medias[0].Formats[0]].RTPPacketsReceived,
LocalSSRC: s.Session.Medias[sd.Medias[0]].Formats[sd.Medias[0].Formats[0]].LocalSSRC, LocalSSRC: s.Session.Medias[sd.Medias[0]].Formats[sd.Medias[0].Formats[0]].LocalSSRC,
@@ -690,7 +691,7 @@ func TestClientPlay(t *testing.T) {
BytesSent: s.Session.Medias[sd.Medias[1]].BytesSent, BytesSent: s.Session.Medias[sd.Medias[1]].BytesSent,
RTCPPacketsReceived: s.Session.Medias[sd.Medias[1]].RTCPPacketsReceived, RTCPPacketsReceived: s.Session.Medias[sd.Medias[1]].RTCPPacketsReceived,
RTCPPacketsSent: s.Session.Medias[sd.Medias[1]].RTCPPacketsSent, RTCPPacketsSent: s.Session.Medias[sd.Medias[1]].RTCPPacketsSent,
Formats: map[format.Format]StatsSessionFormat{ Formats: map[format.Format]SessionStatsFormat{
sd.Medias[1].Formats[0]: { sd.Medias[1].Formats[0]: {
RTPPacketsReceived: s.Session.Medias[sd.Medias[1]].Formats[sd.Medias[1].Formats[0]].RTPPacketsReceived, RTPPacketsReceived: s.Session.Medias[sd.Medias[1]].Formats[sd.Medias[1].Formats[0]].RTPPacketsReceived,
LocalSSRC: s.Session.Medias[sd.Medias[1]].Formats[sd.Medias[1].Formats[0]].LocalSSRC, LocalSSRC: s.Session.Medias[sd.Medias[1]].Formats[sd.Medias[1].Formats[0]].LocalSSRC,
@@ -2427,7 +2428,7 @@ func TestClientPlayPausePlay(t *testing.T) {
packetRecv := make(chan struct{}) packetRecv := make(chan struct{})
c := Client{ c := Client{
Transport: func() *Transport { Transport: func() *TransportProtocol {
if transport == "udp" { if transport == "udp" {
v := TransportUDP v := TransportUDP
return &v return &v
@@ -2746,7 +2747,7 @@ func TestClientPlayErrorTimeout(t *testing.T) {
}() }()
c := Client{ c := Client{
Transport: func() *Transport { Transport: func() *TransportProtocol {
switch transport { switch transport {
case "udp": case "udp":
v := TransportUDP v := TransportUDP
@@ -3424,7 +3425,7 @@ func TestClientPlayDecodeErrors(t *testing.T) {
}() }()
c := Client{ c := Client{
Transport: func() *Transport { Transport: func() *TransportProtocol {
if ca.proto == "udp" { if ca.proto == "udp" {
v := TransportUDP v := TransportUDP
return &v return &v
@@ -3908,7 +3909,7 @@ func TestClientPlayBackChannel(t *testing.T) {
Scheme: u.Scheme, Scheme: u.Scheme,
Host: u.Host, Host: u.Host,
RequestBackChannels: true, RequestBackChannels: true,
Transport: func() *Transport { Transport: func() *TransportProtocol {
if transport == "tcp" { if transport == "tcp" {
return transportPtr(TransportTCP) return transportPtr(TransportTCP)
} }

View File

@@ -416,7 +416,7 @@ func TestClientRecord(t *testing.T) {
TLSConfig: &tls.Config{ TLSConfig: &tls.Config{
InsecureSkipVerify: true, InsecureSkipVerify: true,
}, },
Transport: func() *Transport { Transport: func() *TransportProtocol {
if ca.transport == "udp" { if ca.transport == "udp" {
v := TransportUDP v := TransportUDP
return &v return &v
@@ -449,24 +449,24 @@ func TestClientRecord(t *testing.T) {
s := c.Stats() s := c.Stats()
require.Equal(t, &ClientStats{ require.Equal(t, &ClientStats{
Conn: StatsConn{ Conn: ConnStats{
BytesReceived: s.Conn.BytesReceived, BytesReceived: s.Conn.BytesReceived,
BytesSent: s.Conn.BytesSent, BytesSent: s.Conn.BytesSent,
}, },
Session: StatsSession{ Session: SessionStats{
BytesReceived: s.Session.BytesReceived, BytesReceived: s.Session.BytesReceived,
BytesSent: s.Session.BytesSent, BytesSent: s.Session.BytesSent,
RTPPacketsSent: s.Session.RTPPacketsSent, RTPPacketsSent: s.Session.RTPPacketsSent,
RTPPacketsReceived: s.Session.RTPPacketsReceived, RTPPacketsReceived: s.Session.RTPPacketsReceived,
RTCPPacketsReceived: s.Session.RTCPPacketsReceived, RTCPPacketsReceived: s.Session.RTCPPacketsReceived,
RTCPPacketsSent: s.Session.RTCPPacketsSent, RTCPPacketsSent: s.Session.RTCPPacketsSent,
Medias: map[*description.Media]StatsSessionMedia{ Medias: map[*description.Media]SessionStatsMedia{
medias[0]: { medias[0]: {
BytesReceived: s.Session.Medias[medias[0]].BytesReceived, BytesReceived: s.Session.Medias[medias[0]].BytesReceived,
BytesSent: s.Session.Medias[medias[0]].BytesSent, BytesSent: s.Session.Medias[medias[0]].BytesSent,
RTCPPacketsReceived: s.Session.Medias[medias[0]].RTCPPacketsReceived, RTCPPacketsReceived: s.Session.Medias[medias[0]].RTCPPacketsReceived,
RTCPPacketsSent: s.Session.Medias[medias[0]].RTCPPacketsSent, RTCPPacketsSent: s.Session.Medias[medias[0]].RTCPPacketsSent,
Formats: map[format.Format]StatsSessionFormat{ Formats: map[format.Format]SessionStatsFormat{
medias[0].Formats[0]: { medias[0].Formats[0]: {
RTPPacketsSent: s.Session.Medias[medias[0]].Formats[medias[0].Formats[0]].RTPPacketsSent, RTPPacketsSent: s.Session.Medias[medias[0]].Formats[medias[0].Formats[0]].RTPPacketsSent,
RTPPacketsReceived: s.Session.Medias[medias[0]].Formats[medias[0].Formats[0]].RTPPacketsReceived, RTPPacketsReceived: s.Session.Medias[medias[0]].Formats[medias[0].Formats[0]].RTPPacketsReceived,
@@ -586,7 +586,7 @@ func TestClientRecordSocketError(t *testing.T) {
TLSConfig: &tls.Config{ TLSConfig: &tls.Config{
InsecureSkipVerify: true, InsecureSkipVerify: true,
}, },
Transport: func() *Transport { Transport: func() *TransportProtocol {
if transport == "udp" { if transport == "udp" {
v := TransportUDP v := TransportUDP
return &v return &v
@@ -730,7 +730,7 @@ func TestClientRecordPauseRecordSerial(t *testing.T) {
}() }()
c := Client{ c := Client{
Transport: func() *Transport { Transport: func() *TransportProtocol {
if transport == "udp" { if transport == "udp" {
v := TransportUDP v := TransportUDP
return &v return &v
@@ -889,7 +889,7 @@ func TestClientRecordPauseRecordParallel(t *testing.T) {
}() }()
c := Client{ c := Client{
Transport: func() *Transport { Transport: func() *TransportProtocol {
if transport == "udp" { if transport == "udp" {
v := TransportUDP v := TransportUDP
return &v return &v
@@ -1214,7 +1214,7 @@ func TestClientRecordDecodeErrors(t *testing.T) {
}() }()
c := Client{ c := Client{
Transport: func() *Transport { Transport: func() *TransportProtocol {
if ca.proto == "udp" { if ca.proto == "udp" {
v := TransportUDP v := TransportUDP
return &v return &v
@@ -1388,7 +1388,7 @@ func TestClientRecordRTCPReport(t *testing.T) {
var curTimeMutex sync.Mutex var curTimeMutex sync.Mutex
c := Client{ c := Client{
Transport: func() *Transport { Transport: func() *TransportProtocol {
if ca == "udp" { if ca == "udp" {
v := TransportUDP v := TransportUDP
return &v return &v

View File

@@ -2,6 +2,6 @@ package gortsplib
// ClientStats are client statistics // ClientStats are client statistics
type ClientStats struct { type ClientStats struct {
Conn StatsConn Conn ConnStats
Session StatsSession Session SessionStats
} }

8
client_transport.go Normal file
View File

@@ -0,0 +1,8 @@
package gortsplib
// ClientTransport contains details about the client transport.
type ClientTransport struct {
Conn ConnTransport
// present only when SETUP has been called at least once.
Session *SessionTransport
}

View File

@@ -1,7 +1,12 @@
package gortsplib package gortsplib
// StatsConn are connection statistics. // StatsConn are connection statistics.
type StatsConn struct { //
// Deprecated: renamed into ConnStats.
type StatsConn = ConnStats
// ConnStats are connection statistics.
type ConnStats struct {
// received bytes // received bytes
BytesReceived uint64 BytesReceived uint64
// sent bytes // sent bytes

4
conn_transport.go Normal file
View File

@@ -0,0 +1,4 @@
package gortsplib
// ConnTransport contains details about the transport of a connection.
type ConnTransport struct{}

View File

@@ -276,9 +276,14 @@ func (sc *ServerConn) Session() *ServerSession {
return sc.session return sc.session
} }
// Transport returns transport details.
func (sc *ServerConn) Transport() *ConnTransport {
return &ConnTransport{}
}
// Stats returns connection statistics. // Stats returns connection statistics.
func (sc *ServerConn) Stats() *StatsConn { func (sc *ServerConn) Stats() *ConnStats {
return &StatsConn{ return &ConnStats{
BytesReceived: sc.bc.BytesReceived(), BytesReceived: sc.bc.BytesReceived(),
BytesSent: sc.bc.BytesSent(), BytesSent: sc.bc.BytesSent(),
} }

View File

@@ -99,12 +99,16 @@ type ServerHandlerOnAnnounce interface {
// ServerHandlerOnSetupCtx is the context of OnSetup. // ServerHandlerOnSetupCtx is the context of OnSetup.
type ServerHandlerOnSetupCtx struct { type ServerHandlerOnSetupCtx struct {
Session *ServerSession Session *ServerSession
Conn *ServerConn Conn *ServerConn
Request *base.Request Request *base.Request
Path string Path string
Query string Query string
Transport Transport
// Deprecated: replaced by Transport2.
Transport TransportProtocol
Transport2 *SessionTransport
} }
// ServerHandlerOnSetup can be implemented by a ServerHandler. // ServerHandlerOnSetup can be implemented by a ServerHandler.

View File

@@ -678,6 +678,7 @@ func TestServerPlay(t *testing.T) {
ctx.Session.Stream() ctx.Session.Stream()
ctx.Session.SetuppedTransport() ctx.Session.SetuppedTransport()
ctx.Session.SetuppedSecure() ctx.Session.SetuppedSecure()
ctx.Session.Transport()
}() }()
return &base.Response{ return &base.Response{

View File

@@ -435,8 +435,7 @@ type ServerSession struct {
setuppedMedias map[*description.Media]*serverSessionMedia setuppedMedias map[*description.Media]*serverSessionMedia
setuppedMediasOrdered []*serverSessionMedia setuppedMediasOrdered []*serverSessionMedia
tcpCallbackByChannel map[int]readFunc tcpCallbackByChannel map[int]readFunc
setuppedTransport *Transport setuppedTransport *SessionTransport
setuppedProfile headers.TransportProfile
setuppedStream *ServerStream // play setuppedStream *ServerStream // play
setuppedPath string setuppedPath string
setuppedQuery string setuppedQuery string
@@ -514,21 +513,31 @@ func (ss *ServerSession) State() ServerSessionState {
} }
// SetuppedTransport returns the transport negotiated during SETUP. // SetuppedTransport returns the transport negotiated during SETUP.
func (ss *ServerSession) SetuppedTransport() *Transport { //
// Deprecated: replaced by Transport.
func (ss *ServerSession) SetuppedTransport() *TransportProtocol {
ss.propsMutex.RLock() ss.propsMutex.RLock()
defer ss.propsMutex.RUnlock() defer ss.propsMutex.RUnlock()
return ss.setuppedTransport if ss.setuppedTransport == nil {
return nil
}
return &ss.setuppedTransport.Protocol
} }
// SetuppedSecure returns whether a secure profile is in use. // SetuppedSecure returns whether a secure profile is in use.
// 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.
//
// Deprecated: replaced by Transport.
func (ss *ServerSession) SetuppedSecure() bool { func (ss *ServerSession) SetuppedSecure() bool {
ss.propsMutex.RLock() ss.propsMutex.RLock()
defer ss.propsMutex.RUnlock() defer ss.propsMutex.RUnlock()
return isSecure(ss.setuppedProfile) if ss.setuppedTransport == nil {
return false
}
return isSecure(ss.setuppedTransport.Profile)
} }
// SetuppedStream returns the stream associated with the session. // SetuppedStream returns the stream associated with the session.
@@ -613,6 +622,15 @@ func (ss *ServerSession) UserData() interface{} {
return ss.userData return ss.userData
} }
// Transport returns transport details.
// This is non-nil only if SETUP has been called at least once.
func (ss *ServerSession) Transport() *SessionTransport {
ss.propsMutex.RLock()
defer ss.propsMutex.RUnlock()
return ss.setuppedTransport
}
// Stats returns server session statistics. // Stats returns server session statistics.
func (ss *ServerSession) Stats() *StatsSession { func (ss *ServerSession) Stats() *StatsSession {
ss.propsMutex.RLock() ss.propsMutex.RLock()
@@ -622,15 +640,15 @@ func (ss *ServerSession) Stats() *StatsSession {
ret := make(map[*description.Media]StatsSessionMedia, len(ss.setuppedMedias)) ret := make(map[*description.Media]StatsSessionMedia, len(ss.setuppedMedias))
for med, sm := range ss.setuppedMedias { for med, sm := range ss.setuppedMedias {
ret[med] = StatsSessionMedia{ ret[med] = SessionStatsMedia{
BytesReceived: atomic.LoadUint64(sm.bytesReceived), BytesReceived: atomic.LoadUint64(sm.bytesReceived),
BytesSent: atomic.LoadUint64(sm.bytesSent), BytesSent: atomic.LoadUint64(sm.bytesSent),
RTPPacketsInError: atomic.LoadUint64(sm.rtpPacketsInError), RTPPacketsInError: atomic.LoadUint64(sm.rtpPacketsInError),
RTCPPacketsReceived: atomic.LoadUint64(sm.rtcpPacketsReceived), RTCPPacketsReceived: atomic.LoadUint64(sm.rtcpPacketsReceived),
RTCPPacketsSent: atomic.LoadUint64(sm.rtcpPacketsSent), RTCPPacketsSent: atomic.LoadUint64(sm.rtcpPacketsSent),
RTCPPacketsInError: atomic.LoadUint64(sm.rtcpPacketsInError), RTCPPacketsInError: atomic.LoadUint64(sm.rtcpPacketsInError),
Formats: func() map[format.Format]StatsSessionFormat { Formats: func() map[format.Format]SessionStatsFormat {
ret := make(map[format.Format]StatsSessionFormat, len(sm.formats)) ret := make(map[format.Format]SessionStatsFormat, len(sm.formats))
for _, fo := range sm.formats { for _, fo := range sm.formats {
recvStats := func() *rtpreceiver.Stats { recvStats := func() *rtpreceiver.Stats {
@@ -652,7 +670,7 @@ func (ss *ServerSession) Stats() *StatsSession {
return nil return nil
}() }()
ret[fo.format] = StatsSessionFormat{ //nolint:dupl ret[fo.format] = SessionStatsFormat{ //nolint:dupl
RTPPacketsReceived: atomic.LoadUint64(fo.rtpPacketsReceived), RTPPacketsReceived: atomic.LoadUint64(fo.rtpPacketsReceived),
RTPPacketsSent: atomic.LoadUint64(fo.rtpPacketsSent), RTPPacketsSent: atomic.LoadUint64(fo.rtpPacketsSent),
RTPPacketsLost: atomic.LoadUint64(fo.rtpPacketsLost), RTPPacketsLost: atomic.LoadUint64(fo.rtpPacketsLost),
@@ -707,7 +725,7 @@ func (ss *ServerSession) Stats() *StatsSession {
return ret return ret
}() }()
return &StatsSession{ //nolint:dupl return &SessionStats{ //nolint:dupl
BytesReceived: func() uint64 { BytesReceived: func() uint64 {
v := uint64(0) v := uint64(0)
for _, ms := range mediaStats { for _, ms := range mediaStats {
@@ -943,8 +961,8 @@ func (ss *ServerSession) runInner() error {
// and transport is UDP or UDP-multicast. // and transport is UDP or UDP-multicast.
if (ss.state == ServerSessionStatePrePlay || if (ss.state == ServerSessionStatePrePlay ||
ss.state == ServerSessionStatePlay) && ss.state == ServerSessionStatePlay) &&
(*ss.setuppedTransport == TransportUDP || (ss.setuppedTransport.Protocol == TransportUDP ||
*ss.setuppedTransport == TransportUDPMulticast) { ss.setuppedTransport.Protocol == TransportUDPMulticast) {
v := uint(ss.s.sessionTimeout / time.Second) v := uint(ss.s.sessionTimeout / time.Second)
return &v return &v
} }
@@ -980,7 +998,7 @@ func (ss *ServerSession) runInner() error {
// close the session. // close the session.
if ((ss.state != ServerSessionStateRecord && if ((ss.state != ServerSessionStateRecord &&
ss.state != ServerSessionStatePlay) || ss.state != ServerSessionStatePlay) ||
*ss.setuppedTransport == TransportTCP) && ss.setuppedTransport.Protocol == TransportTCP) &&
len(ss.conns) == 0 { len(ss.conns) == 0 {
return liberrors.ErrServerSessionNotInUse{} return liberrors.ErrServerSessionNotInUse{}
} }
@@ -988,7 +1006,7 @@ func (ss *ServerSession) runInner() error {
case <-ss.chAsyncStartWriter: case <-ss.chAsyncStartWriter:
if (ss.state == ServerSessionStateRecord || if (ss.state == ServerSessionStateRecord ||
ss.state == ServerSessionStatePlay) && ss.state == ServerSessionStatePlay) &&
*ss.setuppedTransport == TransportTCP { ss.setuppedTransport.Protocol == TransportTCP {
ss.startWriter() ss.startWriter()
} }
@@ -1187,18 +1205,18 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
query = ss.setuppedQuery query = ss.setuppedQuery
} }
var transport Transport var protocol TransportProtocol
switch inTH.Protocol { switch inTH.Protocol {
case headers.TransportProtocolUDP: case headers.TransportProtocolUDP:
if inTH.Delivery != nil && *inTH.Delivery == headers.TransportDeliveryMulticast { if inTH.Delivery != nil && *inTH.Delivery == headers.TransportDeliveryMulticast {
transport = TransportUDPMulticast protocol = TransportUDPMulticast
} else { } else {
transport = TransportUDP protocol = TransportUDP
} }
case headers.TransportProtocolTCP: case headers.TransportProtocolTCP:
transport = TransportTCP protocol = TransportTCP
} }
var srtpInCtx *wrappedSRTPContext var srtpInCtx *wrappedSRTPContext
@@ -1220,13 +1238,20 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
} }
} }
if ss.setuppedTransport != nil && (*ss.setuppedTransport != transport || ss.setuppedProfile != inTH.Profile) { if ss.setuppedTransport != nil {
return &base.Response{ cmp := SessionTransport{
StatusCode: base.StatusBadRequest, Protocol: protocol,
}, liberrors.ErrServerMediasDifferentTransports{} Profile: inTH.Profile,
}
if *ss.setuppedTransport != cmp {
return &base.Response{
StatusCode: base.StatusBadRequest,
}, liberrors.ErrServerMediasDifferentTransports{}
}
} }
switch transport { switch protocol {
case TransportUDP: case TransportUDP:
if inTH.ClientPorts == nil { if inTH.ClientPorts == nil {
return &base.Response{ return &base.Response{
@@ -1259,7 +1284,7 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
} }
default: // record default: // record
if transport == TransportUDPMulticast { if protocol == TransportUDPMulticast {
return &base.Response{ return &base.Response{
StatusCode: base.StatusUnsupportedTransport, StatusCode: base.StatusUnsupportedTransport,
}, nil }, nil
@@ -1278,7 +1303,11 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
Request: req, Request: req,
Path: path, Path: path,
Query: query, Query: query,
Transport: transport, Transport: protocol,
Transport2: &SessionTransport{
Protocol: protocol,
Profile: inTH.Profile,
},
}) })
// workaround to prevent a bug in rtspclientsink // workaround to prevent a bug in rtspclientsink
@@ -1332,7 +1361,7 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
if ss.state == ServerSessionStateInitial { if ss.state == ServerSessionStateInitial {
err = stream.readerAdd(ss, err = stream.readerAdd(ss,
inTH.ClientPorts, inTH.ClientPorts,
transport, protocol,
) )
if err != nil { if err != nil {
return &base.Response{ return &base.Response{
@@ -1410,11 +1439,11 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
var udpRTCPWriteAddr *net.UDPAddr var udpRTCPWriteAddr *net.UDPAddr
var tcpChannel int var tcpChannel int
switch transport { switch protocol {
case TransportUDP, TransportUDPMulticast: case TransportUDP, TransportUDPMulticast:
th.Protocol = headers.TransportProtocolUDP th.Protocol = headers.TransportProtocolUDP
if transport == TransportUDP { if protocol == TransportUDP {
udpRTPReadPort = inTH.ClientPorts[0] udpRTPReadPort = inTH.ClientPorts[0]
udpRTCPReadPort = inTH.ClientPorts[1] udpRTCPReadPort = inTH.ClientPorts[1]
@@ -1460,8 +1489,10 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
ss.propsMutex.Lock() ss.propsMutex.Lock()
ss.setuppedTransport = &transport ss.setuppedTransport = &SessionTransport{
ss.setuppedProfile = inTH.Profile Protocol: protocol,
Profile: inTH.Profile,
}
sm := &serverSessionMedia{ sm := &serverSessionMedia{
ss: ss, ss: ss,
@@ -1542,7 +1573,7 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
} }
if ss.state != ServerSessionStatePlay && if ss.state != ServerSessionStatePlay &&
*ss.setuppedTransport != TransportUDPMulticast { ss.setuppedTransport.Protocol != TransportUDPMulticast {
ss.createWriter() ss.createWriter()
} }
@@ -1575,12 +1606,12 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
} }
} }
if *ss.setuppedTransport == TransportTCP { if ss.setuppedTransport.Protocol == TransportTCP {
ss.tcpFrame = &base.InterleavedFrame{} ss.tcpFrame = &base.InterleavedFrame{}
ss.tcpBuffer = make([]byte, ss.s.MaxPacketSize+4) ss.tcpBuffer = make([]byte, ss.s.MaxPacketSize+4)
} }
switch *ss.setuppedTransport { switch ss.setuppedTransport.Protocol {
case TransportUDP: case TransportUDP:
ss.udpCheckStreamTimer = time.NewTimer(ss.s.checkStreamPeriod) ss.udpCheckStreamTimer = time.NewTimer(ss.s.checkStreamPeriod)
ss.startWriter() ss.startWriter()
@@ -1613,7 +1644,7 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
} }
} else { } else {
if ss.state != ServerSessionStatePlay && if ss.state != ServerSessionStatePlay &&
*ss.setuppedTransport != TransportUDPMulticast { ss.setuppedTransport.Protocol != TransportUDPMulticast {
ss.destroyWriter() ss.destroyWriter()
} }
} }
@@ -1670,12 +1701,12 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
} }
} }
if *ss.setuppedTransport == TransportTCP { if ss.setuppedTransport.Protocol == TransportTCP {
ss.tcpFrame = &base.InterleavedFrame{} ss.tcpFrame = &base.InterleavedFrame{}
ss.tcpBuffer = make([]byte, ss.s.MaxPacketSize+4) ss.tcpBuffer = make([]byte, ss.s.MaxPacketSize+4)
} }
switch *ss.setuppedTransport { switch ss.setuppedTransport.Protocol {
case TransportUDP: case TransportUDP:
ss.udpCheckStreamTimer = time.NewTimer(ss.s.checkStreamPeriod) ss.udpCheckStreamTimer = time.NewTimer(ss.s.checkStreamPeriod)
ss.startWriter() ss.startWriter()
@@ -1733,7 +1764,7 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
ss.state = ServerSessionStatePrePlay ss.state = ServerSessionStatePrePlay
ss.propsMutex.Unlock() ss.propsMutex.Unlock()
switch *ss.setuppedTransport { switch ss.setuppedTransport.Protocol {
case TransportUDP: case TransportUDP:
ss.udpCheckStreamTimer = emptyTimer() ss.udpCheckStreamTimer = emptyTimer()
@@ -1746,7 +1777,7 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
} }
case ServerSessionStateRecord: case ServerSessionStateRecord:
switch *ss.setuppedTransport { switch ss.setuppedTransport.Protocol {
case TransportUDP: case TransportUDP:
ss.udpCheckStreamTimer = emptyTimer() ss.udpCheckStreamTimer = emptyTimer()
@@ -1767,7 +1798,7 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
case base.Teardown: case base.Teardown:
var err error var err error
if (ss.state == ServerSessionStatePlay || ss.state == ServerSessionStateRecord) && if (ss.state == ServerSessionStatePlay || ss.state == ServerSessionStateRecord) &&
*ss.setuppedTransport == TransportTCP { ss.setuppedTransport.Protocol == TransportTCP {
err = switchReadFuncError{false} err = switchReadFuncError{false}
} }

View File

@@ -31,7 +31,8 @@ func (sf *serverSessionFormat) initialize() {
sf.rtpPacketsSent = new(uint64) sf.rtpPacketsSent = new(uint64)
sf.rtpPacketsLost = new(uint64) sf.rtpPacketsLost = new(uint64)
udp := *sf.sm.ss.setuppedTransport == TransportUDP || *sf.sm.ss.setuppedTransport == TransportUDPMulticast udp := sf.sm.ss.setuppedTransport.Protocol == TransportUDP ||
sf.sm.ss.setuppedTransport.Protocol == TransportUDPMulticast
if udp { if udp {
sf.writePacketRTPInQueue = sf.writePacketRTPInQueueUDP sf.writePacketRTPInQueue = sf.writePacketRTPInQueueUDP
@@ -119,7 +120,7 @@ func (sf *serverSessionFormat) writePacketRTP(pkt *rtp.Packet) error {
pkt.SSRC = sf.localSSRC pkt.SSRC = sf.localSSRC
maxPlainPacketSize := sf.sm.ss.s.MaxPacketSize maxPlainPacketSize := sf.sm.ss.s.MaxPacketSize
if isSecure(sf.sm.ss.setuppedProfile) { if isSecure(sf.sm.ss.setuppedTransport.Profile) {
maxPlainPacketSize -= srtpOverhead maxPlainPacketSize -= srtpOverhead
} }
@@ -131,7 +132,7 @@ func (sf *serverSessionFormat) writePacketRTP(pkt *rtp.Packet) error {
plain = plain[:n] plain = plain[:n]
var encr []byte var encr []byte
if isSecure(sf.sm.ss.setuppedProfile) { if isSecure(sf.sm.ss.setuppedTransport.Profile) {
encr = make([]byte, sf.sm.ss.s.MaxPacketSize) encr = make([]byte, sf.sm.ss.s.MaxPacketSize)
encr, err = sf.sm.srtpOutCtx.encryptRTP(encr, plain, &pkt.Header) encr, err = sf.sm.srtpOutCtx.encryptRTP(encr, plain, &pkt.Header)
if err != nil { if err != nil {
@@ -139,7 +140,7 @@ func (sf *serverSessionFormat) writePacketRTP(pkt *rtp.Packet) error {
} }
} }
if isSecure(sf.sm.ss.setuppedProfile) { if isSecure(sf.sm.ss.setuppedTransport.Profile) {
return sf.writePacketRTPEncoded(encr) return sf.writePacketRTPEncoded(encr)
} }
return sf.writePacketRTPEncoded(plain) return sf.writePacketRTPEncoded(plain)

View File

@@ -58,7 +58,7 @@ func (sm *serverSessionMedia) initialize() {
sm.formats[forma.PayloadType()] = f sm.formats[forma.PayloadType()] = f
} }
switch *sm.ss.setuppedTransport { switch sm.ss.setuppedTransport.Protocol {
case TransportUDP, TransportUDPMulticast: case TransportUDP, TransportUDPMulticast:
sm.writePacketRTCPInQueue = sm.writePacketRTCPInQueueUDP sm.writePacketRTCPInQueue = sm.writePacketRTCPInQueueUDP
@@ -88,9 +88,9 @@ func (sm *serverSessionMedia) close() {
} }
func (sm *serverSessionMedia) start() error { func (sm *serverSessionMedia) start() error {
switch *sm.ss.setuppedTransport { switch sm.ss.setuppedTransport.Protocol {
case TransportUDP, TransportUDPMulticast: case TransportUDP, TransportUDPMulticast:
if *sm.ss.setuppedTransport == TransportUDP { if sm.ss.setuppedTransport.Protocol == TransportUDP {
if sm.ss.state == ServerSessionStatePlay { if sm.ss.state == ServerSessionStatePlay {
if sm.media.IsBackChannel { if sm.media.IsBackChannel {
sm.ss.s.udpRTPListener.addClient(sm.ss.author.ip(), sm.udpRTPReadPort, sm.readPacketRTPUDPPlay) sm.ss.s.udpRTPListener.addClient(sm.ss.author.ip(), sm.udpRTPReadPort, sm.readPacketRTPUDPPlay)
@@ -136,7 +136,7 @@ func (sm *serverSessionMedia) start() error {
} }
func (sm *serverSessionMedia) stop() { func (sm *serverSessionMedia) stop() {
if *sm.ss.setuppedTransport == TransportUDP { if sm.ss.setuppedTransport.Protocol == TransportUDP {
sm.ss.s.udpRTPListener.removeClient(sm.ss.author.ip(), sm.udpRTPReadPort) sm.ss.s.udpRTPListener.removeClient(sm.ss.author.ip(), sm.udpRTPReadPort)
sm.ss.s.udpRTCPListener.removeClient(sm.ss.author.ip(), sm.udpRTCPReadPort) sm.ss.s.udpRTCPListener.removeClient(sm.ss.author.ip(), sm.udpRTCPReadPort)
} }
@@ -427,7 +427,7 @@ func (sm *serverSessionMedia) writePacketRTCP(pkt rtcp.Packet) error {
} }
maxPlainPacketSize := sm.ss.s.MaxPacketSize maxPlainPacketSize := sm.ss.s.MaxPacketSize
if isSecure(sm.ss.setuppedProfile) { if isSecure(sm.ss.setuppedTransport.Profile) {
maxPlainPacketSize -= srtcpOverhead maxPlainPacketSize -= srtcpOverhead
} }
@@ -436,7 +436,7 @@ func (sm *serverSessionMedia) writePacketRTCP(pkt rtcp.Packet) error {
} }
var encr []byte var encr []byte
if isSecure(sm.ss.setuppedProfile) { if isSecure(sm.ss.setuppedTransport.Profile) {
encr = make([]byte, sm.ss.s.MaxPacketSize) encr = make([]byte, sm.ss.s.MaxPacketSize)
encr, err = sm.srtpOutCtx.encryptRTCP(encr, plain, nil) encr, err = sm.srtpOutCtx.encryptRTCP(encr, plain, nil)
if err != nil { if err != nil {
@@ -444,7 +444,7 @@ func (sm *serverSessionMedia) writePacketRTCP(pkt rtcp.Packet) error {
} }
} }
if isSecure(sm.ss.setuppedProfile) { if isSecure(sm.ss.setuppedTransport.Profile) {
return sm.writePacketRTCPEncoded(encr) return sm.writePacketRTCPEncoded(encr)
} }
return sm.writePacketRTCPEncoded(plain) return sm.writePacketRTCPEncoded(plain)

View File

@@ -211,7 +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, protocol TransportProtocol,
) error { ) error {
st.mutex.Lock() st.mutex.Lock()
defer st.mutex.Unlock() defer st.mutex.Unlock()
@@ -266,7 +266,7 @@ func (st *ServerStream) readerRemove(ss *ServerSession) {
delete(st.readers, ss) delete(st.readers, ss)
if *ss.setuppedTransport == TransportUDPMulticast { if ss.setuppedTransport.Protocol == TransportUDPMulticast {
st.multicastReaderCount-- st.multicastReaderCount--
if st.multicastReaderCount == 0 { if st.multicastReaderCount == 0 {
for _, media := range st.medias { for _, media := range st.medias {
@@ -285,7 +285,7 @@ func (st *ServerStream) readerSetActive(ss *ServerSession) {
return return
} }
if *ss.setuppedTransport == TransportUDPMulticast { if ss.setuppedTransport.Protocol == TransportUDPMulticast {
for medi, sm := range ss.setuppedMedias { for medi, sm := range ss.setuppedMedias {
streamMedia := st.medias[medi] streamMedia := st.medias[medi]
streamMedia.multicastWriter.rtcpl.addClient( streamMedia.multicastWriter.rtcpl.addClient(
@@ -304,7 +304,7 @@ func (st *ServerStream) readerSetInactive(ss *ServerSession) {
return return
} }
if *ss.setuppedTransport == TransportUDPMulticast { if ss.setuppedTransport.Protocol == TransportUDPMulticast {
for medi := range ss.setuppedMedias { for medi := range ss.setuppedMedias {
streamMedia := st.medias[medi] streamMedia := st.medias[medi]
streamMedia.multicastWriter.rtcpl.removeClient(ss.author.ip(), streamMedia.multicastWriter.rtcpl.port()) streamMedia.multicastWriter.rtcpl.removeClient(ss.author.ip(), streamMedia.multicastWriter.rtcpl.port())

View File

@@ -86,7 +86,7 @@ func (sf *serverStreamFormat) writePacketRTP(pkt *rtp.Packet, ntp time.Time) err
if rsm, ok := r.setuppedMedias[sf.sm.media]; ok { if rsm, ok := r.setuppedMedias[sf.sm.media]; ok {
rsf := rsm.formats[pkt.PayloadType] rsf := rsm.formats[pkt.PayloadType]
if isSecure(r.setuppedProfile) { if isSecure(r.setuppedTransport.Profile) {
err = rsf.writePacketRTPEncoded(encr) err = rsf.writePacketRTPEncoded(encr)
if err != nil { if err != nil {
r.onStreamWriteError(err) r.onStreamWriteError(err)

View File

@@ -78,7 +78,7 @@ func (sm *serverStreamMedia) writePacketRTCP(pkt rtcp.Packet) error {
// send unicast // send unicast
for r := range sm.st.activeUnicastReaders { for r := range sm.st.activeUnicastReaders {
if sm, ok := r.setuppedMedias[sm.media]; ok { if sm, ok := r.setuppedMedias[sm.media]; ok {
if isSecure(r.setuppedProfile) { if isSecure(r.setuppedTransport.Profile) {
err = sm.writePacketRTCPEncoded(encr) err = sm.writePacketRTCPEncoded(encr)
if err != nil { if err != nil {
r.onStreamWriteError(err) r.onStreamWriteError(err)

View File

@@ -8,7 +8,22 @@ import (
) )
// StatsSessionFormat are session format statistics. // StatsSessionFormat are session format statistics.
type StatsSessionFormat struct { //
// Deprecated: replaced by SessionStatsFormat
type StatsSessionFormat = SessionStatsFormat
// StatsSessionMedia are session media statistics.
//
// Deprecated: replaced by SessionStatsMedia
type StatsSessionMedia = SessionStatsMedia
// StatsSession are session statistics.
//
// Deprecated: replaced by SessionStats.
type StatsSession = SessionStats
// SessionStatsFormat are session format statistics.
type SessionStatsFormat struct {
// number of RTP packets correctly received and processed // number of RTP packets correctly received and processed
RTPPacketsReceived uint64 RTPPacketsReceived uint64
// number of sent RTP packets // number of sent RTP packets
@@ -29,8 +44,8 @@ type StatsSessionFormat struct {
RTPPacketsLastNTP time.Time RTPPacketsLastNTP time.Time
} }
// StatsSessionMedia are session media statistics. // SessionStatsMedia are session media statistics.
type StatsSessionMedia struct { type SessionStatsMedia struct {
// received bytes // received bytes
BytesReceived uint64 BytesReceived uint64
// sent bytes // sent bytes
@@ -45,11 +60,11 @@ type StatsSessionMedia struct {
RTCPPacketsInError uint64 RTCPPacketsInError uint64
// format statistics // format statistics
Formats map[format.Format]StatsSessionFormat Formats map[format.Format]SessionStatsFormat
} }
// StatsSession are session statistics. // SessionStats are session statistics.
type StatsSession struct { type SessionStats struct {
// received bytes // received bytes
BytesReceived uint64 BytesReceived uint64
// sent bytes // sent bytes
@@ -72,5 +87,5 @@ type StatsSession struct {
RTCPPacketsInError uint64 RTCPPacketsInError uint64
// media statistics // media statistics
Medias map[*description.Media]StatsSessionMedia Medias map[*description.Media]SessionStatsMedia
} }

9
session_transport.go Normal file
View File

@@ -0,0 +1,9 @@
package gortsplib
import "github.com/bluenviron/gortsplib/v4/pkg/headers"
// SessionTransport contains details about the transport of a session.
type SessionTransport struct {
Protocol TransportProtocol
Profile headers.TransportProfile
}

View File

@@ -1,23 +1,28 @@
package gortsplib package gortsplib
// Transport is a RTSP transport protocol. // Transport is a RTSP transport protocol.
type Transport int //
// Deprecated: renamed into TransportProtocol.
type Transport = TransportProtocol
// TransportProtocol is a RTSP transport protocol.
type TransportProtocol int
// transport protocols. // transport protocols.
const ( const (
TransportUDP Transport = iota TransportUDP TransportProtocol = iota
TransportUDPMulticast TransportUDPMulticast
TransportTCP TransportTCP
) )
var transportLabels = map[Transport]string{ var transportLabels = map[TransportProtocol]string{
TransportUDP: "UDP", TransportUDP: "UDP",
TransportUDPMulticast: "UDP-multicast", TransportUDPMulticast: "UDP-multicast",
TransportTCP: "TCP", TransportTCP: "TCP",
} }
// String implements fmt.Stringer. // String implements fmt.Stringer.
func (t Transport) String() string { func (t TransportProtocol) String() string {
if l, ok := transportLabels[t]; ok { if l, ok := transportLabels[t]; ok {
return l return l
} }

View File

@@ -6,10 +6,10 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestTransportString(t *testing.T) { func TestTransportProtocolString(t *testing.T) {
tr := TransportUDPMulticast tr := TransportUDPMulticast
require.NotEqual(t, "unknown", tr.String()) require.NotEqual(t, "unknown", tr.String())
tr = Transport(15) tr = TransportProtocol(15)
require.Equal(t, "unknown", tr.String()) require.Equal(t, "unknown", tr.String())
} }