/* 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" ) // 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 }