Merged in loop-mode (pull request #354)

revid: add input source loop mode

Approved-by: Alan Noble <anoble@gmail.com>
This commit is contained in:
Saxon Milton 2020-01-24 23:24:58 +00:00
commit 89ca5d6052
3 changed files with 56 additions and 15 deletions

View File

@ -128,6 +128,7 @@ func handleFlags() config.Config {
httpAddressPtr = flag.String("HttpAddress", "", "Destination address of http posts")
verticalFlipPtr = flag.Bool("VerticalFlip", false, "Flip video vertically: Yes, No")
horizontalFlipPtr = flag.Bool("HorizontalFlip", false, "Flip video horizontally: Yes, No")
loopPtr = flag.Bool("Loop", false, "Loop input source on completion (true/false)")
bitratePtr = flag.Uint("Bitrate", 0, "Bitrate of recorded video")
heightPtr = flag.Uint("Height", 0, "Height in pixels")
widthPtr = flag.Uint("Width", 0, "Width in pixels")
@ -138,6 +139,7 @@ func handleFlags() config.Config {
saturationPtr = flag.Int("Saturation", 0, "Set Saturation. (100-100)")
exposurePtr = flag.String("Exposure", "auto", "Set exposure mode. ("+strings.Join(raspivid.ExposureModes[:], ",")+")")
autoWhiteBalancePtr = flag.String("Awb", "auto", "Set automatic white balance mode. ("+strings.Join(raspivid.AutoWhiteBalanceModes[:], ",")+")")
fileFPSPtr = flag.Int("FileFPS", 0, "File source frame processing FPS")
// Audio specific flags.
sampleRatePtr = flag.Int("SampleRate", 48000, "Sample rate of recorded audio")
@ -254,6 +256,8 @@ func handleFlags() config.Config {
netsender.ConfigFile = *configFilePtr
}
cfg.FileFPS = *fileFPSPtr
cfg.Loop = *loopPtr
cfg.CameraIP = *cameraIPPtr
cfg.Rotation = *rotationPtr
cfg.HorizontalFlip = *horizontalFlipPtr

View File

@ -86,6 +86,7 @@ const (
defaultAudioInputCodec = codecutil.ADPCM
defaultPSITime = 2
defaultMotionInterval = 5
defaultFileFPS = 0
// Ring buffer defaults.
defaultRBMaxElements = 10000
@ -297,6 +298,12 @@ type Config struct {
MOGMinArea float64 // Used to ignore small areas of motion detection.
MOGThreshold float64 // Intensity value from the KNN motion detection algorithm that is considered motion.
MOGHistory uint // Length of MOG filter's history
// If true will restart reading of input after an io.EOF.
Loop bool
// Defines the rate at which frames from a file source are processed.
FileFPS int
}
// TypeData contains information about all of the variables that
@ -311,6 +318,7 @@ var TypeData = map[string]string{
"CBR": "bool",
"ClipDuration": "uint",
"Exposure": "enum:auto,night,nightpreview,backlight,spotlight,sports,snow,beach,verylong,fixedfps,antishake,fireworks",
"FileFPS": "int",
"Filters": "enums:NoOp,MOG,VariableFPS,KNN",
"FrameRate": "uint",
"Height": "uint",
@ -324,9 +332,10 @@ var TypeData = map[string]string{
"KNNMinArea": "float",
"KNNThreshold": "float",
"logging": "enum:Debug,Info,Warning,Error,Fatal",
"Loop": "bool",
"MinFPS": "float",
"MinFrames": "uint",
"mode": "enum:Normal,Paused,Burst",
"mode": "enum:Normal,Paused,Burst,Loop",
"MOGHistory": "uint",
"MOGMinArea": "float",
"MOGThreshold": "float",
@ -521,6 +530,11 @@ func (c *Config) Validate() error {
}
}
if c.FileFPS <= 0 || (c.FileFPS > 0 && c.Input != InputFile) {
c.Logger.Log(logger.Info, pkg+"FileFPS bad or unset, defaulting", "FileFPS", defaultFileFPS)
c.FileFPS = defaultFileFPS
}
return nil
}

View File

@ -428,13 +428,11 @@ func (r *Revid) Start() error {
return err
}
err = r.input.Start()
if err != nil {
return fmt.Errorf("could not start input device: %w", err)
}
// Calculate delay between frames based on InputFPS.
d := time.Duration(1000/r.cfg.FileFPS) * time.Millisecond
r.wg.Add(1)
go r.processFrom(r.input, 0)
go r.processFrom(r.input, d)
r.running = true
return nil
@ -845,6 +843,11 @@ func (r *Revid) Update(vars map[string]string) error {
break
}
r.cfg.MOGHistory = uint(v)
case "mode":
r.cfg.Loop = false
if value == "Loop" {
r.cfg.Loop = true
}
}
}
r.cfg.Logger.Log(logger.Info, pkg+"revid config changed", "config", fmt.Sprintf("%+v", r.cfg))
@ -853,14 +856,34 @@ func (r *Revid) Update(vars map[string]string) error {
// processFrom is run as a routine to read from a input data source, lex and
// then send individual access units to revid's encoders.
func (r *Revid) processFrom(read io.Reader, delay time.Duration) {
err := r.lexTo(r.filters[0], read, delay)
r.cfg.Logger.Log(logger.Debug, pkg+"finished lexing")
switch err {
case nil: // Do nothing.
case io.EOF: // TODO: handle this depending on loop mode.
default:
r.err <- err
func (r *Revid) processFrom(in device.AVDevice, delay time.Duration) {
defer r.wg.Done()
for l := true; l; l = r.cfg.Loop {
err := in.Start()
if err != nil {
r.err <- fmt.Errorf("could not start input device: %w", err)
return
}
// Lex data from input device, in, until finished or an error is encountered.
// For a continuous source e.g. a camera or microphone, we should remain
// in this call indefinitely unless in.Stop() is called and an io.EOF is forced.
r.cfg.Logger.Log(logger.Info, pkg+"lexing")
err = r.lexTo(r.filters[0], in, delay)
switch err {
case nil, io.EOF:
case io.ErrUnexpectedEOF:
r.cfg.Logger.Log(logger.Info, pkg+"unexpected EOF from input")
default:
r.err <- err
}
err = in.Stop()
if err != nil {
r.err <- fmt.Errorf("could not stop input source: %w", err)
}
}
r.wg.Done()
r.cfg.Logger.Log(logger.Info, pkg+"finished lexing")
}