cmd/revid-cli,revid: reduce stringly typing in config/flags

This commit is contained in:
Dan Kortschak 2018-12-10 09:39:20 +10:30
parent 5a181f3576
commit 1e3b4b1ab8
4 changed files with 129 additions and 230 deletions

View File

@ -33,6 +33,7 @@ import (
"os"
"runtime/pprof"
"strconv"
"strings"
"time"
"bitbucket.org/ausocean/av/revid"
@ -101,22 +102,21 @@ func handleFlags() revid.Config {
output2Ptr = flag.String("Output2", "", "The second output type: Http, Rtmp, File, Udp, Rtp")
rtmpMethodPtr = flag.String("RtmpMethod", "", "The method used to send over rtmp: Ffmpeg, Librtmp")
packetizationPtr = flag.String("Packetization", "", "The method of data packetisation: Flv, Mpegts, None")
quantizationModePtr = flag.String("QuantizationMode", "", "Whether quantization if on or off (variable bitrate): On, Off")
quantizePtr = flag.Bool("Quantize", false, "Quantize input (non-variable bitrate)")
verbosityPtr = flag.String("Verbosity", "", "Verbosity: Info, Warning, Error, Fatal")
framesPerClipPtr = flag.String("FramesPerClip", "", "Number of frames per clip sent")
framesPerClipPtr = flag.Uint("FramesPerClip", 0, "Number of frames per clip sent")
rtmpUrlPtr = flag.String("RtmpUrl", "", "Url of rtmp endpoint")
bitratePtr = flag.String("Bitrate", "", "Bitrate of recorded video")
bitratePtr = flag.Uint("Bitrate", 0, "Bitrate of recorded video")
outputFileNamePtr = flag.String("OutputFileName", "", "The directory of the output file")
inputFileNamePtr = flag.String("InputFileName", "", "The directory of the input file")
heightPtr = flag.String("Height", "", "Height in pixels")
widthPtr = flag.String("Width", "", "Width in pixels")
frameRatePtr = flag.String("FrameRate", "", "Frame rate of captured video")
heightPtr = flag.Uint("Height", 0, "Height in pixels")
widthPtr = flag.Uint("Width", 0, "Width in pixels")
frameRatePtr = flag.Uint("FrameRate", 0, "Frame rate of captured video")
httpAddressPtr = flag.String("HttpAddress", "", "Destination address of http posts")
quantizationPtr = flag.String("Quantization", "", "Desired quantization value: 0-40")
timeoutPtr = flag.String("Timeout", "", "Http timeout in seconds")
intraRefreshPeriodPtr = flag.String("IntraRefreshPeriod", "", "The IntraRefreshPeriod i.e. how many keyframes we send")
verticalFlipPtr = flag.String("VerticalFlip", "", "Flip video vertically: Yes, No")
horizontalFlipPtr = flag.String("HorizontalFlip", "", "Flip video horizontally: Yes, No")
quantizationPtr = flag.Uint("Quantization", 0, "Desired quantization value: 0-40")
intraRefreshPeriodPtr = flag.Uint("IntraRefreshPeriod", 0, "The IntraRefreshPeriod i.e. how many keyframes we send")
verticalFlipPtr = flag.Bool("VerticalFlip", false, "Flip video vertically: Yes, No")
horizontalFlipPtr = flag.Bool("HorizontalFlip", false, "Flip video horizontally: Yes, No")
logPathPtr = flag.String("LogPath", defaultLogPath, "Path for logging files (default is /var/log/netsender/)")
rtpAddrPtr = flag.String("RtpAddr", "", "Rtp destination address: <IP>:<port> (port is generally 6970-6999)")
)
@ -218,16 +218,6 @@ func handleFlags() revid.Config {
logger.Log(smartlogger.Error, pkg+"bad packetization argument")
}
switch *quantizationModePtr {
case "QuantizationOn":
cfg.QuantizationMode = revid.QuantizationOn
case "QuantizationOff":
cfg.QuantizationMode = revid.QuantizationOff
case "":
default:
logger.Log(smartlogger.Error, pkg+"bad quantization mode argument")
}
switch *verbosityPtr {
case "No":
cfg.LogLevel = smartlogger.Fatal
@ -239,32 +229,10 @@ func handleFlags() revid.Config {
logger.Log(smartlogger.Error, pkg+"bad verbosity argument")
}
switch *horizontalFlipPtr {
case "No":
cfg.FlipHorizontal = false
case "Yes":
cfg.FlipHorizontal = true
case "":
cfg.FlipHorizontal = false
default:
logger.Log(smartlogger.Error, pkg+"bad horizontal flip option")
}
switch *verticalFlipPtr {
case "No":
cfg.FlipVertical = false
case "Yes":
cfg.FlipVertical = true
case "":
cfg.FlipVertical = false
default:
logger.Log(smartlogger.Error, pkg+"bad vertical flip option")
}
fpc, err := strconv.Atoi(*framesPerClipPtr)
if err == nil && fpc > 0 {
cfg.FramesPerClip = fpc
}
cfg.Quantize = *quantizePtr
cfg.FlipHorizontal = *horizontalFlipPtr
cfg.FlipVertical = *verticalFlipPtr
cfg.FramesPerClip = *framesPerClipPtr
cfg.RtmpUrl = *rtmpUrlPtr
cfg.Bitrate = *bitratePtr
cfg.OutputFileName = *outputFileNamePtr
@ -274,7 +242,6 @@ func handleFlags() revid.Config {
cfg.FrameRate = *frameRatePtr
cfg.HttpAddress = *httpAddressPtr
cfg.Quantization = *quantizationPtr
cfg.Timeout = *timeoutPtr
cfg.IntraRefreshPeriod = *intraRefreshPeriodPtr
cfg.RtpAddress = *rtpAddrPtr
@ -406,79 +373,76 @@ func updateRevid(ns *netsender.Sender, rv *revid.Revid, cfg revid.Config, vars m
continue
}
case "FramesPerClip":
fpc, err := strconv.Atoi(value)
if fpc > 0 && err == nil {
cfg.FramesPerClip = fpc
} else {
logger.Log(smartlogger.Warning, pkg+"invalid FramesPerClip param", "value", value)
f, err := strconv.ParseUint(value, 10, 0)
if err != nil {
logger.Log(smartlogger.Warning, pkg+"invalid framesperclip param", "value", value)
break
}
cfg.FramesPerClip = uint(f)
case "RtmpUrl":
cfg.RtmpUrl = value
case "Bitrate":
asInt, err := strconv.Atoi(value)
if asInt > 0 && err == nil {
cfg.Bitrate = value
} else {
logger.Log(smartlogger.Warning, pkg+"invalid Bitrate param", "value", value)
r, err := strconv.ParseUint(value, 10, 0)
if err != nil {
logger.Log(smartlogger.Warning, pkg+"invalid framerate param", "value", value)
break
}
cfg.Bitrate = uint(r)
case "OutputFileName":
cfg.OutputFileName = value
case "InputFileName":
cfg.InputFileName = value
case "Height":
asInt, err := strconv.Atoi(value)
if asInt > 0 && err == nil {
cfg.Height = value
} else {
logger.Log(smartlogger.Warning, pkg+"invalid Height param", "value", value)
h, err := strconv.ParseUint(value, 10, 0)
if err != nil {
logger.Log(smartlogger.Warning, pkg+"invalid height param", "value", value)
break
}
cfg.Height = uint(h)
case "Width":
asInt, err := strconv.Atoi(value)
if asInt > 0 && err == nil {
cfg.Width = value
} else {
logger.Log(smartlogger.Warning, pkg+"invalid Width param", "value", value)
w, err := strconv.ParseUint(value, 10, 0)
if err != nil {
logger.Log(smartlogger.Warning, pkg+"invalid width param", "value", value)
break
}
cfg.Width = uint(w)
case "FrameRate":
asInt, err := strconv.Atoi(value)
if asInt > 0 && err == nil {
cfg.FrameRate = value
} else {
logger.Log(smartlogger.Warning, pkg+"invalid FrameRate param", "value", value)
r, err := strconv.ParseUint(value, 10, 0)
if err != nil {
logger.Log(smartlogger.Warning, pkg+"invalid framerate param", "value", value)
break
}
cfg.FrameRate = uint(r)
case "HttpAddress":
cfg.HttpAddress = value
case "Quantization":
asInt, err := strconv.Atoi(value)
if asInt > 0 && err == nil {
cfg.Quantization = value
} else {
logger.Log(smartlogger.Warning, pkg+"invalid Quantization param", "value", value)
}
case "Timeout":
asInt, err := strconv.Atoi(value)
if asInt > 0 && err == nil {
cfg.Timeout = value
q, err := strconv.ParseUint(value, 10, 0)
if err != nil {
logger.Log(smartlogger.Warning, pkg+"invalid quantization param", "value", value)
break
}
cfg.Quantization = uint(q)
case "IntraRefreshPeriod":
asInt, err := strconv.Atoi(value)
if asInt > 0 && err == nil {
cfg.IntraRefreshPeriod = value
p, err := strconv.ParseUint(value, 10, 0)
if err != nil {
logger.Log(smartlogger.Warning, pkg+"invalid intrarefreshperiod param", "value", value)
break
}
cfg.IntraRefreshPeriod = uint(p)
case "HorizontalFlip":
switch value {
case "Yes":
switch strings.ToLower(value) {
case "true":
cfg.FlipHorizontal = true
case "No":
case "false":
cfg.FlipHorizontal = false
default:
logger.Log(smartlogger.Warning, pkg+"invalid HorizontalFlip param", "value", value)
}
case "VerticalFlip":
switch value {
case "Yes":
switch strings.ToLower(value) {
case "true":
cfg.FlipVertical = true
case "No":
case "false":
cfg.FlipVertical = false
default:
logger.Log(smartlogger.Warning, pkg+"invalid VerticalFlip param", "value", value)

View File

@ -29,7 +29,6 @@ package revid
import (
"errors"
"strconv"
"bitbucket.org/ausocean/utils/smartlogger"
)
@ -37,32 +36,36 @@ import (
// 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
LogLevel int8
Input uint8
InputCodec uint8
Output1 uint8
Output2 uint8
RtmpMethod uint8
Packetization 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 int
FramesPerClip uint
RtmpUrl string
Bitrate string
Bitrate uint
OutputFileName string
InputFileName string
Height string
Width string
FrameRate string
Height uint
Width uint
FrameRate uint
HttpAddress string
Quantization string
Timeout string
IntraRefreshPeriod string
Quantization uint
IntraRefreshPeriod uint
RtpAddress string
Logger Logger
SendRetry bool
@ -98,20 +101,18 @@ const (
defaultInput = Raspivid
defaultOutput = Http
defaultPacketization = Flv
defaultFrameRate = "25"
defaultWidth = "1280"
defaultHeight = "720"
defaultIntraRefreshPeriod = "100"
defaultTimeout = "0"
defaultQuantization = "40"
defaultBitrate = "400000"
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
defaultVerbosity = No // FIXME(kortschak): This makes no sense whatsoever. No is currently 15.
defaultRtpAddr = "localhost:6970"
)
@ -129,17 +130,6 @@ func (c *Config) Validate(r *Revid) error {
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:
@ -153,29 +143,23 @@ func (c *Config) Validate(r *Revid) error {
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")
}
// 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
} else {
c.Bitrate = defaultBitrate
}
if (c.Bitrate > 0 && c.Quantization > 0) || (c.Bitrate == 0 && c.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")
}
if c.Quantization > 0 || c.Bitrate == 0 {
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)
@ -183,6 +167,7 @@ func (c *Config) Validate(r *Revid) error {
c.Logger.Log(smartlogger.Warning, pkg+"defaulting quantization", "quantization",
defaultQuantization)
c.Quantization = defaultQuantization
default:
return errors.New("bad input codec defined in config")
}
@ -234,73 +219,36 @@ func (c *Config) Validate(r *Revid) error {
c.FramesPerClip = defaultFramesPerClip
}
if c.Width == "" {
c.Logger.Log(smartlogger.Warning, pkg+"no width defined, defaulting", "width",
defaultWidth)
if c.Width == 0 {
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)
if c.Height == 0 {
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)
if c.FrameRate == 0 {
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)
if c.Bitrate == 0 {
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)
if c.IntraRefreshPeriod == 0 {
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)
if c.Quantization == 0 {
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")
}
} else if c.Quantization > 51 {
return errors.New("quantisation is over threshold")
}
if c.RtpAddress == "" {

View File

@ -133,7 +133,7 @@ var prevTime = now
type packer struct {
owner *Revid
packetCount int
packetCount uint
}
// Write implements the io.Writer interface.
@ -227,7 +227,7 @@ func (r *Revid) reset(config Config) error {
}
r.destination[outNo] = s
case FfmpegRtmp:
s, err := newFfmpegSender(config.RtmpUrl, r.config.FrameRate)
s, err := newFfmpegSender(config.RtmpUrl, fmt.Sprint(r.config.FrameRate))
if err != nil {
return err
}
@ -247,10 +247,7 @@ func (r *Revid) reset(config Config) error {
}
r.destination[outNo] = s
case Rtp:
// TODO: framerate in config should probably be an int, make conversions early
// when setting config fields in revid-cli
fps, _ := strconv.Atoi(r.config.FrameRate)
s, err := newRtpSender(r.config.RtpAddress, r.config.Logger.Log, fps)
s, err := newRtpSender(r.config.RtpAddress, r.config.Logger.Log, r.config.FrameRate)
if err != nil {
return err
}
@ -292,12 +289,10 @@ func (r *Revid) reset(config Config) error {
r.encoder = stream.NopEncoder(&r.packer)
case Mpegts:
r.config.Logger.Log(smartlogger.Info, pkg+"using MPEGTS packetisation")
frameRate, _ := strconv.ParseFloat(r.config.FrameRate, 64)
r.encoder = mts.NewEncoder(&r.packer, frameRate)
r.encoder = mts.NewEncoder(&r.packer, float64(r.config.FrameRate))
case Flv:
r.config.Logger.Log(smartlogger.Info, pkg+"using FLV packetisation")
frameRate, _ := strconv.Atoi(r.config.FrameRate)
r.encoder, err = flv.NewEncoder(&r.packer, true, true, frameRate)
r.encoder, err = flv.NewEncoder(&r.packer, true, true, int(r.config.FrameRate))
if err != nil {
return err
}
@ -447,10 +442,10 @@ func (r *Revid) startRaspivid() error {
"--output", "-",
"--nopreview",
"--timeout", disabled,
"--width", r.config.Width,
"--height", r.config.Height,
"--bitrate", r.config.Bitrate,
"--framerate", r.config.FrameRate,
"--width", fmt.Sprint(r.config.Width),
"--height", fmt.Sprint(r.config.Height),
"--bitrate", fmt.Sprint(r.config.Bitrate),
"--framerate", fmt.Sprint(r.config.FrameRate),
}
if r.config.FlipHorizontal {
args = append(args, "--hflip")
@ -465,10 +460,10 @@ func (r *Revid) startRaspivid() error {
args = append(args,
"--codec", "H264",
"--inline",
"--intra", r.config.IntraRefreshPeriod,
"--intra", fmt.Sprint(r.config.IntraRefreshPeriod),
)
if r.config.QuantizationMode == QuantizationOn {
args = append(args, "-qp", r.config.Quantization)
if r.config.Quantize {
args = append(args, "-qp", fmt.Sprint(r.config.Quantization))
}
case Mjpeg:
args = append(args, "--codec", "MJPEG")
@ -476,11 +471,6 @@ func (r *Revid) startRaspivid() error {
r.config.Logger.Log(smartlogger.Info, pkg+"raspivid args", "raspividArgs", strings.Join(args, " "))
r.cmd = exec.Command("raspivid", args...)
d, err := strconv.Atoi(r.config.FrameRate)
if err != nil {
return err
}
delay := time.Second / time.Duration(d)
stdout, err := r.cmd.StdoutPipe()
if err != nil {
return err
@ -491,6 +481,7 @@ func (r *Revid) startRaspivid() error {
}
r.config.Logger.Log(smartlogger.Info, pkg+"reading camera data")
delay := time.Second / time.Duration(r.config.FrameRate)
err = r.lexTo(r.encoder, stdout, delay)
r.config.Logger.Log(smartlogger.Info, pkg+"finished reading camera data")
return err
@ -498,11 +489,7 @@ func (r *Revid) startRaspivid() error {
// setupInputForFile sets things up for getting input from a file
func (r *Revid) setupInputForFile() error {
fps, err := strconv.Atoi(r.config.FrameRate)
if err != nil {
return err
}
delay := time.Second / time.Duration(fps)
delay := time.Second / time.Duration(r.config.FrameRate)
f, err := os.Open(r.config.InputFileName)
if err != nil {

View File

@ -371,14 +371,14 @@ type rtpSender struct {
encoder *rtp.Encoder
}
func newRtpSender(addr string, log func(lvl int8, msg string, args ...interface{}), fps int) (*rtpSender, error) {
func newRtpSender(addr string, log func(lvl int8, msg string, args ...interface{}), fps uint) (*rtpSender, error) {
conn, err := net.Dial("udp", addr)
if err != nil {
return nil, err
}
s := &rtpSender{
log: log,
encoder: rtp.NewEncoder(conn, fps),
encoder: rtp.NewEncoder(conn, int(fps)),
}
return s, nil
}