diff --git a/cmd/revid-cli/main.go b/cmd/revid-cli/main.go index cc31c69c..454bde10 100644 --- a/cmd/revid-cli/main.go +++ b/cmd/revid-cli/main.go @@ -218,13 +218,13 @@ func handleFlags() config.Config { switch *inputCodecPtr { case "H264": - cfg.InputCodec = codecutil.CodecH264 + cfg.InputCodec = codecutil.H264 case "PCM": - cfg.InputCodec = codecutil.CodecPCM + cfg.InputCodec = codecutil.PCM case "ADPCM": - cfg.InputCodec = codecutil.CodecADPCM + cfg.InputCodec = codecutil.ADPCM case "MJPEG": - cfg.InputCodec = codecutil.CodecMJPEG + cfg.InputCodec = codecutil.MJPEG default: log.Log(logger.Error, pkg+"bad input codec argument") } diff --git a/cmd/rv/main.go b/cmd/rv/main.go index e7e479fe..c329af2e 100644 --- a/cmd/rv/main.go +++ b/cmd/rv/main.go @@ -1,17 +1,19 @@ /* -NAME - revid-cli - command line interface for revid. - DESCRIPTION - See Readme.md + rv is a netsender client using the revid package to perform media collection + and forwarding whos behaviour is controllable via the cloud interfaces + netreceiver and vidgrind. AUTHORS Saxon A. Nelson-Milton + Alan Noble + Dan Kortschak Jack Richardson Trek Hopton + Scott Barnard LICENSE - revid-cli is Copyright (C) 2017-2018 the Australian Ocean Lab (AusOcean) + Copyright (C) 2020 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 @@ -38,6 +40,8 @@ import ( "strconv" "time" + "gopkg.in/natefinch/lumberjack.v2" + "bitbucket.org/ausocean/av/container/mts" "bitbucket.org/ausocean/av/container/mts/meta" "bitbucket.org/ausocean/av/revid" @@ -46,7 +50,6 @@ import ( "bitbucket.org/ausocean/iot/pi/netsender" "bitbucket.org/ausocean/iot/pi/sds" "bitbucket.org/ausocean/utils/logger" - "gopkg.in/natefinch/lumberjack.v2" ) // Copyright information prefixed to all metadata. @@ -70,9 +73,10 @@ const ( modeNormal = "Normal" modePaused = "Paused" modeBurst = "Burst" + modeLoop = "Loop" ) -// Misc consts. +// Misc constants. const ( netSendRetryTime = 5 * time.Second defaultSleepTime = 60 // Seconds @@ -80,12 +84,13 @@ const ( pkg = "revid-cli:" ) -// canProfile is set to false with revid-cli is built with "-tags profile". -var canProfile = true +// This is set to true if the 'profile' build tag is provided on build. +var canProfile = false func main() { mts.Meta = meta.NewWith([][2]string{{metaPreambleKey, metaPreambleData}}) + // Create lumberjack logger to handle logging to file. fileLog := &lumberjack.Logger{ Filename: logPath, MaxSize: logMaxSize, @@ -93,10 +98,14 @@ func main() { MaxAge: logMaxAge, } + // Create netlogger to handle logging to cloud. netLog := netlogger.New() + // Create logger that we call methods on to log, which in turn writes to the + // lumberjack and netloggers. log := logger.New(logVerbosity, io.MultiWriter(fileLog, netLog), logSuppress) + // If rv has been built with the profile tag, then we'll start a CPU profile. if canProfile { profile(log) defer pprof.StopCPUProfile() @@ -112,6 +121,8 @@ func main() { run(rv, ns, log, netLog) } +// run starts the main loop. This will run netsender on every pass of the loop +// (sleeping inbetween), check vars, and if changed, update revid as appropriate. func run(rv *revid.Revid, ns *netsender.Sender, l *logger.Logger, nl *netlogger.Logger) { var vs int for { @@ -127,7 +138,7 @@ func run(rv *revid.Revid, ns *netsender.Sender, l *logger.Logger, nl *netlogger. l.Log(logger.Warning, pkg+"Logs could not be sent", "error", err.Error()) } - // If var sum hasn't changed we continue. + // If var sum hasn't changed we skip rest of loop. newVs := ns.VarSum() if vs == newVs { sleep(ns, l) @@ -142,6 +153,7 @@ func run(rv *revid.Revid, ns *netsender.Sender, l *logger.Logger, nl *netlogger. continue } + // If first time running loop, i.e. rv == nil, then we create a new Revid. if rv == nil { rv, err = revid.New(config.Config{Logger: l}, ns) if err != nil { @@ -151,6 +163,7 @@ func run(rv *revid.Revid, ns *netsender.Sender, l *logger.Logger, nl *netlogger. } } + // Configure revid based on the vars. err = rv.Update(vars) if err != nil { l.Log(logger.Warning, pkg+"Couldn't update revid", "error", err.Error()) @@ -161,7 +174,7 @@ func run(rv *revid.Revid, ns *netsender.Sender, l *logger.Logger, nl *netlogger. switch ns.Mode() { case modePaused: rv.Stop() - case modeNormal: + case modeNormal, modeLoop: err = rv.Start() if err != nil { l.Log(logger.Warning, pkg+"could not start revid", "error", err.Error()) @@ -182,6 +195,8 @@ func run(rv *revid.Revid, ns *netsender.Sender, l *logger.Logger, nl *netlogger. } } +// profile opens a file to hold CPU profiling metrics and then starts the +// CPU profiler. func profile(l *logger.Logger) { f, err := os.Create(profilePath) if err != nil { @@ -192,6 +207,8 @@ func profile(l *logger.Logger) { } } +// sleep uses a delay to halt the program based on the monitoring period +// netsender parameter (mp) defined in the netsender.conf config. func sleep(ns *netsender.Sender, l *logger.Logger) { t, err := strconv.Atoi(ns.Param("mp")) if err != nil { @@ -201,6 +218,8 @@ func sleep(ns *netsender.Sender, l *logger.Logger) { time.Sleep(time.Duration(t) * time.Second) } +// readPin provides a callback function of consistent signature for use by +// netsender to retrieve software defined pin values e.g. revid bitrate (X23). func readPin(rv *revid.Revid) func(pin *netsender.Pin) error { return func(pin *netsender.Pin) error { switch { @@ -218,6 +237,10 @@ func readPin(rv *revid.Revid) func(pin *netsender.Pin) error { } } +// burst starts revid, waits for time specified in the Config.BurstPeriod +// field, and then stops revid. +// +// TODO: move this functionality to the revid API into a Revid.Burst(time) method. func burst(l *logger.Logger, r *revid.Revid, s *netsender.Sender) error { l.Log(logger.Info, pkg+"starting burst") diff --git a/cmd/rv/profile.go b/cmd/rv/profile.go index 7b6874d5..2caa2b95 100644 --- a/cmd/rv/profile.go +++ b/cmd/rv/profile.go @@ -1,17 +1,15 @@ // +build profile /* -NAME - revid-cli - command line interface for Revid. - DESCRIPTION - See Readme.md + profile.go provides an init to change canProfile flag to true if profile tag + provided on build. AUTHORS Dan Kortschak LICENSE - revid-cli is Copyright (C) 2017-2018 the Australian Ocean Lab (AusOcean) + Copyright (C) 2020 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 @@ -32,5 +30,5 @@ package main import _ "net/http/pprof" func init() { - canProfile = false + canProfile = true } diff --git a/cmd/rv/run.sh b/cmd/rv/run.sh index d1c9dced..3521dc0c 100644 --- a/cmd/rv/run.sh +++ b/cmd/rv/run.sh @@ -1,4 +1,4 @@ #!/bin/sh -REVIDPATH=$HOME/go/src/bitbucket.org/ausocean/av/cmd/revid-cli +REVIDPATH=$HOME/go/src/bitbucket.org/ausocean/av/cmd/rv cd $REVIDPATH -sudo "PATH=$PATH:$REVIDPATH" ./revid-cli -NetSender & +sudo "PATH=$PATH:$REVIDPATH" ./rv & diff --git a/cmd/rv/rv b/cmd/rv/rv deleted file mode 100755 index 05d16bfc..00000000 Binary files a/cmd/rv/rv and /dev/null differ diff --git a/cmd/rv/rv.prof b/cmd/rv/rv.prof deleted file mode 100644 index e69de29b..00000000 diff --git a/codec/codecutil/list.go b/codec/codecutil/list.go index 9642cfbc..1d4d1510 100644 --- a/codec/codecutil/list.go +++ b/codec/codecutil/list.go @@ -30,12 +30,12 @@ const numCodecs = 5 // A global list containing all available codecs for reference in any application. // When adding or removing a codec from this list, the numCodecs const must be updated. const ( - CodecUndef = iota - CodecPCM - CodecADPCM - CodecH264 - CodecH265 - CodecMJPEG + Undef = iota + PCM + ADPCM + H264 + H265 + MJPEG ) // IsValid recieves an int representing a codec and checks if it is valid. diff --git a/codec/pcm/pcm.go b/codec/pcm/pcm.go index 3c6fbb5d..98619f81 100644 --- a/codec/pcm/pcm.go +++ b/codec/pcm/pcm.go @@ -70,7 +70,7 @@ type Buffer struct { // DataSize takes audio attributes describing audio data and returns the size of that data. func DataSize(rate, channels, bitDepth int, period float64, codec uint8) int { s := int(float64(channels) * float64(rate) * float64(bitDepth/8) * period) - if codec == codecutil.CodecADPCM { + if codec == codecutil.ADPCM { s = adpcm.EncBytes(s) } return s diff --git a/device/alsa/alsa.go b/device/alsa/alsa.go index 8d53b13f..5f83d016 100644 --- a/device/alsa/alsa.go +++ b/device/alsa/alsa.go @@ -421,8 +421,8 @@ func (d *ALSA) formatBuffer() pcm.Buffer { } switch d.Codec { - case codecutil.CodecPCM: - case codecutil.CodecADPCM: + case codecutil.PCM: + case codecutil.ADPCM: b := bytes.NewBuffer(make([]byte, 0, adpcm.EncBytes(len(formatted.Data)))) enc := adpcm.NewEncoder(b) _, err = enc.Write(formatted.Data) diff --git a/device/alsa/alsa_test.go b/device/alsa/alsa_test.go index 9d529b98..44e54e38 100644 --- a/device/alsa/alsa_test.go +++ b/device/alsa/alsa_test.go @@ -44,7 +44,7 @@ func TestDevice(t *testing.T) { Channels: 1, RecPeriod: 0.3, BitDepth: 16, - InputCodec: codecutil.CodecADPCM, + InputCodec: codecutil.ADPCM, } n := 2 // Number of periods to wait while recording. diff --git a/device/geovision/geovision.go b/device/geovision/geovision.go index 1df72a3c..d09054ad 100644 --- a/device/geovision/geovision.go +++ b/device/geovision/geovision.go @@ -63,7 +63,7 @@ const ( // Configuration defaults. const ( defaultCameraIP = "192.168.1.50" - defaultCodec = codecutil.CodecH264 + defaultCodec = codecutil.H264 defaultHeight = 720 defaultFrameRate = 25 defaultBitrate = 400 @@ -115,7 +115,7 @@ func (g *GeoVision) Set(c avconfig.Config) error { } switch c.InputCodec { - case codecutil.CodecH264, codecutil.CodecH265, codecutil.CodecMJPEG: + case codecutil.H264, codecutil.H265, codecutil.MJPEG: default: errs = append(errs, errGVBadCodec) c.InputCodec = defaultCodec @@ -171,9 +171,9 @@ func (g *GeoVision) Set(c avconfig.Config) error { gvconfig.Channel(g.cfg.CameraChan), gvconfig.CodecOut( map[uint8]gvconfig.Codec{ - codecutil.CodecH264: gvconfig.CodecH264, - codecutil.CodecH265: gvconfig.CodecH265, - codecutil.CodecMJPEG: gvconfig.CodecMJPEG, + codecutil.H264: gvconfig.CodecH264, + codecutil.H265: gvconfig.CodecH265, + codecutil.MJPEG: gvconfig.CodecMJPEG, }[g.cfg.InputCodec], ), gvconfig.Height(int(g.cfg.Height)), diff --git a/device/raspivid/raspivid.go b/device/raspivid/raspivid.go index b0496320..cbae8c83 100644 --- a/device/raspivid/raspivid.go +++ b/device/raspivid/raspivid.go @@ -44,7 +44,7 @@ const pkg = "raspivid: " // Raspivid configuration defaults. const ( - defaultRaspividCodec = codecutil.CodecH264 + defaultRaspividCodec = codecutil.H264 defaultRaspividRotation = 0 defaultRaspividWidth = 1280 defaultRaspividHeight = 720 @@ -133,7 +133,7 @@ func (r *Raspivid) Name() string { func (r *Raspivid) Set(c config.Config) error { var errs device.MultiError switch c.InputCodec { - case codecutil.CodecH264, codecutil.CodecMJPEG: + case codecutil.H264, codecutil.MJPEG: default: c.InputCodec = defaultRaspividCodec errs = append(errs, errBadCodec) @@ -236,7 +236,7 @@ func (r *Raspivid) Start() error { switch r.cfg.InputCodec { default: return fmt.Errorf("revid: invalid input codec: %v", r.cfg.InputCodec) - case codecutil.CodecH264: + case codecutil.H264: args = append(args, "--codec", "H264", "--inline", @@ -245,7 +245,7 @@ func (r *Raspivid) Start() error { if !r.cfg.CBR { args = append(args, "-qp", fmt.Sprint(r.cfg.Quantization)) } - case codecutil.CodecMJPEG: + case codecutil.MJPEG: args = append(args, "--codec", "MJPEG") } r.cfg.Logger.Log(logger.Info, pkg+"raspivid args", "raspividArgs", strings.Join(args, " ")) diff --git a/device/webcam/webcam.go b/device/webcam/webcam.go index f13ef62b..2d5e27e3 100644 --- a/device/webcam/webcam.go +++ b/device/webcam/webcam.go @@ -131,13 +131,13 @@ func (w *Webcam) Start() error { switch w.cfg.InputCodec { default: return fmt.Errorf("revid: invalid input codec: %v", w.cfg.InputCodec) - case codecutil.CodecH264: + case codecutil.H264: args = append(args, "-f", "h264", "-maxrate", fmt.Sprint(br), "-bufsize", fmt.Sprint(br/2), ) - case codecutil.CodecMJPEG: + case codecutil.MJPEG: args = append(args, "-f", "mjpeg", ) diff --git a/revid/audio_linux.go b/revid/audio_linux.go index eb87fd16..9ce5c2bc 100644 --- a/revid/audio_linux.go +++ b/revid/audio_linux.go @@ -42,9 +42,9 @@ func (r *Revid) setupAudio() error { mts.Meta.Add("bitDepth", strconv.Itoa(r.cfg.BitDepth)) switch r.cfg.InputCodec { - case codecutil.CodecPCM: + case codecutil.PCM: mts.Meta.Add("codec", "pcm") - case codecutil.CodecADPCM: + case codecutil.ADPCM: mts.Meta.Add("codec", "adpcm") default: r.cfg.Logger.Log(logger.Fatal, pkg+"no audio codec set in config") diff --git a/revid/config/config.go b/revid/config/config.go index d86360f3..0192b1bd 100644 --- a/revid/config/config.go +++ b/revid/config/config.go @@ -74,7 +74,7 @@ const ( defaultInput = InputRaspivid defaultOutput = OutputHTTP defaultTimeout = 0 - defaultInputCodec = codecutil.CodecH264 + defaultInputCodec = codecutil.H264 defaultVerbosity = logger.Error defaultRtpAddr = "localhost:6970" defaultCameraIP = "192.168.1.50" @@ -83,7 +83,7 @@ const ( defaultFrameRate = 25 defaultWriteRate = 25 defaultClipDuration = 0 - defaultAudioInputCodec = codecutil.CodecADPCM + defaultAudioInputCodec = codecutil.ADPCM defaultPSITime = 2 defaultMotionInterval = 5 defaultFileFPS = 0 @@ -392,7 +392,7 @@ func (c *Config) Validate() error { } switch c.InputCodec { - case codecutil.CodecH264, codecutil.CodecMJPEG, codecutil.CodecPCM, codecutil.CodecADPCM: + case codecutil.H264, codecutil.MJPEG, codecutil.PCM, codecutil.ADPCM: default: switch c.Input { case OutputAudio: diff --git a/revid/revid.go b/revid/revid.go index cb32b2b8..65f0a295 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -176,9 +176,9 @@ func (r *Revid) reset(c config.Config) error { switch r.cfg.Input { case config.InputRaspivid: switch r.cfg.InputCodec { - case codecutil.CodecH264: + case codecutil.H264: st = mts.EncodeH264 - case codecutil.CodecMJPEG: + case codecutil.MJPEG: st = mts.EncodeMJPEG encOptions = append(encOptions, mts.TimeBasedPSI(time.Duration(r.cfg.PSITime)*time.Second)) r.cfg.CBR = true @@ -187,9 +187,9 @@ func (r *Revid) reset(c config.Config) error { } case config.InputFile, config.InputV4L: switch r.cfg.InputCodec { - case codecutil.CodecH264: + case codecutil.H264: st = mts.EncodeH264 - case codecutil.CodecMJPEG: + case codecutil.MJPEG: st = mts.EncodeMJPEG encOptions = append(encOptions, mts.TimeBasedPSI(time.Duration(r.cfg.PSITime)*time.Second)) r.cfg.CBR = true @@ -198,11 +198,11 @@ func (r *Revid) reset(c config.Config) error { } case config.InputRTSP: switch r.cfg.InputCodec { - case codecutil.CodecH265: + case codecutil.H265: st = mts.EncodeH265 - case codecutil.CodecH264: + case codecutil.H264: st = mts.EncodeH264 - case codecutil.CodecMJPEG: + case codecutil.MJPEG: st = mts.EncodeMJPEG encOptions = append(encOptions, mts.TimeBasedPSI(time.Duration(r.cfg.PSITime)*time.Second)) r.cfg.CBR = true @@ -361,27 +361,27 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io. r.input = raspivid.New(r.cfg.Logger) switch r.cfg.InputCodec { - case codecutil.CodecH264: + case codecutil.H264: r.lexTo = h264.Lex - case codecutil.CodecMJPEG: + case codecutil.MJPEG: r.lexTo = mjpeg.Lex } case config.InputV4L: r.input = webcam.New(r.cfg.Logger) switch r.cfg.InputCodec { - case codecutil.CodecH264: + case codecutil.H264: r.lexTo = h264.Lex - case codecutil.CodecMJPEG: + case codecutil.MJPEG: r.lexTo = mjpeg.Lex } case config.InputFile: r.input = file.New() switch r.cfg.InputCodec { - case codecutil.CodecH264: + case codecutil.H264: r.lexTo = h264.Lex - case codecutil.CodecMJPEG: + case codecutil.MJPEG: r.lexTo = mjpeg.Lex } @@ -389,11 +389,11 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io. r.input = geovision.New(r.cfg.Logger) switch r.cfg.InputCodec { - case codecutil.CodecH264: + case codecutil.H264: r.lexTo = h264.NewExtractor().Extract - case codecutil.CodecH265: + case codecutil.H265: r.lexTo = h265.NewLexer(false).Lex - case codecutil.CodecMJPEG: + case codecutil.MJPEG: r.lexTo = mjpeg.NewExtractor().Extract } @@ -540,9 +540,9 @@ func (r *Revid) Update(vars map[string]string) error { case "InputCodec": switch value { case "H264": - r.cfg.InputCodec = codecutil.CodecH264 + r.cfg.InputCodec = codecutil.H264 case "MJPEG": - r.cfg.InputCodec = codecutil.CodecMJPEG + r.cfg.InputCodec = codecutil.MJPEG default: r.cfg.Logger.Log(logger.Warning, pkg+"invalid InputCodec variable value", "value", value) }