/* 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 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" ) // Config map keys. const ( keyAutoWhiteBalance = "AutoWhiteBalance" keyBitDepth = "BitDepth" keyBitrate = "Bitrate" keyBrightness = "Brightness" keyBurstPeriod = "BurstPeriod" keyCameraChan = "CameraChan" keyCameraIP = "CameraIP" keyCBR = "CBR" keyClipDuration = "ClipDuration" keyChannels = "Channels" keyExposure = "Exposure" keyFileFPS = "FileFPS" keyFilters = "Filters" keyFrameRate = "FrameRate" keyHeight = "Height" keyHorizontalFlip = "HorizontalFlip" keyHTTPAddress = "HTTPAddress" keyInput = "Input" keyInputCodec = "InputCodec" keyInputPath = "InputPath" keyLogging = "logging" keyLoop = "Loop" keyMinFPS = "MinFPS" keyMinFrames = "MinFrames" keyMode = "mode" keyMotionDownscaling = "MotionDownscaling" keyMotionHistory = "MotionHistory" keyMotionInterval = "MotionInterval" keyMotionKernel = "MotionKernel" keyMotionMinArea = "MotionMinArea" keyMotionPadding = "MotionPadding" keyMotionPixels = "MotionPixels" keyMotionThreshold = "MotionThreshold" keyOutput = "Output" keyOutputPath = "OutputPath" keyOutputs = "Outputs" keyPSITime = "PSITime" keyQuantization = "Quantization" keyRBCapacity = "RBCapacity" keyRBWriteTimeout = "RBWriteTimeout" keyRecPeriod = "RecPeriod" keyRotation = "Rotation" keyRTMPURL = "RTMPURL" keyRTPAddress = "RTPAddress" keySampleRate = "SampleRate" keySaturation = "Saturation" keySuppress = "Suppress" keyVBRBitrate = "VBRBitrate" keyVBRQuality = "VBRQuality" keyVerticalFlip = "VerticalFlip" keyWidth = "Width" ) // Config map parameter types. const ( typeString = "string" typeUint = "uint" typeBool = "bool" typeFloat = "float" ) // 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 defaultClipDuration = 0 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: keyAutoWhiteBalance, Type_: "enum:off,auto,sun,cloud,shade,tungsten,fluorescent,incandescent,flash,horizon", Update: func(c *Config, v string) { c.AutoWhiteBalance = v }, }, { Name: keyBitDepth, Type_: typeUint, Update: func(c *Config, v string) { c.BitDepth = parseUint(keyBitDepth, v, c) }, }, { Name: keyBitrate, Type_: typeUint, Update: func(c *Config, v string) { c.Bitrate = parseUint(keyBitrate, v, c) }, }, { Name: keyBrightness, Type_: typeUint, Update: func(c *Config, v string) { c.Brightness = parseUint(keyBrightness, v, c) }, }, { Name: keyBurstPeriod, Type_: typeUint, Update: func(c *Config, v string) { c.BurstPeriod = parseUint(keyBurstPeriod, v, c) }, Validate: func(c *Config) { if c.BurstPeriod <= 0 { c.LogInvalidField(keyBurstPeriod, defaultBurstPeriod) c.BurstPeriod = defaultBurstPeriod } }, }, { Name: keyCameraChan, Type_: typeUint, Update: func(c *Config, v string) { c.CameraChan = uint8(parseUint(keyCameraChan, v, c)) }, }, { Name: keyCameraIP, Type_: typeString, Update: func(c *Config, v string) { c.CameraIP = v }, Validate: func(c *Config) { if c.CameraIP == "" { c.LogInvalidField(keyCameraIP, defaultCameraIP) c.CameraIP = defaultCameraIP } }, }, { Name: keyCBR, Type_: typeBool, Update: func(c *Config, v string) { c.CBR = parseBool(keyCBR, v, c) }, }, { Name: keyClipDuration, Type_: typeUint, 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(keyClipDuration, defaultClipDuration) c.ClipDuration = defaultClipDuration } }, }, { Name: keyChannels, Type_: typeUint, Update: func(c *Config, v string) { c.Channels = parseUint(keyChannels, v, c) }, }, { Name: keyExposure, Type_: "enum:auto,night,nightpreview,backlight,spotlight,sports,snow,beach,verylong,fixedfps,antishake,fireworks", Update: func(c *Config, v string) { c.Exposure = v }, }, { Name: keyFileFPS, Type_: typeUint, Update: func(c *Config, v string) { c.FileFPS = parseUint(keyFileFPS, v, c) }, Validate: func(c *Config) { if c.FileFPS <= 0 || (c.FileFPS > 0 && c.Input != InputFile) { c.LogInvalidField(keyFileFPS, defaultFileFPS) c.FileFPS = defaultFileFPS } }, }, { Name: keyFilters, 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: keyFrameRate, Type_: typeUint, Update: func(c *Config, v string) { c.FrameRate = parseUint(keyFrameRate, v, c) }, Validate: func(c *Config) { if c.FrameRate <= 0 || c.FrameRate > 60 { c.LogInvalidField(keyFrameRate, defaultFrameRate) c.FrameRate = defaultFrameRate } }, }, { Name: keyHeight, Type_: typeUint, Update: func(c *Config, v string) { c.Height = parseUint(keyHeight, v, c) }, }, { Name: keyHorizontalFlip, Type_: typeBool, Update: func(c *Config, v string) { c.HorizontalFlip = parseBool(keyHorizontalFlip, v, c) }, }, { Name: keyHTTPAddress, Type_: typeString, Update: func(c *Config, v string) { c.HTTPAddress = v }, }, { Name: keyInput, Type_: "enum:raspivid,rtsp,v4l,file,audio", Update: func(c *Config, v string) { c.Input = parseEnum( keyInput, 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(keyInput, defaultInput) c.Input = defaultInput } }, }, { Name: keyInputCodec, Type_: "enum:H264,H265,MJPEG,PCM,ADPCM", Update: func(c *Config, v string) { c.InputCodec = parseEnum( keyInputCodec, 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: c.LogInvalidField(keyInputCodec, defaultInputCodec) c.InputCodec = defaultInputCodec } }, }, { Name: keyInputPath, Type_: typeString, Update: func(c *Config, v string) { c.InputPath = v }, }, { Name: keyLogging, 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: keyLoop, Type_: typeBool, Update: func(c *Config, v string) { c.Loop = parseBool(keyLoop, v, c) }, }, { Name: keyMinFPS, Type_: typeUint, Update: func(c *Config, v string) { c.MinFPS = parseUint(keyMinFPS, v, c) }, Validate: func(c *Config) { c.MinFPS = lessThanOrEqual(keyMinFPS, c.MinFPS, 0, c, defaultMinFPS) }, }, { Name: keyMinFrames, Type_: typeUint, Update: func(c *Config, v string) { c.MinFrames = parseUint(keyMinFrames, v, c) }, Validate: func(c *Config) { const maxMinFrames = 1000 if c.MinFrames <= 0 || c.MinFrames > maxMinFrames { c.LogInvalidField(keyMinFrames, defaultMinFrames) c.MinFrames = defaultMinFrames } }, }, { Name: keyMode, Type_: "enum:Normal,Paused,Burst,Loop", Update: func(c *Config, v string) { c.Loop = false if v == keyLoop { c.Loop = true } }, }, { Name: keyMotionDownscaling, Type_: typeUint, Update: func(c *Config, v string) { c.MotionDownscaling = parseUint(keyMotionDownscaling, v, c) }, }, { Name: keyMotionHistory, Type_: typeUint, Update: func(c *Config, v string) { c.MotionHistory = parseUint(keyMotionHistory, v, c) }, }, { Name: keyMotionInterval, Type_: typeUint, Update: func(c *Config, v string) { c.MotionInterval = parseUint(keyMotionInterval, v, c) }, }, { Name: keyMotionKernel, Type_: typeUint, Update: func(c *Config, v string) { c.MotionKernel = parseUint(keyMotionKernel, v, c) }, }, { Name: keyMotionMinArea, Type_: typeFloat, 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: keyMotionPadding, Type_: typeUint, Update: func(c *Config, v string) { c.MotionPadding = parseUint(keyMotionPadding, v, c) }, }, { Name: keyMotionPixels, Type_: typeUint, Update: func(c *Config, v string) { c.MotionPixels = parseUint(keyMotionPixels, v, c) }, }, { Name: keyMotionThreshold, Type_: typeFloat, 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: keyOutput, 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: keyOutputPath, Type_: typeString, Update: func(c *Config, v string) { c.OutputPath = v }, }, { Name: keyOutputs, 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(keyOutputs, defaultOutput) c.Outputs = append(c.Outputs, defaultOutput) } }, }, { Name: keyPSITime, Type_: typeUint, Update: func(c *Config, v string) { c.PSITime = parseUint(keyPSITime, v, c) }, Validate: func(c *Config) { c.PSITime = lessThanOrEqual(keyPSITime, c.PSITime, 0, c, defaultPSITime) }, }, { Name: keyQuantization, Type_: typeUint, Update: func(c *Config, v string) { c.Quantization = parseUint(keyQuantization, v, c) }, }, { Name: keyRBCapacity, Type_: typeUint, Update: func(c *Config, v string) { c.RBCapacity = parseUint(keyRBCapacity, v, c) }, Validate: func(c *Config) { c.RBCapacity = lessThanOrEqual(keyRBCapacity, c.RBCapacity, 0, c, defaultRBCapacity) }, }, { Name: keyRBWriteTimeout, Type_: typeUint, Update: func(c *Config, v string) { c.RBWriteTimeout = parseUint(keyRBWriteTimeout, v, c) }, Validate: func(c *Config) { c.RBWriteTimeout = lessThanOrEqual(keyRBWriteTimeout, c.RBWriteTimeout, 0, c, defaultRBWriteTimeout) }, }, { Name: keyRecPeriod, Type_: typeFloat, Update: func(c *Config, v string) { _v, err := strconv.ParseFloat(v, 64) if err != nil { c.Logger.Log(logger.Warning, fmt.Sprintf("invalid %s param", keyRecPeriod), "value", v) } c.RecPeriod = _v }, }, { Name: keyRotation, Type_: typeUint, Update: func(c *Config, v string) { c.Rotation = parseUint(keyRotation, v, c) }, }, { Name: keyRTMPURL, Type_: typeString, Update: func(c *Config, v string) { c.RTMPURL = v }, }, { Name: keyRTPAddress, Type_: typeString, Update: func(c *Config, v string) { c.RTPAddress = v }, Validate: func(c *Config) { if c.RTPAddress == "" { c.LogInvalidField(keyRTPAddress, defaultRTPAddr) c.RTPAddress = defaultRTPAddr } }, }, { Name: keySampleRate, Type_: typeUint, Update: func(c *Config, v string) { c.SampleRate = parseUint(keySampleRate, v, c) }, }, { Name: keySaturation, 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: keySuppress, Type_: typeBool, Update: func(c *Config, v string) { c.Suppress = parseBool(keySuppress, v, c) c.Logger.(*logger.Logger).SetSuppress(c.Suppress) }, }, { Name: keyVBRBitrate, Type_: typeUint, Update: func(c *Config, v string) { c.VBRBitrate = parseUint(keyVBRBitrate, v, c) }, }, { Name: keyVBRQuality, Type_: "enum:standard,fair,good,great,excellent", Update: func(c *Config, v string) { c.VBRQuality = Quality(parseEnum( keyVBRQuality, v, map[string]uint8{ "standard": uint8(QualityStandard), "fair": uint8(QualityFair), "good": uint8(QualityGood), "great": uint8(QualityGreat), "excellent": uint8(QualityExcellent), }, c, )) }, }, { Name: keyVerticalFlip, Type_: typeBool, Update: func(c *Config, v string) { c.VerticalFlip = parseBool(keyVerticalFlip, v, c) }, }, { Name: keyWidth, Type_: typeUint, Update: func(c *Config, v string) { c.Width = parseUint(keyWidth, v, c) }, }, } 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 }