2018-01-09 07:26:34 +03:00
|
|
|
/*
|
|
|
|
NAME
|
2019-02-04 11:44:02 +03:00
|
|
|
revid.go
|
2018-01-09 07:26:34 +03:00
|
|
|
|
|
|
|
AUTHORS
|
2018-06-07 14:50:57 +03:00
|
|
|
Saxon A. Nelson-Milton <saxon@ausocean.org>
|
|
|
|
Alan Noble <alan@ausocean.org>
|
2019-04-18 09:51:18 +03:00
|
|
|
Dan Kortschak <dan@ausocean.org>
|
2019-05-08 13:01:25 +03:00
|
|
|
Trek Hopton <trek@ausocean.org>
|
2020-01-20 10:10:45 +03:00
|
|
|
Scott Barnard <scott@ausocean.org>
|
2018-01-09 07:26:34 +03:00
|
|
|
|
|
|
|
LICENSE
|
2020-01-02 06:08:31 +03:00
|
|
|
revid is Copyright (C) 2017-2020 the Australian Ocean Lab (AusOcean)
|
2018-01-09 07:26:34 +03:00
|
|
|
|
|
|
|
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
|
2019-04-22 08:44:08 +03:00
|
|
|
for more details.
|
2018-01-09 07:26:34 +03:00
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
2019-04-22 08:44:08 +03:00
|
|
|
in gpl.txt. If not, see http://www.gnu.org/licenses.
|
2018-01-09 07:26:34 +03:00
|
|
|
*/
|
|
|
|
|
2019-05-08 13:01:25 +03:00
|
|
|
// Package revid provides an API for reading, transcoding, and writing audio/video streams and files.
|
2018-01-09 07:26:34 +03:00
|
|
|
package revid
|
|
|
|
|
|
|
|
import (
|
2022-01-06 07:58:02 +03:00
|
|
|
"errors"
|
2018-10-03 15:39:51 +03:00
|
|
|
"fmt"
|
2018-01-16 08:06:51 +03:00
|
|
|
"io"
|
2019-02-01 02:17:31 +03:00
|
|
|
"sync"
|
2018-04-16 07:54:21 +03:00
|
|
|
"time"
|
2018-02-14 10:02:57 +03:00
|
|
|
|
2019-11-12 08:34:07 +03:00
|
|
|
"bitbucket.org/ausocean/av/device"
|
2019-12-20 03:12:51 +03:00
|
|
|
"bitbucket.org/ausocean/av/filter"
|
2019-11-06 09:57:10 +03:00
|
|
|
"bitbucket.org/ausocean/av/revid/config"
|
2018-06-26 09:23:55 +03:00
|
|
|
"bitbucket.org/ausocean/iot/pi/netsender"
|
2020-01-20 10:10:45 +03:00
|
|
|
"bitbucket.org/ausocean/utils/bitrate"
|
2020-04-09 09:25:01 +03:00
|
|
|
)
|
|
|
|
|
2020-05-19 06:07:34 +03:00
|
|
|
// Misc consts.
|
2019-05-20 13:15:59 +03:00
|
|
|
const (
|
2021-05-17 04:48:44 +03:00
|
|
|
poolStartingElementSize = 10000 // Bytes.
|
|
|
|
rtmpConnectionMaxTries = 5
|
2019-05-20 13:15:59 +03:00
|
|
|
)
|
|
|
|
|
2018-09-11 10:26:33 +03:00
|
|
|
type Logger interface {
|
|
|
|
SetLevel(int8)
|
|
|
|
Log(level int8, message string, params ...interface{})
|
|
|
|
}
|
|
|
|
|
2018-04-16 08:12:16 +03:00
|
|
|
// Revid provides methods to control a revid session; providing methods
|
2018-01-31 02:51:53 +03:00
|
|
|
// to start, stop and change the state of an instance using the Config struct.
|
2018-06-09 05:01:21 +03:00
|
|
|
type Revid struct {
|
2018-10-04 04:02:41 +03:00
|
|
|
// config holds the Revid configuration.
|
|
|
|
// For historical reasons it also handles logging.
|
|
|
|
// FIXME(kortschak): The relationship of concerns
|
|
|
|
// in config/ns is weird.
|
2019-11-06 09:57:10 +03:00
|
|
|
cfg config.Config
|
2019-04-15 02:12:56 +03:00
|
|
|
|
2018-10-04 04:02:41 +03:00
|
|
|
// ns holds the netsender.Sender responsible for HTTP.
|
|
|
|
ns *netsender.Sender
|
|
|
|
|
2019-11-12 08:34:07 +03:00
|
|
|
// input will capture audio or video from which we can read data.
|
|
|
|
input device.AVDevice
|
2019-05-06 08:42:05 +03:00
|
|
|
|
|
|
|
// closeInput holds the cleanup function return from setupInput and is called
|
|
|
|
// in Revid.Stop().
|
|
|
|
closeInput func() error
|
2018-10-04 04:02:41 +03:00
|
|
|
|
2018-10-19 03:50:08 +03:00
|
|
|
// lexTo, encoder and packer handle transcoding the input stream.
|
2019-06-18 10:54:32 +03:00
|
|
|
lexTo func(dest io.Writer, src io.Reader, delay time.Duration) error
|
2019-03-09 07:58:07 +03:00
|
|
|
|
2022-01-06 07:58:02 +03:00
|
|
|
// probe allows us to "probe" frames after being lexed before going off to
|
|
|
|
// later encoding stages. This is useful if we wish to perform some processing
|
|
|
|
// on frames to derive metrics, for example, we might like to probe frames to
|
|
|
|
// derive turbidity levels. This is provided through SetProbe.
|
|
|
|
probe io.WriteCloser
|
|
|
|
|
2019-12-27 08:36:30 +03:00
|
|
|
// filters will hold the filter interface that will write to the chosen filter from the lexer.
|
|
|
|
filters []filter.Filter
|
2019-12-20 03:12:51 +03:00
|
|
|
|
|
|
|
// encoders will hold the multiWriteCloser that writes to encoders from the filter.
|
2019-04-18 10:25:48 +03:00
|
|
|
encoders io.WriteCloser
|
2019-04-08 12:32:42 +03:00
|
|
|
|
2019-10-01 17:14:38 +03:00
|
|
|
// running is used to keep track of revid's running state between methods.
|
|
|
|
running bool
|
|
|
|
|
2019-04-18 10:25:48 +03:00
|
|
|
// wg will be used to wait for any processing routines to finish.
|
2019-02-03 14:25:40 +03:00
|
|
|
wg sync.WaitGroup
|
|
|
|
|
2019-04-22 08:44:08 +03:00
|
|
|
// err will channel errors from revid routines to the handle errors routine.
|
2019-01-31 07:33:50 +03:00
|
|
|
err chan error
|
2020-01-20 10:10:45 +03:00
|
|
|
|
|
|
|
// bitrate is used for bitrate calculations.
|
|
|
|
bitrate bitrate.Calculator
|
2020-05-02 08:14:05 +03:00
|
|
|
|
2020-05-02 15:19:39 +03:00
|
|
|
// stop is used to signal stopping when looping an input.
|
2020-05-02 08:14:05 +03:00
|
|
|
stop chan struct{}
|
2018-01-23 06:15:06 +03:00
|
|
|
}
|
|
|
|
|
2018-08-10 07:37:54 +03:00
|
|
|
// New returns a pointer to a new Revid with the desired configuration, and/or
|
|
|
|
// an error if construction of the new instance was not successful.
|
2019-11-06 09:57:10 +03:00
|
|
|
func New(c config.Config, ns *netsender.Sender) (*Revid, error) {
|
2019-01-31 07:33:50 +03:00
|
|
|
r := Revid{ns: ns, err: make(chan error)}
|
2019-09-12 10:47:46 +03:00
|
|
|
err := r.setConfig(c)
|
|
|
|
if err != nil {
|
2019-12-04 06:42:20 +03:00
|
|
|
return nil, fmt.Errorf("could not set config, failed with error: %w", err)
|
2019-09-12 10:47:46 +03:00
|
|
|
}
|
2019-01-31 12:12:20 +03:00
|
|
|
go r.handleErrors()
|
|
|
|
return &r, nil
|
|
|
|
}
|
|
|
|
|
2019-04-15 04:18:12 +03:00
|
|
|
// Config returns a copy of revids current config.
|
2019-11-06 09:57:10 +03:00
|
|
|
func (r *Revid) Config() config.Config {
|
|
|
|
return r.cfg
|
2019-04-15 04:18:12 +03:00
|
|
|
}
|
|
|
|
|
2018-06-27 01:20:05 +03:00
|
|
|
// Bitrate returns the result of the most recent bitrate check.
|
2018-06-17 14:27:50 +03:00
|
|
|
func (r *Revid) Bitrate() int {
|
2020-01-20 10:10:45 +03:00
|
|
|
return r.bitrate.Bitrate()
|
2019-04-15 02:12:56 +03:00
|
|
|
}
|
|
|
|
|
2018-06-09 05:01:21 +03:00
|
|
|
// Start invokes a Revid to start processing video from a defined input
|
2018-02-27 18:10:38 +03:00
|
|
|
// and packetising (if theres packetization) to a defined output.
|
2019-01-13 14:31:04 +03:00
|
|
|
func (r *Revid) Start() error {
|
2020-12-16 05:56:27 +03:00
|
|
|
if r.running {
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Warning("start called, but revid already running")
|
2019-03-02 05:19:09 +03:00
|
|
|
return nil
|
2018-02-10 09:59:56 +03:00
|
|
|
}
|
2019-10-01 17:14:38 +03:00
|
|
|
|
2020-05-02 08:14:05 +03:00
|
|
|
r.stop = make(chan struct{})
|
|
|
|
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Debug("resetting revid")
|
2019-11-06 09:57:10 +03:00
|
|
|
err := r.reset(r.cfg)
|
2019-09-12 10:38:22 +03:00
|
|
|
if err != nil {
|
|
|
|
r.Stop()
|
|
|
|
return err
|
|
|
|
}
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Info("revid reset")
|
2019-11-12 08:34:07 +03:00
|
|
|
|
2020-08-14 05:50:51 +03:00
|
|
|
// Calculate delay between frames based on FileFPS for video or
|
2020-08-13 09:55:37 +03:00
|
|
|
// between recording periods for audio.
|
2020-01-25 02:45:27 +03:00
|
|
|
d := time.Duration(0)
|
2020-08-12 04:34:45 +03:00
|
|
|
if r.cfg.Input == config.InputAudio {
|
2020-08-14 06:24:07 +03:00
|
|
|
d = time.Duration(r.cfg.RecPeriod * float64(time.Second))
|
2020-08-14 06:04:20 +03:00
|
|
|
} else if r.cfg.FileFPS != 0 {
|
2020-08-14 06:24:07 +03:00
|
|
|
d = time.Duration(1000/r.cfg.FileFPS) * time.Millisecond
|
2020-01-25 02:45:27 +03:00
|
|
|
}
|
2020-03-27 15:20:51 +03:00
|
|
|
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Debug("starting input processing routine")
|
2019-11-12 08:34:07 +03:00
|
|
|
r.wg.Add(1)
|
2020-01-24 12:35:43 +03:00
|
|
|
go r.processFrom(r.input, d)
|
2019-11-12 08:34:07 +03:00
|
|
|
|
2019-10-01 17:14:38 +03:00
|
|
|
r.running = true
|
2019-09-12 10:38:22 +03:00
|
|
|
return nil
|
2018-02-10 09:59:56 +03:00
|
|
|
}
|
|
|
|
|
2019-04-15 02:12:56 +03:00
|
|
|
// Stop closes down the pipeline. This closes encoders and sender output routines,
|
|
|
|
// connections, and/or files.
|
2019-03-02 05:12:36 +03:00
|
|
|
func (r *Revid) Stop() {
|
2020-12-16 05:56:27 +03:00
|
|
|
if !r.running {
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Warning("stop called but revid isn't running")
|
2019-03-02 05:12:36 +03:00
|
|
|
return
|
2018-03-17 16:29:42 +03:00
|
|
|
}
|
2018-05-07 05:53:50 +03:00
|
|
|
|
2020-05-02 08:14:05 +03:00
|
|
|
close(r.stop)
|
|
|
|
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Debug("stopping input")
|
2019-11-12 08:34:07 +03:00
|
|
|
err := r.input.Stop()
|
|
|
|
if err != nil {
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Error("could not stop input", "error", err.Error())
|
2020-03-27 15:20:51 +03:00
|
|
|
} else {
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Info("input stopped")
|
2019-04-08 12:32:42 +03:00
|
|
|
}
|
2019-05-06 08:42:05 +03:00
|
|
|
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Debug("closing pipeline")
|
2019-11-12 08:34:07 +03:00
|
|
|
err = r.encoders.Close()
|
2019-04-15 02:12:56 +03:00
|
|
|
if err != nil {
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Error("failed to close pipeline", "error", err.Error())
|
2020-03-27 15:20:51 +03:00
|
|
|
} else {
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Info("pipeline closed")
|
2019-04-08 12:32:42 +03:00
|
|
|
}
|
2019-12-23 04:29:17 +03:00
|
|
|
|
2019-12-27 09:18:28 +03:00
|
|
|
for _, filter := range r.filters {
|
|
|
|
err = filter.Close()
|
|
|
|
if err != nil {
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Error("failed to close filters", "error", err.Error())
|
2020-03-27 15:20:51 +03:00
|
|
|
} else {
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Info("filters closed")
|
2019-12-27 09:18:28 +03:00
|
|
|
}
|
2019-12-20 08:07:49 +03:00
|
|
|
}
|
2019-12-27 09:18:28 +03:00
|
|
|
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Debug("waiting for routines to finish")
|
2019-04-14 04:43:17 +03:00
|
|
|
r.wg.Wait()
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Info("routines finished")
|
2019-10-22 15:34:33 +03:00
|
|
|
|
2019-10-01 17:14:38 +03:00
|
|
|
r.running = false
|
2020-01-30 05:50:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Burst starts revid, waits for time specified, and then stops revid.
|
2020-01-31 07:01:17 +03:00
|
|
|
func (r *Revid) Burst() error {
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Debug("starting revid")
|
2020-01-30 05:50:44 +03:00
|
|
|
err := r.Start()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("could not start revid: %w", err)
|
|
|
|
}
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Info("revid started")
|
2020-01-30 05:50:44 +03:00
|
|
|
|
2020-01-31 07:01:17 +03:00
|
|
|
dur := time.Duration(r.cfg.BurstPeriod) * time.Second
|
|
|
|
time.Sleep(dur)
|
|
|
|
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Debug("stopping revid")
|
2020-01-30 05:50:44 +03:00
|
|
|
r.Stop()
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Info("revid stopped")
|
2020-03-27 15:20:51 +03:00
|
|
|
|
2020-01-30 05:50:44 +03:00
|
|
|
return nil
|
2019-10-01 17:14:38 +03:00
|
|
|
}
|
|
|
|
|
2020-12-16 05:56:27 +03:00
|
|
|
func (r *Revid) Running() bool {
|
2019-10-01 17:14:38 +03:00
|
|
|
return r.running
|
2018-02-10 09:59:56 +03:00
|
|
|
}
|
|
|
|
|
2019-04-15 02:12:56 +03:00
|
|
|
// Update takes a map of variables and their values and edits the current config
|
|
|
|
// if the variables are recognised as valid parameters.
|
2019-02-03 13:47:44 +03:00
|
|
|
func (r *Revid) Update(vars map[string]string) error {
|
2020-12-16 05:56:27 +03:00
|
|
|
if r.running {
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Debug("revid running; stopping for re-config")
|
2019-03-02 05:19:09 +03:00
|
|
|
r.Stop()
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Info("revid was running; stopped for re-config")
|
2019-02-03 13:47:44 +03:00
|
|
|
}
|
2019-04-15 02:12:56 +03:00
|
|
|
|
2019-02-03 13:47:44 +03:00
|
|
|
//look through the vars and update revid where needed
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Debug("checking vars from server", "vars", vars)
|
2020-05-19 09:02:02 +03:00
|
|
|
r.cfg.Update(vars)
|
2022-05-27 09:12:52 +03:00
|
|
|
r.cfg.Logger.Info("finished reconfig")
|
|
|
|
r.cfg.Logger.Debug("config changed", "config", r.cfg)
|
2019-09-12 10:38:22 +03:00
|
|
|
return nil
|
2019-02-03 13:47:44 +03:00
|
|
|
}
|
2022-01-06 07:58:02 +03:00
|
|
|
|
|
|
|
func (r *Revid) SetProbe(p io.WriteCloser) error {
|
|
|
|
if r.running {
|
|
|
|
return errors.New("cannot set probe when revid is running")
|
|
|
|
}
|
|
|
|
r.probe = p
|
|
|
|
return nil
|
|
|
|
}
|