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-03-10 10:18:18 +03:00
|
|
|
"errors"
|
2018-12-14 06:05:56 +03:00
|
|
|
"fmt"
|
2018-11-18 05:02:11 +03:00
|
|
|
"net"
|
2018-06-09 07:38:48 +03:00
|
|
|
"os"
|
2019-01-31 14:00:08 +03:00
|
|
|
"strconv"
|
2018-06-09 07:38:48 +03:00
|
|
|
|
2019-03-01 06:00:06 +03:00
|
|
|
"github.com/Comcast/gots/packet"
|
|
|
|
|
2018-06-09 07:38:48 +03:00
|
|
|
"bitbucket.org/ausocean/av/rtmp"
|
2018-12-13 04:52:06 +03:00
|
|
|
"bitbucket.org/ausocean/av/stream/mts"
|
2018-11-25 15:40:38 +03:00
|
|
|
"bitbucket.org/ausocean/av/stream/rtp"
|
2018-06-15 10:12:29 +03:00
|
|
|
"bitbucket.org/ausocean/iot/pi/netsender"
|
2019-01-02 08:09:47 +03:00
|
|
|
"bitbucket.org/ausocean/utils/logger"
|
2018-06-09 07:38:48 +03:00
|
|
|
)
|
|
|
|
|
2019-03-01 05:58:34 +03:00
|
|
|
// Sender is intended to provided functionality for the sending of a byte slice
|
|
|
|
// to an implemented destination.
|
2019-03-01 02:46:54 +03:00
|
|
|
type Sender interface {
|
2019-03-01 05:58:34 +03:00
|
|
|
// send takes the bytes slice d and sends to a particular destination as
|
|
|
|
// implemented.
|
2019-02-16 06:56:51 +03:00
|
|
|
send(d []byte) error
|
|
|
|
}
|
|
|
|
|
2019-03-10 04:51:53 +03:00
|
|
|
// multiSender allows for the sending through multi loadSenders using a single
|
|
|
|
// call to multiSender.Write.
|
2019-03-09 07:58:07 +03:00
|
|
|
type multiSender struct {
|
2019-03-12 08:02:01 +03:00
|
|
|
active func() bool
|
2019-03-09 07:58:07 +03:00
|
|
|
senders []loadSender
|
2019-03-12 07:43:24 +03:00
|
|
|
retry bool
|
2019-03-09 07:58:07 +03:00
|
|
|
}
|
|
|
|
|
2019-03-12 08:02:01 +03:00
|
|
|
// newMultiSender returns a pointer to a new multiSender. active is a function
|
|
|
|
// to indicate the state of the multiSenders owner i.e. whether it is running
|
|
|
|
// or not.
|
2019-03-12 13:23:27 +03:00
|
|
|
func newMultiSender(senders []loadSender, active func() bool) (*multiSender, error) {
|
2019-03-12 08:09:51 +03:00
|
|
|
if active == nil {
|
|
|
|
return nil, errors.New("multi sender requires that active func is provided")
|
|
|
|
}
|
|
|
|
s := &multiSender{
|
2019-03-12 08:02:01 +03:00
|
|
|
senders: senders,
|
|
|
|
active: active,
|
|
|
|
}
|
2019-03-12 08:09:51 +03:00
|
|
|
return s, nil
|
2019-03-09 07:58:07 +03:00
|
|
|
}
|
|
|
|
|
2019-03-10 04:51:53 +03:00
|
|
|
// Write implements io.Writer. The written slice will be sent to each loadSender
|
2019-03-12 08:02:01 +03:00
|
|
|
// in multiSender.senders as long as s.active() is true. If a send fails, and
|
|
|
|
// s.retry is true, the send will be tried again.
|
2019-03-09 07:58:07 +03:00
|
|
|
func (s *multiSender) Write(d []byte) (int, error) {
|
2019-03-12 07:43:24 +03:00
|
|
|
for _, sender := range s.senders {
|
2019-03-09 07:58:07 +03:00
|
|
|
sender.load(d)
|
2019-03-12 08:02:01 +03:00
|
|
|
for s.active() {
|
2019-03-12 07:43:24 +03:00
|
|
|
err := sender.send()
|
|
|
|
if err != nil {
|
2019-03-12 08:02:01 +03:00
|
|
|
sender.handleSendFail(err)
|
2019-03-12 07:43:24 +03:00
|
|
|
}
|
2019-03-12 13:23:27 +03:00
|
|
|
if err == nil || !sender.retrySend() {
|
2019-03-12 07:43:24 +03:00
|
|
|
break
|
2019-03-09 07:58:07 +03:00
|
|
|
}
|
|
|
|
}
|
2019-03-12 07:43:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, sender := range s.senders {
|
2019-03-10 09:19:41 +03:00
|
|
|
sender.release()
|
2019-03-09 07:58:07 +03:00
|
|
|
}
|
2019-03-12 07:43:24 +03:00
|
|
|
|
2019-03-09 07:58:07 +03:00
|
|
|
return len(d), nil
|
|
|
|
}
|
|
|
|
|
2019-03-01 05:58:34 +03:00
|
|
|
// minimalHttpSender implements Sender for posting HTTP to netreceiver or vidgrind.
|
2019-02-16 06:56:51 +03:00
|
|
|
type minimalHttpSender struct {
|
|
|
|
client *netsender.Sender
|
2019-03-01 05:58:34 +03:00
|
|
|
log func(lvl int8, msg string, args ...interface{})
|
2019-02-16 06:56:51 +03:00
|
|
|
}
|
|
|
|
|
2019-03-01 05:58:34 +03:00
|
|
|
// newMinimalHttpSender returns a pointer to a new minimalHttpSender.
|
2019-02-16 06:56:51 +03:00
|
|
|
func newMinimalHttpSender(ns *netsender.Sender, log func(lvl int8, msg string, args ...interface{})) *minimalHttpSender {
|
|
|
|
return &minimalHttpSender{
|
|
|
|
client: ns,
|
|
|
|
log: log,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-01 05:58:34 +03:00
|
|
|
// send takes a bytes slice d and sends to http using s' http client.
|
2019-02-16 06:56:51 +03:00
|
|
|
func (s *minimalHttpSender) send(d []byte) error {
|
|
|
|
return httpSend(d, s.client, s.log)
|
|
|
|
}
|
|
|
|
|
2018-06-09 07:38:48 +03:00
|
|
|
// loadSender is a destination to send a *ring.Chunk to.
|
|
|
|
// When a loadSender has finished using the *ring.Chunk
|
|
|
|
// it must be Closed.
|
|
|
|
type loadSender interface {
|
|
|
|
// load assigns the *ring.Chunk to the loadSender.
|
|
|
|
// The load call may fail, but must not mutate the
|
|
|
|
// the chunk.
|
2019-03-09 07:58:07 +03:00
|
|
|
load(d []byte) error
|
2018-06-09 07:38:48 +03:00
|
|
|
|
|
|
|
// send performs a destination-specific send
|
|
|
|
// operation. It must not mutate the chunk.
|
|
|
|
send() error
|
|
|
|
|
|
|
|
// release releases the *ring.Chunk.
|
|
|
|
release()
|
|
|
|
|
|
|
|
// close cleans up after use of the loadSender.
|
|
|
|
close() error
|
2019-03-09 07:58:07 +03:00
|
|
|
|
2019-03-10 04:51:53 +03:00
|
|
|
// handleSendFail performs any actions necessary in response to a failed send.
|
2019-03-10 09:07:28 +03:00
|
|
|
handleSendFail(err error) error
|
2019-03-12 13:23:27 +03:00
|
|
|
|
|
|
|
// retry returns true if this sender has been set for send retry.
|
|
|
|
retrySend() bool
|
2018-06-09 07:38:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// restart is an optional interface for loadSenders that
|
|
|
|
// can restart their connection.
|
|
|
|
type restarter interface {
|
|
|
|
restart() error
|
|
|
|
}
|
|
|
|
|
|
|
|
// fileSender implements loadSender for a local file destination.
|
|
|
|
type fileSender struct {
|
|
|
|
file *os.File
|
2019-03-09 07:58:07 +03:00
|
|
|
data []byte
|
2018-06-09 07:38:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func newFileSender(path string) (*fileSender, error) {
|
|
|
|
f, err := os.Create(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &fileSender{file: f}, nil
|
|
|
|
}
|
|
|
|
|
2019-03-09 07:58:07 +03:00
|
|
|
func (s *fileSender) load(d []byte) error {
|
|
|
|
s.data = d
|
2018-06-09 07:38:48 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *fileSender) send() error {
|
2019-03-09 07:58:07 +03:00
|
|
|
_, err := s.file.Write(s.data)
|
2018-06-09 07:38:48 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-03-09 07:58:07 +03:00
|
|
|
func (s *fileSender) release() {}
|
2018-06-09 07:38:48 +03:00
|
|
|
|
2019-03-12 13:23:27 +03:00
|
|
|
func (s *fileSender) close() error { return s.file.Close() }
|
2018-06-09 07:38:48 +03:00
|
|
|
|
2019-03-10 09:07:28 +03:00
|
|
|
func (s *fileSender) handleSendFail(err error) error { return nil }
|
2019-03-09 07:58:07 +03:00
|
|
|
|
2019-03-12 13:23:27 +03:00
|
|
|
func (s *fileSender) retrySend() bool { return false }
|
|
|
|
|
2019-03-01 05:58:34 +03:00
|
|
|
// mtsSender implemented loadSender and provides sending capability specifically
|
|
|
|
// for use with MPEGTS packetization. It handles the construction of appropriately
|
|
|
|
// lengthed clips based on PSI. It also fixes 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-03-01 02:46:54 +03:00
|
|
|
sender Sender
|
2019-02-16 22:02:44 +03:00
|
|
|
buf []byte
|
|
|
|
next []byte
|
2019-03-01 07:18:26 +03:00
|
|
|
pkt packet.Packet
|
2019-02-16 22:02:44 +03:00
|
|
|
failed bool
|
|
|
|
discarded bool
|
|
|
|
repairer *mts.DiscontinuityRepairer
|
2019-03-01 02:30:33 +03:00
|
|
|
curPid int
|
2019-03-12 13:23:27 +03:00
|
|
|
retry bool
|
2019-02-15 04:31:07 +03:00
|
|
|
}
|
|
|
|
|
2019-03-01 05:58:34 +03:00
|
|
|
// newMtsSender returns a new mtsSender.
|
2019-03-12 13:23:27 +03:00
|
|
|
func newMtsSender(s Sender, retry bool, log func(lvl int8, msg string, args ...interface{})) *mtsSender {
|
2019-02-15 15:47:13 +03:00
|
|
|
return &mtsSender{
|
2019-02-16 06:56:51 +03:00
|
|
|
sender: s,
|
2019-02-15 04:31:07 +03:00
|
|
|
repairer: mts.NewDiscontinuityRepairer(),
|
2019-03-12 13:23:27 +03:00
|
|
|
retry: retry,
|
2019-02-15 04:31:07 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-01 05:58:34 +03:00
|
|
|
// load takes a *ring.Chunk and assigns to s.next, also grabbing it's pid and
|
|
|
|
// assigning to s.curPid. s.next if exists is also appended to the sender buf.
|
2019-03-09 07:58:07 +03:00
|
|
|
func (s *mtsSender) load(d []byte) error {
|
2019-02-16 22:02:44 +03:00
|
|
|
if s.next != nil {
|
|
|
|
s.buf = append(s.buf, s.next...)
|
|
|
|
}
|
2019-03-09 07:58:07 +03:00
|
|
|
bytes := make([]byte, len(d))
|
|
|
|
copy(bytes, d)
|
2019-03-01 07:44:01 +03:00
|
|
|
s.next = bytes
|
|
|
|
copy(s.pkt[:], bytes)
|
2019-03-01 07:18:26 +03:00
|
|
|
s.curPid = s.pkt.PID()
|
2019-03-01 02:30:33 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-03-01 05:58:34 +03:00
|
|
|
// send checks the currently loaded paackets PID; if it is a PAT then what is in
|
|
|
|
// the mtsSenders buffer is fixed and sent.
|
2019-03-01 07:44:01 +03:00
|
|
|
func (ms *mtsSender) send() error {
|
|
|
|
if ms.curPid == mts.PatPid && len(ms.buf) > 0 {
|
|
|
|
err := ms.fixAndSend()
|
2019-02-15 04:31:07 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-03-01 07:44:01 +03:00
|
|
|
ms.buf = ms.buf[:0]
|
2019-02-15 04:31:07 +03:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-03-01 05:58:34 +03:00
|
|
|
// fixAndSend checks for discontinuities in the senders buffer and then sends.
|
|
|
|
// If a discontinuity is found the PAT packet at the start of the clip has it's
|
|
|
|
// discontintuity indicator set to true.
|
2019-02-16 06:56:51 +03:00
|
|
|
func (ms *mtsSender) fixAndSend() error {
|
|
|
|
err := ms.repairer.Repair(ms.buf)
|
2019-03-01 07:44:01 +03:00
|
|
|
if err == nil {
|
|
|
|
err = ms.sender.send(ms.buf)
|
|
|
|
if err == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2019-02-15 16:05:45 +03:00
|
|
|
}
|
2019-03-01 07:44:01 +03:00
|
|
|
ms.failed = true
|
|
|
|
ms.repairer.Failed()
|
|
|
|
return err
|
2019-02-15 04:31:07 +03:00
|
|
|
}
|
|
|
|
|
2019-02-15 15:47:13 +03:00
|
|
|
func (s *mtsSender) close() error { return nil }
|
2019-02-15 04:31:07 +03:00
|
|
|
|
2019-03-01 05:58:34 +03:00
|
|
|
// release will set the s.fail flag to false and clear the buffer if
|
|
|
|
// the previous send was a fail. The currently loaded chunk is also closed.
|
2019-02-15 15:47:13 +03:00
|
|
|
func (s *mtsSender) release() {
|
2019-02-16 22:02:44 +03:00
|
|
|
if s.failed {
|
2019-02-15 04:31:07 +03:00
|
|
|
s.buf = s.buf[:0]
|
2019-02-16 22:02:44 +03:00
|
|
|
s.failed = false
|
2019-02-15 04:31:07 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-10 09:07:28 +03:00
|
|
|
func (s *mtsSender) handleSendFail(err error) error { return nil }
|
2019-03-09 07:58:07 +03:00
|
|
|
|
2019-03-12 13:23:27 +03:00
|
|
|
func (s *mtsSender) retrySend() bool {
|
|
|
|
return s.retry
|
|
|
|
}
|
|
|
|
|
2018-06-15 10:12:29 +03:00
|
|
|
// httpSender implements loadSender for posting HTTP to NetReceiver
|
2018-06-09 07:38:48 +03:00
|
|
|
type httpSender struct {
|
2018-06-15 10:12:29 +03:00
|
|
|
client *netsender.Sender
|
2018-06-09 07:38:48 +03:00
|
|
|
|
2018-09-11 10:45:45 +03:00
|
|
|
log func(lvl int8, msg string, args ...interface{})
|
2018-06-09 07:38:48 +03:00
|
|
|
|
2019-03-09 07:58:07 +03:00
|
|
|
data []byte
|
2019-03-12 13:23:27 +03:00
|
|
|
|
|
|
|
retry bool
|
2018-06-09 07:38:48 +03:00
|
|
|
}
|
|
|
|
|
2019-03-12 13:23:27 +03:00
|
|
|
func newHttpSender(ns *netsender.Sender, retry bool, log func(lvl int8, msg string, args ...interface{})) *httpSender {
|
2018-06-09 07:38:48 +03:00
|
|
|
return &httpSender{
|
2018-06-26 09:23:55 +03:00
|
|
|
client: ns,
|
2018-06-09 07:38:48 +03:00
|
|
|
log: log,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-09 07:58:07 +03:00
|
|
|
func (s *httpSender) load(d []byte) error {
|
|
|
|
s.data = d
|
2018-06-09 07:38:48 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-06-27 01:20:05 +03:00
|
|
|
func (s *httpSender) send() error {
|
2019-03-09 07:58:07 +03:00
|
|
|
return httpSend(s.data, s.client, s.log)
|
2019-02-15 04:31:07 +03:00
|
|
|
}
|
|
|
|
|
2019-02-16 06:56:51 +03:00
|
|
|
func httpSend(d []byte, client *netsender.Sender, log func(lvl int8, msg string, args ...interface{})) error {
|
2018-06-26 09:23:55 +03:00
|
|
|
// Only send if "V0" is configured as an input.
|
|
|
|
send := false
|
2019-02-16 06:56:51 +03:00
|
|
|
ip := client.Param("ip")
|
2018-06-26 09:23:55 +03:00
|
|
|
pins := netsender.MakePins(ip, "V")
|
|
|
|
for i, pin := range pins {
|
|
|
|
if pin.Name == "V0" {
|
|
|
|
send = true
|
2019-02-15 04:31:07 +03:00
|
|
|
pins[i].Value = len(d)
|
|
|
|
pins[i].Data = d
|
2018-06-26 09:23:55 +03:00
|
|
|
pins[i].MimeType = "video/mp2t"
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2019-01-08 12:09:22 +03:00
|
|
|
|
2019-01-07 03:15:22 +03:00
|
|
|
if !send {
|
|
|
|
return nil
|
|
|
|
}
|
2019-01-08 12:09:22 +03:00
|
|
|
var err error
|
|
|
|
var reply string
|
2019-02-16 06:56:51 +03:00
|
|
|
reply, _, err = client.Send(netsender.RequestRecv, pins)
|
2019-01-07 03:15:22 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2018-06-26 09:23:55 +03:00
|
|
|
}
|
2019-02-16 06:56:51 +03:00
|
|
|
return extractMeta(reply, log)
|
2018-12-13 08:21:51 +03:00
|
|
|
}
|
|
|
|
|
2018-12-14 09:16:36 +03:00
|
|
|
// extractMeta looks at a reply at extracts any time or location data - then used
|
|
|
|
// to update time and location information in the mpegts encoder.
|
2019-02-16 06:56:51 +03:00
|
|
|
func extractMeta(r string, log func(lvl int8, msg string, args ...interface{})) error {
|
2018-12-14 09:28:02 +03:00
|
|
|
dec, err := netsender.NewJSONDecoder(r)
|
2018-12-13 08:21:51 +03:00
|
|
|
if err != nil {
|
2018-12-14 06:05:56 +03:00
|
|
|
return nil
|
2018-12-13 08:21:51 +03:00
|
|
|
}
|
2018-12-13 04:52:06 +03:00
|
|
|
// Extract time from reply
|
2018-12-13 08:21:51 +03:00
|
|
|
t, err := dec.Int("ts")
|
2018-12-13 04:52:06 +03:00
|
|
|
if err != nil {
|
2019-02-16 06:56:51 +03:00
|
|
|
log(logger.Warning, pkg+"No timestamp in reply")
|
2018-12-13 04:52:06 +03:00
|
|
|
} else {
|
2019-02-16 06:56:51 +03:00
|
|
|
log(logger.Debug, fmt.Sprintf("%v got timestamp: %v", pkg, t))
|
2019-01-31 14:00:08 +03:00
|
|
|
mts.Meta.Add("ts", strconv.Itoa(t))
|
2018-12-13 04:52:06 +03:00
|
|
|
}
|
|
|
|
|
2018-12-14 08:32:47 +03:00
|
|
|
// Extract location from reply
|
2018-12-13 08:21:51 +03:00
|
|
|
g, err := dec.String("ll")
|
2018-12-13 04:52:06 +03:00
|
|
|
if err != nil {
|
2019-02-16 06:56:51 +03:00
|
|
|
log(logger.Warning, pkg+"No location in reply")
|
2018-12-13 04:52:06 +03:00
|
|
|
} else {
|
2019-02-16 06:56:51 +03:00
|
|
|
log(logger.Debug, fmt.Sprintf("%v got location: %v", pkg, g))
|
2019-01-31 14:00:08 +03:00
|
|
|
mts.Meta.Add("loc", g)
|
2018-12-13 04:52:06 +03:00
|
|
|
}
|
|
|
|
|
2018-12-13 08:21:51 +03:00
|
|
|
return nil
|
2018-11-28 07:56:17 +03:00
|
|
|
}
|
|
|
|
|
2019-03-09 07:58:07 +03:00
|
|
|
func (s *httpSender) release() {}
|
2018-06-09 07:38:48 +03:00
|
|
|
|
|
|
|
func (s *httpSender) close() error { return nil }
|
|
|
|
|
2019-03-10 09:07:28 +03:00
|
|
|
func (s *httpSender) handleSendFail(err error) error { return nil }
|
2019-03-09 07:58:07 +03:00
|
|
|
|
2019-03-12 13:23:27 +03:00
|
|
|
func (s *httpSender) retrySend() bool { return s.retry }
|
|
|
|
|
2018-06-09 07:38:48 +03:00
|
|
|
// rtmpSender implements loadSender for a native RTMP destination.
|
|
|
|
type rtmpSender struct {
|
2019-01-19 05:40:38 +03:00
|
|
|
conn *rtmp.Conn
|
2018-06-09 07:38:48 +03:00
|
|
|
|
|
|
|
url string
|
|
|
|
timeout uint
|
|
|
|
retries int
|
2018-09-11 10:45:45 +03:00
|
|
|
log func(lvl int8, msg string, args ...interface{})
|
2018-06-09 07:38:48 +03:00
|
|
|
|
2019-03-12 13:23:27 +03:00
|
|
|
data []byte
|
|
|
|
retry bool
|
2018-06-09 07:38:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
var _ restarter = (*rtmpSender)(nil)
|
|
|
|
|
2019-03-12 13:23:27 +03:00
|
|
|
func newRtmpSender(url string, timeout uint, retries int, retry bool, log func(lvl int8, msg string, args ...interface{})) (*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++ {
|
2019-01-19 05:40:38 +03:00
|
|
|
conn, err = rtmp.Dial(url, timeout, log)
|
2018-06-09 07:38:48 +03:00
|
|
|
if err == nil {
|
|
|
|
break
|
|
|
|
}
|
2019-01-02 08:09:47 +03:00
|
|
|
log(logger.Error, err.Error())
|
2018-06-09 07:38:48 +03:00
|
|
|
if n < retries-1 {
|
2019-01-02 08:09:47 +03:00
|
|
|
log(logger.Info, pkg+"retry rtmp connection")
|
2018-06-09 07:38:48 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
s := &rtmpSender{
|
2019-01-19 05:40:38 +03:00
|
|
|
conn: conn,
|
2018-06-09 07:38:48 +03:00
|
|
|
url: url,
|
|
|
|
timeout: timeout,
|
|
|
|
retries: retries,
|
|
|
|
log: log,
|
2019-03-12 13:23:27 +03:00
|
|
|
retry: retry,
|
2018-06-09 07:38:48 +03:00
|
|
|
}
|
|
|
|
return s, nil
|
|
|
|
}
|
|
|
|
|
2019-03-09 07:58:07 +03:00
|
|
|
func (s *rtmpSender) load(d []byte) error {
|
|
|
|
s.data = make([]byte, len(d))
|
|
|
|
copy(s.data, d)
|
2018-06-09 07:38:48 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *rtmpSender) send() error {
|
2019-03-10 10:18:18 +03:00
|
|
|
if s.conn == nil {
|
|
|
|
return errors.New("no rtmp connection, cannot write")
|
|
|
|
}
|
2019-03-09 07:58:07 +03:00
|
|
|
_, err := s.conn.Write(s.data)
|
2018-06-09 07:38:48 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-03-09 07:58:07 +03:00
|
|
|
func (s *rtmpSender) release() {}
|
2018-06-09 07:38:48 +03:00
|
|
|
|
|
|
|
func (s *rtmpSender) restart() error {
|
2019-03-03 10:54:54 +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++ {
|
2019-01-19 05:40:38 +03:00
|
|
|
s.conn, err = rtmp.Dial(s.url, s.timeout, s.log)
|
2018-06-09 07:38:48 +03:00
|
|
|
if err == nil {
|
|
|
|
break
|
|
|
|
}
|
2019-01-02 08:09:47 +03:00
|
|
|
s.log(logger.Error, err.Error())
|
2018-06-09 07:38:48 +03:00
|
|
|
if n < s.retries-1 {
|
2019-01-02 08:09:47 +03:00
|
|
|
s.log(logger.Info, pkg+"retry rtmp connection")
|
2018-06-09 07:38:48 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *rtmpSender) close() error {
|
2019-03-03 10:54:54 +03:00
|
|
|
if s.conn != nil {
|
|
|
|
return s.conn.Close()
|
|
|
|
}
|
|
|
|
return nil
|
2018-06-09 07:38:48 +03:00
|
|
|
}
|
2018-11-18 05:02:11 +03:00
|
|
|
|
2019-03-12 13:23:27 +03:00
|
|
|
func (s *rtmpSender) handleSendFail(err error) error { return s.restart() }
|
|
|
|
|
|
|
|
func (s *rtmpSender) retrySend() bool { return s.retry }
|
2019-03-09 07:58:07 +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 {
|
|
|
|
log func(lvl int8, msg string, args ...interface{})
|
|
|
|
encoder *rtp.Encoder
|
2019-03-09 07:58:07 +03:00
|
|
|
data []byte
|
2018-11-25 15:40:38 +03:00
|
|
|
}
|
|
|
|
|
2018-12-10 02:09:20 +03:00
|
|
|
func newRtpSender(addr string, log func(lvl int8, msg string, args ...interface{}), fps uint) (*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)),
|
2018-11-25 15:40:38 +03:00
|
|
|
}
|
|
|
|
return s, nil
|
|
|
|
}
|
|
|
|
|
2019-03-09 07:58:07 +03:00
|
|
|
func (s *rtpSender) load(d []byte) error {
|
|
|
|
s.data = make([]byte, len(d))
|
|
|
|
copy(s.data, d)
|
2019-02-15 04:31:07 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *rtpSender) close() error { return nil }
|
|
|
|
|
2019-03-09 07:58:07 +03:00
|
|
|
func (s *rtpSender) release() {}
|
2019-02-15 04:31:07 +03:00
|
|
|
|
|
|
|
func (s *rtpSender) send() error {
|
2019-03-09 07:58:07 +03:00
|
|
|
_, err := s.encoder.Write(s.data)
|
2018-11-25 15:40:38 +03:00
|
|
|
return err
|
|
|
|
}
|
2019-03-09 07:58:07 +03:00
|
|
|
|
2019-03-10 09:07:28 +03:00
|
|
|
func (s *rtpSender) handleSendFail(err error) error { return nil }
|
2019-03-12 13:23:27 +03:00
|
|
|
|
|
|
|
func (s *rtpSender) retrySend() bool { return false }
|