/* NAME Config.go DESCRIPTION See Readme.md AUTHORS Saxon A. Nelson-Milton <saxon@ausocean.org> 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 revid import ( "errors" "strconv" "bitbucket.org/ausocean/utils/smartlogger" ) // Config provides parameters relevant to a revid instance. A new config must // be passed to the constructor. type Config struct { Input uint8 InputCodec uint8 Output1 uint8 Output2 uint8 RtmpMethod uint8 Packetization uint8 QuantizationMode uint8 LogLevel int8 // FlipHorizonatla and FlipVertical specify // whether video frames should be flipped. FlipHorizontal bool FlipVertical bool FramesPerClip int RtmpUrl string Bitrate string OutputFileName string InputFileName string Height string Width string FrameRate string HttpAddress string Quantization string Timeout string IntraRefreshPeriod string RtpAddress string Logger Logger SendRetry bool } // Enums for config struct const ( NothingDefined = iota Raspivid H264Codec File Http H264 Mjpeg None Mpegts Ffmpeg Flv LibRtmp QuantizationOn QuantizationOff Yes No Rtmp FfmpegRtmp Udp MpegtsRtp Rtp ) // Default config settings const ( defaultInput = Raspivid defaultOutput = Http defaultPacketization = Flv defaultFrameRate = "25" defaultWidth = "1280" defaultHeight = "720" defaultIntraRefreshPeriod = "100" defaultTimeout = "0" defaultQuantization = "40" defaultBitrate = "400000" defaultQuantizationMode = QuantizationOff defaultFramesPerClip = 1 defaultVerticalFlip = No defaultHorizontalFlip = No httpFramesPerClip = 560 defaultInputCodec = H264 defaultVerbosity = No defaultRtpAddr = "localhost:6970" ) // Validate checks for any errors in the config fields and defaults settings // if particular parameters have not been defined. func (c *Config) Validate(r *Revid) error { switch c.LogLevel { case Yes: case No: case NothingDefined: c.LogLevel = defaultVerbosity c.Logger.Log(smartlogger.Warning, pkg+"no LogLevel mode defined, defaulting", "LogLevel", defaultVerbosity) default: return errors.New("bad LogLevel defined in config") } switch c.QuantizationMode { case QuantizationOn: case QuantizationOff: case NothingDefined: c.Logger.Log(smartlogger.Warning, pkg+"no quantization mode defined, defaulting", "quantizationMode", QuantizationOff) c.QuantizationMode = QuantizationOff default: return errors.New("bad QuantizationMode defined in config") } switch c.Input { case Raspivid: case File: case NothingDefined: c.Logger.Log(smartlogger.Warning, pkg+"no input type defined, defaulting", "input", defaultInput) c.Input = defaultInput default: return errors.New("bad input type defined in config") } switch c.InputCodec { case H264: if c.Bitrate != "" && c.Quantization != "" { bitrate, err := strconv.Atoi(c.Bitrate) if err != nil { return errors.New("bitrate not an integer") } quantization, err := strconv.Atoi(c.Quantization) if err != nil { return errors.New("quantization not an integer") } if (bitrate > 0 && quantization > 0) || (bitrate == 0 && quantization == 0) { return errors.New("bad bitrate and quantization combination for H264 input") } } case Mjpeg: if c.Quantization != "" { quantization, err := strconv.Atoi(c.Quantization) if err != nil { return errors.New("quantization not an integer") } if quantization > 0 || c.Bitrate == "" { return errors.New("bad bitrate or quantization for mjpeg input") } } case NothingDefined: c.Logger.Log(smartlogger.Warning, pkg+"no input codec defined, defaulting", "inputCodec", defaultInputCodec) c.InputCodec = defaultInputCodec c.Logger.Log(smartlogger.Warning, pkg+"defaulting quantization", "quantization", defaultQuantization) c.Quantization = defaultQuantization default: return errors.New("bad input codec defined in config") } switch c.Output1 { case File: case Udp: case Rtmp, FfmpegRtmp: if c.RtmpUrl == "" { c.Logger.Log(smartlogger.Info, pkg+"no RTMP URL: falling back to HTTP") c.Output1 = Http break } c.Logger.Log(smartlogger.Info, pkg+"defaulting frames per clip for rtmp out", "framesPerClip", defaultFramesPerClip) c.FramesPerClip = defaultFramesPerClip case NothingDefined: c.Logger.Log(smartlogger.Warning, pkg+"no output defined, defaulting", "output", defaultOutput) c.Output1 = defaultOutput fallthrough case Http, Rtp: c.Logger.Log(smartlogger.Info, pkg+"defaulting frames per clip for http out", "framesPerClip", httpFramesPerClip) c.FramesPerClip = httpFramesPerClip default: return errors.New("bad output type defined in config") } switch c.Output2 { case File: case Rtp: case Udp: case Rtmp, FfmpegRtmp: if c.RtmpUrl == "" { c.Logger.Log(smartlogger.Info, pkg+"no RTMP URL: falling back to HTTP") c.Output2 = Http break } case NothingDefined: case Http: default: return errors.New("bad output2 type defined in config") } if c.FramesPerClip < 1 { c.Logger.Log(smartlogger.Warning, pkg+"no FramesPerClip defined, defaulting", "framesPerClip", defaultFramesPerClip) c.FramesPerClip = defaultFramesPerClip } if c.Width == "" { c.Logger.Log(smartlogger.Warning, pkg+"no width defined, defaulting", "width", defaultWidth) c.Width = defaultWidth } else { if integer, err := strconv.Atoi(c.Width); integer < 0 || err != nil { return errors.New("width not unsigned integer") } } if c.Height == "" { c.Logger.Log(smartlogger.Warning, pkg+"no height defined, defaulting", "height", defaultHeight) c.Height = defaultHeight } else { if integer, err := strconv.Atoi(c.Height); integer < 0 || err != nil { return errors.New("height not unsigned integer") } } if c.FrameRate == "" { c.Logger.Log(smartlogger.Warning, pkg+"no frame rate defined, defaulting", "fps", defaultFrameRate) c.FrameRate = defaultFrameRate } else { if integer, err := strconv.Atoi(c.FrameRate); integer < 0 || err != nil { return errors.New("frame rate not unsigned integer") } } if c.Bitrate == "" { c.Logger.Log(smartlogger.Warning, pkg+"no bitrate defined, defaulting", "bitrate", defaultBitrate) c.Bitrate = defaultBitrate } else { if integer, err := strconv.Atoi(c.Bitrate); integer < 0 || err != nil { return errors.New("bitrate not unsigned integer") } } if c.Timeout == "" { c.Logger.Log(smartlogger.Warning, pkg+"no timeout defined, defaulting", "timeout", defaultTimeout) c.Timeout = defaultTimeout } else { if integer, err := strconv.Atoi(c.Timeout); integer < 0 || err != nil { return errors.New("timeout not unsigned integer") } } if c.IntraRefreshPeriod == "" { c.Logger.Log(smartlogger.Warning, pkg+"no intra refresh defined, defaulting", "intraRefresh", defaultIntraRefreshPeriod) c.IntraRefreshPeriod = defaultIntraRefreshPeriod } else { if integer, err := strconv.Atoi(c.IntraRefreshPeriod); integer < 0 || err != nil { return errors.New("intra refresh not unsigned integer") } } if c.Quantization == "" { c.Logger.Log(smartlogger.Warning, pkg+"no quantization defined, defaulting", "quantization", defaultQuantization) c.Quantization = defaultQuantization } else { if integer, err := strconv.Atoi(c.Quantization); integer < 0 || integer > 51 || err != nil { return errors.New("quantisation not unsigned integer or is over threshold") } } if c.RtpAddress == "" { c.RtpAddress = defaultRtpAddr } return nil }