mirror of https://bitbucket.org/ausocean/av.git
Merge branch 'master' into m3u-live-minor
This commit is contained in:
commit
1e474695ab
8
Makefile
8
Makefile
|
@ -1,7 +1,7 @@
|
||||||
all: revid-cli audio-netsender
|
all: rv audio-netsender
|
||||||
|
|
||||||
revid-cli:
|
rv:
|
||||||
cd cmd/revid-cli; go build
|
cd cmd/rv; go build
|
||||||
|
|
||||||
audio-netsender:
|
audio-netsender:
|
||||||
cd cmd/audio-netsender; go build
|
cd cmd/audio-netsender; go build
|
||||||
|
|
|
@ -1,415 +0,0 @@
|
||||||
/*
|
|
||||||
NAME
|
|
||||||
revid-cli - command line interface for revid.
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
See Readme.md
|
|
||||||
|
|
||||||
AUTHORS
|
|
||||||
Saxon A. Nelson-Milton <saxon@ausocean.org>
|
|
||||||
Jack Richardson <jack@ausocean.org>
|
|
||||||
Trek Hopton <trek@ausocean.org>
|
|
||||||
|
|
||||||
LICENSE
|
|
||||||
revid-cli 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// revid-cli is a command line interface for revid.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime/pprof"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"bitbucket.org/ausocean/av/codec/codecutil"
|
|
||||||
"bitbucket.org/ausocean/av/container/mts"
|
|
||||||
"bitbucket.org/ausocean/av/container/mts/meta"
|
|
||||||
"bitbucket.org/ausocean/av/device/raspivid"
|
|
||||||
"bitbucket.org/ausocean/av/revid"
|
|
||||||
"bitbucket.org/ausocean/av/revid/config"
|
|
||||||
"bitbucket.org/ausocean/iot/pi/netlogger"
|
|
||||||
"bitbucket.org/ausocean/iot/pi/netsender"
|
|
||||||
"bitbucket.org/ausocean/iot/pi/sds"
|
|
||||||
"bitbucket.org/ausocean/utils/logger"
|
|
||||||
"gopkg.in/natefinch/lumberjack.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Revid modes
|
|
||||||
const (
|
|
||||||
normal = "Normal"
|
|
||||||
paused = "Paused"
|
|
||||||
burst = "Burst"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Other misc consts
|
|
||||||
const (
|
|
||||||
netSendRetryTime = 5 * time.Second
|
|
||||||
defaultRunDuration = 24 * time.Hour
|
|
||||||
revidStopTime = 5 * time.Second
|
|
||||||
defaultLogPath = "/var/log/netsender"
|
|
||||||
pkg = "revid-cli:"
|
|
||||||
defaultLogVerbosity = logger.Info
|
|
||||||
defaultSleepTime = 60 // Seconds
|
|
||||||
)
|
|
||||||
|
|
||||||
// canProfile is set to false with revid-cli is built with "-tags profile".
|
|
||||||
var canProfile = true
|
|
||||||
|
|
||||||
// The logger that will be used throughout.
|
|
||||||
var (
|
|
||||||
netLog *netlogger.Logger
|
|
||||||
log *logger.Logger
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
metaPreambleKey = "copyright"
|
|
||||||
metaPreambleData = "ausocean.org/license/content2019"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
mts.Meta = meta.NewWith([][2]string{{metaPreambleKey, metaPreambleData}})
|
|
||||||
|
|
||||||
useNetsender := flag.Bool("NetSender", false, "Are we checking vars through netsender?")
|
|
||||||
runDurationPtr := flag.Duration("runDuration", defaultRunDuration, "How long do you want revid to run for?")
|
|
||||||
|
|
||||||
cfg := handleFlags()
|
|
||||||
if !*useNetsender {
|
|
||||||
rv, err := revid.New(cfg, nil)
|
|
||||||
if err != nil {
|
|
||||||
cfg.Logger.Log(logger.Fatal, pkg+"failed to initialiase revid", "error", err.Error())
|
|
||||||
}
|
|
||||||
if err = rv.Start(); err != nil {
|
|
||||||
cfg.Logger.Log(logger.Fatal, pkg+"failed to start revid", "error", err.Error())
|
|
||||||
}
|
|
||||||
time.Sleep(*runDurationPtr)
|
|
||||||
rv.Stop()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
run(cfg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// handleFlags parses command line flags and returns a revid configuration
|
|
||||||
// based on them.
|
|
||||||
func handleFlags() config.Config {
|
|
||||||
var cfg config.Config
|
|
||||||
|
|
||||||
var (
|
|
||||||
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
|
|
||||||
inputCodecPtr = flag.String("InputCodec", "H264", "The codec of the input: H264, Mjpeg, PCM, ADPCM")
|
|
||||||
inputPtr = flag.String("Input", "", "The input type: Raspivid, File, v4l, Audio, RTSP")
|
|
||||||
cameraIPPtr = flag.String("CameraIP", "", "The IP of the RTSP server")
|
|
||||||
verbosityPtr = flag.String("Verbosity", "Info", "Verbosity: Debug, Info, Warning, Error, Fatal")
|
|
||||||
rtpAddrPtr = flag.String("RtpAddr", "", "Rtp destination address: <IP>:<port> (port is generally 6970-6999)")
|
|
||||||
logPathPtr = flag.String("LogPath", defaultLogPath, "The log path")
|
|
||||||
configFilePtr = flag.String("ConfigFile", "", "NetSender config file")
|
|
||||||
rtmpUrlPtr = flag.String("RtmpUrl", "", "Url of rtmp endpoint")
|
|
||||||
outputPathPtr = flag.String("OutputPath", "", "The directory of the output file")
|
|
||||||
inputFilePtr = flag.String("InputPath", "", "The directory of the input file")
|
|
||||||
httpAddressPtr = flag.String("HttpAddress", "", "Destination address of http posts")
|
|
||||||
verticalFlipPtr = flag.Bool("VerticalFlip", false, "Flip video vertically: Yes, No")
|
|
||||||
horizontalFlipPtr = flag.Bool("HorizontalFlip", false, "Flip video horizontally: Yes, No")
|
|
||||||
loopPtr = flag.Bool("Loop", false, "Loop input source on completion (true/false)")
|
|
||||||
bitratePtr = flag.Uint("Bitrate", 0, "Bitrate of recorded video")
|
|
||||||
heightPtr = flag.Uint("Height", 0, "Height in pixels")
|
|
||||||
widthPtr = flag.Uint("Width", 0, "Width in pixels")
|
|
||||||
frameRatePtr = flag.Uint("FrameRate", 0, "Frame rate of captured video")
|
|
||||||
quantizationPtr = flag.Uint("Quantization", 0, "Desired quantization value: 0-40")
|
|
||||||
rotationPtr = flag.Uint("Rotation", 0, "Rotate video output. (0-359 degrees)")
|
|
||||||
brightnessPtr = flag.Uint("Brightness", 50, "Set brightness. (0-100) ")
|
|
||||||
saturationPtr = flag.Int("Saturation", 0, "Set Saturation. (100-100)")
|
|
||||||
exposurePtr = flag.String("Exposure", "auto", "Set exposure mode. ("+strings.Join(raspivid.ExposureModes[:], ",")+")")
|
|
||||||
autoWhiteBalancePtr = flag.String("Awb", "auto", "Set automatic white balance mode. ("+strings.Join(raspivid.AutoWhiteBalanceModes[:], ",")+")")
|
|
||||||
fileFPSPtr = flag.Int("FileFPS", 0, "File source frame processing FPS")
|
|
||||||
|
|
||||||
// Audio specific flags.
|
|
||||||
sampleRatePtr = flag.Int("SampleRate", 48000, "Sample rate of recorded audio")
|
|
||||||
channelsPtr = flag.Int("Channels", 1, "Record in Mono or Stereo (1 or 2)")
|
|
||||||
recPeriodPtr = flag.Float64("recPeriod", 1, "How many seconds to record at a time")
|
|
||||||
bitDepthPtr = flag.Int("bitDepth", 16, "Bit Depth to record audio at.")
|
|
||||||
)
|
|
||||||
|
|
||||||
var outputs flagStrings
|
|
||||||
flag.Var(&outputs, "Output", "output type: Http, Rtmp, File, Udp, Rtp (may be used more than once)")
|
|
||||||
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
switch *verbosityPtr {
|
|
||||||
case "Debug":
|
|
||||||
cfg.LogLevel = logger.Debug
|
|
||||||
case "Info":
|
|
||||||
cfg.LogLevel = logger.Info
|
|
||||||
case "Warning":
|
|
||||||
cfg.LogLevel = logger.Warning
|
|
||||||
case "Error":
|
|
||||||
cfg.LogLevel = logger.Error
|
|
||||||
case "Fatal":
|
|
||||||
cfg.LogLevel = logger.Fatal
|
|
||||||
default:
|
|
||||||
cfg.LogLevel = defaultLogVerbosity
|
|
||||||
}
|
|
||||||
|
|
||||||
netLog = netlogger.New()
|
|
||||||
log = logger.New(
|
|
||||||
cfg.LogLevel,
|
|
||||||
io.MultiWriter(
|
|
||||||
&lumberjack.Logger{
|
|
||||||
Filename: filepath.Join(*logPathPtr, "netsender.log"),
|
|
||||||
MaxSize: 500, // MB
|
|
||||||
MaxBackups: 10,
|
|
||||||
MaxAge: 28, // days
|
|
||||||
},
|
|
||||||
netLog,
|
|
||||||
),
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
|
|
||||||
cfg.Logger = log
|
|
||||||
|
|
||||||
if *cpuprofile != "" {
|
|
||||||
if canProfile {
|
|
||||||
f, err := os.Create(*cpuprofile)
|
|
||||||
if err != nil {
|
|
||||||
log.Log(logger.Fatal, pkg+"could not create CPU profile", "error", err.Error())
|
|
||||||
}
|
|
||||||
if err := pprof.StartCPUProfile(f); err != nil {
|
|
||||||
log.Log(logger.Fatal, pkg+"could not start CPU profile", "error", err.Error())
|
|
||||||
}
|
|
||||||
defer pprof.StopCPUProfile()
|
|
||||||
} else {
|
|
||||||
log.Log(logger.Warning, pkg+"ignoring cpuprofile flag - http/pprof built in.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch *inputPtr {
|
|
||||||
case "Raspivid":
|
|
||||||
cfg.Input = config.InputRaspivid
|
|
||||||
case "v4l":
|
|
||||||
cfg.Input = config.InputV4L
|
|
||||||
case "File":
|
|
||||||
cfg.Input = config.InputFile
|
|
||||||
case "Audio":
|
|
||||||
cfg.Input = config.InputAudio
|
|
||||||
case "RTSP":
|
|
||||||
cfg.Input = config.InputRTSP
|
|
||||||
case "":
|
|
||||||
default:
|
|
||||||
log.Log(logger.Error, pkg+"bad input argument")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch *inputCodecPtr {
|
|
||||||
case "H264":
|
|
||||||
cfg.InputCodec = codecutil.H264
|
|
||||||
case "PCM":
|
|
||||||
cfg.InputCodec = codecutil.PCM
|
|
||||||
case "ADPCM":
|
|
||||||
cfg.InputCodec = codecutil.ADPCM
|
|
||||||
case "MJPEG":
|
|
||||||
cfg.InputCodec = codecutil.MJPEG
|
|
||||||
default:
|
|
||||||
log.Log(logger.Error, pkg+"bad input codec argument")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch *inputPtr {
|
|
||||||
case "Audio":
|
|
||||||
cfg.WriteRate = 1.0 / (*recPeriodPtr)
|
|
||||||
default:
|
|
||||||
cfg.WriteRate = float64(*frameRatePtr)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, o := range outputs {
|
|
||||||
switch o {
|
|
||||||
case "File":
|
|
||||||
cfg.Outputs = append(cfg.Outputs, config.OutputFile)
|
|
||||||
case "Http":
|
|
||||||
cfg.Outputs = append(cfg.Outputs, config.OutputHTTP)
|
|
||||||
case "Rtmp":
|
|
||||||
cfg.Outputs = append(cfg.Outputs, config.OutputRTMP)
|
|
||||||
case "Rtp":
|
|
||||||
cfg.Outputs = append(cfg.Outputs, config.OutputRTP)
|
|
||||||
case "":
|
|
||||||
default:
|
|
||||||
log.Log(logger.Error, pkg+"bad output argument", "arg", o)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if *configFilePtr != "" {
|
|
||||||
netsender.ConfigFile = *configFilePtr
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.FileFPS = *fileFPSPtr
|
|
||||||
cfg.Loop = *loopPtr
|
|
||||||
cfg.CameraIP = *cameraIPPtr
|
|
||||||
cfg.Rotation = *rotationPtr
|
|
||||||
cfg.HorizontalFlip = *horizontalFlipPtr
|
|
||||||
cfg.VerticalFlip = *verticalFlipPtr
|
|
||||||
cfg.RTMPURL = *rtmpUrlPtr
|
|
||||||
cfg.Bitrate = *bitratePtr
|
|
||||||
cfg.OutputPath = *outputPathPtr
|
|
||||||
cfg.InputPath = *inputFilePtr
|
|
||||||
cfg.Height = *heightPtr
|
|
||||||
cfg.Width = *widthPtr
|
|
||||||
cfg.FrameRate = *frameRatePtr
|
|
||||||
cfg.HTTPAddress = *httpAddressPtr
|
|
||||||
cfg.Quantization = *quantizationPtr
|
|
||||||
cfg.RTPAddress = *rtpAddrPtr
|
|
||||||
cfg.Brightness = *brightnessPtr
|
|
||||||
cfg.Saturation = *saturationPtr
|
|
||||||
cfg.Exposure = *exposurePtr
|
|
||||||
cfg.AutoWhiteBalance = *autoWhiteBalancePtr
|
|
||||||
cfg.SampleRate = *sampleRatePtr
|
|
||||||
cfg.Channels = *channelsPtr
|
|
||||||
cfg.RecPeriod = *recPeriodPtr
|
|
||||||
cfg.BitDepth = *bitDepthPtr
|
|
||||||
|
|
||||||
return cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize then run the main NetSender client
|
|
||||||
func run(cfg config.Config) {
|
|
||||||
log.Log(logger.Info, pkg+"running in NetSender mode")
|
|
||||||
|
|
||||||
var rv *revid.Revid
|
|
||||||
|
|
||||||
readPin := func(pin *netsender.Pin) error {
|
|
||||||
switch {
|
|
||||||
case pin.Name == "X23":
|
|
||||||
pin.Value = -1
|
|
||||||
if rv != nil {
|
|
||||||
pin.Value = rv.Bitrate()
|
|
||||||
}
|
|
||||||
case pin.Name[0] == 'X':
|
|
||||||
return sds.ReadSystem(pin)
|
|
||||||
default:
|
|
||||||
pin.Value = -1
|
|
||||||
}
|
|
||||||
return nil // Return error only if we want NetSender to generate an error
|
|
||||||
}
|
|
||||||
|
|
||||||
ns, err := netsender.New(log, nil, readPin, nil, config.TypeData)
|
|
||||||
if err != nil {
|
|
||||||
log.Log(logger.Fatal, pkg+"could not initialise netsender client: "+err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
var vs int
|
|
||||||
for {
|
|
||||||
err = ns.Run()
|
|
||||||
if err != nil {
|
|
||||||
log.Log(logger.Warning, pkg+"Run Failed. Retrying...", "error", err.Error())
|
|
||||||
time.Sleep(netSendRetryTime)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
err = netLog.Send(ns)
|
|
||||||
if err != nil {
|
|
||||||
log.Log(logger.Warning, pkg+"Logs could not be sent", "error", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// If var sum hasn't changed we continue.
|
|
||||||
var vars map[string]string
|
|
||||||
newVs := ns.VarSum()
|
|
||||||
if vs == newVs {
|
|
||||||
goto sleep
|
|
||||||
}
|
|
||||||
vs = newVs
|
|
||||||
|
|
||||||
vars, err = ns.Vars()
|
|
||||||
if err != nil {
|
|
||||||
log.Log(logger.Error, pkg+"netSender failed to get vars", "error", err.Error())
|
|
||||||
time.Sleep(netSendRetryTime)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if rv == nil {
|
|
||||||
rv, err = revid.New(cfg, ns)
|
|
||||||
if err != nil {
|
|
||||||
log.Log(logger.Warning, pkg+"could not initialise revid", "error", err.Error())
|
|
||||||
goto sleep
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = rv.Update(vars)
|
|
||||||
if err != nil {
|
|
||||||
log.Log(logger.Warning, pkg+"Couldn't update revid", "error", err.Error())
|
|
||||||
goto sleep
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ns.Mode() {
|
|
||||||
case paused:
|
|
||||||
rv.Stop()
|
|
||||||
case normal:
|
|
||||||
err = rv.Start()
|
|
||||||
if err != nil {
|
|
||||||
log.Log(logger.Warning, pkg+"could not start revid", "error", err.Error())
|
|
||||||
ns.SetMode(paused, &vs)
|
|
||||||
goto sleep
|
|
||||||
}
|
|
||||||
case burst:
|
|
||||||
log.Log(logger.Info, pkg+"Starting burst...")
|
|
||||||
|
|
||||||
err = rv.Start()
|
|
||||||
if err != nil {
|
|
||||||
log.Log(logger.Warning, pkg+"could not start burst", "error", err.Error())
|
|
||||||
ns.SetMode(paused, &vs)
|
|
||||||
goto sleep
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(time.Duration(rv.Config().BurstPeriod) * time.Second)
|
|
||||||
log.Log(logger.Info, pkg+"Stopping burst...")
|
|
||||||
rv.Stop()
|
|
||||||
ns.SetMode(paused, &vs)
|
|
||||||
}
|
|
||||||
|
|
||||||
sleep:
|
|
||||||
sleepTime, err := strconv.Atoi(ns.Param("mp"))
|
|
||||||
if err != nil {
|
|
||||||
log.Log(logger.Error, pkg+"could not get sleep time, using default")
|
|
||||||
sleepTime = defaultSleepTime
|
|
||||||
}
|
|
||||||
time.Sleep(time.Duration(sleepTime) * time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// flagStrings implements an appending string set flag.
|
|
||||||
type flagStrings []string
|
|
||||||
|
|
||||||
func (v *flagStrings) String() string {
|
|
||||||
if *v != nil {
|
|
||||||
return strings.Join(*v, ",")
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *flagStrings) Set(s string) error {
|
|
||||||
if s == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for _, e := range *v {
|
|
||||||
if e == s {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*v = append(*v, s)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *flagStrings) Get() interface{} { return *v }
|
|
|
@ -1,4 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
REVIDPATH=$HOME/go/src/bitbucket.org/ausocean/av/cmd/revid-cli
|
|
||||||
cd $REVIDPATH
|
|
||||||
sudo "PATH=$PATH:$REVIDPATH" ./revid-cli -NetSender &
|
|
|
@ -0,0 +1,274 @@
|
||||||
|
/*
|
||||||
|
DESCRIPTION
|
||||||
|
rv is a netsender client using the revid package to perform media collection
|
||||||
|
and forwarding whose behaviour is controllable via the cloud interfaces
|
||||||
|
netreceiver and vidgrind.
|
||||||
|
|
||||||
|
AUTHORS
|
||||||
|
Saxon A. Nelson-Milton <saxon@ausocean.org>
|
||||||
|
Alan Noble <alan@ausocean.org>
|
||||||
|
Dan Kortschak <dan@ausocean.org>
|
||||||
|
Jack Richardson <jack@ausocean.org>
|
||||||
|
Trek Hopton <trek@ausocean.org>
|
||||||
|
Scott Barnard <scott@ausocean.org>
|
||||||
|
|
||||||
|
LICENSE
|
||||||
|
Copyright (C) 2020 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.
|
||||||
|
|
||||||
|
USAGE
|
||||||
|
There must firstly be a netsender configuration file under /etc/netsender.conf.
|
||||||
|
Example:
|
||||||
|
|
||||||
|
ma 00:00:00:00:00:01
|
||||||
|
dk 0
|
||||||
|
wi
|
||||||
|
ip V0, T0
|
||||||
|
op
|
||||||
|
mp 60
|
||||||
|
ap 0
|
||||||
|
tg
|
||||||
|
hw
|
||||||
|
sh vidgrind.appspot.com
|
||||||
|
|
||||||
|
Revid configuration is controlled by valid variables given values on netreceiver
|
||||||
|
or vidgrind interface. See revid/config for valid variables.
|
||||||
|
|
||||||
|
To run rv simply build and call:
|
||||||
|
./rv
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package rv is a netsender client for revid.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"runtime/pprof"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gopkg.in/natefinch/lumberjack.v2"
|
||||||
|
|
||||||
|
"bitbucket.org/ausocean/av/container/mts"
|
||||||
|
"bitbucket.org/ausocean/av/container/mts/meta"
|
||||||
|
"bitbucket.org/ausocean/av/revid"
|
||||||
|
"bitbucket.org/ausocean/av/revid/config"
|
||||||
|
"bitbucket.org/ausocean/iot/pi/netlogger"
|
||||||
|
"bitbucket.org/ausocean/iot/pi/netsender"
|
||||||
|
"bitbucket.org/ausocean/iot/pi/sds"
|
||||||
|
"bitbucket.org/ausocean/utils/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Copyright information prefixed to all metadata.
|
||||||
|
const (
|
||||||
|
metaPreambleKey = "copyright"
|
||||||
|
metaPreambleData = "ausocean.org/license/content2019"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Logging configuration.
|
||||||
|
const (
|
||||||
|
logPath = "/var/log/netsender/netsender.log"
|
||||||
|
logMaxSize = 500 // MB
|
||||||
|
logMaxBackup = 10
|
||||||
|
logMaxAge = 28 // days
|
||||||
|
logVerbosity = logger.Info
|
||||||
|
logSuppress = true
|
||||||
|
)
|
||||||
|
|
||||||
|
// Revid modes.
|
||||||
|
const (
|
||||||
|
modeNormal = "Normal"
|
||||||
|
modePaused = "Paused"
|
||||||
|
modeBurst = "Burst"
|
||||||
|
modeLoop = "Loop"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Misc constants.
|
||||||
|
const (
|
||||||
|
netSendRetryTime = 5 * time.Second
|
||||||
|
defaultSleepTime = 60 // Seconds
|
||||||
|
profilePath = "rv.prof"
|
||||||
|
pkg = "rv: "
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is set to true if the 'profile' build tag is provided on build.
|
||||||
|
var canProfile = false
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mts.Meta = meta.NewWith([][2]string{{metaPreambleKey, metaPreambleData}})
|
||||||
|
|
||||||
|
// Create lumberjack logger to handle logging to file.
|
||||||
|
fileLog := &lumberjack.Logger{
|
||||||
|
Filename: logPath,
|
||||||
|
MaxSize: logMaxSize,
|
||||||
|
MaxBackups: logMaxBackup,
|
||||||
|
MaxAge: logMaxAge,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create netlogger to handle logging to cloud.
|
||||||
|
netLog := netlogger.New()
|
||||||
|
|
||||||
|
// Create logger that we call methods on to log, which in turn writes to the
|
||||||
|
// lumberjack and netloggers.
|
||||||
|
log := logger.New(logVerbosity, io.MultiWriter(fileLog, netLog), logSuppress)
|
||||||
|
|
||||||
|
// If rv has been built with the profile tag, then we'll start a CPU profile.
|
||||||
|
if canProfile {
|
||||||
|
profile(log)
|
||||||
|
defer pprof.StopCPUProfile()
|
||||||
|
}
|
||||||
|
|
||||||
|
var rv *revid.Revid
|
||||||
|
|
||||||
|
ns, err := netsender.New(log, nil, readPin(rv), nil, config.TypeData)
|
||||||
|
if err != nil {
|
||||||
|
log.Log(logger.Fatal, pkg+"could not initialise netsender client: "+err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
rv, err = revid.New(config.Config{Logger: log}, ns)
|
||||||
|
if err != nil {
|
||||||
|
log.Log(logger.Fatal, pkg+"could not initialise revid", "error", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
run(rv, ns, log, netLog)
|
||||||
|
}
|
||||||
|
|
||||||
|
// run starts the main loop. This will run netsender on every pass of the loop
|
||||||
|
// (sleeping inbetween), check vars, and if changed, update revid as appropriate.
|
||||||
|
func run(rv *revid.Revid, ns *netsender.Sender, l *logger.Logger, nl *netlogger.Logger) {
|
||||||
|
var vs int
|
||||||
|
for {
|
||||||
|
err := ns.Run()
|
||||||
|
if err != nil {
|
||||||
|
l.Log(logger.Warning, pkg+"Run Failed. Retrying...", "error", err.Error())
|
||||||
|
time.Sleep(netSendRetryTime)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = nl.Send(ns)
|
||||||
|
if err != nil {
|
||||||
|
l.Log(logger.Warning, pkg+"Logs could not be sent", "error", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// If var sum hasn't changed we skip rest of loop.
|
||||||
|
newVs := ns.VarSum()
|
||||||
|
if vs == newVs {
|
||||||
|
sleep(ns, l)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
vs = newVs
|
||||||
|
|
||||||
|
vars, err := ns.Vars()
|
||||||
|
if err != nil {
|
||||||
|
l.Log(logger.Error, pkg+"netSender failed to get vars", "error", err.Error())
|
||||||
|
time.Sleep(netSendRetryTime)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure revid based on the vars.
|
||||||
|
err = rv.Update(vars)
|
||||||
|
if err != nil {
|
||||||
|
l.Log(logger.Warning, pkg+"Couldn't update revid", "error", err.Error())
|
||||||
|
sleep(ns, l)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ns.Mode() {
|
||||||
|
case modePaused:
|
||||||
|
rv.Stop()
|
||||||
|
case modeNormal, modeLoop:
|
||||||
|
err = rv.Start()
|
||||||
|
if err != nil {
|
||||||
|
l.Log(logger.Warning, pkg+"could not start revid", "error", err.Error())
|
||||||
|
ns.SetMode(modePaused, &vs)
|
||||||
|
sleep(ns, l)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case modeBurst:
|
||||||
|
err = burst(l, rv, ns)
|
||||||
|
if err != nil {
|
||||||
|
l.Log(logger.Warning, pkg+"could not start burst", "error", err.Error())
|
||||||
|
ns.SetMode(modePaused, &vs)
|
||||||
|
sleep(ns, l)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ns.SetMode(modePaused, &vs)
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(ns, l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// profile opens a file to hold CPU profiling metrics and then starts the
|
||||||
|
// CPU profiler.
|
||||||
|
func profile(l *logger.Logger) {
|
||||||
|
f, err := os.Create(profilePath)
|
||||||
|
if err != nil {
|
||||||
|
l.Log(logger.Fatal, pkg+"could not create CPU profile", "error", err.Error())
|
||||||
|
}
|
||||||
|
if err := pprof.StartCPUProfile(f); err != nil {
|
||||||
|
l.Log(logger.Fatal, pkg+"could not start CPU profile", "error", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sleep uses a delay to halt the program based on the monitoring period
|
||||||
|
// netsender parameter (mp) defined in the netsender.conf config.
|
||||||
|
func sleep(ns *netsender.Sender, l *logger.Logger) {
|
||||||
|
t, err := strconv.Atoi(ns.Param("mp"))
|
||||||
|
if err != nil {
|
||||||
|
l.Log(logger.Error, pkg+"could not get sleep time, using default", "error", err)
|
||||||
|
t = defaultSleepTime
|
||||||
|
}
|
||||||
|
time.Sleep(time.Duration(t) * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// readPin provides a callback function of consistent signature for use by
|
||||||
|
// netsender to retrieve software defined pin values e.g. revid bitrate (X23).
|
||||||
|
func readPin(rv *revid.Revid) func(pin *netsender.Pin) error {
|
||||||
|
return func(pin *netsender.Pin) error {
|
||||||
|
switch {
|
||||||
|
case pin.Name == "X23":
|
||||||
|
pin.Value = -1
|
||||||
|
if rv != nil {
|
||||||
|
pin.Value = rv.Bitrate()
|
||||||
|
}
|
||||||
|
case pin.Name[0] == 'X':
|
||||||
|
return sds.ReadSystem(pin)
|
||||||
|
default:
|
||||||
|
pin.Value = -1
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// burst starts revid, waits for time specified in the Config.BurstPeriod
|
||||||
|
// field, and then stops revid.
|
||||||
|
//
|
||||||
|
// TODO: move this functionality to the revid API into a Revid.Burst(time) method.
|
||||||
|
func burst(l *logger.Logger, r *revid.Revid, s *netsender.Sender) error {
|
||||||
|
l.Log(logger.Info, pkg+"starting burst")
|
||||||
|
|
||||||
|
err := r.Start()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not start revid: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(time.Duration(r.Config().BurstPeriod) * time.Second)
|
||||||
|
l.Log(logger.Info, pkg+"stopping burst")
|
||||||
|
r.Stop()
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,17 +1,15 @@
|
||||||
// +build profile
|
// +build profile
|
||||||
|
|
||||||
/*
|
/*
|
||||||
NAME
|
|
||||||
revid-cli - command line interface for Revid.
|
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
See Readme.md
|
profile.go provides an init to change canProfile flag to true if profile tag
|
||||||
|
provided on build.
|
||||||
|
|
||||||
AUTHORS
|
AUTHORS
|
||||||
Dan Kortschak <dan@ausocean.org>
|
Dan Kortschak <dan@ausocean.org>
|
||||||
|
|
||||||
LICENSE
|
LICENSE
|
||||||
revid-cli is Copyright (C) 2017-2018 the Australian Ocean Lab (AusOcean)
|
Copyright (C) 2020 the Australian Ocean Lab (AusOcean)
|
||||||
|
|
||||||
It is free software: you can redistribute it and/or modify them
|
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
|
under the terms of the GNU General Public License as published by the
|
||||||
|
@ -32,5 +30,5 @@ package main
|
||||||
import _ "net/http/pprof"
|
import _ "net/http/pprof"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
canProfile = false
|
canProfile = true
|
||||||
}
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/sh
|
||||||
|
REVIDPATH=$HOME/go/src/bitbucket.org/ausocean/av/cmd/rv
|
||||||
|
cd $REVIDPATH
|
||||||
|
sudo "PATH=$PATH:$REVIDPATH" ./rv &
|
|
@ -30,7 +30,8 @@ const numCodecs = 5
|
||||||
// A global list containing all available codecs for reference in any application.
|
// A global list containing all available codecs for reference in any application.
|
||||||
// When adding or removing a codec from this list, the numCodecs const must be updated.
|
// When adding or removing a codec from this list, the numCodecs const must be updated.
|
||||||
const (
|
const (
|
||||||
PCM = iota
|
Undef = iota
|
||||||
|
PCM
|
||||||
ADPCM
|
ADPCM
|
||||||
H264
|
H264
|
||||||
H265
|
H265
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
DESCRIPTION
|
||||||
|
cavlc_fuzz.go provides exported wrappers for unexported CAVLC functions such
|
||||||
|
that the fuzz package can access these functions for testing.
|
||||||
|
|
||||||
|
AUTHORS
|
||||||
|
Saxon A. Nelson-Milton <saxon@ausocean.org>
|
||||||
|
|
||||||
|
LICENSE
|
||||||
|
Copyright (C) 2019 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
|
||||||
|
in gpl.txt. If not, see http://www.gnu.org/licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package h264dec
|
||||||
|
|
||||||
|
import "bitbucket.org/ausocean/av/codec/h264/h264dec/bits"
|
||||||
|
|
||||||
|
func FuzzParseLevelPrefix(br *bits.BitReader) (int, error) {
|
||||||
|
return parseLevelPrefix(br)
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1 @@
|
||||||
|
01
|
|
@ -0,0 +1 @@
|
||||||
|
001
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
DESCRIPTION
|
||||||
|
fuzz.go provides a function with the required signature such that it may be
|
||||||
|
accessed by go-fuzz. The function will compare the output of C level_prefix
|
||||||
|
parser with a go version.
|
||||||
|
|
||||||
|
AUTHORS
|
||||||
|
Saxon A. Nelson-Milton <saxon@ausocean.org>
|
||||||
|
|
||||||
|
LICENSE
|
||||||
|
Copyright (C) 2019 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
|
||||||
|
in gpl.txt. If not, see http://www.gnu.org/licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// +build gofuzz
|
||||||
|
|
||||||
|
package fuzzParseLevelPrefix
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include "../helpers.c"
|
||||||
|
#include "parse_level_prefix.h"
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"bitbucket.org/ausocean/av/codec/h264/h264dec"
|
||||||
|
"bitbucket.org/ausocean/av/codec/h264/h264dec/bits"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Fuzz(data []byte) int {
|
||||||
|
// Create C based BitReader based on data.
|
||||||
|
cbr := C.new_BitReader((*C.char)(unsafe.Pointer(&data[0])), C.int(len(data)))
|
||||||
|
if cbr == nil {
|
||||||
|
panic("new_BitReader returned NULL pointer")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the level_prefix from the C code. If got is < 0, then the C code
|
||||||
|
// doesn't like it and we shouldn't have that input in the corpus, so return -1.
|
||||||
|
want := C.read_levelprefix(cbr)
|
||||||
|
if want < 0 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the level_prefix from the go code. If the C code was okay with this
|
||||||
|
// input, but the go code isn't then that's bad, so panic - want crasher info.
|
||||||
|
got, err := h264dec.FuzzParseLevelPrefix(bits.NewBitReader(bytes.NewReader(data)))
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("error from go parseLevelPrefix: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare the result of the C and go code. If they are not the same then
|
||||||
|
// panic - our go code is not doing what it should.
|
||||||
|
if int(got) != int(want) {
|
||||||
|
panic(fmt.Sprintf("did not get expected results for input: %v\nGot: %v\nWant: %v\n", data, got, want))
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
DESCRIPTION
|
||||||
|
parse_level_prefix.c contains a function that will parse the level_prefix
|
||||||
|
when performaing CAVLC decoding; extracted from Emeric Grange's h264 decoder
|
||||||
|
contained in MiniVideo (https://github.com/emericg/MiniVideo). This is used
|
||||||
|
to generate input and output data for testing purposes.
|
||||||
|
|
||||||
|
AUTHORS
|
||||||
|
Emeric Grange <emeric.grange@gmail.com>
|
||||||
|
Saxon A. Nelson-Milton <saxon@ausocean.org>
|
||||||
|
|
||||||
|
LICENSE
|
||||||
|
COPYRIGHT (C) 2018 Emeric Grange - All Rights Reserved
|
||||||
|
|
||||||
|
This file is part of MiniVideo.
|
||||||
|
|
||||||
|
MiniVideo is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
MiniVideo 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 Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with MiniVideo. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "parse_level_prefix.h"
|
||||||
|
#include "../helpers.h"
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Parsing process for level_prefix.
|
||||||
|
* \param *dc The current DecodingContext.
|
||||||
|
* \return leadingZeroBits.
|
||||||
|
*
|
||||||
|
* From 'ITU-T H.264' recommendation:
|
||||||
|
* 9.2.2.1 Parsing process for level_prefix
|
||||||
|
*
|
||||||
|
* The parsing process for this syntax element consists in reading the bits
|
||||||
|
* starting at the current location in the bitstream up to and including the
|
||||||
|
* first non-zero bit, and counting the number of leading bits that are equal to 0.
|
||||||
|
*
|
||||||
|
* level_prefix and level_suffix specify the value of a non-zero transform coefficient level.
|
||||||
|
* The range of level_prefix and level_suffix is specified in subclause 9.2.2.
|
||||||
|
*/
|
||||||
|
int read_levelprefix(struct BitReader *br){
|
||||||
|
int leadingZeroBits = -1;
|
||||||
|
int b = 0;
|
||||||
|
for (b = 0; !b; leadingZeroBits++){
|
||||||
|
b = read_bits(br,1);
|
||||||
|
if( br->err != 0 ){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return leadingZeroBits;
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef PARSE_LEVEL_PREFIX_H
|
||||||
|
#define PARSE_LEVEL_PREFIX_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "../helpers.h"
|
||||||
|
|
||||||
|
int read_levelprefix(struct BitReader *br);
|
||||||
|
|
||||||
|
#endif // PARSE_LEVEL_PREFIX_H
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
DESCRIPTION
|
||||||
|
helpers.c provides C helper functions for interfacing with C code used for
|
||||||
|
testing in this package.
|
||||||
|
|
||||||
|
AUTHORS
|
||||||
|
Saxon A. Nelson-Milton <saxon@ausocean.org>
|
||||||
|
|
||||||
|
LICENSE
|
||||||
|
Copyright (C) 2019 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
|
||||||
|
in gpl.txt. If not, see http://www.gnu.org/licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "helpers.h"
|
||||||
|
|
||||||
|
typedef struct BitReader BitReader;
|
||||||
|
typedef struct Reader Reader;
|
||||||
|
|
||||||
|
BitReader* new_BitReader(char* d, int l){
|
||||||
|
Reader* r = (Reader*)malloc(sizeof(Reader));
|
||||||
|
if(r == NULL){
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
r->data = d;
|
||||||
|
r->curr = 0;
|
||||||
|
r->len = l;
|
||||||
|
r->err = 0;
|
||||||
|
|
||||||
|
BitReader* br = (BitReader*)malloc(sizeof(BitReader));
|
||||||
|
if(br == NULL){
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
br->r = r;
|
||||||
|
br->n = 0;
|
||||||
|
br->bits = 0;
|
||||||
|
br->nRead = 0;
|
||||||
|
br->err = 0;
|
||||||
|
return br;
|
||||||
|
}
|
||||||
|
|
||||||
|
char next_byte(Reader *r) {
|
||||||
|
if(r->curr >= r->len){
|
||||||
|
r->err = 1;
|
||||||
|
}
|
||||||
|
char next = r->data[r->curr];
|
||||||
|
r->curr++;
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t read_bits(BitReader *br, int n) {
|
||||||
|
while( n > br->bits ){
|
||||||
|
char b = next_byte(br->r);
|
||||||
|
if(br->r->err != 0){
|
||||||
|
br->err = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
br->nRead++;
|
||||||
|
br->n <<= 8;
|
||||||
|
br->n |= (uint64_t)b;
|
||||||
|
br->bits += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t r = (br->n >> (br->bits-n)) & ((1 << n) - 1);
|
||||||
|
br->bits -= n;
|
||||||
|
return r;
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
DESCRIPTION
|
||||||
|
helpers.h defines some structs and function signatures that will help to
|
||||||
|
interface with C code used in our fuzz testing.
|
||||||
|
|
||||||
|
AUTHORS
|
||||||
|
Saxon A. Nelson-Milton <saxon@ausocean.org>
|
||||||
|
|
||||||
|
LICENSE
|
||||||
|
Copyright (C) 2019 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
|
||||||
|
in gpl.txt. If not, see http://www.gnu.org/licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HELPERS_H
|
||||||
|
#define HELPERS_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct Reader {
|
||||||
|
char* data;
|
||||||
|
int curr;
|
||||||
|
int len;
|
||||||
|
int err;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BitReader {
|
||||||
|
struct Reader* r;
|
||||||
|
uint64_t n;
|
||||||
|
int bits;
|
||||||
|
int nRead;
|
||||||
|
int err;
|
||||||
|
};
|
||||||
|
|
||||||
|
// new_BitReader returns a new instance of a BitReader given the backing array
|
||||||
|
// d and the length of the data l.
|
||||||
|
struct BitReader* new_BitReader(char*, int);
|
||||||
|
|
||||||
|
// next_byte provides the next byte from a Reader r and advances it's byte index.
|
||||||
|
char next_byte(struct Reader*);
|
||||||
|
|
||||||
|
// read_bits intends to emulate the BitReader.ReadBits function defined in the
|
||||||
|
// bits package. This is used when a bit reader is required to obtain bytes from
|
||||||
|
// a stream in the C test code.
|
||||||
|
uint64_t read_bits(struct BitReader*, int);
|
||||||
|
|
||||||
|
#endif // HELPERS_H
|
|
@ -182,10 +182,12 @@ func (w *Webcam) Start() error {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
w.cfg.Logger.Log(logger.Info, "starting webcam")
|
||||||
err = w.cmd.Start()
|
err = w.cmd.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to start ffmpeg: %w", err)
|
return fmt.Errorf("failed to start ffmpeg: %w", err)
|
||||||
}
|
}
|
||||||
|
w.cfg.Logger.Log(logger.Info, "webcam started")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
/*
|
||||||
|
DESCRIPTION
|
||||||
|
rvcl is command line interface for revid. The user can provide configuration
|
||||||
|
by passing a JSON string directly, or by specifying a file containing the JSON.
|
||||||
|
|
||||||
|
AUTHORS
|
||||||
|
Saxon A. Nelson-Milton <saxon@ausocean.org>
|
||||||
|
Dan Kortschak <dan@ausocean.org>
|
||||||
|
Scott Barnard <scott@ausocean.org>
|
||||||
|
|
||||||
|
LICENSE
|
||||||
|
Copyright (C) 2020 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.
|
||||||
|
|
||||||
|
USAGE
|
||||||
|
User may providied path to a config JSON file using the 'config-file' flag.
|
||||||
|
For example, to stream a H.264 file to youtube we will need to create a file
|
||||||
|
containing configuration JSON:
|
||||||
|
{
|
||||||
|
"Input":"file",
|
||||||
|
"InputPath":"<path to h264 file>",
|
||||||
|
"Output":"rtmp",
|
||||||
|
"RtmpUrl":"<rtmp url>",
|
||||||
|
"FileFPS":"25"
|
||||||
|
}
|
||||||
|
|
||||||
|
Then, using rvcl we can stream:
|
||||||
|
|
||||||
|
./rvcl -config-file="config.json"
|
||||||
|
|
||||||
|
We can also use the 'config' flag to directly pass JSON as a string.
|
||||||
|
NB: Quotations must have backslashes, '\', proceeding them for rvcl to consider
|
||||||
|
the string valid JSON. For example, to play a file to youtube using the JSON
|
||||||
|
config described above, we would do the following:
|
||||||
|
|
||||||
|
./rvcl -config="{\"Input\":\"file\",\"InputPath\":\"<file path>\",
|
||||||
|
\"Output\":\"rtmp\",\"RtmpUrl\":\"<rtmp url>\",\"FileFPS\":\"25\"}"
|
||||||
|
|
||||||
|
rvcl passes given variables to revid i.e. all variables defined by revid are
|
||||||
|
valid. See revid config package i.e. bitbucket.org/ausocean/av/revid/config for
|
||||||
|
revid specific variables, and see AVDevice implementations i.e.
|
||||||
|
bitbucket.org/ausocean/av/device for input device specific variables.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// rvcl is a command line interface for revid.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime/pprof"
|
||||||
|
|
||||||
|
"gopkg.in/natefinch/lumberjack.v2"
|
||||||
|
|
||||||
|
"bitbucket.org/ausocean/av/container/mts"
|
||||||
|
"bitbucket.org/ausocean/av/container/mts/meta"
|
||||||
|
"bitbucket.org/ausocean/av/revid"
|
||||||
|
"bitbucket.org/ausocean/av/revid/config"
|
||||||
|
"bitbucket.org/ausocean/iot/pi/netsender"
|
||||||
|
"bitbucket.org/ausocean/utils/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Copyright information prefixed to all metadata.
|
||||||
|
const (
|
||||||
|
metaPreambleKey = "copyright"
|
||||||
|
metaPreambleData = "ausocean.org/license/content2019"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Logging configuration.
|
||||||
|
const (
|
||||||
|
logMaxSize = 500 // MB
|
||||||
|
logMaxBackups = 10
|
||||||
|
logMaxAge = 28 // days
|
||||||
|
logLevel = logger.Debug
|
||||||
|
logPath = "/var/log/netsender/netsender.log"
|
||||||
|
logSuppress = false
|
||||||
|
)
|
||||||
|
|
||||||
|
// Misc consts.
|
||||||
|
const (
|
||||||
|
pkg = "rvcl: "
|
||||||
|
profilePath = "rvcl.prof"
|
||||||
|
)
|
||||||
|
|
||||||
|
// canProfile is set to false with revid-cli is built with "-tags profile".
|
||||||
|
var canProfile = false
|
||||||
|
|
||||||
|
// The logger that will be used throughout.
|
||||||
|
var log *logger.Logger
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mts.Meta = meta.NewWith([][2]string{{metaPreambleKey, metaPreambleData}})
|
||||||
|
|
||||||
|
// If built with profile tag, we will start CPU profiling.
|
||||||
|
if canProfile {
|
||||||
|
profile()
|
||||||
|
defer pprof.StopCPUProfile()
|
||||||
|
}
|
||||||
|
|
||||||
|
// User can provide config through single JSON string flag, or through JSON file.
|
||||||
|
var (
|
||||||
|
configPtr = flag.String("config", "", "Provide configuration JSON to revid (see readme for further information).")
|
||||||
|
configFilePtr = flag.String("config-file", "", "Location of revid configuration file (see readme for further information).")
|
||||||
|
)
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// Create config map according to flags or file, and panic if both are defined.
|
||||||
|
var (
|
||||||
|
cfg map[string]string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
switch {
|
||||||
|
case *configPtr != "" && *configFilePtr != "": // This doesn't make sense so panic.
|
||||||
|
panic("cannot define both command-line config and file config")
|
||||||
|
case *configPtr != "": // Decode JSON file to map.
|
||||||
|
err = json.Unmarshal([]byte(*configPtr), &cfg)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("could not decode JSON config: %w", err))
|
||||||
|
}
|
||||||
|
case *configFilePtr != "": // Decode JSON string to map from command line flag.
|
||||||
|
f, err := os.Open(*configFilePtr)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("could not open config file: %w", err))
|
||||||
|
}
|
||||||
|
err = json.NewDecoder(f).Decode(&cfg)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("could not decode JSON config: %w", err))
|
||||||
|
}
|
||||||
|
default: // No config information has been provided; give empty map to force defaults.
|
||||||
|
cfg = map[string]string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create logger that methods will be called on by the netsender client and
|
||||||
|
// revid to log messages. Logs will go the lumberjack logger to handle file
|
||||||
|
// writing of messages.
|
||||||
|
log = logger.New(
|
||||||
|
logLevel,
|
||||||
|
&lumberjack.Logger{
|
||||||
|
Filename: logPath,
|
||||||
|
MaxSize: logMaxSize, // MB
|
||||||
|
MaxBackups: logMaxBackups,
|
||||||
|
MaxAge: logMaxAge, // days
|
||||||
|
},
|
||||||
|
logSuppress,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create a netsender client. This is used only for HTTP sending of media
|
||||||
|
// in this binary.
|
||||||
|
ns, err := netsender.New(log, nil, nil, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Log(logger.Fatal, pkg+"could not initialise netsender client: "+err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
rv, err := revid.New(config.Config{Logger: log}, ns)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("could not create revid: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure revid with configuration map obtained through flags or file.
|
||||||
|
// If config is empty, defaults will be adopted by revid.
|
||||||
|
err = rv.Update(cfg)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("could not update revid config: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = rv.Start()
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("could not start revid: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run indefinitely.
|
||||||
|
select {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// profile creates a file to hold CPU profile metrics and begins CPU profiling.
|
||||||
|
func profile() {
|
||||||
|
f, err := os.Create(profilePath)
|
||||||
|
if err != nil {
|
||||||
|
log.Log(logger.Fatal, pkg+"could not create CPU profile", "error", err.Error())
|
||||||
|
}
|
||||||
|
if err := pprof.StartCPUProfile(f); err != nil {
|
||||||
|
log.Log(logger.Fatal, pkg+"could not start CPU profile", "error", err.Error())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
// +build profile
|
||||||
|
|
||||||
|
/*
|
||||||
|
DESCRIPTION
|
||||||
|
profile.go provides an init function for setting canProfile flag to true to
|
||||||
|
allow for CPU profiling.
|
||||||
|
|
||||||
|
AUTHORS
|
||||||
|
Dan Kortschak <dan@ausocean.org>
|
||||||
|
|
||||||
|
LICENSE
|
||||||
|
Copyright (C) 2020 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 main
|
||||||
|
|
||||||
|
import _ "net/http/pprof"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
canProfile = true
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/sh
|
||||||
|
REVIDPATH=$HOME/go/src/bitbucket.org/ausocean/av/cmd/rvcl
|
||||||
|
cd $REVIDPATH
|
||||||
|
sudo "PATH=$PATH:$REVIDPATH" ./rvcl -NetSender &
|
|
@ -0,0 +1,56 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# All-purpose upgrade script.
|
||||||
|
# Upgrades source(s) to given Git tag, runs make in each directory,
|
||||||
|
# and write tags to tags.conf upon success, exiting 0.
|
||||||
|
# NB: Customize SrcDirs as needed to reflect dependencies.
|
||||||
|
Usage="Usage: upgrade.sh [-d] tag"
|
||||||
|
BaseDir=$GOPATH/src/bitbucket.org/ausocean
|
||||||
|
VarDir=/var/netsender
|
||||||
|
LogFile=/var/log/netsender/stream.log
|
||||||
|
SrcDirs=($BaseDir/utils $BaseDir/iot $BaseDir/av)
|
||||||
|
if [ "$1" == "-d" ]; then
|
||||||
|
set -x
|
||||||
|
GitFlags=""
|
||||||
|
NewTag="$2"
|
||||||
|
else
|
||||||
|
# capture stdout and stderr
|
||||||
|
exec 2> $LogFile
|
||||||
|
exec 1>&2
|
||||||
|
GitFlags="--quiet"
|
||||||
|
NewTag="$1"
|
||||||
|
fi
|
||||||
|
if [ -z "$GOPATH" ]; then
|
||||||
|
echo "Error: GOPATH not defined"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ -z "$NewTag" ]; then
|
||||||
|
echo "$Usage"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
for dir in ${SrcDirs[@]}; do
|
||||||
|
pushd $dir
|
||||||
|
if [ ! "$?" == 0 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
git fetch $GitFlags --depth=1 origin refs/tags/$NewTag:refs/tags/$NewTag
|
||||||
|
if [ ! "$?" == 0 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
git checkout $GitFlags --force tags/$NewTag
|
||||||
|
if [ ! "$?" == 0 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ -e Makefile ]; then
|
||||||
|
make
|
||||||
|
if [ ! "$?" == 0 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
popd
|
||||||
|
done
|
||||||
|
if [ ! -d "$VarDir" ]; then
|
||||||
|
echo "Error: $VarDir does not exit."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
git tag > "$VarDir/tags.conf"
|
||||||
|
exit $?
|
10
go.sum
10
go.sum
|
@ -1,17 +1,7 @@
|
||||||
bitbucket.org/ausocean/iot v1.2.9 h1:3tzgiekH+Z0yXhkwnqBzxxe8qQJ2O7YTkz4s0T6stgw=
|
|
||||||
bitbucket.org/ausocean/iot v1.2.9/go.mod h1:Q5FwaOKnCty3dVeVtki6DLwYa5vhNpOaeu1lwLyPCg8=
|
|
||||||
bitbucket.org/ausocean/iot v1.2.10 h1:TTu+ykH5gQA8wU/pN0aS55ySQ/XcGxV4s4LKx3Wye5k=
|
|
||||||
bitbucket.org/ausocean/iot v1.2.10/go.mod h1:Q5FwaOKnCty3dVeVtki6DLwYa5vhNpOaeu1lwLyPCg8=
|
|
||||||
bitbucket.org/ausocean/iot v1.2.11 h1:MwYQK1F2ESA5jPVSCB0lBUN8HBiNDHGkh/OMGJKw8Oc=
|
|
||||||
bitbucket.org/ausocean/iot v1.2.11/go.mod h1:Q5FwaOKnCty3dVeVtki6DLwYa5vhNpOaeu1lwLyPCg8=
|
|
||||||
bitbucket.org/ausocean/iot v1.2.12 h1:Ixf0CTmWOMJVrJ6IYMEluTrCLlu9LM1eNSBZ+ZUnDmU=
|
|
||||||
bitbucket.org/ausocean/iot v1.2.12/go.mod h1:Q5FwaOKnCty3dVeVtki6DLwYa5vhNpOaeu1lwLyPCg8=
|
|
||||||
bitbucket.org/ausocean/iot v1.2.13 h1:E9LcW3HYqRgJqxNhPJUCfVRvoV2IAU4B7JSDNxB/x2k=
|
bitbucket.org/ausocean/iot v1.2.13 h1:E9LcW3HYqRgJqxNhPJUCfVRvoV2IAU4B7JSDNxB/x2k=
|
||||||
bitbucket.org/ausocean/iot v1.2.13/go.mod h1:Q5FwaOKnCty3dVeVtki6DLwYa5vhNpOaeu1lwLyPCg8=
|
bitbucket.org/ausocean/iot v1.2.13/go.mod h1:Q5FwaOKnCty3dVeVtki6DLwYa5vhNpOaeu1lwLyPCg8=
|
||||||
bitbucket.org/ausocean/utils v1.2.11 h1:zA0FOaPjN960ryp8PKCkV5y50uWBYrIxCVnXjwbvPqg=
|
bitbucket.org/ausocean/utils v1.2.11 h1:zA0FOaPjN960ryp8PKCkV5y50uWBYrIxCVnXjwbvPqg=
|
||||||
bitbucket.org/ausocean/utils v1.2.11/go.mod h1:uXzX9z3PLemyURTMWRhVI8uLhPX4uuvaaO85v2hcob8=
|
bitbucket.org/ausocean/utils v1.2.11/go.mod h1:uXzX9z3PLemyURTMWRhVI8uLhPX4uuvaaO85v2hcob8=
|
||||||
bitbucket.org/ausocean/utils v1.2.12 h1:VnskjWTDM475TnQRhBQE0cNp9D6Y6OELrd4UkD2VVIQ=
|
|
||||||
bitbucket.org/ausocean/utils v1.2.12/go.mod h1:uXzX9z3PLemyURTMWRhVI8uLhPX4uuvaaO85v2hcob8=
|
|
||||||
bitbucket.org/ausocean/utils v1.2.13 h1:tUaIywtoMc1+zl1GCVQokX4mL5X7LNHX5O51AgAPrWA=
|
bitbucket.org/ausocean/utils v1.2.13 h1:tUaIywtoMc1+zl1GCVQokX4mL5X7LNHX5O51AgAPrWA=
|
||||||
bitbucket.org/ausocean/utils v1.2.13/go.mod h1:uXzX9z3PLemyURTMWRhVI8uLhPX4uuvaaO85v2hcob8=
|
bitbucket.org/ausocean/utils v1.2.13/go.mod h1:uXzX9z3PLemyURTMWRhVI8uLhPX4uuvaaO85v2hcob8=
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
|
|
|
@ -373,19 +373,19 @@ func (c *Config) Validate() error {
|
||||||
switch c.LogLevel {
|
switch c.LogLevel {
|
||||||
case logger.Debug, logger.Info, logger.Warning, logger.Error, logger.Fatal:
|
case logger.Debug, logger.Info, logger.Warning, logger.Error, logger.Fatal:
|
||||||
default:
|
default:
|
||||||
|
c.logInvalidField("LogLevel", defaultVerbosity)
|
||||||
c.LogLevel = defaultVerbosity
|
c.LogLevel = defaultVerbosity
|
||||||
c.Logger.Log(logger.Info, pkg+"bad LogLevel mode defined, defaulting", "LogLevel", defaultVerbosity)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.CameraIP == "" {
|
if c.CameraIP == "" {
|
||||||
c.Logger.Log(logger.Info, pkg+"no CameraIP defined, defaulting", "CameraIP", defaultCameraIP)
|
c.logInvalidField("CameraIP", defaultCameraIP)
|
||||||
c.CameraIP = defaultCameraIP
|
c.CameraIP = defaultCameraIP
|
||||||
}
|
}
|
||||||
|
|
||||||
switch c.Input {
|
switch c.Input {
|
||||||
case InputRaspivid, InputV4L, InputFile, InputAudio, InputRTSP:
|
case InputRaspivid, InputV4L, InputFile, InputAudio, InputRTSP:
|
||||||
case NothingDefined:
|
case NothingDefined:
|
||||||
c.Logger.Log(logger.Info, pkg+"no input type defined, defaulting", "input", defaultInput)
|
c.logInvalidField("Input", defaultInput)
|
||||||
c.Input = defaultInput
|
c.Input = defaultInput
|
||||||
default:
|
default:
|
||||||
return errors.New("bad input type defined in config")
|
return errors.New("bad input type defined in config")
|
||||||
|
@ -396,16 +396,16 @@ func (c *Config) Validate() error {
|
||||||
default:
|
default:
|
||||||
switch c.Input {
|
switch c.Input {
|
||||||
case OutputAudio:
|
case OutputAudio:
|
||||||
c.Logger.Log(logger.Info, pkg+"input is audio but no codec defined, defaulting", "inputCodec", defaultAudioInputCodec)
|
c.logInvalidField("InputCodec", defaultAudioInputCodec)
|
||||||
c.InputCodec = defaultAudioInputCodec
|
c.InputCodec = defaultAudioInputCodec
|
||||||
default:
|
default:
|
||||||
c.Logger.Log(logger.Info, pkg+"no input codec defined, defaulting", "inputCodec", defaultInputCodec)
|
c.logInvalidField("InputCodec", defaultInputCodec)
|
||||||
c.InputCodec = defaultInputCodec
|
c.InputCodec = defaultInputCodec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Outputs == nil {
|
if c.Outputs == nil {
|
||||||
c.Logger.Log(logger.Info, pkg+"no output defined, defaulting", "output", defaultOutput)
|
c.logInvalidField("Outputs", defaultOutput)
|
||||||
c.Outputs = append(c.Outputs, defaultOutput)
|
c.Outputs = append(c.Outputs, defaultOutput)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,14 +423,14 @@ func (c *Config) Validate() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.BurstPeriod == 0 {
|
if c.BurstPeriod == 0 {
|
||||||
c.Logger.Log(logger.Info, pkg+"no burst period defined, defaulting", "burstPeriod", defaultBurstPeriod)
|
c.logInvalidField("BurstPeriod", defaultBurstPeriod)
|
||||||
c.BurstPeriod = defaultBurstPeriod
|
c.BurstPeriod = defaultBurstPeriod
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxMinFrames = 1000
|
const maxMinFrames = 1000
|
||||||
switch {
|
switch {
|
||||||
case c.MinFrames == 0:
|
case c.MinFrames == 0:
|
||||||
c.Logger.Log(logger.Info, pkg+"no min period defined, defaulting", "MinFrames", defaultMinFrames)
|
c.logInvalidField("MinFrames", defaultMinFrames)
|
||||||
c.MinFrames = defaultMinFrames
|
c.MinFrames = defaultMinFrames
|
||||||
case c.MinFrames < 0:
|
case c.MinFrames < 0:
|
||||||
return errors.New("refresh period is less than 0")
|
return errors.New("refresh period is less than 0")
|
||||||
|
@ -439,87 +439,88 @@ func (c *Config) Validate() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.FrameRate <= 0 {
|
if c.FrameRate <= 0 {
|
||||||
c.Logger.Log(logger.Info, pkg+"frame rate bad or unset, defaulting", "FrameRate", defaultFrameRate)
|
c.logInvalidField("FrameRate", defaultFrameRate)
|
||||||
c.FrameRate = defaultFrameRate
|
c.FrameRate = defaultFrameRate
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.WriteRate <= 0 {
|
if c.WriteRate <= 0 {
|
||||||
c.Logger.Log(logger.Info, pkg+"write rate bad or unset, defaulting", "writeRate", defaultWriteRate)
|
c.logInvalidField("writeRate", defaultWriteRate)
|
||||||
c.WriteRate = defaultWriteRate
|
c.WriteRate = defaultWriteRate
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.ClipDuration == 0 {
|
if c.ClipDuration == 0 {
|
||||||
c.Logger.Log(logger.Info, pkg+"no clip duration defined, defaulting", "ClipDuration", defaultClipDuration)
|
c.logInvalidField("ClipDuration", defaultClipDuration)
|
||||||
c.ClipDuration = defaultClipDuration
|
c.ClipDuration = defaultClipDuration
|
||||||
} else if c.ClipDuration < 0 {
|
} else if c.ClipDuration < 0 {
|
||||||
return errors.New("clip duration is less than 0")
|
return errors.New("clip duration is less than 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.RTPAddress == "" {
|
if c.RTPAddress == "" {
|
||||||
|
c.logInvalidField("RTPAddress", defaultRtpAddr)
|
||||||
c.RTPAddress = defaultRtpAddr
|
c.RTPAddress = defaultRtpAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.RBMaxElements <= 0 {
|
if c.RBMaxElements <= 0 {
|
||||||
c.Logger.Log(logger.Info, pkg+"RBMaxElements bad or unset, defaulting", "RBMaxElements", defaultRBMaxElements)
|
c.logInvalidField("RBMaxElements", defaultRBMaxElements)
|
||||||
c.RBMaxElements = defaultRBMaxElements
|
c.RBMaxElements = defaultRBMaxElements
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.RBCapacity <= 0 {
|
if c.RBCapacity <= 0 {
|
||||||
c.Logger.Log(logger.Info, pkg+"RBCapacity bad or unset, defaulting", "RBCapacity", defaultRBCapacity)
|
c.logInvalidField("RBCapacity", defaultRBCapacity)
|
||||||
c.RBCapacity = defaultRBCapacity
|
c.RBCapacity = defaultRBCapacity
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.RBWriteTimeout <= 0 {
|
if c.RBWriteTimeout <= 0 {
|
||||||
c.Logger.Log(logger.Info, pkg+"RBWriteTimeout bad or unset, defaulting", "RBWriteTimeout", defaultRBWriteTimeout)
|
c.logInvalidField("RBWriteTimeout", defaultRBWriteTimeout)
|
||||||
c.RBWriteTimeout = defaultRBWriteTimeout
|
c.RBWriteTimeout = defaultRBWriteTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.PSITime <= 0 {
|
if c.PSITime <= 0 {
|
||||||
c.Logger.Log(logger.Info, pkg+"PSITime bad or unset, defaulting", "PSITime", defaultPSITime)
|
c.logInvalidField("PSITime", defaultPSITime)
|
||||||
c.PSITime = defaultPSITime
|
c.PSITime = defaultPSITime
|
||||||
}
|
}
|
||||||
if c.MotionInterval <= 0 {
|
if c.MotionInterval <= 0 {
|
||||||
c.Logger.Log(logger.Info, pkg+"MotionInterval bad or unset, defaulting", "MotionInterval", defaultMotionInterval)
|
c.logInvalidField("MotionInterval", defaultMotionInterval)
|
||||||
c.MotionInterval = defaultMotionInterval
|
c.MotionInterval = defaultMotionInterval
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.MinFPS <= 0 {
|
if c.MinFPS <= 0 {
|
||||||
c.Logger.Log(logger.Info, pkg+"MinFPS bad or unset, defaulting", "MinFPS", defaultMinFPS)
|
c.logInvalidField("MinFPS", defaultMinFPS)
|
||||||
c.MinFPS = defaultMinFPS
|
c.MinFPS = defaultMinFPS
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.KNNMinArea <= 0 {
|
if c.KNNMinArea <= 0 {
|
||||||
c.Logger.Log(logger.Info, pkg+"KNNMinArea bad or unset, defaulting", "KNNMinArea", defaultKNNMinArea)
|
c.logInvalidField("KNNMinArea", defaultKNNMinArea)
|
||||||
c.KNNMinArea = defaultKNNMinArea
|
c.KNNMinArea = defaultKNNMinArea
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.KNNThreshold <= 0 {
|
if c.KNNThreshold <= 0 {
|
||||||
c.Logger.Log(logger.Info, pkg+"KNNThreshold bad or unset, defaulting", "KNNThreshold", defaultKNNThreshold)
|
c.logInvalidField("KNNThreshold", defaultKNNThreshold)
|
||||||
c.KNNThreshold = defaultKNNThreshold
|
c.KNNThreshold = defaultKNNThreshold
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.KNNHistory == 0 {
|
if c.KNNHistory == 0 {
|
||||||
c.Logger.Log(logger.Info, pkg+"KNNHistory bad or unset, defaulting", "KNNHistory", defaultKNNHistory)
|
c.logInvalidField("KNNHistory", defaultKNNHistory)
|
||||||
c.KNNHistory = defaultKNNHistory
|
c.KNNHistory = defaultKNNHistory
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.KNNKernel <= 0 {
|
if c.KNNKernel <= 0 {
|
||||||
c.Logger.Log(logger.Info, pkg+"KNNKernel bad or unset, defaulting", "KNNKernel", defaultKNNKernel)
|
c.logInvalidField("KNNKernel", defaultKNNKernel)
|
||||||
c.KNNKernel = defaultKNNKernel
|
c.KNNKernel = defaultKNNKernel
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.MOGMinArea <= 0 {
|
if c.MOGMinArea <= 0 {
|
||||||
c.Logger.Log(logger.Info, pkg+"MOGMinArea bad or unset, defaulting", "MOGMinArea", defaultMOGMinArea)
|
c.logInvalidField("MOGMinArea", defaultMOGMinArea)
|
||||||
c.MOGMinArea = defaultMOGMinArea
|
c.MOGMinArea = defaultMOGMinArea
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.MOGThreshold <= 0 {
|
if c.MOGThreshold <= 0 {
|
||||||
c.Logger.Log(logger.Info, pkg+"MOGThreshold bad or unset, defaulting", "MOGThreshold", defaultMOGThreshold)
|
c.logInvalidField("MOGThreshold", defaultMOGThreshold)
|
||||||
c.MOGThreshold = defaultMOGThreshold
|
c.MOGThreshold = defaultMOGThreshold
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.MOGHistory == 0 {
|
if c.MOGHistory == 0 {
|
||||||
c.Logger.Log(logger.Info, pkg+"MOGHistory bad or unset, defaulting", "MOGHistory", defaultMOGHistory)
|
c.logInvalidField("MOGHistory", defaultMOGHistory)
|
||||||
c.MOGHistory = defaultMOGHistory
|
c.MOGHistory = defaultMOGHistory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,13 +530,13 @@ func (c *Config) Validate() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if os == raspian {
|
if os == raspian {
|
||||||
c.Logger.Log(logger.Info, "ShowWindows disabled on Raspbian GNU/Linux")
|
c.Logger.Log(logger.Info, pkg+"ShowWindows disabled on Raspbian GNU/Linux", "ShowWindows", false)
|
||||||
c.ShowWindows = false
|
c.ShowWindows = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.FileFPS <= 0 || (c.FileFPS > 0 && c.Input != InputFile) {
|
if c.FileFPS <= 0 || (c.FileFPS > 0 && c.Input != InputFile) {
|
||||||
c.Logger.Log(logger.Info, pkg+"FileFPS bad or unset, defaulting", "FileFPS", defaultFileFPS)
|
c.logInvalidField("FileFPS", defaultFileFPS)
|
||||||
c.FileFPS = defaultFileFPS
|
c.FileFPS = defaultFileFPS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -551,6 +552,10 @@ func osName() (string, error) {
|
||||||
return string(out)[6 : len(out)-2], nil
|
return string(out)[6 : len(out)-2], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Config) logInvalidField(name string, def interface{}) {
|
||||||
|
c.Logger.Log(logger.Info, pkg+name+" bad or unset, defaulting", name, def)
|
||||||
|
}
|
||||||
|
|
||||||
// stringInSlice returns true if want is in slice.
|
// stringInSlice returns true if want is in slice.
|
||||||
func stringInSlice(want string, slice []string) bool {
|
func stringInSlice(want string, slice []string) bool {
|
||||||
for _, s := range slice {
|
for _, s := range slice {
|
||||||
|
|
|
@ -194,7 +194,7 @@ func (r *Revid) reset(c config.Config) error {
|
||||||
encOptions = append(encOptions, mts.TimeBasedPSI(time.Duration(r.cfg.PSITime)*time.Second))
|
encOptions = append(encOptions, mts.TimeBasedPSI(time.Duration(r.cfg.PSITime)*time.Second))
|
||||||
r.cfg.CBR = true
|
r.cfg.CBR = true
|
||||||
default:
|
default:
|
||||||
panic("unknown input codec for v4l or input file input")
|
panic(fmt.Sprintf("unknown input codec %d for v4l or input file input", r.cfg.InputCodec))
|
||||||
}
|
}
|
||||||
case config.InputRTSP:
|
case config.InputRTSP:
|
||||||
switch r.cfg.InputCodec {
|
switch r.cfg.InputCodec {
|
||||||
|
@ -567,14 +567,14 @@ func (r *Revid) Update(vars map[string]string) error {
|
||||||
}
|
}
|
||||||
case "Output":
|
case "Output":
|
||||||
r.cfg.Outputs = make([]uint8, 1)
|
r.cfg.Outputs = make([]uint8, 1)
|
||||||
switch value {
|
switch strings.ToLower(value) {
|
||||||
case "File":
|
case "file":
|
||||||
r.cfg.Outputs[0] = config.OutputFile
|
r.cfg.Outputs[0] = config.OutputFile
|
||||||
case "Http":
|
case "http":
|
||||||
r.cfg.Outputs[0] = config.OutputHTTP
|
r.cfg.Outputs[0] = config.OutputHTTP
|
||||||
case "Rtmp":
|
case "rtmp":
|
||||||
r.cfg.Outputs[0] = config.OutputRTMP
|
r.cfg.Outputs[0] = config.OutputRTMP
|
||||||
case "Rtp":
|
case "rtp":
|
||||||
r.cfg.Outputs[0] = config.OutputRTP
|
r.cfg.Outputs[0] = config.OutputRTP
|
||||||
default:
|
default:
|
||||||
r.cfg.Logger.Log(logger.Warning, pkg+"invalid output param", "value", value)
|
r.cfg.Logger.Log(logger.Warning, pkg+"invalid output param", "value", value)
|
||||||
|
|
Loading…
Reference in New Issue