Merged in is-running-method (pull request #363)

device: add IsRunning method to AVDevice interface

Approved-by: Saxon Milton <saxon.milton@gmail.com>
This commit is contained in:
Scott Barnard 2020-01-31 04:29:02 +00:00 committed by Saxon Milton
commit 0dfb2df939
11 changed files with 402 additions and 20 deletions

View File

@ -462,3 +462,8 @@ func nearestPowerOfTwo(n int) int {
}
return v
}
// IsRunning is used to determine if the ALSA device is running.
func (d *ALSA) IsRunning() bool {
return d.mode == running
}

View File

@ -4,9 +4,10 @@ NAME
AUTHOR
Trek Hopton <trek@ausocean.org>
Scott Barnard <scott@ausocean.org>
LICENSE
This file is Copyright (C) 2019 the Australian Ocean Lab (AusOcean)
This file is Copyright (C) 2019-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
@ -25,6 +26,7 @@ LICENSE
package alsa
import (
"bytes"
"io/ioutil"
"os"
"strconv"
@ -106,3 +108,47 @@ func TestNearestPowerOfTwo(t *testing.T) {
})
}
}
func TestIsRunning(t *testing.T) {
const dur = 250 * time.Millisecond
const sampleRate = 1000
const channels = 1
const bitDepth = 16
const recPeriod = 1
l := logger.New(logger.Debug, &bytes.Buffer{}, true) // Discard logs.
d := New(l)
err := d.Set(config.Config{
SampleRate: sampleRate,
Channels: channels,
BitDepth: bitDepth,
RecPeriod: recPeriod,
InputCodec: codecutil.ADPCM,
})
if err != nil {
t.Skipf("could not set device: %w", err)
}
err = d.Start()
if err != nil {
t.Fatalf("could not start device %w", err)
}
time.Sleep(dur)
if !d.IsRunning() {
t.Error("device isn't running, when it should be")
}
err = d.Stop()
if err != nil {
t.Error(err.Error())
}
time.Sleep(dur)
if d.IsRunning() {
t.Error("device is running, when it should not be")
}
}

View File

@ -55,6 +55,9 @@ type AVDevice interface {
// Stop will stop the AVDevice from capturing media data. From this point
// Reads will no longer be successful.
Stop() error
// IsRunning is used to determine if the device is running.
IsRunning() bool
}
// multiError implements the built in error interface. multiError is used here

View File

@ -36,8 +36,9 @@ import (
// AVFile is an implementation of the AVDevice interface for a file containg
// audio or video data.
type AVFile struct {
f io.ReadCloser
cfg config.Config
f io.ReadCloser
cfg config.Config
isRunning bool
}
// NewAVFile returns a new AVFile.
@ -62,11 +63,19 @@ func (m *AVFile) Start() error {
if err != nil {
return fmt.Errorf("could not open media file: %w", err)
}
m.isRunning = true
return nil
}
// Stop will close the file such that any further reads will fail.
func (m *AVFile) Stop() error { return m.f.Close() }
func (m *AVFile) Stop() error {
err := m.f.Close()
if err == nil {
m.isRunning = false
return nil
}
return err
}
// Read implements io.Reader. If start has not been called, or Start has been
// called and Stop has since been called, an error is returned.
@ -76,3 +85,8 @@ func (m *AVFile) Read(p []byte) (int, error) {
}
return 0, errors.New("AV file is closed")
}
// IsRunning is used to determine if the AVFile device is running.
func (m *AVFile) IsRunning() bool {
return m.f != nil && m.isRunning
}

68
device/file/file_test.go Normal file
View File

@ -0,0 +1,68 @@
/*
DESCRIPTION
file_test.go tests the file AVDevice.
AUTHORS
Scott Barnard <scott@ausocean.org>
LICENSE
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
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 file
import (
"testing"
"time"
"bitbucket.org/ausocean/av/revid/config"
)
func TestIsRunning(t *testing.T) {
const dur = 250 * time.Millisecond
const path = "../../../test/test-data/av/input/motion-detection/mjpeg/school.mjpeg"
d := New()
err := d.Set(config.Config{
InputPath: path,
})
if err != nil {
t.Skipf("could not set device: %w", err)
}
err = d.Start()
if err != nil {
t.Fatalf("could not start device %w", err)
}
time.Sleep(dur)
if !d.IsRunning() {
t.Error("device isn't running, when it should be")
}
err = d.Stop()
if err != nil {
t.Error(err.Error())
}
time.Sleep(dur)
if d.IsRunning() {
t.Error("device is running, when it should not be")
}
}

View File

@ -88,11 +88,12 @@ var (
// IP camera. This has been designed to implement the GV-BX4700-8F in particular.
// Any other models are untested.
type GeoVision struct {
cfg avconfig.Config
log avconfig.Logger
rtpClt *rtp.Client
rtspClt *rtsp.Client
rtcpClt *rtcp.Client
cfg avconfig.Config
log avconfig.Logger
rtpClt *rtp.Client
rtspClt *rtsp.Client
rtcpClt *rtcp.Client
isRunning bool
}
// NewGeoVision returns a new GeoVision.
@ -278,6 +279,7 @@ func (g *GeoVision) Start() error {
}
g.log.Log(logger.Debug, pkg+"RTSP server PLAY response", "response", resp.String())
g.log.Log(logger.Info, pkg+"play requested, now receiving stream")
g.isRunning = true
return nil
}
@ -299,6 +301,8 @@ func (g *GeoVision) Stop() error {
g.log.Log(logger.Info, pkg+"RTP, RTSP and RTCP clients stopped and closed")
g.isRunning = false
return nil
}
@ -339,3 +343,8 @@ func parseSvrRTCPPort(resp rtsp.Response) (int, error) {
}
return 0, errors.New("SETUP response did not provide RTCP port")
}
// IsRunning is used to determine if the geovision is running.
func (g *GeoVision) IsRunning() bool {
return g.isRunning
}

View File

@ -0,0 +1,74 @@
/*
DESCRIPTION
geovision_test.go tests the geovision AVDevice.
AUTHORS
Scott Barnard <scott@ausocean.org>
LICENSE
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
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 geovision
import (
"bytes"
"testing"
"time"
"bitbucket.org/ausocean/av/codec/codecutil"
"bitbucket.org/ausocean/av/revid/config"
"bitbucket.org/ausocean/utils/logger"
)
func TestIsRunning(t *testing.T) {
const dur = 250 * time.Millisecond
const ip = "192.168.4.20"
l := logger.New(logger.Debug, &bytes.Buffer{}, true) // Discard logs.
d := New(l)
err := d.Set(config.Config{
Logger: l,
InputCodec: codecutil.H264,
CameraIP: ip,
})
if err != nil {
t.Skipf("could not set device: %w", err)
}
err = d.Start()
if err != nil {
t.Fatalf("could not start device %w", err)
}
time.Sleep(dur)
if !d.IsRunning() {
t.Error("device isn't running, when it should be")
}
err = d.Stop()
if err != nil {
t.Error(err.Error())
}
time.Sleep(dur)
if d.IsRunning() {
t.Error("device is running, when it should not be")
}
}

View File

@ -107,11 +107,12 @@ var AutoWhiteBalanceModes = [...]string{
// Raspivid is an implementation of AVDevice that provides control over the
// raspivid command to allow reading of data from a Raspberry Pi camera.
type Raspivid struct {
cfg config.Config
cmd *exec.Cmd
out io.ReadCloser
log config.Logger
done chan struct{}
cfg config.Config
cmd *exec.Cmd
out io.ReadCloser
log config.Logger
done chan struct{}
isRunning bool
}
// New returns a new Raspivid.
@ -287,6 +288,7 @@ func (r *Raspivid) Start() error {
if err != nil {
return fmt.Errorf("could not start raspivid command: %w", err)
}
r.isRunning = true
return nil
}
@ -310,5 +312,11 @@ func (r *Raspivid) Stop() error {
if err != nil {
return fmt.Errorf("could not kill raspivid process: %w", err)
}
r.isRunning = false
return r.out.Close()
}
// IsRunning is used to determine if the pi's camera is running.
func (r *Raspivid) IsRunning() bool {
return r.isRunning
}

View File

@ -0,0 +1,71 @@
/*
DESCRIPTION
raspivid_test.go tests the raspivid AVDevice.
AUTHORS
Scott Barnard <scott@ausocean.org>
LICENSE
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
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 raspivid
import (
"bytes"
"testing"
"time"
"bitbucket.org/ausocean/av/codec/codecutil"
"bitbucket.org/ausocean/av/revid/config"
"bitbucket.org/ausocean/utils/logger"
)
func TestIsRunning(t *testing.T) {
const dur = 250 * time.Millisecond
l := logger.New(logger.Debug, &bytes.Buffer{}, true) // Discard logs.
d := New(l)
err := d.Set(config.Config{
Logger: l,
InputCodec: codecutil.H264,
})
if err != nil {
t.Skipf("could not set device: %w", err)
}
err = d.Start()
if err != nil {
t.Fatalf("could not start device %w", err)
}
time.Sleep(dur)
if !d.IsRunning() {
t.Error("device isn't running, when it should be")
}
err = d.Stop()
if err != nil {
t.Error(err.Error())
}
time.Sleep(dur)
if d.IsRunning() {
t.Error("device is running, when it should not be")
}
}

View File

@ -62,11 +62,12 @@ var (
// Webcam is an implementation of the AVDevice interface for a Webcam. Webcam
// uses an ffmpeg process to pipe the video data from the webcam.
type Webcam struct {
out io.ReadCloser
log config.Logger
cfg config.Config
cmd *exec.Cmd
done chan struct{}
out io.ReadCloser
log config.Logger
cfg config.Config
cmd *exec.Cmd
done chan struct{}
isRunning bool
}
// New returns a new Webcam.
@ -161,6 +162,8 @@ func (w *Webcam) Start() error {
return fmt.Errorf("could not pipe command error: %w", err)
}
w.isRunning = true
go func() {
for {
select {
@ -202,7 +205,12 @@ func (w *Webcam) Stop() error {
if err != nil {
return fmt.Errorf("could not kill ffmpeg process: %w", err)
}
return w.out.Close()
err = w.out.Close()
if err == nil {
w.isRunning = false
return nil
}
return err
}
// Read implements io.Reader. If the pipe is nil a read error is returned.
@ -212,3 +220,8 @@ func (w *Webcam) Read(p []byte) (int, error) {
}
return 0, errors.New("webcam not streaming")
}
// IsRunning is used to determine if the webcam is running.
func (w *Webcam) IsRunning() bool {
return w.isRunning
}

View File

@ -0,0 +1,71 @@
/*
DESCRIPTION
webcam_test.go tests the webcam AVDevice.
AUTHORS
Scott Barnard <scott@ausocean.org>
LICENSE
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
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 webcam
import (
"bytes"
"testing"
"time"
"bitbucket.org/ausocean/av/codec/codecutil"
"bitbucket.org/ausocean/av/revid/config"
"bitbucket.org/ausocean/utils/logger"
)
func TestIsRunning(t *testing.T) {
const dur = 250 * time.Millisecond
l := logger.New(logger.Debug, &bytes.Buffer{}, true) // Discard logs.
d := New(l)
err := d.Set(config.Config{
Logger: l,
InputCodec: codecutil.H264,
})
if err != nil {
t.Skipf("could not set device: %w", err)
}
err = d.Start()
if err != nil {
t.Fatalf("could not start device %w", err)
}
time.Sleep(dur)
if !d.IsRunning() {
t.Error("device isn't running, when it should be")
}
err = d.Stop()
if err != nil {
t.Error(err.Error())
}
time.Sleep(dur)
if d.IsRunning() {
t.Error("device is running, when it should not be")
}
}