mirror of https://bitbucket.org/ausocean/av.git
revid: now using AVDevice implementations
Also renamed constructors of devices to reduce stutter e.g. raspivid.NewRaspivid to raspivid.New
This commit is contained in:
parent
7da3778485
commit
dec39a3636
|
@ -94,7 +94,7 @@ type Logger interface {
|
|||
type OpenError error
|
||||
|
||||
// NewALSA initializes and returns an ALSA device which has its logger set as the given logger.
|
||||
func NewALSA(l Logger) *ALSA { return &ALSA{l: l} }
|
||||
func New(l Logger) *ALSA { return &ALSA{l: l} }
|
||||
|
||||
// Set will take a Config struct, check the validity of the relevant fields
|
||||
// and then performs any configuration necessary. If fields are not valid,
|
||||
|
@ -185,10 +185,11 @@ func (d *ALSA) Start() error {
|
|||
|
||||
// Stop will stop recording audio and close the device.
|
||||
// Once an ALSA device has been stopped it cannot be started again. This is likely to change in future.
|
||||
func (d *ALSA) Stop() {
|
||||
func (d *ALSA) Stop() error {
|
||||
d.mu.Lock()
|
||||
d.mode = stopped
|
||||
d.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChunkSize returns the number of bytes written to the ringbuffer per d.RecPeriod.
|
||||
|
|
|
@ -49,7 +49,7 @@ func TestDevice(t *testing.T) {
|
|||
|
||||
// Create a new ALSA device, start, read/lex, and then stop it.
|
||||
l := logger.New(logger.Debug, os.Stderr, true)
|
||||
ai := NewALSA(l)
|
||||
ai := New(l)
|
||||
err := ai.Set(c)
|
||||
// If there was an error opening the device, skip this test.
|
||||
if _, ok := err.(OpenError); ok {
|
||||
|
|
|
@ -41,7 +41,7 @@ type AVFile struct {
|
|||
}
|
||||
|
||||
// NewAVFile returns a new AVFile.
|
||||
func NewAVFile() *AVFile { return &AVFile{} }
|
||||
func New() *AVFile { return &AVFile{} }
|
||||
|
||||
// Set simply sets the AVFile's config to the passed config.
|
||||
func (m *AVFile) Set(c config.Config) error {
|
||||
|
|
|
@ -95,7 +95,7 @@ type GeoVision struct {
|
|||
}
|
||||
|
||||
// NewGeoVision returns a new GeoVision.
|
||||
func NewGeoVision(l avconfig.Logger) *GeoVision { return &GeoVision{log: l} }
|
||||
func New(l avconfig.Logger) *GeoVision { return &GeoVision{log: l} }
|
||||
|
||||
// Set will take a Config struct, check the validity of the relevant fields
|
||||
// and then performs any configuration necessary using config to control the
|
||||
|
|
|
@ -111,8 +111,8 @@ type Raspivid struct {
|
|||
log config.Logger
|
||||
}
|
||||
|
||||
// NewRaspivid returns a new Raspivid.
|
||||
func NewRaspivid(l config.Logger) *Raspivid { return &Raspivid{log: l} }
|
||||
// New returns a new Raspivid.
|
||||
func New(l config.Logger) *Raspivid { return &Raspivid{log: l} }
|
||||
|
||||
// Set will take a Config struct, check the validity of the relevant fields
|
||||
// and then performs any configuration necessary. If fields are not valid,
|
||||
|
|
|
@ -65,8 +65,8 @@ type Webcam struct {
|
|||
cmd *exec.Cmd
|
||||
}
|
||||
|
||||
// NewWebcam returns a new Webcam.
|
||||
func NewWebcam(l config.Logger) *Webcam {
|
||||
// New returns a new Webcam.
|
||||
func New(l config.Logger) *Webcam {
|
||||
return &Webcam{log: l}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
LICENSE
|
||||
Copyright (C) 2019 the Australian Ocean Lab (AusOcean)
|
||||
|
||||
This 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 (
|
||||
"time"
|
||||
|
||||
"bitbucket.org/ausocean/av/device/alsa"
|
||||
"bitbucket.org/ausocean/utils/logger"
|
||||
)
|
||||
|
||||
// startAudioDevice is used to start capturing audio from an ALSA audio device and processing it.
|
||||
// It returns a function that can be used to stop the device and any errors that occur.
|
||||
func (r *Revid) startAudioDevice() (func() error, error) {
|
||||
// Create audio device.
|
||||
ai := alsa.NewALSA(r.cfg.Logger)
|
||||
|
||||
err := ai.Set(r.cfg)
|
||||
if err != nil {
|
||||
r.cfg.Logger.Log(logger.Fatal, pkg+"failed to setup ALSA device", "error", err.Error())
|
||||
}
|
||||
|
||||
// Start ALSA audio device
|
||||
err = ai.Start()
|
||||
if err != nil {
|
||||
r.cfg.Logger.Log(logger.Fatal, pkg+"failed to start ALSA device", "error", err.Error())
|
||||
}
|
||||
|
||||
// Process output from ALSA audio device.
|
||||
r.cfg.ChunkSize = ai.ChunkSize()
|
||||
r.wg.Add(1)
|
||||
go r.processFrom(ai, time.Duration(float64(time.Second)/r.cfg.WriteRate))
|
||||
return func() error {
|
||||
ai.Stop()
|
||||
return nil
|
||||
}, nil
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
LICENSE
|
||||
Copyright (C) 2019 the Australian Ocean Lab (AusOcean)
|
||||
|
||||
This 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.
|
||||
*/
|
||||
|
||||
// startAudioDevice is used to start capturing audio from an audio device
|
||||
// TODO: Implement on Windows.
|
||||
package revid
|
||||
|
||||
func (r *Revid) startAudioDevice() (func() error, error) {
|
||||
panic("Audio not implemented on Windows")
|
||||
}
|
|
@ -44,6 +44,12 @@ import (
|
|||
"bitbucket.org/ausocean/av/codec/mjpeg"
|
||||
"bitbucket.org/ausocean/av/container/flv"
|
||||
"bitbucket.org/ausocean/av/container/mts"
|
||||
"bitbucket.org/ausocean/av/device"
|
||||
"bitbucket.org/ausocean/av/device/audio"
|
||||
"bitbucket.org/ausocean/av/device/file"
|
||||
"bitbucket.org/ausocean/av/device/geovision"
|
||||
"bitbucket.org/ausocean/av/device/raspivid"
|
||||
"bitbucket.org/ausocean/av/device/webcam"
|
||||
"bitbucket.org/ausocean/av/revid/config"
|
||||
"bitbucket.org/ausocean/iot/pi/netsender"
|
||||
"bitbucket.org/ausocean/utils/ioext"
|
||||
|
@ -89,9 +95,8 @@ type Revid struct {
|
|||
// ns holds the netsender.Sender responsible for HTTP.
|
||||
ns *netsender.Sender
|
||||
|
||||
// setupInput holds the current approach to setting up
|
||||
// the input stream. It returns a function used for cleaning up, and any errors.
|
||||
setupInput func() (func() error, error)
|
||||
// input will capture audio or video from which we can read data.
|
||||
input device.AVDevice
|
||||
|
||||
// closeInput holds the cleanup function return from setupInput and is called
|
||||
// in Revid.Stop().
|
||||
|
@ -314,20 +319,25 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io.
|
|||
|
||||
switch r.cfg.Input {
|
||||
case config.InputRaspivid:
|
||||
r.setupInput = r.startRaspivid
|
||||
r.input = raspivid.New(r.cfg.Logger)
|
||||
|
||||
switch r.cfg.InputCodec {
|
||||
case codecutil.H264:
|
||||
r.lexTo = h264.Lex
|
||||
case codecutil.MJPEG:
|
||||
r.lexTo = mjpeg.Lex
|
||||
}
|
||||
|
||||
case config.InputV4L:
|
||||
r.setupInput = r.startV4L
|
||||
r.input = webcam.New(r.cfg.Logger)
|
||||
r.lexTo = h264.Lex
|
||||
|
||||
case config.InputFile:
|
||||
r.setupInput = r.setupInputForFile
|
||||
r.input = file.New()
|
||||
|
||||
case config.InputRTSP:
|
||||
r.setupInput = r.startRTSPCamera
|
||||
r.input = geovision.New(r.cfg.Logger)
|
||||
|
||||
switch r.cfg.InputCodec {
|
||||
case codecutil.H264:
|
||||
r.lexTo = h264.NewExtractor().Extract
|
||||
|
@ -336,11 +346,13 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io.
|
|||
case codecutil.MJPEG:
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
case config.InputAudio:
|
||||
mts.Meta.Add("sampleRate", strconv.Itoa(r.cfg.SampleRate))
|
||||
mts.Meta.Add("channels", strconv.Itoa(r.cfg.Channels))
|
||||
mts.Meta.Add("period", fmt.Sprintf("%.6f", r.cfg.RecPeriod))
|
||||
mts.Meta.Add("bitDepth", strconv.Itoa(r.cfg.BitDepth))
|
||||
|
||||
switch r.cfg.InputCodec {
|
||||
case codecutil.PCM:
|
||||
mts.Meta.Add("codec", "pcm")
|
||||
|
@ -349,8 +361,17 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io.
|
|||
default:
|
||||
r.cfg.Logger.Log(logger.Fatal, pkg+"no audio codec set in config")
|
||||
}
|
||||
r.setupInput = r.startAudioDevice
|
||||
r.lexTo = codecutil.NewByteLexer(&r.cfg.ChunkSize).Lex
|
||||
|
||||
r.input = audio.New(r.cfg.Logger)
|
||||
cs := r.input.(*audio.ALSA).ChunkSize()
|
||||
r.lexTo = codecutil.NewByteLexer(&cs).Lex
|
||||
}
|
||||
|
||||
// Configure the input device. We know that defaults are set, so no need to
|
||||
// return error, but we should log.
|
||||
err := r.input.Set(r.cfg)
|
||||
if err != nil {
|
||||
r.cfg.Logger.Log(logger.Warning, pkg+"errors from configuring input device", "errors", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -375,10 +396,15 @@ func (r *Revid) Start() error {
|
|||
r.Stop()
|
||||
return err
|
||||
}
|
||||
r.closeInput, err = r.setupInput()
|
||||
|
||||
err = r.input.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("could not start input device: %w", err)
|
||||
}
|
||||
|
||||
r.wg.Add(1)
|
||||
go r.processFrom(r.input, 0)
|
||||
|
||||
r.running = true
|
||||
return nil
|
||||
}
|
||||
|
@ -396,15 +422,13 @@ func (r *Revid) Stop() {
|
|||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
if r.closeInput != nil {
|
||||
err := r.closeInput()
|
||||
if err != nil {
|
||||
r.cfg.Logger.Log(logger.Error, pkg+"could not close input", "error", err.Error())
|
||||
}
|
||||
err := r.input.Stop()
|
||||
if err != nil {
|
||||
r.cfg.Logger.Log(logger.Error, pkg+"could not stop input", "error", err.Error())
|
||||
}
|
||||
|
||||
r.cfg.Logger.Log(logger.Info, pkg+"closing pipeline")
|
||||
err := r.encoders.Close()
|
||||
err = r.encoders.Close()
|
||||
if err != nil {
|
||||
r.cfg.Logger.Log(logger.Error, pkg+"failed to close pipeline", "error", err.Error())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue