av/revid/config.go

355 lines
9.8 KiB
Go
Raw Normal View History

/*
NAME
Config.go
DESCRIPTION
See Readme.md
AUTHORS
2018-06-07 14:50:57 +03:00
Saxon A. Nelson-Milton <saxon@ausocean.org>
LICENSE
2018-06-08 03:02:13 +03:00
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
2018-06-07 14:50:57 +03:00
along with revid in gpl.txt. If not, see http://www.gnu.org/licenses.
*/
2018-02-10 10:08:14 +03:00
package revid
import (
"errors"
"bitbucket.org/ausocean/utils/logger"
)
2019-03-15 10:28:24 +03:00
// Possible modes for raspivid --exposure parameter.
var ExposureModes = [...]string{
"auto",
"night",
"nightpreview",
"backlight",
"spotlight",
"sports",
"snow",
"beach",
"verylong",
"fixedfps",
"antishake",
"fireworks",
}
2019-03-15 10:28:24 +03:00
// Possible modes for raspivid --awb parameter.
var AutoWhiteBalanceModes = [...]string{
"off",
"auto",
"sun",
"cloud",
"shade",
"tungsten",
"fluorescent",
"incandescent",
"flash",
"horizon",
2018-02-10 10:08:14 +03:00
}
// Enums to define inputs, outputs and codecs.
2018-02-10 10:08:14 +03:00
const (
// Indicates no option has been set.
2018-06-07 14:50:57 +03:00
NothingDefined = iota
// Input/Output.
File
// Inputs.
2018-06-07 14:50:57 +03:00
Raspivid
V4L
// Outputs.
RTMP
RTP
HTTP
MPEGTS
// Codecs.
2018-06-07 14:50:57 +03:00
H264
MJPEG
2018-02-10 10:08:14 +03:00
)
// Default config settings
const (
2018-05-06 11:38:45 +03:00
defaultInput = Raspivid
defaultOutput = HTTP
defaultFrameRate = 25
defaultWidth = 1280
defaultHeight = 720
defaultIntraRefreshPeriod = 100
defaultTimeout = 0
defaultQuantization = 40
defaultBitrate = 400000
2018-06-20 08:08:34 +03:00
defaultFramesPerClip = 1
httpFramesPerClip = 560
defaultInputCodec = H264
defaultVerbosity = logger.Error
defaultRtpAddr = "localhost:6970"
defaultBurstPeriod = 10 // Seconds
defaultRotation = 0 // Degrees
defaultBrightness = 50
defaultExposure = "auto"
defaultAutoWhiteBalance = "auto"
2018-02-10 10:08:14 +03:00
)
// 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:
// Raspivid:
// Read data from a Raspberry Pi Camera.
// V4l:
// Read from webcam.
// File:
// Location must be specified in InputPath field.
Input uint8
// InputCodec defines the input codec we wish to use, and therefore define the
// lexer for use in the pipeline. In most cases this defaults according to a
// particular input. Both Raspivid and V4l use H264, but File input may use
// H264 or MJPEG.
InputCodec uint8
// Outputs define the outputs we wish to output data too.
//
// Valid outputs are defined by enums:
// File:
// Location must be defined by the OutputPath field. MPEG-TS packetization
// is used.
// HTTP:
// Destination is defined by the sh field located in /etc/netsender.conf.
// MPEGT-TS packetization is used.
// RTMP:
// Destination URL must be defined in the RtmpUrl field. FLV packetization
// is used.
// RTP:
// Destination is defined by RtpAddr field, otherwise it will default to
// localhost:6970. MPEGT-TS packetization is used.
Outputs []uint8
// Quantize specifies whether the input to revid will have constant or variable
// bitrate.
Quantize bool
// FlipHorizonatla and FlipVertical specify whether video frames should be flipped.
FlipHorizontal bool
FlipVertical bool
FramesPerClip uint
// RtmpUrl specifies the Rtmp output destination URL. This must be defined if
// RTMP is to be used as an output.
RTMPURL string
Bitrate uint
OutputPath string
InputPath string
Height uint
Width uint
FrameRate uint
HTTPAddress string
Quantization uint
IntraRefreshPeriod uint
RTPAddress string
Logger Logger
SendRetry bool
BurstPeriod uint
Rotation uint
Brightness uint
Saturation int
Exposure string
AutoWhiteBalance string
}
2018-03-17 16:29:42 +03:00
// Validate checks for any errors in the config fields and defaults settings
2018-02-10 10:08:14 +03:00
// if particular parameters have not been defined.
2018-06-09 05:01:21 +03:00
func (c *Config) Validate(r *Revid) error {
switch c.LogLevel {
case logger.Debug:
case logger.Info:
case logger.Warning:
case logger.Error:
case logger.Fatal:
default:
c.LogLevel = defaultVerbosity
c.Logger.Log(logger.Info, pkg+"bad LogLevel mode defined, defaulting", "LogLevel", defaultVerbosity)
2018-03-17 16:29:42 +03:00
}
2018-06-09 05:01:21 +03:00
switch c.Input {
case Raspivid, V4L, File:
2018-02-10 10:08:14 +03:00
case NothingDefined:
c.Logger.Log(logger.Info, pkg+"no input type defined, defaulting", "input", defaultInput)
2018-06-09 05:01:21 +03:00
c.Input = defaultInput
2018-02-10 10:08:14 +03:00
default:
return errors.New("bad input type defined in config")
2018-02-10 10:08:14 +03:00
}
2018-06-09 05:01:21 +03:00
switch c.InputCodec {
2018-02-10 10:08:14 +03:00
case H264:
// FIXME(kortschak): This is not really what we want.
// Configuration really needs to be rethought here.
if c.Quantize && c.Quantization == 0 {
c.Quantization = defaultQuantization
2018-02-10 10:08:14 +03:00
}
if (c.Bitrate > 0 && c.Quantize) || (c.Bitrate == 0 && !c.Quantize) {
return errors.New("bad bitrate and quantization combination for H264 input")
}
case MJPEG:
if c.Quantization > 0 || c.Bitrate == 0 {
return errors.New("bad bitrate or quantization for mjpeg input")
2018-02-10 10:08:14 +03:00
}
2018-02-10 10:08:14 +03:00
case NothingDefined:
c.Logger.Log(logger.Info, pkg+"no input codec defined, defaulting", "inputCodec", defaultInputCodec)
c.InputCodec = defaultInputCodec
c.Logger.Log(logger.Info, pkg+"defaulting quantization", "quantization", defaultQuantization)
2018-06-09 05:01:21 +03:00
c.Quantization = defaultQuantization
2018-02-10 10:08:14 +03:00
default:
return errors.New("bad input codec defined in config")
2018-02-10 10:08:14 +03:00
}
if c.Outputs == nil {
c.Logger.Log(logger.Info, pkg+"no output defined, defaulting", "output", defaultOutput)
c.Outputs = append(c.Outputs, defaultOutput)
} else {
for i, o := range c.Outputs {
switch o {
case File:
case RTMP:
if c.RTMPURL == "" {
c.Logger.Log(logger.Info, pkg+"no RTMP URL: falling back to HTTP")
c.Outputs[i] = HTTP
// FIXME(kortschak): Does this want the same line as below?
// c.FramesPerClip = httpFramesPerClip
break
}
c.Logger.Log(logger.Info, pkg+"defaulting frames per clip for rtmp out", "framesPerClip", defaultFramesPerClip)
c.FramesPerClip = defaultFramesPerClip
c.SendRetry = true
case HTTP, RTP:
c.Logger.Log(logger.Info, pkg+"defaulting frames per clip for http out", "framesPerClip", httpFramesPerClip)
c.FramesPerClip = httpFramesPerClip
default:
return errors.New("bad output type defined in config")
}
2018-06-08 03:02:13 +03:00
}
}
if c.BurstPeriod == 0 {
c.Logger.Log(logger.Info, pkg+"no burst period defined, defaulting", "burstPeriod", defaultBurstPeriod)
c.BurstPeriod = defaultBurstPeriod
}
2018-06-20 08:08:34 +03:00
if c.FramesPerClip < 1 {
c.Logger.Log(logger.Info, pkg+"no FramesPerClip defined, defaulting", "framesPerClip", defaultFramesPerClip)
2018-06-13 08:15:03 +03:00
c.FramesPerClip = defaultFramesPerClip
2018-02-10 10:08:14 +03:00
}
if c.Rotation > 359 {
c.Logger.Log(logger.Warning, pkg+"bad rotate angle, defaulting", "angle", defaultRotation)
c.Rotation = defaultRotation
}
if c.Width == 0 {
c.Logger.Log(logger.Info, pkg+"no width defined, defaulting", "width", defaultWidth)
2018-06-09 05:01:21 +03:00
c.Width = defaultWidth
2018-02-10 10:08:14 +03:00
}
if c.Height == 0 {
c.Logger.Log(logger.Info, pkg+"no height defined, defaulting", "height", defaultHeight)
2018-06-09 05:01:21 +03:00
c.Height = defaultHeight
2018-02-10 10:08:14 +03:00
}
if c.FrameRate == 0 {
c.Logger.Log(logger.Info, pkg+"no frame rate defined, defaulting", "fps", defaultFrameRate)
2018-06-09 05:01:21 +03:00
c.FrameRate = defaultFrameRate
2018-02-10 10:08:14 +03:00
}
if c.Bitrate == 0 {
c.Logger.Log(logger.Info, pkg+"no bitrate defined, defaulting", "bitrate", defaultBitrate)
2018-06-09 05:01:21 +03:00
c.Bitrate = defaultBitrate
2018-02-10 10:08:14 +03:00
}
if c.IntraRefreshPeriod == 0 {
c.Logger.Log(logger.Info, pkg+"no intra refresh defined, defaulting", "intraRefresh", defaultIntraRefreshPeriod)
2018-06-09 05:01:21 +03:00
c.IntraRefreshPeriod = defaultIntraRefreshPeriod
2018-02-10 10:08:14 +03:00
}
if c.Quantization == 0 {
c.Logger.Log(logger.Info, pkg+"no quantization defined, defaulting", "quantization", defaultQuantization)
2018-06-09 05:01:21 +03:00
c.Quantization = defaultQuantization
} else if c.Quantization > 51 {
return errors.New("quantisation is over threshold")
2018-02-10 10:08:14 +03:00
}
if c.RTPAddress == "" {
c.RTPAddress = defaultRtpAddr
}
switch {
case c.Brightness == 0:
c.Logger.Log(logger.Info, pkg+"brightness undefined, defaulting", "brightness", defaultBrightness)
c.Brightness = defaultBrightness
case c.Brightness < 0 || c.Brightness > 100:
return errors.New(pkg + "bad brightness defined in config")
}
if c.Saturation < -100 || c.Saturation > 100 {
return errors.New(pkg + "bad saturation setting in config")
}
switch {
case c.Exposure == "":
c.Logger.Log(logger.Info, pkg+"exposure undefined, defaulting", "exposure", defaultExposure)
c.Exposure = defaultExposure
case !stringInSlice(c.Exposure, ExposureModes[:]):
return errors.New(pkg + "bad exposure setting in config")
}
switch {
case c.AutoWhiteBalance == "":
c.Logger.Log(logger.Info, pkg+"auto white balance undefined, defaulting", "autoWhiteBalance", defaultAutoWhiteBalance)
c.AutoWhiteBalance = defaultAutoWhiteBalance
case !stringInSlice(c.AutoWhiteBalance, AutoWhiteBalanceModes[:]):
return errors.New(pkg + "bad auto white balance setting in config")
}
return nil
2018-02-10 10:08:14 +03:00
}
2019-03-15 10:28:24 +03:00
// 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
}