2018-06-09 07:38:48 +03:00
/ *
NAME
senders . go
DESCRIPTION
See Readme . md
AUTHORS
Saxon A . Nelson - Milton < saxon @ ausocean . org >
Alan Noble < alan @ ausocean . org >
LICENSE
revid is Copyright ( C ) 2017 - 2018 the Australian Ocean Lab ( AusOcean )
It is free software : you can redistribute it and / or modify them
under the terms of the GNU General Public License as published by the
Free Software Foundation , either version 3 of the License , or ( at your
option ) any later version .
It is distributed in the hope that it will be useful , but WITHOUT
ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License
for more details .
You should have received a copy of the GNU General Public License
along with revid in gpl . txt . If not , see http : //www.gnu.org/licenses.
* /
package revid
import (
2019-08-25 10:44:06 +03:00
"errors"
2018-12-14 06:05:56 +03:00
"fmt"
2019-03-29 05:19:26 +03:00
"io"
2018-11-18 05:02:11 +03:00
"net"
2018-06-09 07:38:48 +03:00
"os"
2019-04-08 12:32:42 +03:00
"sync"
"time"
2018-06-09 07:38:48 +03:00
2019-03-01 06:00:06 +03:00
"github.com/Comcast/gots/packet"
2019-03-25 04:21:03 +03:00
"bitbucket.org/ausocean/av/container/mts"
"bitbucket.org/ausocean/av/protocol/rtmp"
"bitbucket.org/ausocean/av/protocol/rtp"
2018-06-15 10:12:29 +03:00
"bitbucket.org/ausocean/iot/pi/netsender"
2022-05-27 09:12:52 +03:00
"bitbucket.org/ausocean/utils/logging"
2021-05-17 04:33:41 +03:00
"bitbucket.org/ausocean/utils/pool"
2018-06-09 07:38:48 +03:00
)
2021-05-17 04:33:41 +03:00
// Sender pool buffer read timeouts.
2019-08-23 09:11:54 +03:00
const (
2022-11-13 02:08:49 +03:00
rtmpPoolReadTimeout = 1 * time . Second
mtsPoolReadTimeout = 1 * time . Second
mtsBufferPoolMaxAlloc = 5 << 20 // 5MiB.
maxBuffLen = 50000000
2019-08-23 09:11:54 +03:00
)
2020-04-10 19:32:44 +03:00
var (
2021-05-17 04:48:44 +03:00
adjustedRTMPPoolElementSize int
adjustedMTSPoolElementSize int
2020-04-10 19:32:44 +03:00
)
2023-02-04 10:47:29 +03:00
var errReportCallbackNil = errors . New ( "report callback is nil" )
type httpSenderOption func ( s * httpSender ) error
// withReportCallback provides a functional option to set the report callback
// function. This can be used to record the number of bytes sent.
func withReportCallback ( report func ( sent int ) ) httpSenderOption {
return func ( s * httpSender ) error {
if report == nil {
return errReportCallbackNil
}
s . report = report
return nil
}
}
// withHTTPAddress provides a functional option to set the destination http
// address.
func withHTTPAddress ( addr string ) httpSenderOption {
return func ( s * httpSender ) error {
s . addr = addr
return nil
}
}
2019-04-02 06:15:36 +03:00
// httpSender provides an implemntation of io.Writer to perform sends to a http
// destination.
2019-04-01 04:37:28 +03:00
type httpSender struct {
2020-01-22 07:56:14 +03:00
client * netsender . Sender
2022-06-08 07:35:51 +03:00
log logging . Logger
2020-01-22 07:56:14 +03:00
report func ( sent int )
2023-02-04 10:47:29 +03:00
addr string
2019-02-16 06:56:51 +03:00
}
2019-05-20 18:15:54 +03:00
// newHttpSender returns a pointer to a new httpSender.
2022-09-09 04:54:29 +03:00
// report is callback that can be used to report the amount of data sent per write.
// This can be set to nil.
2023-02-04 10:47:29 +03:00
func newHTTPSender ( ns * netsender . Sender , log logging . Logger , opts ... httpSenderOption ) ( * httpSender , error ) {
s := & httpSender { client : ns , log : log }
for _ , opt := range opts {
err := opt ( s )
if err != nil {
return nil , err
}
2019-02-16 06:56:51 +03:00
}
2023-02-04 10:47:29 +03:00
return s , nil
2019-02-16 06:56:51 +03:00
}
2019-04-02 06:15:36 +03:00
// Write implements io.Writer.
2019-04-01 04:37:28 +03:00
func ( s * httpSender ) Write ( d [ ] byte ) ( int , error ) {
2023-02-09 03:14:16 +03:00
s . log . Debug ( "HTTP sending" , "address" , s . addr )
2023-02-04 10:47:29 +03:00
err := httpSend ( d , s . client , s . log , s . addr )
2020-01-20 10:10:45 +03:00
if err == nil {
2022-06-08 07:35:51 +03:00
s . log . Debug ( "good send" , "len" , len ( d ) )
2022-09-09 04:54:29 +03:00
if s . report != nil {
s . report ( len ( d ) )
}
2020-03-27 15:20:51 +03:00
} else {
2022-06-08 07:35:51 +03:00
s . log . Debug ( "bad send" , "error" , err )
2020-01-20 10:10:45 +03:00
}
return len ( d ) , err
2019-02-16 06:56:51 +03:00
}
2019-04-08 12:32:42 +03:00
func ( s * httpSender ) Close ( ) error { return nil }
2023-02-04 10:47:29 +03:00
func httpSend ( d [ ] byte , client * netsender . Sender , log logging . Logger , addr string ) error {
2020-08-14 16:34:56 +03:00
// Only send if "V0" or "S0" are configured as input.
2019-04-01 04:37:28 +03:00
send := false
ip := client . Param ( "ip" )
2022-06-08 07:35:51 +03:00
log . Debug ( "making pins, and sending recv request" , "ip" , ip )
2020-08-14 16:34:56 +03:00
pins := netsender . MakePins ( ip , "V,S" )
2019-04-01 04:37:28 +03:00
for i , pin := range pins {
2020-08-15 11:08:45 +03:00
switch pin . Name {
case "V0" :
pins [ i ] . MimeType = "video/mp2t"
case "S0" :
pins [ i ] . MimeType = "audio/x-wav"
default :
continue
2019-04-01 04:37:28 +03:00
}
2020-08-15 11:08:45 +03:00
pins [ i ] . Value = len ( d )
pins [ i ] . Data = d
send = true
break
2019-04-01 04:37:28 +03:00
}
if ! send {
return nil
}
2023-02-04 10:47:29 +03:00
reply , _ , err := client . Send ( netsender . RequestRecv , pins , netsender . WithRecvAddress ( addr ) )
2019-04-01 04:37:28 +03:00
if err != nil {
return err
}
2022-06-08 07:35:51 +03:00
log . Debug ( "good request" , "reply" , reply )
2019-04-01 04:37:28 +03:00
return extractMeta ( reply , log )
}
// extractMeta looks at a reply at extracts any time or location data - then used
// to update time and location information in the mpegts encoder.
2022-06-08 07:35:51 +03:00
func extractMeta ( r string , log logging . Logger ) error {
2019-04-01 04:37:28 +03:00
dec , err := netsender . NewJSONDecoder ( r )
if err != nil {
return nil
}
2021-02-11 02:28:05 +03:00
// Extract time from reply if mts.Realtime has not been set.
if ! mts . RealTime . IsSet ( ) {
t , err := dec . Int ( "ts" )
if err != nil {
2022-06-08 07:35:51 +03:00
log . Warning ( "No timestamp in reply" )
2021-02-11 02:28:05 +03:00
} else {
2022-06-08 07:35:51 +03:00
log . Debug ( "got timestamp" , "ts" , t )
2021-02-11 02:28:05 +03:00
mts . RealTime . Set ( time . Unix ( int64 ( t ) , 0 ) )
}
2019-04-01 04:37:28 +03:00
}
// Extract location from reply
g , err := dec . String ( "ll" )
if err != nil {
2022-06-08 07:35:51 +03:00
log . Debug ( "No location in reply" )
2019-04-01 04:37:28 +03:00
} else {
2022-06-08 07:35:51 +03:00
log . Debug ( fmt . Sprintf ( "got location: %v" , g ) )
2021-02-22 08:47:36 +03:00
mts . Meta . Add ( mts . LocationKey , g )
2019-04-01 04:37:28 +03:00
}
return nil
}
2018-06-09 07:38:48 +03:00
// fileSender implements loadSender for a local file destination.
type fileSender struct {
2021-01-27 09:07:33 +03:00
file * os . File
data [ ] byte
multiFile bool
path string
init bool
2022-05-27 09:12:52 +03:00
log logging . Logger
2018-06-09 07:38:48 +03:00
}
2021-01-27 09:07:33 +03:00
// newFileSender returns a new fileSender. Setting multi true will write a new
// file for each write to this sender.
2022-05-27 09:12:52 +03:00
func newFileSender ( l logging . Logger , path string , multiFile bool ) ( * fileSender , error ) {
2021-01-27 09:07:33 +03:00
return & fileSender {
path : path ,
log : l ,
multiFile : multiFile ,
init : true ,
} , nil
2018-06-09 07:38:48 +03:00
}
2019-04-01 04:41:05 +03:00
// Write implements io.Writer.
func ( s * fileSender ) Write ( d [ ] byte ) ( int , error ) {
2021-01-27 09:07:33 +03:00
if s . init || s . multiFile {
fileName := s . path + time . Now ( ) . String ( )
s . log . Debug ( "creating new output file" , "init" , s . init , "multiFile" , s . multiFile , "fileName" , fileName )
f , err := os . Create ( fileName )
if err != nil {
return 0 , fmt . Errorf ( "could not create file to write media to: %w" , err )
}
s . file = f
s . init = false
}
s . log . Debug ( "writing output file" , "len(d)" , len ( d ) )
2019-04-01 04:41:05 +03:00
return s . file . Write ( d )
2018-06-09 07:38:48 +03:00
}
2019-04-08 12:32:42 +03:00
func ( s * fileSender ) Close ( ) error { return s . file . Close ( ) }
2018-06-09 07:38:48 +03:00
2019-04-08 12:32:42 +03:00
// mtsSender implements io.WriteCloser and provides sending capability specifically
2019-03-01 05:58:34 +03:00
// for use with MPEGTS packetization. It handles the construction of appropriately
2019-08-25 10:44:06 +03:00
// lengthed clips based on clip duration and PSI. It also accounts for
// discontinuities by setting the discontinuity indicator for the first packet of a clip.
2019-02-15 15:47:13 +03:00
type mtsSender struct {
2019-04-08 12:32:42 +03:00
dst io . WriteCloser
2019-04-01 04:20:11 +03:00
buf [ ] byte
2021-05-17 04:33:41 +03:00
pool * pool . Buffer
2019-04-01 04:20:11 +03:00
next [ ] byte
pkt packet . Packet
repairer * mts . DiscontinuityRepairer
curPid int
2019-08-25 10:44:06 +03:00
clipDur time . Duration
prev time . Time
2019-04-18 10:25:48 +03:00
done chan struct { }
2022-06-08 07:35:51 +03:00
log logging . Logger
2019-04-08 12:32:42 +03:00
wg sync . WaitGroup
2019-03-29 05:19:26 +03:00
}
2019-03-01 05:58:34 +03:00
// newMtsSender returns a new mtsSender.
2022-06-08 07:35:51 +03:00
func newMTSSender ( dst io . WriteCloser , log logging . Logger , rb * pool . Buffer , clipDur time . Duration ) * mtsSender {
log . Debug ( "setting up mtsSender" , "clip duration" , int ( clipDur ) )
2019-04-08 12:32:42 +03:00
s := & mtsSender {
2019-04-01 04:32:15 +03:00
dst : dst ,
2019-02-15 04:31:07 +03:00
repairer : mts . NewDiscontinuityRepairer ( ) ,
2019-04-08 12:32:42 +03:00
log : log ,
2021-05-17 04:33:41 +03:00
pool : rb ,
2019-04-18 10:25:48 +03:00
done : make ( chan struct { } ) ,
2019-08-25 10:44:06 +03:00
clipDur : clipDur ,
2019-02-15 04:31:07 +03:00
}
2022-11-13 02:08:49 +03:00
// mtsSender will do particularly large writes to the pool buffer; let's
// increase its max allowable allocation.
pool . MaxAlloc ( mtsBufferPoolMaxAlloc )
2019-04-08 12:32:42 +03:00
s . wg . Add ( 1 )
go s . output ( )
return s
2019-02-15 04:31:07 +03:00
}
2019-04-10 05:45:46 +03:00
// output starts an mtsSender's data handling routine.
2019-04-08 12:32:42 +03:00
func ( s * mtsSender ) output ( ) {
2021-05-17 04:33:41 +03:00
var chunk * pool . Chunk
2019-04-08 12:32:42 +03:00
for {
select {
2019-04-18 10:25:48 +03:00
case <- s . done :
2022-06-08 07:35:51 +03:00
s . log . Info ( "terminating sender output routine" )
2019-04-08 12:32:42 +03:00
defer s . wg . Done ( )
return
default :
2020-04-09 09:25:01 +03:00
// If chunk is nil then we're ready to get another from the ringBuffer.
if chunk == nil {
2019-04-08 12:32:42 +03:00
var err error
2021-05-17 04:48:44 +03:00
chunk , err = s . pool . Next ( mtsPoolReadTimeout )
2019-04-08 12:32:42 +03:00
switch err {
2019-04-15 03:55:35 +03:00
case nil , io . EOF :
2019-04-10 05:49:28 +03:00
continue
2021-05-17 04:33:41 +03:00
case pool . ErrTimeout :
2022-06-08 07:35:51 +03:00
s . log . Debug ( "mtsSender: pool buffer read timeout" )
2019-04-08 12:32:42 +03:00
continue
default :
2022-06-08 07:35:51 +03:00
s . log . Error ( "unexpected error" , "error" , err . Error ( ) )
2019-04-08 12:32:42 +03:00
continue
}
2019-04-10 05:49:28 +03:00
}
2020-04-09 09:25:01 +03:00
err := s . repairer . Repair ( chunk . Bytes ( ) )
2019-04-10 05:49:28 +03:00
if err != nil {
2020-04-09 09:25:01 +03:00
chunk . Close ( )
chunk = nil
2019-04-10 05:49:28 +03:00
continue
}
2022-06-08 07:35:51 +03:00
s . log . Debug ( "mtsSender: writing" )
2020-04-09 09:25:01 +03:00
_ , err = s . dst . Write ( chunk . Bytes ( ) )
2019-04-10 05:49:28 +03:00
if err != nil {
2022-06-08 07:35:51 +03:00
s . log . Debug ( "failed write, repairing MTS" , "error" , err )
2019-04-10 05:49:28 +03:00
s . repairer . Failed ( )
continue
2020-03-27 15:20:51 +03:00
} else {
2022-06-08 07:35:51 +03:00
s . log . Debug ( "good write" )
2019-04-08 12:32:42 +03:00
}
2020-04-09 09:25:01 +03:00
chunk . Close ( )
chunk = nil
2019-04-08 12:32:42 +03:00
}
}
}
2019-04-10 05:45:46 +03:00
// Write implements io.Writer.
func ( s * mtsSender ) Write ( d [ ] byte ) ( int , error ) {
2019-08-25 10:44:06 +03:00
if len ( d ) < mts . PacketSize {
return 0 , errors . New ( "do not have full MTS packet" )
}
2019-04-10 05:45:46 +03:00
if s . next != nil {
2022-06-08 07:35:51 +03:00
s . log . Debug ( "appending packet to clip" )
2019-04-10 05:45:46 +03:00
s . buf = append ( s . buf , s . next ... )
}
bytes := make ( [ ] byte , len ( d ) )
copy ( bytes , d )
s . next = bytes
2019-08-25 10:44:06 +03:00
p , _ := mts . PID ( bytes )
s . curPid = int ( p )
2021-02-03 04:47:23 +03:00
curDur := time . Now ( ) . Sub ( s . prev )
2022-06-08 07:35:51 +03:00
s . log . Debug ( "checking send conditions" , "curDuration" , int ( curDur ) , "sendDur" , int ( s . clipDur ) , "curPID" , s . curPid , "len" , len ( s . buf ) )
2021-02-03 04:47:23 +03:00
if curDur >= s . clipDur && s . curPid == mts . PatPid && len ( s . buf ) > 0 {
2022-06-08 07:35:51 +03:00
s . log . Debug ( "writing clip to pool buffer for sending" , "size" , len ( s . buf ) )
2019-08-25 10:44:06 +03:00
s . prev = time . Now ( )
2021-05-17 04:33:41 +03:00
n , err := s . pool . Write ( s . buf )
2019-11-08 02:36:51 +03:00
if err == nil {
2021-05-17 04:33:41 +03:00
s . pool . Flush ( )
2019-11-08 02:36:51 +03:00
}
2019-04-10 05:45:46 +03:00
if err != nil {
2022-11-06 09:55:28 +03:00
s . log . Warning ( "ringBuffer write error" , "error" , err . Error ( ) , "n" , n , "writeSize" , len ( s . buf ) , "rbElementSize" , adjustedMTSPoolElementSize )
2021-05-17 04:33:41 +03:00
if err == pool . ErrTooLong {
2021-05-17 04:48:44 +03:00
adjustedMTSPoolElementSize = len ( s . buf ) * 2
numElements := maxBuffLen / adjustedMTSPoolElementSize
s . pool = pool . NewBuffer ( maxBuffLen / adjustedMTSPoolElementSize , adjustedMTSPoolElementSize , 5 * time . Second )
2022-06-08 07:35:51 +03:00
s . log . Info ( "adjusted MTS pool buffer element size" , "new size" , adjustedMTSPoolElementSize , "num elements" , numElements , "size(MB)" , numElements * adjustedMTSPoolElementSize )
2020-04-09 09:25:01 +03:00
}
2019-04-10 05:45:46 +03:00
}
s . buf = s . buf [ : 0 ]
}
return len ( d ) , nil
}
// Close implements io.Closer.
func ( s * mtsSender ) Close ( ) error {
2022-06-08 07:35:51 +03:00
s . log . Debug ( "closing sender output routine" )
2019-04-18 10:25:48 +03:00
close ( s . done )
2019-04-10 05:45:46 +03:00
s . wg . Wait ( )
2022-06-08 07:35:51 +03:00
s . log . Info ( "sender output routine closed" )
2019-04-10 05:45:46 +03:00
return nil
}
2018-06-09 07:38:48 +03:00
// rtmpSender implements loadSender for a native RTMP destination.
type rtmpSender struct {
2020-01-22 07:56:14 +03:00
conn * rtmp . Conn
url string
retries int
2022-06-08 07:35:51 +03:00
log logging . Logger
2021-05-17 04:33:41 +03:00
pool * pool . Buffer
2020-01-22 07:56:14 +03:00
done chan struct { }
wg sync . WaitGroup
report func ( sent int )
2018-06-09 07:38:48 +03:00
}
2022-06-08 07:35:51 +03:00
func newRtmpSender ( url string , retries int , rb * pool . Buffer , log logging . Logger , report func ( sent int ) ) ( * rtmpSender , error ) {
2019-01-19 05:40:38 +03:00
var conn * rtmp . Conn
2018-06-09 07:38:48 +03:00
var err error
for n := 0 ; n < retries ; n ++ {
2022-06-08 07:35:51 +03:00
conn , err = rtmp . Dial ( url , log . Log )
2018-06-09 07:38:48 +03:00
if err == nil {
break
}
2022-06-08 07:35:51 +03:00
log . Error ( "dial error" , "error" , err )
2018-06-09 07:38:48 +03:00
if n < retries - 1 {
2022-06-08 07:35:51 +03:00
log . Info ( "retrying dial" )
2018-06-09 07:38:48 +03:00
}
}
s := & rtmpSender {
2020-01-22 07:56:14 +03:00
conn : conn ,
url : url ,
retries : retries ,
log : log ,
2021-05-17 04:33:41 +03:00
pool : rb ,
2020-01-22 07:56:14 +03:00
done : make ( chan struct { } ) ,
report : report ,
2018-06-09 07:38:48 +03:00
}
2019-04-15 04:18:12 +03:00
s . wg . Add ( 1 )
go s . output ( )
2019-03-13 10:44:00 +03:00
return s , err
2018-06-09 07:38:48 +03:00
}
2019-04-15 04:18:12 +03:00
// output starts an mtsSender's data handling routine.
func ( s * rtmpSender ) output ( ) {
2021-05-17 04:33:41 +03:00
var chunk * pool . Chunk
2019-04-15 04:18:12 +03:00
for {
select {
2019-04-18 10:25:48 +03:00
case <- s . done :
2022-06-08 07:35:51 +03:00
s . log . Info ( "terminating sender output routine" )
2019-04-15 04:18:12 +03:00
defer s . wg . Done ( )
return
default :
2021-05-17 04:33:41 +03:00
// If chunk is nil then we're ready to get another from the pool buffer.
2020-04-09 09:25:01 +03:00
if chunk == nil {
2019-04-15 04:18:12 +03:00
var err error
2021-05-17 04:48:44 +03:00
chunk , err = s . pool . Next ( rtmpPoolReadTimeout )
2019-04-15 04:18:12 +03:00
switch err {
case nil , io . EOF :
continue
2021-05-17 04:33:41 +03:00
case pool . ErrTimeout :
2022-06-08 07:35:51 +03:00
s . log . Debug ( "rtmpSender: pool buffer read timeout" )
2019-04-15 04:18:12 +03:00
continue
default :
2022-06-08 07:35:51 +03:00
s . log . Error ( "unexpected error" , "error" , err . Error ( ) )
2019-04-15 04:18:12 +03:00
continue
}
}
if s . conn == nil {
2022-06-08 07:35:51 +03:00
s . log . Warning ( "no rtmp connection, re-dialing" )
2019-04-15 04:18:12 +03:00
err := s . restart ( )
if err != nil {
2022-06-08 07:35:51 +03:00
s . log . Warning ( "could not restart connection" , "error" , err )
2019-04-15 04:18:12 +03:00
continue
}
}
2020-04-09 09:25:01 +03:00
_ , err := s . conn . Write ( chunk . Bytes ( ) )
2019-04-15 04:50:36 +03:00
switch err {
case nil , rtmp . ErrInvalidFlvTag :
2022-06-08 07:35:51 +03:00
s . log . Debug ( "good write to conn" )
2019-04-15 04:50:36 +03:00
default :
2022-06-08 07:35:51 +03:00
s . log . Warning ( "send error, re-dialing" , "error" , err )
2019-04-15 04:18:12 +03:00
err = s . restart ( )
if err != nil {
2022-06-08 07:35:51 +03:00
s . log . Warning ( "could not restart connection" , "error" , err )
2019-04-15 04:18:12 +03:00
}
continue
}
2020-04-09 09:25:01 +03:00
chunk . Close ( )
chunk = nil
2019-04-15 04:18:12 +03:00
}
}
}
2019-03-29 08:54:47 +03:00
// Write implements io.Writer.
2019-03-29 05:19:26 +03:00
func ( s * rtmpSender ) Write ( d [ ] byte ) ( int , error ) {
2022-06-08 07:35:51 +03:00
s . log . Debug ( "writing to pool buffer" )
2021-05-17 04:33:41 +03:00
_ , err := s . pool . Write ( d )
2019-11-08 02:36:51 +03:00
if err == nil {
2021-05-17 04:33:41 +03:00
s . pool . Flush ( )
2022-06-08 07:35:51 +03:00
s . log . Debug ( "good pool buffer write" , "len" , len ( d ) )
2020-03-27 15:20:51 +03:00
} else {
2022-06-08 07:35:51 +03:00
s . log . Warning ( "pool buffer write error" , "error" , err . Error ( ) )
2021-05-17 04:33:41 +03:00
if err == pool . ErrTooLong {
2021-05-17 04:48:44 +03:00
adjustedRTMPPoolElementSize = len ( d ) * 2
numElements := maxBuffLen / adjustedRTMPPoolElementSize
s . pool = pool . NewBuffer ( numElements , adjustedRTMPPoolElementSize , 5 * time . Second )
2022-06-08 07:35:51 +03:00
s . log . Info ( "adjusted RTMP pool buffer element size" , "new size" , adjustedRTMPPoolElementSize , "num elements" , numElements , "size(MB)" , numElements * adjustedRTMPPoolElementSize )
2020-04-09 09:25:01 +03:00
}
2019-03-03 10:54:54 +03:00
}
2020-01-22 07:56:14 +03:00
s . report ( len ( d ) )
2019-04-15 04:18:12 +03:00
return len ( d ) , nil
2018-06-09 07:38:48 +03:00
}
func ( s * rtmpSender ) restart ( ) error {
2019-04-15 04:50:36 +03:00
s . close ( )
2019-03-03 10:11:35 +03:00
var err error
2018-06-09 07:38:48 +03:00
for n := 0 ; n < s . retries ; n ++ {
2022-06-08 07:35:51 +03:00
s . log . Debug ( "dialing" , "dials" , n )
s . conn , err = rtmp . Dial ( s . url , s . log . Log )
2018-06-09 07:38:48 +03:00
if err == nil {
break
}
2022-06-08 07:35:51 +03:00
s . log . Error ( "dial error" , "error" , err )
2018-06-09 07:38:48 +03:00
if n < s . retries - 1 {
2022-06-08 07:35:51 +03:00
s . log . Info ( "retry rtmp connection" )
2018-06-09 07:38:48 +03:00
}
}
return err
}
2019-04-08 12:32:42 +03:00
func ( s * rtmpSender ) Close ( ) error {
2022-06-08 07:35:51 +03:00
s . log . Debug ( "closing output routine" )
2019-04-18 10:25:48 +03:00
if s . done != nil {
close ( s . done )
2019-04-15 04:50:36 +03:00
}
s . wg . Wait ( )
2022-06-08 07:35:51 +03:00
s . log . Info ( "output routine closed" )
2019-04-15 04:50:36 +03:00
return s . close ( )
}
func ( s * rtmpSender ) close ( ) error {
2022-06-08 07:35:51 +03:00
s . log . Debug ( "closing connection" )
2019-04-10 05:50:39 +03:00
if s . conn == nil {
return nil
2019-03-03 10:54:54 +03:00
}
2019-04-10 05:50:39 +03:00
return s . conn . Close ( )
2018-06-09 07:38:48 +03:00
}
2018-11-18 05:02:11 +03:00
2018-11-25 16:15:38 +03:00
// TODO: Write restart func for rtpSender
// rtpSender implements loadSender for a native udp destination with rtp packetization.
2018-11-25 15:40:38 +03:00
type rtpSender struct {
2022-06-08 07:35:51 +03:00
log logging . Logger
2018-11-25 15:40:38 +03:00
encoder * rtp . Encoder
2019-03-09 07:58:07 +03:00
data [ ] byte
2020-01-23 02:37:28 +03:00
report func ( sent int )
2018-11-25 15:40:38 +03:00
}
2022-06-08 07:35:51 +03:00
func newRtpSender ( addr string , log logging . Logger , fps uint , report func ( sent int ) ) ( * rtpSender , error ) {
2018-11-25 15:40:38 +03:00
conn , err := net . Dial ( "udp" , addr )
if err != nil {
return nil , err
}
s := & rtpSender {
log : log ,
2018-12-10 02:09:20 +03:00
encoder : rtp . NewEncoder ( conn , int ( fps ) ) ,
2020-01-23 02:37:28 +03:00
report : report ,
2018-11-25 15:40:38 +03:00
}
return s , nil
}
2019-04-02 05:23:42 +03:00
// Write implements io.Writer.
func ( s * rtpSender ) Write ( d [ ] byte ) ( int , error ) {
2019-04-09 09:14:18 +03:00
s . data = make ( [ ] byte , len ( d ) )
copy ( s . data , d )
_ , err := s . encoder . Write ( s . data )
if err != nil {
2022-06-08 07:35:51 +03:00
s . log . Warning ( "rtpSender: write error" , err . Error ( ) )
2019-04-09 09:14:18 +03:00
}
2020-01-23 02:37:28 +03:00
s . report ( len ( d ) )
2019-04-09 09:14:18 +03:00
return len ( d ) , nil
2019-02-15 04:31:07 +03:00
}
2019-04-08 12:32:42 +03:00
func ( s * rtpSender ) Close ( ) error { return nil }