revid: added raspivid.go file to hold Raspivid implementation of AVDevice interface

Wrote consts for default values, wrote global errors, wrote multiError type (might move)
wrote Set method.
This commit is contained in:
Saxon 2019-11-01 21:45:46 +10:30
parent 20bf962fa3
commit 924858c1c0
6 changed files with 194 additions and 49 deletions

View File

@ -82,19 +82,19 @@ const (
NothingDefined = iota
// Input/Output.
File
// Inputs.
Raspivid
V4L
RTSP
Audio
InputFile
InputRaspivid
InputV4L
InputRTSP
InputAudio
// Outputs.
RTMP
RTP
HTTP
MPEGTS
OutputAudio
OutputRTMP
OutputRTP
OutputHTTP
OutputMPEGTS
OutputFile
// Codecs.
H264
@ -105,8 +105,8 @@ const (
// Default config settings
const (
// General revid defaults.
defaultInput = Raspivid
defaultOutput = HTTP
defaultInput = InputRaspivid
defaultOutput = OutputHTTP
defaultFrameRate = 25
defaultWriteRate = 25
defaultTimeout = 0
@ -327,7 +327,7 @@ func (c *Config) Validate() error {
}
switch c.Input {
case Raspivid, V4L, File, Audio, RTSP:
case InputRaspivid, InputV4L, InputFile, InputAudio, InputRTSP:
case NothingDefined:
c.Logger.Log(logger.Info, pkg+"no input type defined, defaulting", "input", defaultInput)
c.Input = defaultInput
@ -339,7 +339,7 @@ func (c *Config) Validate() error {
case codecutil.H264, codecutil.MJPEG, codecutil.PCM, codecutil.ADPCM:
default:
switch c.Input {
case Audio:
case OutputAudio:
c.Logger.Log(logger.Info, pkg+"input is audio but no codec defined, defaulting", "inputCodec", defaultAudioInputCodec)
c.InputCodec = defaultAudioInputCodec
default:
@ -358,8 +358,8 @@ func (c *Config) Validate() error {
var haveRTMPOut bool
for i, o := range c.Outputs {
switch o {
case File:
case RTMP:
case OutputFile:
case OutputRTMP:
haveRTMPOut = true
if c.Bitrate == 0 {
c.Bitrate = defaultBitrate
@ -367,11 +367,11 @@ func (c *Config) Validate() error {
c.Quantization = 0
if c.RTMPURL == "" {
c.Logger.Log(logger.Info, pkg+"no RTMP URL: falling back to HTTP")
c.Outputs[i] = HTTP
c.Outputs[i] = OutputHTTP
haveRTMPOut = false
}
fallthrough
case HTTP, RTP:
case OutputHTTP, OutputRTP:
if !haveRTMPOut {
c.Bitrate = 0
if c.Quantization == 0 {

View File

@ -41,7 +41,7 @@ func TestValidate(t *testing.T) {
err error
}{
{
in: Config{Outputs: []uint8{HTTP}, Logger: &dumbLogger{}},
in: Config{Outputs: []uint8{OutputHTTP}, Logger: &dumbLogger{}},
check: func(c Config) error {
if c.Bitrate != 0 {
return fmt.Errorf("did not get expected bitrate. Got: %v, Want: %v", c.Bitrate, 0)
@ -53,7 +53,7 @@ func TestValidate(t *testing.T) {
},
},
{
in: Config{Outputs: []uint8{RTMP}, RTMPURL: "dummURL", Logger: &dumbLogger{}},
in: Config{Outputs: []uint8{OutputRTMP}, RTMPURL: "dummURL", Logger: &dumbLogger{}},
check: func(c Config) error {
if c.Bitrate != defaultBitrate {
return fmt.Errorf("did not get expected bitrate. Got: %v, Want: %v", c.Bitrate, defaultBitrate)
@ -65,12 +65,12 @@ func TestValidate(t *testing.T) {
},
},
{
in: Config{Outputs: []uint8{HTTP}, Quantization: 50, Logger: &dumbLogger{}},
in: Config{Outputs: []uint8{OutputHTTP}, Quantization: 50, Logger: &dumbLogger{}},
check: func(c Config) error { return nil },
err: errInvalidQuantization,
},
{
in: Config{Outputs: []uint8{HTTP}, Quantization: 20, Logger: &dumbLogger{}},
in: Config{Outputs: []uint8{OutputHTTP}, Quantization: 20, Logger: &dumbLogger{}},
check: func(c Config) error {
if c.Bitrate != 0 {
return fmt.Errorf("did not get expected bitrate. Got: %v, Want: %v", c.Bitrate, 0)

View File

@ -55,9 +55,9 @@ const (
type AVDevice interface {
io.Reader
Set(c Config) error
Start() error
Stop() error
Set() error
}
// startRaspivid sets up things for input from raspivid i.e. starts

145
revid/raspivid.go Normal file
View File

@ -0,0 +1,145 @@
/*
DESCRIPTION
raspivid.go provides an implementation of the AVDevice interface for raspivid.
AUTHORS
Saxon A. Nelson-Milton <saxon@ausocean.org>
LICENSE
Copyright (C) 2017-2019 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
in gpl.txt. If not, see http://www.gnu.org/licenses.
*/
package revid
import (
"errors"
"fmt"
"bitbucket.org/ausocean/av/codec/codecutil"
)
// Raspivid AVDevice configuration defaults.
const (
raspividDefaultCodec = codecutil.H264
raspividDefaultRotation = 0
raspividDefaultWidth = 1280
raspividDefaultHeight = 720
raspividDefaultBrightness = 50
raspividDefaultSaturation = 0
raspividDefaultExposure = "auto"
raspividDefaultAutoWhiteBalance = "auto"
raspividDefaultMinFrames = 100
raspividDefaultQuantization = 30
raspividDefaultBitrate = 48000
raspividDefaultFramerate = 25
)
type Raspivid struct {
c Config
}
var (
errBadCodec = errors.New("codec bad or unset, defaulting")
errBadRotation = errors.New("rotation bad or unset, defaulting")
errBadWidth = errors.New("width bad or unset, defaulting")
errBadHeight = errors.New("height bad or unset, defaulting")
errBadFrameRate = errors.New("framerate bad or unset, defaulting")
errBadBitrate = errors.New("bitrate bad or unset, defaulting")
errBadMinFrames = errors.New("min frames bad or unset, defaulting")
errBadSaturation = errors.New("saturation bad or unset, defaulting")
errBadBrightness = errors.New("brightness bad or unset, defaulting")
errBadExposure = errors.New("exposure bad or unset, defaulting")
errBadAutoWhiteBalance = errors.New("auto white balance bad or unset, defaulting")
errBadQuantization = errors.New("quantization bad or unset, defaulting")
)
type multiError []error
func (me multiError) Error() string {
return fmt.Sprintf("%v", me)
}
func (r *Raspivid) Set(c Config) error {
var errs []error
switch c.InputCodec {
case codecutil.H264, codecutil.MJPEG:
default:
c.InputCodec = raspividDefaultCodec
errs = append(errs, errBadCodec)
}
if c.Rotation > 359 {
c.Rotation = raspividDefaultRotation
errs = append(errs, errBadRotation)
}
if c.Width == 0 {
c.Width = raspividDefaultWidth
errs = append(errs, errBadWidth)
}
if c.Height == 0 {
c.Height = raspividDefaultHeight
errs = append(errs, errBadHeight)
}
if c.FrameRate == 0 {
c.FrameRate = raspividDefaultFramerate
errs = append(errs, errBadFrameRate)
}
if c.VBR {
c.Bitrate = 0
if c.Quantization < 10 || c.Quantization > 40 {
errs = append(errs, errBadQuantization)
c.Quantization = raspividDefaultQuantization
}
} else {
c.Quantization = 0
if c.Bitrate <= 0 {
errs = append(errs, errBadBitrate)
c.Bitrate = raspividDefaultBitrate
}
}
if c.MinFrames <= 0 {
errs = append(errs, errBadMinFrames)
c.MinFrames = raspividDefaultMinFrames
}
if c.Brightness <= 0 || c.Brightness > 100 {
errs = append(errs, errBadBrightness)
c.Brightness = raspividDefaultBrightness
}
if c.Saturation < -100 || c.Saturation > 100 {
errs = append(errs, errBadSaturation)
c.Saturation = raspividDefaultSaturation
}
if c.Exposure == "" || !stringInSlice(c.Exposure, ExposureModes[:]) {
errs = append(errs, errBadExposure)
c.Exposure = raspividDefaultExposure
}
if c.AutoWhiteBalance == "" || !stringInSlice(c.AutoWhiteBalance, AutoWhiteBalanceModes[:]) {
errs = append(errs, errBadAutoWhiteBalance)
c.AutoWhiteBalance = raspividDefaultAutoWhiteBalance
}
r.c = c
return multiError(errs)
}

View File

@ -168,7 +168,7 @@ func (r *Revid) reset(config Config) error {
var encOptions []func(*mts.Encoder) error
switch r.config.Input {
case Raspivid:
case InputRaspivid:
switch r.config.InputCodec {
case codecutil.H264:
st = mts.EncodeH264
@ -178,9 +178,9 @@ func (r *Revid) reset(config Config) error {
default:
panic("unknown input codec for raspivid input")
}
case File, V4L:
case InputFile, InputV4L:
st = mts.EncodeH264
case RTSP:
case InputRTSP:
switch r.config.InputCodec {
case codecutil.H265:
st = mts.EncodeH265
@ -192,7 +192,7 @@ func (r *Revid) reset(config Config) error {
default:
panic("unknown input codec for RTSP input")
}
case Audio:
case InputAudio:
st = mts.EncodeAudio
default:
panic("unknown input type")
@ -246,7 +246,7 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io.
var w io.WriteCloser
for _, out := range r.config.Outputs {
switch out {
case HTTP:
case OutputHTTP:
w = newMtsSender(
newHttpSender(r.ns, r.config.Logger.Log),
r.config.Logger.Log,
@ -254,19 +254,19 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io.
r.config.ClipDuration,
)
mtsSenders = append(mtsSenders, w)
case RTP:
case OutputRTP:
w, err := newRtpSender(r.config.RTPAddress, r.config.Logger.Log, r.config.FrameRate)
if err != nil {
r.config.Logger.Log(logger.Warning, pkg+"rtp connect error", "error", err.Error())
}
mtsSenders = append(mtsSenders, w)
case File:
case OutputFile:
w, err := newFileSender(r.config.OutputPath)
if err != nil {
return err
}
mtsSenders = append(mtsSenders, w)
case RTMP:
case OutputRTMP:
w, err := newRtmpSender(
r.config.RTMPURL,
rtmpConnectionTimeout,
@ -305,7 +305,7 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io.
r.encoders = multiWriter(encoders...)
switch r.config.Input {
case Raspivid:
case InputRaspivid:
r.setupInput = r.startRaspivid
switch r.config.InputCodec {
case codecutil.H264:
@ -313,12 +313,12 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io.
case codecutil.MJPEG:
r.lexTo = mjpeg.Lex
}
case V4L:
case InputV4L:
r.setupInput = r.startV4L
r.lexTo = h264.Lex
case File:
case InputFile:
r.setupInput = r.setupInputForFile
case RTSP:
case InputRTSP:
r.setupInput = r.startRTSPCamera
switch r.config.InputCodec {
case codecutil.H264:
@ -328,7 +328,7 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io.
case codecutil.MJPEG:
panic("not implemented")
}
case Audio:
case InputAudio:
r.setupInput = r.startAudioDevice
r.lexTo = codecutil.NewByteLexer(&r.config.ChunkSize).Lex
}
@ -423,7 +423,7 @@ func (r *Revid) Update(vars map[string]string) error {
for key, value := range vars {
switch key {
case "Input":
v, ok := map[string]uint8{"raspivid": Raspivid, "rtsp": RTSP}[strings.ToLower(value)]
v, ok := map[string]uint8{"raspivid": InputRaspivid, "rtsp": InputRTSP}[strings.ToLower(value)]
if !ok {
r.config.Logger.Log(logger.Warning, pkg+"invalid input var", "value", value)
break
@ -463,13 +463,13 @@ func (r *Revid) Update(vars map[string]string) error {
for i, output := range outputs {
switch output {
case "File":
r.config.Outputs[i] = File
r.config.Outputs[i] = OutputFile
case "Http":
r.config.Outputs[i] = HTTP
r.config.Outputs[i] = OutputHTTP
case "Rtmp":
r.config.Outputs[i] = RTMP
r.config.Outputs[i] = OutputRTMP
case "Rtp":
r.config.Outputs[i] = RTP
r.config.Outputs[i] = OutputRTP
default:
r.config.Logger.Log(logger.Warning, pkg+"invalid output param", "value", value)
continue

View File

@ -58,7 +58,7 @@ func TestRaspivid(t *testing.T) {
var c Config
c.Logger = &logger
c.Input = Raspivid
c.Input = InputRaspivid
rv, err := New(c, ns)
if err != nil {
@ -148,7 +148,7 @@ func TestResetEncoderSenderSetup(t *testing.T) {
encoders []encoder
}{
{
outputs: []uint8{HTTP},
outputs: []uint8{OutputHTTP},
encoders: []encoder{
{
encoderType: mtsEncoderStr,
@ -157,7 +157,7 @@ func TestResetEncoderSenderSetup(t *testing.T) {
},
},
{
outputs: []uint8{RTMP},
outputs: []uint8{OutputRTMP},
encoders: []encoder{
{
encoderType: flvEncoderStr,
@ -166,7 +166,7 @@ func TestResetEncoderSenderSetup(t *testing.T) {
},
},
{
outputs: []uint8{RTP},
outputs: []uint8{OutputRTP},
encoders: []encoder{
{
encoderType: mtsEncoderStr,
@ -175,7 +175,7 @@ func TestResetEncoderSenderSetup(t *testing.T) {
},
},
{
outputs: []uint8{HTTP, RTMP},
outputs: []uint8{OutputHTTP, OutputRTMP},
encoders: []encoder{
{
encoderType: mtsEncoderStr,
@ -188,7 +188,7 @@ func TestResetEncoderSenderSetup(t *testing.T) {
},
},
{
outputs: []uint8{HTTP, RTP, RTMP},
outputs: []uint8{OutputHTTP, OutputRTP, OutputRTMP},
encoders: []encoder{
{
encoderType: mtsEncoderStr,
@ -201,7 +201,7 @@ func TestResetEncoderSenderSetup(t *testing.T) {
},
},
{
outputs: []uint8{RTP, RTMP},
outputs: []uint8{OutputRTP, OutputRTMP},
encoders: []encoder{
{
encoderType: mtsEncoderStr,