client: support writing to ONVIF back channels (#101) (#462)

This commit is contained in:
Alessandro Ros
2023-11-15 13:20:29 +01:00
committed by GitHub
parent aaef8c29a7
commit f78b04cf4e
9 changed files with 414 additions and 92 deletions

View File

@@ -245,6 +245,8 @@ type Client struct {
UserAgent string
// disable automatic RTCP sender reports.
DisableRTCPSenderReports bool
// explicitly request back channels to the server.
RequestBackChannels bool
// pointer to a variable that stores received bytes.
BytesReceived *uint64
// pointer to a variable that stores sent bytes.
@@ -301,6 +303,8 @@ type Client struct {
lastDescribeURL *base.URL
baseURL *base.URL
effectiveTransport *Transport
backChannelSetupped bool
stdChannelSetupped bool
medias map[*description.Media]*clientMedia
tcpCallbackByChannel map[int]readFunc
lastRange *headers.Range
@@ -662,9 +666,16 @@ func (c *Client) doClose() {
}
if c.nconn != nil && c.baseURL != nil {
header := base.Header{}
if c.backChannelSetupped {
header["Require"] = base.HeaderValue{"www.onvif.org/ver20/backchannel"}
}
c.do(&base.Request{ //nolint:errcheck
Method: base.Teardown,
URL: c.baseURL,
Header: header,
}, true)
}
@@ -696,6 +707,8 @@ func (c *Client) reset() {
c.useGetParameter = false
c.baseURL = nil
c.effectiveTransport = nil
c.backChannelSetupped = false
c.stdChannelSetupped = false
c.medias = nil
c.tcpCallbackByChannel = nil
}
@@ -776,13 +789,13 @@ func (c *Client) trySwitchingProtocol2(medi *description.Media, baseURL *base.UR
func (c *Client) startReadRoutines() {
// allocate writer here because it's needed by RTCP receiver / sender
if c.state == clientStatePlay {
if c.state == clientStateRecord || c.backChannelSetupped {
c.writer.allocateBuffer(c.WriteQueueSize)
} else {
// when reading, buffer is only used to send RTCP receiver reports,
// that are much smaller than RTP packets and are sent at a fixed interval.
// decrease RAM consumption by allocating less buffers.
c.writer.allocateBuffer(8)
} else {
c.writer.allocateBuffer(c.WriteQueueSize)
}
c.timeDecoder = rtptime.NewGlobalDecoder()
@@ -791,7 +804,7 @@ func (c *Client) startReadRoutines() {
cm.start()
}
if c.state == clientStatePlay {
if c.state == clientStatePlay && c.stdChannelSetupped {
c.keepaliveTimer = time.NewTimer(c.keepalivePeriod)
switch *c.effectiveTransport {
@@ -991,7 +1004,7 @@ func (c *Client) isInTCPTimeout() bool {
func (c *Client) doCheckTimeout() error {
if *c.effectiveTransport == TransportUDP ||
*c.effectiveTransport == TransportUDPMulticast {
if c.checkTimeoutInitial {
if c.checkTimeoutInitial && !c.backChannelSetupped {
c.checkTimeoutInitial = false
if c.atLeastOneUDPPacketHasBeenReceived() {
@@ -1092,12 +1105,18 @@ func (c *Client) doDescribe(u *base.URL) (*description.Session, *base.Response,
return nil, nil, err
}
header := base.Header{
"Accept": base.HeaderValue{"application/sdp"},
}
if c.RequestBackChannels {
header["Require"] = base.HeaderValue{"www.onvif.org/ver20/backchannel"}
}
res, err := c.do(&base.Request{
Method: base.Describe,
URL: u,
Header: base.Header{
"Accept": base.HeaderValue{"application/sdp"},
},
Header: header,
}, false)
if err != nil {
return nil, nil, err
@@ -1334,12 +1353,18 @@ func (c *Client) doSetup(
return nil, err
}
header := base.Header{
"Transport": th.Marshal(),
}
if medi.IsBackChannel {
header["Require"] = base.HeaderValue{"www.onvif.org/ver20/backchannel"}
}
res, err := c.do(&base.Request{
Method: base.Setup,
URL: mediaURL,
Header: base.Header{
"Transport": th.Marshal(),
},
Header: header,
}, false)
if err != nil {
cm.close()
@@ -1509,6 +1534,12 @@ func (c *Client) doSetup(
c.baseURL = baseURL
c.effectiveTransport = &desiredTransport
if medi.IsBackChannel {
c.backChannelSetupped = true
} else {
c.stdChannelSetupped = true
}
if c.state == clientStateInitial {
c.state = clientStatePrePlay
}
@@ -1590,12 +1621,18 @@ func (c *Client) doPlay(ra *headers.Range) (*base.Response, error) {
}
}
header := base.Header{
"Range": ra.Marshal(),
}
if c.backChannelSetupped {
header["Require"] = base.HeaderValue{"www.onvif.org/ver20/backchannel"}
}
res, err := c.do(&base.Request{
Method: base.Play,
URL: c.baseURL,
Header: base.Header{
"Range": ra.Marshal(),
},
Header: header,
}, false)
if err != nil {
c.stopReadRoutines()