av/revid/config.go

372 lines
10 KiB
Go
Raw Normal View History

/*
NAME
Config.go
AUTHORS
2018-06-07 14:50:57 +03:00
Saxon A. Nelson-Milton <saxon@ausocean.org>
Trek Hopton <trek@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/av/codec/codecutil"
"bitbucket.org/ausocean/utils/logger"
)
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.
type Config struct {
2019-04-23 12:35:47 +03:00
Logger Logger
LogLevel int8
2019-04-23 12:35:47 +03:00
// IO
Input uint8
InputCodec uint8
Outputs []uint8
RtmpMethod uint8
Packetization uint8
Quantize bool // Determines whether input to revid will have constant or variable bitrate.
2018-02-10 10:08:14 +03:00
RtmpUrl string
2019-06-03 11:20:48 +03:00
RTSPURL string
Bitrate uint
OutputPath string
InputPath string
2018-02-10 10:08:14 +03:00
HttpAddress string
Quantization uint
IntraRefreshPeriod uint
RtpAddress string
2018-12-14 13:20:26 +03:00
SendRetry bool
WriteRate float64 // How many times a second revid encoders will be written to.
2019-04-23 12:35:47 +03:00
// Video
Height uint
Width uint
FrameRate uint
FramesPerClip uint
BurstPeriod uint
// Transformation
FlipHorizontal bool
FlipVertical bool
Rotation uint
// Color correction
Brightness uint
Saturation int
Exposure string
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.
}
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 for config struct
const (
2018-06-07 14:50:57 +03:00
NothingDefined = iota
Raspivid
V4L
Audio
2018-06-07 14:50:57 +03:00
File
Http
None
Mpegts
Ffmpeg
Flv
LibRtmp
QuantizationOn
QuantizationOff
Yes
No
Rtmp
2018-06-07 14:50:57 +03:00
FfmpegRtmp
Udp
MpegtsRtp
Rtp
RTSP
2018-02-10 10:08:14 +03:00
)
// Default config settings
const (
2018-05-06 11:38:45 +03:00
defaultInput = Raspivid
2018-06-08 03:02:13 +03:00
defaultOutput = Http
2018-05-06 11:38:45 +03:00
defaultPacketization = Flv
defaultFrameRate = 25
2019-04-23 12:35:47 +03:00
defaultWriteRate = 25
defaultWidth = 1280
defaultHeight = 720
defaultIntraRefreshPeriod = 100
defaultTimeout = 0
defaultQuantization = 40
defaultBitrate = 400000
defaultQuantizationMode = QuantizationOff
2018-06-20 08:08:34 +03:00
defaultFramesPerClip = 1
httpFramesPerClip = 560
defaultInputCodec = codecutil.H264
defaultVerbosity = logger.Error
defaultRtpAddr = "localhost:6970"
defaultBurstPeriod = 10 // Seconds
defaultRotation = 0 // Degrees
defaultBrightness = 50
defaultExposure = "auto"
defaultAutoWhiteBalance = "auto"
defaultAudioInputCodec = codecutil.ADPCM
defaultSampleRate = 48000
defaultBitDepth = 16
defaultChannels = 1
defaultRecPeriod = 1.0
2018-02-10 10:08:14 +03:00
)
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:
2018-03-17 16:29:42 +03:00
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 {
2019-06-03 11:20:48 +03:00
case Raspivid, V4L, File, Audio, RTSP:
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 {
case codecutil.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 codecutil.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
}
case codecutil.PCM, codecutil.ADPCM:
default:
switch c.Input {
case Audio:
c.Logger.Log(logger.Info, pkg+"input is audio but no codec defined, defaulting", "inputCodec", defaultAudioInputCodec)
c.InputCodec = defaultAudioInputCodec
default:
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)
c.Quantization = defaultQuantization
}
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)
c.Packetization = defaultPacketization
} else {
for i, o := range c.Outputs {
switch o {
case File:
case Udp:
case Rtmp, FfmpegRtmp:
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.Packetization = Flv
c.SendRetry = true
case Http, Rtp:
c.Logger.Log(logger.Info, pkg+"defaulting frames per clip for http out", "framesPerClip", httpFramesPerClip)
c.FramesPerClip = httpFramesPerClip
c.Packetization = Mpegts
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.SampleRate == 0 {
c.Logger.Log(logger.Info, pkg+"no sample rate defined, defaulting", "sampleRate", defaultSampleRate)
c.SampleRate = defaultSampleRate
}
if c.Channels == 0 {
c.Logger.Log(logger.Info, pkg+"no number of channels defined, defaulting", "Channels", defaultChannels)
c.Channels = defaultChannels
}
if c.BitDepth == 0 {
c.Logger.Log(logger.Info, pkg+"no bit depth defined, defaulting", "BitDepth", defaultBitDepth)
c.BitDepth = defaultBitDepth
}
if c.RecPeriod == 0 {
c.Logger.Log(logger.Info, pkg+"no record period defined, defaulting", "recPeriod", defaultRecPeriod)
c.RecPeriod = defaultRecPeriod
}
if c.WriteRate == 0 {
c.Logger.Log(logger.Info, pkg+"no write rate defined, defaulting", "writeRate", defaultWriteRate)
c.WriteRate = defaultWriteRate
}
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
}