av/revid/config/variables.go

570 lines
15 KiB
Go
Raw Normal View History

/*
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: "Suppress",
Type_: "bool",
Update: func(c *Config, v string) {
c.Suppress = parseBool("Suppress", v, c)
c.Logger.(*logger.Logger).SetSuppress(c.Suppress)
},
},
{
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
}