mirror of https://bitbucket.org/ausocean/av.git
Merged in nicer-update-func (pull request #411)
revid/config: variables struct providing names, types, update and validation Approved-by: Trek Hopton <trek.hopton@gmail.com>
This commit is contained in:
parent
a286d6c140
commit
ce50b5fbd1
|
@ -102,7 +102,7 @@ const (
|
||||||
defaultSleepTime = 60 // Seconds
|
defaultSleepTime = 60 // Seconds
|
||||||
profilePath = "rv.prof"
|
profilePath = "rv.prof"
|
||||||
pkg = "rv: "
|
pkg = "rv: "
|
||||||
runPreDelay = 20 * time.Second
|
runPreDelay = 20 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is set to true if the 'profile' build tag is provided on build.
|
// This is set to true if the 'profile' build tag is provided on build.
|
||||||
|
@ -136,7 +136,7 @@ func main() {
|
||||||
var rv *revid.Revid
|
var rv *revid.Revid
|
||||||
|
|
||||||
log.Log(logger.Debug, "initialising netsender client")
|
log.Log(logger.Debug, "initialising netsender client")
|
||||||
ns, err := netsender.New(log, nil, readPin(rv), nil, config.TypeData)
|
ns, err := netsender.New(log, nil, readPin(rv), nil, createVarMap())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log(logger.Fatal, pkg+"could not initialise netsender client: "+err.Error())
|
log.Log(logger.Fatal, pkg+"could not initialise netsender client: "+err.Error())
|
||||||
}
|
}
|
||||||
|
@ -146,9 +146,9 @@ func main() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log(logger.Fatal, pkg+"could not initialise revid", "error", err.Error())
|
log.Log(logger.Fatal, pkg+"could not initialise revid", "error", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// NB: Problems were encountered with communicating with RTSP inputs. When trying to
|
// NB: Problems were encountered with communicating with RTSP inputs. When trying to
|
||||||
// connect it would fail due to timeout; as if things had not been set up quickly
|
// connect it would fail due to timeout; as if things had not been set up quickly
|
||||||
// enough before revid tried to do things. This delay fixes this, but there is probably
|
// enough before revid tried to do things. This delay fixes this, but there is probably
|
||||||
// a better way to solve this problem.
|
// a better way to solve this problem.
|
||||||
time.Sleep(runPreDelay)
|
time.Sleep(runPreDelay)
|
||||||
|
@ -235,6 +235,14 @@ func run(rv *revid.Revid, ns *netsender.Sender, l *logger.Logger, nl *netlogger.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createVarMap() map[string]string {
|
||||||
|
var m map[string]string
|
||||||
|
for _, v := range config.Variables {
|
||||||
|
m[v.Name] = v.Type_
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
// profile opens a file to hold CPU profiling metrics and then starts the
|
// profile opens a file to hold CPU profiling metrics and then starts the
|
||||||
// CPU profiler.
|
// CPU profiler.
|
||||||
func profile(l *logger.Logger) {
|
func profile(l *logger.Logger) {
|
||||||
|
|
|
@ -41,7 +41,7 @@ type VariableFPS struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewVariableFPS returns a pointer to a new VariableFPS filter struct.
|
// NewVariableFPS returns a pointer to a new VariableFPS filter struct.
|
||||||
func NewVariableFPS(dst io.WriteCloser, minFPS float64, filter Filter) *VariableFPS {
|
func NewVariableFPS(dst io.WriteCloser, minFPS uint, filter Filter) *VariableFPS {
|
||||||
frames := uint(25 / minFPS)
|
frames := uint(25 / minFPS)
|
||||||
return &VariableFPS{filter, dst, frames, 0}
|
return &VariableFPS{filter, dst, frames, 0}
|
||||||
}
|
}
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -8,6 +8,7 @@ require (
|
||||||
github.com/Comcast/gots v0.0.0-20190305015453-8d56e473f0f7
|
github.com/Comcast/gots v0.0.0-20190305015453-8d56e473f0f7
|
||||||
github.com/go-audio/audio v0.0.0-20181013203223-7b2a6ca21480
|
github.com/go-audio/audio v0.0.0-20181013203223-7b2a6ca21480
|
||||||
github.com/go-audio/wav v0.0.0-20181013172942-de841e69b884
|
github.com/go-audio/wav v0.0.0-20181013172942-de841e69b884
|
||||||
|
github.com/google/go-cmp v0.4.1
|
||||||
github.com/mewkiz/flac v1.0.5
|
github.com/mewkiz/flac v1.0.5
|
||||||
github.com/pkg/errors v0.8.1
|
github.com/pkg/errors v0.8.1
|
||||||
github.com/yobert/alsa v0.0.0-20180630182551-d38d89fa843e
|
github.com/yobert/alsa v0.0.0-20180630182551-d38d89fa843e
|
||||||
|
|
3
go.sum
3
go.sum
|
@ -25,6 +25,8 @@ github.com/go-audio/audio v0.0.0-20181013203223-7b2a6ca21480/go.mod h1:6uAu0+H2l
|
||||||
github.com/go-audio/wav v0.0.0-20181013172942-de841e69b884 h1:2TaXIaVA4ff/MHHezOj83tCypALTFAcXOImcFWNa3jw=
|
github.com/go-audio/wav v0.0.0-20181013172942-de841e69b884 h1:2TaXIaVA4ff/MHHezOj83tCypALTFAcXOImcFWNa3jw=
|
||||||
github.com/go-audio/wav v0.0.0-20181013172942-de841e69b884/go.mod h1:UiqzUyfX0zs3pJ/DPyvS5v8sN6s5bXPUDDIVA5v8dks=
|
github.com/go-audio/wav v0.0.0-20181013172942-de841e69b884/go.mod h1:UiqzUyfX0zs3pJ/DPyvS5v8sN6s5bXPUDDIVA5v8dks=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0=
|
||||||
|
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/jacobsa/go-serial v0.0.0-20180131005756-15cf729a72d4/go.mod h1:2RvX5ZjVtsznNZPEt4xwJXNJrM3VTZoQf7V6gk0ysvs=
|
github.com/jacobsa/go-serial v0.0.0-20180131005756-15cf729a72d4/go.mod h1:2RvX5ZjVtsznNZPEt4xwJXNJrM3VTZoQf7V6gk0ysvs=
|
||||||
github.com/kidoman/embd v0.0.0-20170508013040-d3d8c0c5c68d/go.mod h1:ACKj9jnzOzj1lw2ETilpFGK7L9dtJhAzT7T1OhAGtRQ=
|
github.com/kidoman/embd v0.0.0-20170508013040-d3d8c0c5c68d/go.mod h1:ACKj9jnzOzj1lw2ETilpFGK7L9dtJhAzT7T1OhAGtRQ=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
|
@ -62,6 +64,7 @@ golang.org/x/image v0.0.0-20200119044424-58c23975cae1 h1:5h3ngYt7+vXCDZCup/HkCQg
|
||||||
golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/sys v0.0.0-20190913121621-c3b328c6e5a7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190913121621-c3b328c6e5a7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
|
|
|
@ -27,10 +27,8 @@ LICENSE
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"bitbucket.org/ausocean/av/codec/codecutil"
|
|
||||||
"bitbucket.org/ausocean/utils/logger"
|
"bitbucket.org/ausocean/utils/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -65,33 +63,6 @@ const (
|
||||||
MJPEG
|
MJPEG
|
||||||
)
|
)
|
||||||
|
|
||||||
// Default config settings
|
|
||||||
const (
|
|
||||||
// General revid defaults.
|
|
||||||
defaultInput = InputRaspivid
|
|
||||||
defaultOutput = OutputHTTP
|
|
||||||
defaultTimeout = 0
|
|
||||||
defaultInputCodec = codecutil.H264
|
|
||||||
defaultVerbosity = logger.Error
|
|
||||||
defaultRtpAddr = "localhost:6970"
|
|
||||||
defaultCameraIP = "192.168.1.50"
|
|
||||||
defaultBurstPeriod = 10 // Seconds
|
|
||||||
defaultMinFrames = 100
|
|
||||||
defaultFrameRate = 25
|
|
||||||
defaultWriteRate = 25
|
|
||||||
defaultClipDuration = 0
|
|
||||||
defaultAudioInputCodec = codecutil.ADPCM
|
|
||||||
defaultPSITime = 2
|
|
||||||
defaultFileFPS = 0
|
|
||||||
|
|
||||||
// Ring buffer defaults.
|
|
||||||
defaultRBCapacity = 50000000 // => 50MB
|
|
||||||
defaultRBWriteTimeout = 5 // Seconds.
|
|
||||||
|
|
||||||
// Motion filter parameter defaults.
|
|
||||||
defaultMinFPS = 1.0
|
|
||||||
)
|
|
||||||
|
|
||||||
// Quality represents video quality.
|
// Quality represents video quality.
|
||||||
type Quality int
|
type Quality int
|
||||||
|
|
||||||
|
@ -257,10 +228,10 @@ type Config struct {
|
||||||
Width uint // Width defines the input video width Raspivid input.
|
Width uint // Width defines the input video width Raspivid input.
|
||||||
Bitrate uint // Bitrate specifies the bitrate for constant bitrate in kbps.
|
Bitrate uint // Bitrate specifies the bitrate for constant bitrate in kbps.
|
||||||
|
|
||||||
HorizontalFlip bool // HorizontalFlip flips video horizontally for Raspivid input.
|
HorizontalFlip bool // HorizontalFlip flips video horizontally for Raspivid input.
|
||||||
VerticalFlip bool // VerticalFlip flips video vertically for Raspivid input.
|
VerticalFlip bool // VerticalFlip flips video vertically for Raspivid input.
|
||||||
Filters []uint8 // Defines the methods of filtering to be used in between lexing and encoding.
|
Filters []uint // Defines the methods of filtering to be used in between lexing and encoding.
|
||||||
PSITime uint // Sets the time between a packet being sent.
|
PSITime uint // Sets the time between a packet being sent.
|
||||||
|
|
||||||
// Ring buffer parameters.
|
// Ring buffer parameters.
|
||||||
RBCapacity uint // The number of bytes the ring buffer will occupy.
|
RBCapacity uint // The number of bytes the ring buffer will occupy.
|
||||||
|
@ -268,9 +239,9 @@ type Config struct {
|
||||||
|
|
||||||
// Motion filter parameters.
|
// Motion filter parameters.
|
||||||
// Some parameters can be used with any filter, while others can only be used by a few.
|
// Some parameters can be used with any filter, while others can only be used by a few.
|
||||||
MinFPS float64 // The reduced framerate of the video when there is no motion.
|
MinFPS uint // The reduced framerate of the video when there is no motion.
|
||||||
MotionInterval uint // Sets the number of frames that are held before the filter is used (on the nth frame).
|
MotionInterval uint // Sets the number of frames that are held before the filter is used (on the nth frame).
|
||||||
MotionDownscaling uint // Downscaling factor of frames used for motion detection.
|
MotionDownscaling uint // Downscaling factor of frames used for motion detection.
|
||||||
|
|
||||||
MotionMinArea float64 // Used to ignore small areas of motion detection (KNN & MOG only).
|
MotionMinArea float64 // Used to ignore small areas of motion detection (KNN & MOG only).
|
||||||
MotionThreshold float64 // Intensity value that is considered motion.
|
MotionThreshold float64 // Intensity value that is considered motion.
|
||||||
|
@ -286,187 +257,32 @@ type Config struct {
|
||||||
FileFPS uint
|
FileFPS uint
|
||||||
}
|
}
|
||||||
|
|
||||||
// TypeData contains information about all of the variables that
|
|
||||||
// can be set over the web. It is a psuedo const.
|
|
||||||
var TypeData = map[string]string{
|
|
||||||
"AutoWhiteBalance": "enum:off,auto,sun,cloud,shade,tungsten,fluorescent,incandescent,flash,horizon",
|
|
||||||
"BitDepth": "int",
|
|
||||||
"Bitrate": "uint",
|
|
||||||
"Brightness": "uint",
|
|
||||||
"BurstPeriod": "uint",
|
|
||||||
"CameraChan": "int",
|
|
||||||
"CameraIP": "string",
|
|
||||||
"CBR": "bool",
|
|
||||||
"ClipDuration": "uint",
|
|
||||||
"Exposure": "enum:auto,night,nightpreview,backlight,spotlight,sports,snow,beach,verylong,fixedfps,antishake,fireworks",
|
|
||||||
"FileFPS": "int",
|
|
||||||
"Filters": "enums:NoOp,MOG,VariableFPS,KNN,Difference,Basic",
|
|
||||||
"FrameRate": "uint",
|
|
||||||
"Height": "uint",
|
|
||||||
"HorizontalFlip": "bool",
|
|
||||||
"HTTPAddress": "string",
|
|
||||||
"Input": "enum:raspivid,rtsp,v4l,file,audio",
|
|
||||||
"InputCodec": "enum:H264,MJPEG,PCM,ADPCM",
|
|
||||||
"InputPath": "string",
|
|
||||||
"logging": "enum:Debug,Info,Warning,Error,Fatal",
|
|
||||||
"Loop": "bool",
|
|
||||||
"MinFPS": "float",
|
|
||||||
"MinFrames": "uint",
|
|
||||||
"mode": "enum:Normal,Paused,Burst,Loop",
|
|
||||||
"MotionDownscaling": "uint",
|
|
||||||
"MotionHistory": "uint",
|
|
||||||
"MotionInterval": "int",
|
|
||||||
"MotionKernel": "uint",
|
|
||||||
"MotionMinArea": "float",
|
|
||||||
"MotionPadding": "uint",
|
|
||||||
"MotionPixels": "int",
|
|
||||||
"MotionThreshold": "float",
|
|
||||||
"Output": "enum:File,Http,Rtmp,Rtp",
|
|
||||||
"OutputPath": "string",
|
|
||||||
"Outputs": "enums:File,Http,Rtmp,Rtp",
|
|
||||||
"Quantization": "uint",
|
|
||||||
"RBCapacity": "uint",
|
|
||||||
"RBWriteTimeout": "uint",
|
|
||||||
"Rotation": "uint",
|
|
||||||
"RTMPURL": "string",
|
|
||||||
"RTPAddress": "string",
|
|
||||||
"Saturation": "int",
|
|
||||||
"VBRBitrate": "int",
|
|
||||||
"VBRQuality": "enum:standard,fair,good,great,excellent",
|
|
||||||
"VerticalFlip": "bool",
|
|
||||||
"Width": "uint",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validation errors.
|
|
||||||
var (
|
|
||||||
errInvalidQuantization = errors.New("invalid quantization")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Validate checks for any errors in the config fields and defaults settings
|
// Validate checks for any errors in the config fields and defaults settings
|
||||||
// if particular parameters have not been defined.
|
// if particular parameters have not been defined.
|
||||||
func (c *Config) Validate() error {
|
func (c *Config) Validate() error {
|
||||||
switch c.LogLevel {
|
for _, v := range Variables {
|
||||||
case logger.Debug, logger.Info, logger.Warning, logger.Error, logger.Fatal:
|
if v.Validate != nil {
|
||||||
default:
|
v.Validate(c)
|
||||||
c.LogInvalidField("LogLevel", defaultVerbosity)
|
|
||||||
c.LogLevel = defaultVerbosity
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.CameraIP == "" {
|
|
||||||
c.LogInvalidField("CameraIP", defaultCameraIP)
|
|
||||||
c.CameraIP = defaultCameraIP
|
|
||||||
}
|
|
||||||
|
|
||||||
switch c.Input {
|
|
||||||
case InputRaspivid, InputV4L, InputFile, InputAudio, InputRTSP:
|
|
||||||
case NothingDefined:
|
|
||||||
c.LogInvalidField("Input", defaultInput)
|
|
||||||
c.Input = defaultInput
|
|
||||||
default:
|
|
||||||
return errors.New("bad input type defined in config")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch c.InputCodec {
|
|
||||||
case codecutil.H264, codecutil.MJPEG, codecutil.PCM, codecutil.ADPCM:
|
|
||||||
default:
|
|
||||||
switch c.Input {
|
|
||||||
case OutputAudio:
|
|
||||||
c.LogInvalidField("InputCodec", defaultAudioInputCodec)
|
|
||||||
c.InputCodec = defaultAudioInputCodec
|
|
||||||
default:
|
|
||||||
c.LogInvalidField("InputCodec", defaultInputCodec)
|
|
||||||
c.InputCodec = defaultInputCodec
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Outputs == nil {
|
|
||||||
c.LogInvalidField("Outputs", defaultOutput)
|
|
||||||
c.Outputs = append(c.Outputs, defaultOutput)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, o := range c.Outputs {
|
|
||||||
switch o {
|
|
||||||
case OutputFile, OutputHTTP, OutputRTP:
|
|
||||||
case OutputRTMP:
|
|
||||||
if c.RTMPURL == "" {
|
|
||||||
c.Logger.Log(logger.Info, "no RTMP URL: falling back to HTTP")
|
|
||||||
c.Outputs[i] = OutputHTTP
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return errors.New("bad output type defined in config")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.BurstPeriod == 0 {
|
|
||||||
c.LogInvalidField("BurstPeriod", defaultBurstPeriod)
|
|
||||||
c.BurstPeriod = defaultBurstPeriod
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxMinFrames = 1000
|
|
||||||
switch {
|
|
||||||
case c.MinFrames == 0:
|
|
||||||
c.LogInvalidField("MinFrames", defaultMinFrames)
|
|
||||||
c.MinFrames = defaultMinFrames
|
|
||||||
case c.MinFrames < 0:
|
|
||||||
return errors.New("refresh period is less than 0")
|
|
||||||
case c.MinFrames > maxMinFrames:
|
|
||||||
return errors.New("refresh period is greater than 1000")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.FrameRate <= 0 {
|
|
||||||
c.LogInvalidField("FrameRate", defaultFrameRate)
|
|
||||||
c.FrameRate = defaultFrameRate
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.WriteRate <= 0 {
|
|
||||||
c.LogInvalidField("writeRate", defaultWriteRate)
|
|
||||||
c.WriteRate = defaultWriteRate
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.ClipDuration == 0 {
|
|
||||||
c.LogInvalidField("ClipDuration", defaultClipDuration)
|
|
||||||
c.ClipDuration = defaultClipDuration
|
|
||||||
} else if c.ClipDuration < 0 {
|
|
||||||
return errors.New("clip duration is less than 0")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.RTPAddress == "" {
|
|
||||||
c.LogInvalidField("RTPAddress", defaultRtpAddr)
|
|
||||||
c.RTPAddress = defaultRtpAddr
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.RBWriteTimeout <= 0 {
|
|
||||||
c.Logger.Log(logger.Info, "RBWriteTimeout bad or unset, defaulting", "RBWriteTimeout", defaultRBWriteTimeout)
|
|
||||||
c.RBWriteTimeout = defaultRBWriteTimeout
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.RBCapacity <= 0 {
|
|
||||||
c.Logger.Log(logger.Info, "RBCapacity bad or unset, defaulting", "RBCapacity", defaultRBCapacity)
|
|
||||||
c.RBCapacity = defaultRBCapacity
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.PSITime <= 0 {
|
|
||||||
c.LogInvalidField("PSITime", defaultPSITime)
|
|
||||||
c.PSITime = defaultPSITime
|
|
||||||
}
|
|
||||||
if c.MinFPS <= 0 {
|
|
||||||
c.LogInvalidField("MinFPS", defaultMinFPS)
|
|
||||||
c.MinFPS = defaultMinFPS
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.FileFPS <= 0 || (c.FileFPS > 0 && c.Input != InputFile) {
|
|
||||||
c.LogInvalidField("FileFPS", defaultFileFPS)
|
|
||||||
c.FileFPS = defaultFileFPS
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update takes a map of configuration variable names and their corresponding
|
||||||
|
// values, parses the string values and converting into correct type, and then
|
||||||
|
// sets the config struct fields as appropriate.
|
||||||
|
func (c *Config) Update(vars map[string]string) {
|
||||||
|
for _, value := range Variables {
|
||||||
|
if v, ok := vars[value.Name]; ok && value.Update != nil {
|
||||||
|
value.Update(c, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Config) LogInvalidField(name string, def interface{}) {
|
func (c *Config) LogInvalidField(name string, def interface{}) {
|
||||||
c.Logger.Log(logger.Info, name+" bad or unset, defaulting", name, def)
|
c.Logger.Log(logger.Info, name+" bad or unset, defaulting", name, def)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
if s == want {
|
if s == want {
|
||||||
|
|
|
@ -0,0 +1,177 @@
|
||||||
|
/*
|
||||||
|
DESCRIPTION
|
||||||
|
config_test.go provides testing for the Config struct methods (Validate and Update).
|
||||||
|
|
||||||
|
AUTHORS
|
||||||
|
Saxon A. Nelson-Milton <saxon@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 config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"bitbucket.org/ausocean/av/codec/codecutil"
|
||||||
|
"bitbucket.org/ausocean/utils/logger"
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dumbLogger struct{}
|
||||||
|
|
||||||
|
func (dl *dumbLogger) Log(l int8, m string, a ...interface{}) {}
|
||||||
|
func (dl *dumbLogger) SetLevel(l int8) {}
|
||||||
|
|
||||||
|
func TestValidate(t *testing.T) {
|
||||||
|
dl := &dumbLogger{}
|
||||||
|
|
||||||
|
want := Config{
|
||||||
|
Logger: dl,
|
||||||
|
Input: defaultInput,
|
||||||
|
Outputs: []uint8{defaultOutput},
|
||||||
|
InputCodec: defaultInputCodec,
|
||||||
|
RTPAddress: defaultRTPAddr,
|
||||||
|
CameraIP: defaultCameraIP,
|
||||||
|
BurstPeriod: defaultBurstPeriod,
|
||||||
|
MinFrames: defaultMinFrames,
|
||||||
|
FrameRate: defaultFrameRate,
|
||||||
|
WriteRate: defaultWriteRate,
|
||||||
|
ClipDuration: defaultClipDuration,
|
||||||
|
PSITime: defaultPSITime,
|
||||||
|
FileFPS: defaultFileFPS,
|
||||||
|
RBCapacity: defaultRBCapacity,
|
||||||
|
RBWriteTimeout: defaultRBWriteTimeout,
|
||||||
|
MinFPS: defaultMinFPS,
|
||||||
|
}
|
||||||
|
|
||||||
|
got := Config{Logger: dl}
|
||||||
|
err := (&got).Validate()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("did not expect error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmp.Equal(got, want) {
|
||||||
|
t.Errorf("configs not equal\nwant: %v\ngot: %v", want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdate(t *testing.T) {
|
||||||
|
updateMap := map[string]string{
|
||||||
|
"AutoWhiteBalance": "sun",
|
||||||
|
"BitDepth": "3",
|
||||||
|
"Bitrate": "200000",
|
||||||
|
"Brightness": "30",
|
||||||
|
"BurstPeriod": "10",
|
||||||
|
"CameraChan": "2",
|
||||||
|
"CameraIP": "192.168.1.5",
|
||||||
|
"CBR": "true",
|
||||||
|
"ClipDuration": "5",
|
||||||
|
"Exposure": "night",
|
||||||
|
"FileFPS": "30",
|
||||||
|
"Filters": "MOG",
|
||||||
|
"FrameRate": "30",
|
||||||
|
"Height": "300",
|
||||||
|
"HorizontalFlip": "true",
|
||||||
|
"HTTPAddress": "http://address",
|
||||||
|
"Input": "rtsp",
|
||||||
|
"InputCodec": "MJPEG",
|
||||||
|
"InputPath": "/inputpath",
|
||||||
|
"logging": "Error",
|
||||||
|
"Loop": "true",
|
||||||
|
"MinFPS": "30",
|
||||||
|
"MinFrames": "30",
|
||||||
|
"MotionDownscaling": "3",
|
||||||
|
"MotionHistory": "4",
|
||||||
|
"MotionInterval": "6",
|
||||||
|
"MotionKernel": "2",
|
||||||
|
"MotionMinArea": "9",
|
||||||
|
"MotionPadding": "8",
|
||||||
|
"MotionPixels": "100",
|
||||||
|
"MotionThreshold": "34",
|
||||||
|
"OutputPath": "/outputpath",
|
||||||
|
"Outputs": "Rtmp,Rtp",
|
||||||
|
"Quantization": "30",
|
||||||
|
"RBCapacity": "100000",
|
||||||
|
"RBWriteTimeout": "50",
|
||||||
|
"Rotation": "180",
|
||||||
|
"RTMPURL": "rtmp://url",
|
||||||
|
"RTPAddress": "ip:port",
|
||||||
|
"Saturation": "-10",
|
||||||
|
"VBRBitrate": "300000",
|
||||||
|
"VBRQuality": "excellent",
|
||||||
|
"VerticalFlip": "true",
|
||||||
|
"Width": "300",
|
||||||
|
}
|
||||||
|
|
||||||
|
dl := &dumbLogger{}
|
||||||
|
|
||||||
|
want := Config{
|
||||||
|
Logger: dl,
|
||||||
|
AutoWhiteBalance: "sun",
|
||||||
|
BitDepth: 3,
|
||||||
|
Bitrate: 200000,
|
||||||
|
Brightness: 30,
|
||||||
|
BurstPeriod: 10,
|
||||||
|
CameraChan: 2,
|
||||||
|
CameraIP: "192.168.1.5",
|
||||||
|
CBR: true,
|
||||||
|
ClipDuration: 5 * time.Second,
|
||||||
|
Exposure: "night",
|
||||||
|
FileFPS: 30,
|
||||||
|
Filters: []uint{FilterMOG},
|
||||||
|
FrameRate: 30,
|
||||||
|
Height: 300,
|
||||||
|
HorizontalFlip: true,
|
||||||
|
HTTPAddress: "http://address",
|
||||||
|
Input: InputRTSP,
|
||||||
|
InputCodec: codecutil.MJPEG,
|
||||||
|
InputPath: "/inputpath",
|
||||||
|
LogLevel: logger.Error,
|
||||||
|
Loop: true,
|
||||||
|
MinFPS: 30,
|
||||||
|
MinFrames: 30,
|
||||||
|
MotionDownscaling: 3,
|
||||||
|
MotionHistory: 4,
|
||||||
|
MotionInterval: 6,
|
||||||
|
MotionKernel: 2,
|
||||||
|
MotionMinArea: 9,
|
||||||
|
MotionPadding: 8,
|
||||||
|
MotionPixels: 100,
|
||||||
|
MotionThreshold: 34,
|
||||||
|
OutputPath: "/outputpath",
|
||||||
|
Outputs: []uint8{OutputRTMP, OutputRTP},
|
||||||
|
Quantization: 30,
|
||||||
|
RBCapacity: 100000,
|
||||||
|
RBWriteTimeout: 50,
|
||||||
|
Rotation: 180,
|
||||||
|
RTMPURL: "rtmp://url",
|
||||||
|
RTPAddress: "ip:port",
|
||||||
|
Saturation: -10,
|
||||||
|
VBRBitrate: 300000,
|
||||||
|
VBRQuality: QualityExcellent,
|
||||||
|
VerticalFlip: true,
|
||||||
|
Width: 300,
|
||||||
|
}
|
||||||
|
|
||||||
|
got := Config{Logger: dl}
|
||||||
|
got.Update(updateMap)
|
||||||
|
if !cmp.Equal(want, got) {
|
||||||
|
t.Errorf("configs not equal\nwant: %v\ngot: %v", want, got)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,561 @@
|
||||||
|
/*
|
||||||
|
DESCRIPTION
|
||||||
|
variables.go contains a list of structs that provide a variable Name, type in
|
||||||
|
a string format, a function for updating the variable in the Config struct
|
||||||
|
from a string, and finally, a validation function to check the validity of the
|
||||||
|
corresponding field value in the Config.
|
||||||
|
|
||||||
|
AUTHORS
|
||||||
|
Saxon A. Nelson-Milton <saxon@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 config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"bitbucket.org/ausocean/av/codec/codecutil"
|
||||||
|
"bitbucket.org/ausocean/utils/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Default variable values.
|
||||||
|
const (
|
||||||
|
// General revid defaults.
|
||||||
|
defaultInput = InputRaspivid
|
||||||
|
defaultOutput = OutputHTTP
|
||||||
|
defaultInputCodec = codecutil.H264
|
||||||
|
defaultVerbosity = logger.Error
|
||||||
|
defaultRTPAddr = "localhost:6970"
|
||||||
|
defaultCameraIP = "192.168.1.50"
|
||||||
|
defaultBurstPeriod = 10 // Seconds
|
||||||
|
defaultMinFrames = 100
|
||||||
|
defaultFrameRate = 25
|
||||||
|
defaultWriteRate = 25
|
||||||
|
defaultClipDuration = 0
|
||||||
|
defaultAudioInputCodec = codecutil.ADPCM
|
||||||
|
defaultPSITime = 2
|
||||||
|
defaultFileFPS = 0
|
||||||
|
|
||||||
|
// Ring buffer defaults.
|
||||||
|
defaultRBCapacity = 50000000 // => 50MB
|
||||||
|
defaultRBWriteTimeout = 5 // Seconds.
|
||||||
|
|
||||||
|
// Motion filter parameter defaults.
|
||||||
|
defaultMinFPS = 1.0
|
||||||
|
)
|
||||||
|
|
||||||
|
var Variables = []struct {
|
||||||
|
Name string
|
||||||
|
Type_ string
|
||||||
|
Update func(*Config, string)
|
||||||
|
Validate func(*Config)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Name: "AutoWhiteBalance",
|
||||||
|
Type_: "enum:off,auto,sun,cloud,shade,tungsten,fluorescent,incandescent,flash,horizon",
|
||||||
|
Update: func(c *Config, v string) { c.AutoWhiteBalance = v },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "BitDepth",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.BitDepth = parseUint("BitDepth", v, c) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Bitrate",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.Bitrate = parseUint("Bitrate", v, c) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Brightness",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.Brightness = parseUint("Brightness", v, c) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "BurstPeriod",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.BurstPeriod = parseUint("BurstPeriod", v, c) },
|
||||||
|
Validate: func(c *Config) {
|
||||||
|
if c.BurstPeriod <= 0 {
|
||||||
|
c.LogInvalidField("BurstPeriod", defaultBurstPeriod)
|
||||||
|
c.BurstPeriod = defaultBurstPeriod
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "CameraChan",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.CameraChan = uint8(parseUint("CameraChan", v, c)) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "CameraIP",
|
||||||
|
Type_: "string",
|
||||||
|
Update: func(c *Config, v string) { c.CameraIP = v },
|
||||||
|
Validate: func(c *Config) {
|
||||||
|
if c.CameraIP == "" {
|
||||||
|
c.LogInvalidField("CameraIP", defaultCameraIP)
|
||||||
|
c.CameraIP = defaultCameraIP
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "CBR",
|
||||||
|
Type_: "bool",
|
||||||
|
Update: func(c *Config, v string) { c.CBR = parseBool("CBR", v, c) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "ClipDuration",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) {
|
||||||
|
_v, err := strconv.Atoi(v)
|
||||||
|
if err != nil {
|
||||||
|
c.Logger.Log(logger.Warning, "invalid ClipDuration param", "value", v)
|
||||||
|
}
|
||||||
|
c.ClipDuration = time.Duration(_v) * time.Second
|
||||||
|
},
|
||||||
|
Validate: func(c *Config) {
|
||||||
|
if c.ClipDuration <= 0 {
|
||||||
|
c.LogInvalidField("ClipDuration", defaultClipDuration)
|
||||||
|
c.ClipDuration = defaultClipDuration
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Exposure",
|
||||||
|
Type_: "enum:auto,night,nightpreview,backlight,spotlight,sports,snow,beach,verylong,fixedfps,antishake,fireworks",
|
||||||
|
Update: func(c *Config, v string) { c.Exposure = v },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "FileFPS",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.FileFPS = parseUint("FileFPS", v, c) },
|
||||||
|
Validate: func(c *Config) {
|
||||||
|
if c.FileFPS <= 0 || (c.FileFPS > 0 && c.Input != InputFile) {
|
||||||
|
c.LogInvalidField("FileFPS", defaultFileFPS)
|
||||||
|
c.FileFPS = defaultFileFPS
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Filters",
|
||||||
|
Type_: "enums:NoOp,MOG,VariableFPS,KNN,Difference,Basic",
|
||||||
|
Update: func(c *Config, v string) {
|
||||||
|
filters := strings.Split(v, ",")
|
||||||
|
m := map[string]uint{"NoOp": FilterNoOp, "MOG": FilterMOG, "VariableFPS": FilterVariableFPS, "KNN": FilterKNN, "Difference": FilterDiff, "Basic": FilterBasic}
|
||||||
|
c.Filters = make([]uint, len(filters))
|
||||||
|
for i, filter := range filters {
|
||||||
|
v, ok := m[filter]
|
||||||
|
if !ok {
|
||||||
|
c.Logger.Log(logger.Warning, "invalid Filters param", "value", v)
|
||||||
|
}
|
||||||
|
c.Filters[i] = uint(v)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "FrameRate",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.FrameRate = parseUint("FrameRate", v, c) },
|
||||||
|
Validate: func(c *Config) {
|
||||||
|
if c.FrameRate <= 0 || c.FrameRate > 60 {
|
||||||
|
c.LogInvalidField("FrameRate", defaultFrameRate)
|
||||||
|
c.FrameRate = defaultFrameRate
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Height",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.Height = parseUint("Height", v, c) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "HorizontalFlip",
|
||||||
|
Type_: "bool",
|
||||||
|
Update: func(c *Config, v string) { c.HorizontalFlip = parseBool("HorizontalFlip", v, c) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "HTTPAddress",
|
||||||
|
Type_: "string",
|
||||||
|
Update: func(c *Config, v string) { c.HTTPAddress = v },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Input",
|
||||||
|
Type_: "enum:raspivid,rtsp,v4l,file,audio",
|
||||||
|
Update: func(c *Config, v string) {
|
||||||
|
c.Input = parseEnum(
|
||||||
|
"Input",
|
||||||
|
v,
|
||||||
|
map[string]uint8{
|
||||||
|
"raspivid": InputRaspivid,
|
||||||
|
"rtsp": InputRTSP,
|
||||||
|
"v4l": InputV4L,
|
||||||
|
"file": InputFile,
|
||||||
|
"audio": InputAudio,
|
||||||
|
},
|
||||||
|
c,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
Validate: func(c *Config) {
|
||||||
|
switch c.Input {
|
||||||
|
case InputRaspivid, InputV4L, InputFile, InputAudio, InputRTSP:
|
||||||
|
default:
|
||||||
|
c.LogInvalidField("Input", defaultInput)
|
||||||
|
c.Input = defaultInput
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "InputCodec",
|
||||||
|
Type_: "enum:H264,H265,MJPEG,PCM,ADPCM",
|
||||||
|
Update: func(c *Config, v string) {
|
||||||
|
c.InputCodec = parseEnum(
|
||||||
|
"InputCodec",
|
||||||
|
v,
|
||||||
|
map[string]uint8{
|
||||||
|
"h264": codecutil.H264,
|
||||||
|
"h265": codecutil.H265,
|
||||||
|
"mjpeg": codecutil.MJPEG,
|
||||||
|
"pcm": codecutil.PCM,
|
||||||
|
"adpcm": codecutil.ADPCM,
|
||||||
|
},
|
||||||
|
c,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
Validate: func(c *Config) {
|
||||||
|
switch c.InputCodec {
|
||||||
|
case codecutil.H264, codecutil.MJPEG, codecutil.PCM, codecutil.ADPCM:
|
||||||
|
default:
|
||||||
|
switch c.Input {
|
||||||
|
case OutputAudio:
|
||||||
|
c.LogInvalidField("InputCodec", defaultAudioInputCodec)
|
||||||
|
c.InputCodec = defaultAudioInputCodec
|
||||||
|
default:
|
||||||
|
c.LogInvalidField("InputCodec", defaultInputCodec)
|
||||||
|
c.InputCodec = defaultInputCodec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "InputPath",
|
||||||
|
Type_: "string",
|
||||||
|
Update: func(c *Config, v string) { c.InputPath = v },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "logging",
|
||||||
|
Type_: "enum:Debug,Info,Warning,Error,Fatal",
|
||||||
|
Update: func(c *Config, v string) {
|
||||||
|
switch v {
|
||||||
|
case "Debug":
|
||||||
|
c.LogLevel = logger.Debug
|
||||||
|
case "Info":
|
||||||
|
c.LogLevel = logger.Info
|
||||||
|
case "Warning":
|
||||||
|
c.LogLevel = logger.Warning
|
||||||
|
case "Error":
|
||||||
|
c.LogLevel = logger.Error
|
||||||
|
case "Fatal":
|
||||||
|
c.LogLevel = logger.Fatal
|
||||||
|
default:
|
||||||
|
c.Logger.Log(logger.Warning, "invalid Logging param", "value", v)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Validate: func(c *Config) {
|
||||||
|
switch c.LogLevel {
|
||||||
|
case logger.Debug, logger.Info, logger.Warning, logger.Error, logger.Fatal:
|
||||||
|
default:
|
||||||
|
c.LogInvalidField("LogLevel", defaultVerbosity)
|
||||||
|
c.LogLevel = defaultVerbosity
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Loop",
|
||||||
|
Type_: "bool",
|
||||||
|
Update: func(c *Config, v string) { c.Loop = parseBool("Loop", v, c) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "MinFPS",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.MinFPS = parseUint("MinFPS", v, c) },
|
||||||
|
Validate: func(c *Config) { c.MinFPS = lessThanOrEqual("MinFPS", c.MinFPS, 0, c, defaultMinFPS) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "MinFrames",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.MinFrames = parseUint("MinFrames", v, c) },
|
||||||
|
Validate: func(c *Config) {
|
||||||
|
const maxMinFrames = 1000
|
||||||
|
if c.MinFrames <= 0 || c.MinFrames > maxMinFrames {
|
||||||
|
c.LogInvalidField("MinFrames", defaultMinFrames)
|
||||||
|
c.MinFrames = defaultMinFrames
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "mode",
|
||||||
|
Type_: "enum:Normal,Paused,Burst,Loop",
|
||||||
|
Update: func(c *Config, v string) {
|
||||||
|
c.Loop = false
|
||||||
|
if v == "Loop" {
|
||||||
|
c.Loop = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "MotionDownscaling",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.MotionDownscaling = parseUint("MotionDownscaling", v, c) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "MotionHistory",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.MotionHistory = parseUint("MotionHistory", v, c) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "MotionInterval",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.MotionInterval = parseUint("MotionInterval", v, c) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "MotionKernel",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.MotionKernel = parseUint("MotionKernel", v, c) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "MotionMinArea",
|
||||||
|
Type_: "float",
|
||||||
|
Update: func(c *Config, v string) {
|
||||||
|
f, err := strconv.ParseFloat(v, 64)
|
||||||
|
if err != nil {
|
||||||
|
c.Logger.Log(logger.Warning, "invalid MotionMinArea var", "value", v)
|
||||||
|
}
|
||||||
|
c.MotionMinArea = f
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "MotionPadding",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.MotionPadding = parseUint("MotionPadding", v, c) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "MotionPixels",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.MotionPixels = parseUint("MotionPixels", v, c) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "MotionThreshold",
|
||||||
|
Type_: "float",
|
||||||
|
Update: func(c *Config, v string) {
|
||||||
|
f, err := strconv.ParseFloat(v, 64)
|
||||||
|
if err != nil {
|
||||||
|
c.Logger.Log(logger.Warning, "invalid MotionThreshold var", "value", v)
|
||||||
|
}
|
||||||
|
c.MotionThreshold = f
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Output",
|
||||||
|
Type_: "enum:File,HTTP,RTMP,RTP",
|
||||||
|
Update: func(c *Config, v string) {
|
||||||
|
c.Outputs = make([]uint8, 1)
|
||||||
|
switch strings.ToLower(v) {
|
||||||
|
case "file":
|
||||||
|
c.Outputs[0] = OutputFile
|
||||||
|
case "http":
|
||||||
|
c.Outputs[0] = OutputHTTP
|
||||||
|
case "rtmp":
|
||||||
|
c.Outputs[0] = OutputRTMP
|
||||||
|
case "rtp":
|
||||||
|
c.Outputs[0] = OutputRTP
|
||||||
|
default:
|
||||||
|
c.Logger.Log(logger.Warning, "invalid output param", "value", v)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "OutputPath",
|
||||||
|
Type_: "string",
|
||||||
|
Update: func(c *Config, v string) { c.OutputPath = v },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Outputs",
|
||||||
|
Type_: "enums:File,HTTP,RTMP,RTP",
|
||||||
|
Update: func(c *Config, v string) {
|
||||||
|
outputs := strings.Split(v, ",")
|
||||||
|
c.Outputs = make([]uint8, len(outputs))
|
||||||
|
for i, output := range outputs {
|
||||||
|
switch strings.ToLower(output) {
|
||||||
|
case "file":
|
||||||
|
c.Outputs[i] = OutputFile
|
||||||
|
case "http":
|
||||||
|
c.Outputs[i] = OutputHTTP
|
||||||
|
case "rtmp":
|
||||||
|
c.Outputs[i] = OutputRTMP
|
||||||
|
case "rtp":
|
||||||
|
c.Outputs[i] = OutputRTP
|
||||||
|
default:
|
||||||
|
c.Logger.Log(logger.Warning, "invalid outputs param", "value", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Validate: func(c *Config) {
|
||||||
|
if c.Outputs == nil {
|
||||||
|
c.LogInvalidField("Outputs", defaultOutput)
|
||||||
|
c.Outputs = append(c.Outputs, defaultOutput)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "PSITime",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.PSITime = parseUint("PSITime", v, c) },
|
||||||
|
Validate: func(c *Config) { c.PSITime = lessThanOrEqual("PSITime", c.PSITime, 0, c, defaultPSITime) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Quantization",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.Quantization = parseUint("Quantization", v, c) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "RBCapacity",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.RBCapacity = parseUint("RBCapacity", v, c) },
|
||||||
|
Validate: func(c *Config) { c.RBCapacity = lessThanOrEqual("RBCapacity", c.RBCapacity, 0, c, defaultRBCapacity) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "RBWriteTimeout",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.RBWriteTimeout = parseUint("RBWriteTimeout", v, c) },
|
||||||
|
Validate: func(c *Config) {
|
||||||
|
c.RBWriteTimeout = lessThanOrEqual("RBWriteTimeout", c.RBWriteTimeout, 0, c, defaultRBWriteTimeout)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Rotation",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.Rotation = parseUint("Rotation", v, c) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "RTMPURL",
|
||||||
|
Type_: "string",
|
||||||
|
Update: func(c *Config, v string) { c.RTMPURL = v },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "RTPAddress",
|
||||||
|
Type_: "string",
|
||||||
|
Update: func(c *Config, v string) { c.RTPAddress = v },
|
||||||
|
Validate: func(c *Config) {
|
||||||
|
if c.RTPAddress == "" {
|
||||||
|
c.LogInvalidField("RTPAddress", defaultRTPAddr)
|
||||||
|
c.RTPAddress = defaultRTPAddr
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Saturation",
|
||||||
|
Type_: "int",
|
||||||
|
Update: func(c *Config, v string) {
|
||||||
|
_v, err := strconv.Atoi(v)
|
||||||
|
if err != nil {
|
||||||
|
c.Logger.Log(logger.Warning, "invalid saturation param", "value", v)
|
||||||
|
}
|
||||||
|
c.Saturation = _v
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "VBRBitrate",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.VBRBitrate = parseUint("VBRBitrate", v, c) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "VBRQuality",
|
||||||
|
Type_: "enum:standard,fair,good,great,excellent",
|
||||||
|
Update: func(c *Config, v string) {
|
||||||
|
c.VBRQuality = Quality(parseEnum(
|
||||||
|
"VBRQuality",
|
||||||
|
v,
|
||||||
|
map[string]uint8{
|
||||||
|
"standard": uint8(QualityStandard),
|
||||||
|
"fair": uint8(QualityFair),
|
||||||
|
"good": uint8(QualityGood),
|
||||||
|
"great": uint8(QualityGreat),
|
||||||
|
"excellent": uint8(QualityExcellent),
|
||||||
|
},
|
||||||
|
c,
|
||||||
|
))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "VerticalFlip",
|
||||||
|
Type_: "bool",
|
||||||
|
Update: func(c *Config, v string) { c.VerticalFlip = parseBool("VerticalFlip", v, c) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Width",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.Width = parseUint("Width", v, c) },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "WriteRate",
|
||||||
|
Type_: "uint",
|
||||||
|
Update: func(c *Config, v string) { c.WriteRate = float64(parseUint("WriteRate", v, c)) },
|
||||||
|
Validate: func(c *Config) {
|
||||||
|
c.WriteRate = float64(lessThanOrEqual("WriteRate", uint(c.WriteRate), 0, c, defaultWriteRate))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseUint(n, v string, c *Config) uint {
|
||||||
|
_v, err := strconv.Atoi(v)
|
||||||
|
if err != nil {
|
||||||
|
c.Logger.Log(logger.Warning, fmt.Sprintf("invalid %s param", n), "value", v)
|
||||||
|
}
|
||||||
|
return uint(_v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseBool(n, v string, c *Config) (b bool) {
|
||||||
|
switch strings.ToLower(v) {
|
||||||
|
case "true":
|
||||||
|
b = true
|
||||||
|
case "false":
|
||||||
|
b = false
|
||||||
|
default:
|
||||||
|
c.Logger.Log(logger.Warning, fmt.Sprintf("invalid value for %s param", n), "value", v)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseEnum(n, v string, enums map[string]uint8, c *Config) uint8 {
|
||||||
|
_v, ok := enums[strings.ToLower(v)]
|
||||||
|
if !ok {
|
||||||
|
c.Logger.Log(logger.Warning, fmt.Sprintf("invalid value for %s param", n), "value", v)
|
||||||
|
}
|
||||||
|
return _v
|
||||||
|
}
|
||||||
|
|
||||||
|
func lessThanOrEqual(n string, v, cmp uint, c *Config, def uint) uint {
|
||||||
|
if v <= cmp {
|
||||||
|
c.LogInvalidField(n, def)
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
321
revid/revid.go
321
revid/revid.go
|
@ -33,8 +33,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -557,324 +555,7 @@ func (r *Revid) Update(vars map[string]string) error {
|
||||||
defer r.mu.Unlock()
|
defer r.mu.Unlock()
|
||||||
//look through the vars and update revid where needed
|
//look through the vars and update revid where needed
|
||||||
r.cfg.Logger.Log(logger.Debug, "checking vars from server", "vars", vars)
|
r.cfg.Logger.Log(logger.Debug, "checking vars from server", "vars", vars)
|
||||||
for key, value := range vars {
|
r.cfg.Update(vars)
|
||||||
switch key {
|
|
||||||
case "Input":
|
|
||||||
v, ok := map[string]uint8{"raspivid": config.InputRaspivid, "rtsp": config.InputRTSP, "v4l": config.InputV4L, "file": config.InputFile}[strings.ToLower(value)]
|
|
||||||
if !ok {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid input var", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.Input = v
|
|
||||||
case "Saturation":
|
|
||||||
s, err := strconv.Atoi(value)
|
|
||||||
if err != nil {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid saturation param", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.Saturation = int(s)
|
|
||||||
case "Brightness":
|
|
||||||
b, err := strconv.Atoi(value)
|
|
||||||
if err != nil {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid brightness param", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.Brightness = uint(b)
|
|
||||||
case "Exposure":
|
|
||||||
r.cfg.Exposure = value
|
|
||||||
case "AutoWhiteBalance":
|
|
||||||
r.cfg.AutoWhiteBalance = value
|
|
||||||
case "InputCodec":
|
|
||||||
switch value {
|
|
||||||
case "H264":
|
|
||||||
r.cfg.InputCodec = codecutil.H264
|
|
||||||
case "MJPEG":
|
|
||||||
r.cfg.InputCodec = codecutil.MJPEG
|
|
||||||
default:
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid InputCodec variable value", "value", value)
|
|
||||||
}
|
|
||||||
case "Outputs":
|
|
||||||
outputs := strings.Split(value, ",")
|
|
||||||
r.cfg.Outputs = make([]uint8, len(outputs))
|
|
||||||
|
|
||||||
for i, output := range outputs {
|
|
||||||
switch output {
|
|
||||||
case "File":
|
|
||||||
r.cfg.Outputs[i] = config.OutputFile
|
|
||||||
case "Http":
|
|
||||||
r.cfg.Outputs[i] = config.OutputHTTP
|
|
||||||
case "Rtmp":
|
|
||||||
r.cfg.Outputs[i] = config.OutputRTMP
|
|
||||||
case "Rtp":
|
|
||||||
r.cfg.Outputs[i] = config.OutputRTP
|
|
||||||
default:
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid outputs param", "value", value)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "Output":
|
|
||||||
r.cfg.Outputs = make([]uint8, 1)
|
|
||||||
switch strings.ToLower(value) {
|
|
||||||
case "file":
|
|
||||||
r.cfg.Outputs[0] = config.OutputFile
|
|
||||||
case "http":
|
|
||||||
r.cfg.Outputs[0] = config.OutputHTTP
|
|
||||||
case "rtmp":
|
|
||||||
r.cfg.Outputs[0] = config.OutputRTMP
|
|
||||||
case "rtp":
|
|
||||||
r.cfg.Outputs[0] = config.OutputRTP
|
|
||||||
default:
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid output param", "value", value)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
case "RTMPURL":
|
|
||||||
r.cfg.RTMPURL = value
|
|
||||||
case "RTPAddress":
|
|
||||||
r.cfg.RTPAddress = value
|
|
||||||
case "Bitrate":
|
|
||||||
v, err := strconv.Atoi(value)
|
|
||||||
if err != nil {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid framerate param", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.Bitrate = uint(v)
|
|
||||||
case "OutputPath":
|
|
||||||
r.cfg.OutputPath = value
|
|
||||||
case "InputPath":
|
|
||||||
r.cfg.InputPath = value
|
|
||||||
case "Height":
|
|
||||||
h, err := strconv.Atoi(value)
|
|
||||||
if err != nil {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid height param", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.Height = uint(h)
|
|
||||||
case "Width":
|
|
||||||
w, err := strconv.Atoi(value)
|
|
||||||
if err != nil {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid width param", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.Width = uint(w)
|
|
||||||
case "FrameRate":
|
|
||||||
v, err := strconv.Atoi(value)
|
|
||||||
if err != nil {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid framerate param", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.FrameRate = uint(v)
|
|
||||||
case "Rotation":
|
|
||||||
v, err := strconv.Atoi(value)
|
|
||||||
if err != nil || v > 359 {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid rotation param", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.Rotation = uint(v)
|
|
||||||
case "HTTPAddress":
|
|
||||||
r.cfg.HTTPAddress = value
|
|
||||||
case "Quantization":
|
|
||||||
v, err := strconv.Atoi(value)
|
|
||||||
if err != nil {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid quantization param", "value", v)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.Quantization = uint(v)
|
|
||||||
case "MinFrames":
|
|
||||||
v, err := strconv.Atoi(value)
|
|
||||||
if err != nil {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid MinFrames param", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.MinFrames = uint(v)
|
|
||||||
|
|
||||||
case "ClipDuration":
|
|
||||||
v, err := strconv.Atoi(value)
|
|
||||||
if err != nil {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid ClipDuration param", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.ClipDuration = time.Duration(v) * time.Second
|
|
||||||
|
|
||||||
case "HorizontalFlip":
|
|
||||||
switch strings.ToLower(value) {
|
|
||||||
case "true":
|
|
||||||
r.cfg.HorizontalFlip = true
|
|
||||||
case "false":
|
|
||||||
r.cfg.HorizontalFlip = false
|
|
||||||
default:
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid HorizontalFlip param", "value", value)
|
|
||||||
}
|
|
||||||
case "VerticalFlip":
|
|
||||||
switch strings.ToLower(value) {
|
|
||||||
case "true":
|
|
||||||
r.cfg.VerticalFlip = true
|
|
||||||
case "false":
|
|
||||||
r.cfg.VerticalFlip = false
|
|
||||||
default:
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid VerticalFlip param", "value", value)
|
|
||||||
}
|
|
||||||
case "Filters":
|
|
||||||
filters := strings.Split(value, ",")
|
|
||||||
m := map[string]int{"NoOp": config.FilterNoOp, "MOG": config.FilterMOG, "VariableFPS": config.FilterVariableFPS, "KNN": config.FilterKNN, "Difference": config.FilterDiff, "Basic": config.FilterBasic}
|
|
||||||
r.cfg.Filters = make([]uint8, len(filters))
|
|
||||||
for i, filter := range filters {
|
|
||||||
v, ok := m[filter]
|
|
||||||
if !ok {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid Filters param", "value", value)
|
|
||||||
}
|
|
||||||
r.cfg.Filters[i] = uint8(v)
|
|
||||||
}
|
|
||||||
case "PSITime":
|
|
||||||
v, err := strconv.Atoi(value)
|
|
||||||
if err != nil || v < 0 {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid PSITime var", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.PSITime = uint(v)
|
|
||||||
case "BurstPeriod":
|
|
||||||
v, err := strconv.Atoi(value)
|
|
||||||
if err != nil {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid BurstPeriod param", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.BurstPeriod = uint(v)
|
|
||||||
case "logging":
|
|
||||||
switch value {
|
|
||||||
case "Debug":
|
|
||||||
r.cfg.LogLevel = logger.Debug
|
|
||||||
case "Info":
|
|
||||||
r.cfg.LogLevel = logger.Info
|
|
||||||
case "Warning":
|
|
||||||
r.cfg.LogLevel = logger.Warning
|
|
||||||
case "Error":
|
|
||||||
r.cfg.LogLevel = logger.Error
|
|
||||||
case "Fatal":
|
|
||||||
r.cfg.LogLevel = logger.Fatal
|
|
||||||
default:
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid Logging param", "value", value)
|
|
||||||
}
|
|
||||||
case "RBCapacity":
|
|
||||||
v, err := strconv.Atoi(value)
|
|
||||||
if err != nil || v < 0 {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid RBCapacity var", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.RBCapacity = uint(v)
|
|
||||||
case "RBWriteTimeout":
|
|
||||||
v, err := strconv.Atoi(value)
|
|
||||||
if err != nil || v <= 0 {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid RBWriteTimeout var", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.RBWriteTimeout = uint(v)
|
|
||||||
case "CBR":
|
|
||||||
v, ok := map[string]bool{"true": true, "false": false}[strings.ToLower(value)]
|
|
||||||
if !ok {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid CBR var", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.CBR = v
|
|
||||||
case "CameraIP":
|
|
||||||
r.cfg.CameraIP = value
|
|
||||||
case "VBRQuality":
|
|
||||||
v, ok := map[string]config.Quality{"standard": config.QualityStandard, "fair": config.QualityFair, "good": config.QualityGood, "great": config.QualityGreat, "excellent": config.QualityExcellent}[strings.ToLower(value)]
|
|
||||||
if !ok {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid VBRQuality var", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.VBRQuality = v
|
|
||||||
case "VBRBitrate":
|
|
||||||
v, err := strconv.Atoi(value)
|
|
||||||
if err != nil || v <= 0 {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid VBRBitrate var", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.VBRBitrate = uint(v)
|
|
||||||
case "CameraChan":
|
|
||||||
v, err := strconv.Atoi(value)
|
|
||||||
if err != nil || (v != 1 && v != 2) {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid CameraChan var", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.CameraChan = uint8(v)
|
|
||||||
case "MinFPS":
|
|
||||||
v, err := strconv.ParseFloat(value, 64)
|
|
||||||
if err != nil {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid MinFPS var", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.MinFPS = v
|
|
||||||
case "MotionMinArea":
|
|
||||||
v, err := strconv.ParseFloat(value, 64)
|
|
||||||
if err != nil {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid MotionMinArea var", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.MotionMinArea = v
|
|
||||||
case "MotionThreshold":
|
|
||||||
v, err := strconv.ParseFloat(value, 64)
|
|
||||||
if err != nil {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid MotionThreshold var", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.MotionThreshold = v
|
|
||||||
case "MotionKernel":
|
|
||||||
v, err := strconv.Atoi(value)
|
|
||||||
if err != nil {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid MotionKernel var", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.MotionKernel = uint(v)
|
|
||||||
case "MotionHistory":
|
|
||||||
v, err := strconv.Atoi(value)
|
|
||||||
if err != nil || v <= 0 {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid MotionHistory var", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.MotionHistory = uint(v)
|
|
||||||
case "MotionPadding":
|
|
||||||
v, err := strconv.Atoi(value)
|
|
||||||
if err != nil || v <= 0 {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid MotionPadding var", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.MotionPadding = uint(v)
|
|
||||||
case "MotionPixels":
|
|
||||||
v, err := strconv.Atoi(value)
|
|
||||||
if err != nil {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid MotionPixels var", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.MotionPixels = uint(v)
|
|
||||||
case "MotionDownscaling":
|
|
||||||
v, err := strconv.Atoi(value)
|
|
||||||
if err != nil {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid MotionDownscaling var", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.MotionDownscaling = uint(v)
|
|
||||||
case "MotionInterval":
|
|
||||||
v, err := strconv.Atoi(value)
|
|
||||||
if err != nil || v < 0 {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid MotionInterval var", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.MotionInterval = uint(v)
|
|
||||||
case "FileFPS":
|
|
||||||
v, err := strconv.Atoi(value)
|
|
||||||
if err != nil {
|
|
||||||
r.cfg.Logger.Log(logger.Warning, "invalid FileFPS var", "value", value)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.cfg.FileFPS = uint(v)
|
|
||||||
case "mode":
|
|
||||||
r.cfg.Loop = false
|
|
||||||
if value == "Loop" {
|
|
||||||
r.cfg.Loop = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
r.cfg.Logger.Log(logger.Info, "finished reconfig")
|
r.cfg.Logger.Log(logger.Info, "finished reconfig")
|
||||||
r.cfg.Logger.Log(logger.Debug, "config changed", "config", r.cfg)
|
r.cfg.Logger.Log(logger.Debug, "config changed", "config", r.cfg)
|
||||||
return nil
|
return nil
|
||||||
|
|
Loading…
Reference in New Issue