/* NAME Config.go AUTHORS Saxon A. Nelson-Milton Trek Hopton LICENSE Config.go is Copyright (C) 2017-2018 the Australian Ocean Lab (AusOcean) It is free software: you can redistribute it and/or modify them under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with revid in gpl.txt. If not, see http://www.gnu.org/licenses. */ package config import ( "errors" "time" "bitbucket.org/ausocean/av/codec/codecutil" "bitbucket.org/ausocean/utils/logger" ) const pkg = "config: " type Logger interface { SetLevel(int8) Log(level int8, message string, params ...interface{}) } // Enums to define inputs, outputs and codecs. const ( // Indicates no option has been set. NothingDefined = iota // Input/Output. InputFile InputRaspivid InputV4L InputRTSP InputAudio // Outputs. OutputAudio OutputRTMP OutputRTP OutputHTTP OutputMPEGTS OutputFile // Codecs. H264 H265 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. defaultRBMaxElements = 10000 defaultRBCapacity = 200000000 // bytes (200MB) defaultRBWriteTimeout = 5 // Motion filter parameter defaults. defaultMinFPS = 1.0 ) // Quality represents video quality. type Quality int // The different video qualities that can be used for variable bitrate when // using the GeoVision camera. const ( QualityStandard Quality = iota QualityFair QualityGood QualityGreat QualityExcellent ) // The different media filters. const ( FilterNoOp = iota FilterMOG FilterVariableFPS FilterKNN FilterDiff FilterBasic ) // Config provides parameters relevant to a revid instance. A new config must // be passed to the constructor. Default values for these fields are defined // as consts above. type Config struct { // LogLevel is the revid logging verbosity level. // Valid values are defined by enums from the logger package: logger.Debug, // logger.Info, logger.Warning logger.Error, logger.Fatal. LogLevel int8 // Input defines the input data source. // // Valid values are defined by enums: // InputRaspivid: // Read data from a Raspberry Pi Camera. // InputV4l: // Read from webcam. // InputFile: // Location must be specified in InputPath field. // InputRTSP: // CameraIP should also be defined. Input uint8 // InputCodec defines the input codec we wish to use, and therefore defines the // lexer for use in the pipeline. This defaults to H264, but H265 is also a // valid option if we expect this from the input. InputCodec uint8 // Outputs define the outputs we wish to output data too. // // Valid outputs are defined by enums: // OutputFile: // Location must be defined by the OutputPath field. MPEG-TS packetization // is used. // OutputHTTP: // Destination is defined by the sh field located in /etc/netsender.conf. // MPEGT-TS packetization is used. // OutputRTMP: // Destination URL must be defined in the RtmpUrl field. FLV packetization // is used. // OutputRTP: // Destination is defined by RtpAddr field, otherwise it will default to // localhost:6970. MPEGT-TS packetization is used. Outputs []uint8 // RTMPURL specifies the Rtmp output destination URL. This must be defined if // RTMP is to be used as an output. RTMPURL string // CameraIP is the IP address of the camera in the case of the input camera // being an IP camera. CameraIP string // OutputPath defines the output destination for File output. This must be // defined if File output is to be used. OutputPath string // InputPath defines the input file location for File Input. This must be // defined if File input is to be used. InputPath string // FrameRate defines the input frame rate if configurable by the chosen input. // Raspivid input supports custom framerate. FrameRate uint // WriteRate is how many times a second revid encoders will be written to. WriteRate float64 // HTTPAddress defines a custom HTTP destination if we do not wish to use that // defined in /etc/netsender.conf. HTTPAddress string // CBR indicates whether we wish to use constant or variable bitrate. If CBR // is true then we will use constant bitrate, and variable bitrate otherwise. // In the case of the Pi camera, variable bitrate quality is controlled by // the Quantization parameter below. In the case of the GeoVision camera, // variable bitrate quality is controlled by firstly the VBRQuality parameter // and second the VBRBitrate parameter. CBR bool // Quantization defines the quantization level, which will determine variable // bitrate quality in the case of input from the Pi Camera. Quantization uint // VBRQuality describes the general quality of video from the GeoVision camera // under variable bitrate. VBRQuality can be one 5 consts defined: // qualityStandard, qualityFair, qualityGood, qualityGreat and qualityExcellent. VBRQuality Quality // VBRBitrate describes maximal bitrate for the GeoVision camera when under // variable bitrate. VBRBitrate int // This is the channel we're using for the GeoVision camera. CameraChan int // MinFrames defines the frequency of key NAL units SPS, PPS and IDR in // number of NAL units. This will also determine the frequency of PSI if the // output container is MPEG-TS. If ClipDuration is less than MinFrames, // ClipDuration will default to MinFrames. MinFrames uint // ClipDuration is the duration of MTS data that is sent using HTTP or RTP // output. This defaults to 0, therefore MinFrames will determine the length of // clips by default. ClipDuration time.Duration // Logger holds an implementation of the Logger interface as defined in revid.go. // This must be set for revid to work correctly. Logger Logger // Brightness and saturation define the brightness and saturation levels for // Raspivid input. Brightness uint Saturation int // Exposure defines the exposure mode used by the Raspivid input. Valid modes // are defined in the exported []string ExposureModes defined at the start // of the file. Exposure string // AutoWhiteBalance defines the auto white balance mode used by Raspivid input. // Valid modes are defined in the exported []string AutoWhiteBalanceModes // defined at the start of the file. AutoWhiteBalance string // Audio SampleRate int // Samples a second (Hz). RecPeriod float64 // How many seconds to record at a time. Channels int // Number of audio channels, 1 for mono, 2 for stereo. BitDepth int // Sample bit depth. RTPAddress string // RTPAddress defines the RTP output destination. BurstPeriod uint // BurstPeriod defines the revid burst period in seconds. Rotation uint // Rotation defines the video rotation angle in degrees Raspivid input. Height uint // Height defines the input video height Raspivid input. Width uint // Width defines the input video width Raspivid input. Bitrate uint // Bitrate specifies the bitrate for constant bitrate in kbps. HorizontalFlip bool // HorizontalFlip flips video horizontally for Raspivid input. VerticalFlip bool // VerticalFlip flips video vertically for Raspivid input. Filters []int // Defines the methods of filtering to be used in between lexing and encoding. PSITime int // Sets the time between a packet being sent. // Ring buffer parameters. RBMaxElements int // The maximum possible number of elements in ring buffer. RBCapacity int // The total number of bytes available for the ring buffer. RBWriteTimeout int // The ringbuffer write timeout in seconds. // Motion filter parameters. // 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. MotionInterval int // Sets the number of frames that are held before the filter is used (on the nth frame). MotionDownscaling int // Downscaling factor of frames used for motion detection. MotionMinArea float64 // Used to ignore small areas of motion detection (KNN & MOG only). MotionThreshold float64 // Intensity value that is considered motion. MotionHistory uint // Length of filter's history (KNN & MOG only). MotionKernel uint // Size of kernel used for filling holes and removing noise (KNN only). MotionPixels int // Number of pixels with motion that is needed for a whole frame to be considered as moving (Basic only). // If true will restart reading of input after an io.EOF. Loop bool // Defines the rate at which frames from a file source are processed. FileFPS int } // 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", "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", "InputCodec": "enum:H264,MJPEG", "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", "MotionPixels": "int", "MotionThreshold": "float", "Output": "enum:File,Http,Rtmp,Rtp", "OutputPath": "string", "Outputs": "enums:File,Http,Rtmp,Rtp", "Quantization": "uint", "RBCapacity": "uint", "RBMaxElements": "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 // if particular parameters have not been defined. func (c *Config) Validate() error { switch c.LogLevel { case logger.Debug, logger.Info, logger.Warning, logger.Error, logger.Fatal: default: 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, pkg+"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.RBMaxElements <= 0 { c.LogInvalidField("RBMaxElements", defaultRBMaxElements) c.RBMaxElements = defaultRBMaxElements } if c.RBCapacity <= 0 { c.LogInvalidField("RBCapacity", defaultRBCapacity) c.RBCapacity = defaultRBCapacity } if c.RBWriteTimeout <= 0 { c.LogInvalidField("RBWriteTimeout", defaultRBWriteTimeout) c.RBWriteTimeout = defaultRBWriteTimeout } 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 } func (c *Config) LogInvalidField(name string, def interface{}) { c.Logger.Log(logger.Info, pkg+name+" bad or unset, defaulting", name, def) } // stringInSlice returns true if want is in slice. func stringInSlice(want string, slice []string) bool { for _, s := range slice { if s == want { return true } } return false }