av/device/raspistill/imp_testing.go

182 lines
4.4 KiB
Go
Raw Normal View History

// +build test
/*
DESCRIPTION
test.go provides test implementations of the raspistill methods when the
"test" build tag is specified. In this mode, raspistill simply provides
arbitrary loaded JPEG images when Raspistill.Read() is called.
AUTHORS
Saxon Nelson-Milton <saxon@ausocean.org>
LICENSE
Copyright (C) 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 raspistill
import (
"fmt"
"io"
"io/ioutil"
"os"
"strconv"
"sync"
"time"
"bitbucket.org/ausocean/av/revid/config"
)
const (
noOfImages = 6
imgDir = "/go/src/bitbucket.org/ausocean/test/test-data/av/input/jpeg/"
jpgExt = ".jpg"
)
type raspistill struct {
log config.Logger
cfg config.Config
isRunning bool
images [noOfImages][]byte
imgCnt int
durTicker *time.Ticker
intvlTicker *time.Ticker
buf []byte
init bool
term chan struct{}
mu sync.Mutex
}
func new(l config.Logger) raspistill {
l.Debug("creating new test raspistill input")
r := raspistill{log: l}
// Get to the right file location of the JPEG images.
home, err := os.UserHomeDir()
if err != nil {
panic(fmt.Sprintf("could not get home directory: %v", err))
}
// Load the test images into the images slice.
// We expect the 6 images test images to be named 0.jpg through to 5.jpg.
r.log.Debug("loading test JPEG images")
for i, _ := range r.images {
path := imgDir + strconv.Itoa(i) + jpgExt
r.images[i], err = ioutil.ReadFile(home + imgDir + strconv.Itoa(i) + jpgExt)
if err != nil {
r.log.Fatal("error loading test image", "imageNum", i, "error", err)
}
r.log.Debug("image loaded", "path/name", path, "size", len(r.images[i]))
}
return r
}
// stop sets isRunning flag to false, indicating no further captures. Calls on
// Raspistill.read return error.
func (r *Raspistill) stop() error {
r.log.Debug("stopping test raspistill")
r.isRunning = false
return nil
}
// start creates the timelapse interval and duration tickers i.e. also starting
// them and sets isRunning flag to true indicating that raspistill is capturing.
func (r *Raspistill) start() error {
r.log.Debug("starting test raspistill")
r.durTicker = time.NewTicker(r.cfg.TimelapseDuration)
r.intvlTicker = time.NewTicker(r.cfg.TimelapseInterval)
r.term = make(chan struct{})
r.loadImg()
go r.capture()
r.setRunning(true)
return nil
}
func (r *Raspistill) loadImg() {
r.log.Debug("appending new image on to buffer and copying next image p", "imgCnt", r.imgCnt)
imgBytes := r.images[r.imgCnt%noOfImages]
if len(imgBytes) == 0 {
panic("length of image bytes should not be 0")
}
r.imgCnt++
r.mu.Lock()
r.buf = append(r.buf, imgBytes...)
r.log.Debug("added img to buf", "len(imgBytes)", len(imgBytes))
r.mu.Unlock()
}
func (r *Raspistill) capture() {
for {
select {
case t := <-r.intvlTicker.C:
r.log.Debug("got interval tick", "tick", t)
r.loadImg()
r.intvlTicker.Reset(r.cfg.TimelapseInterval)
case <-r.term:
r.setRunning(false)
return
case t := <-r.durTicker.C:
r.log.Debug("got duration tick, timelapse over", "tick", t)
r.buf = nil
return
}
}
}
// read blocks until either another timelapse interval has completed, in which
// case we provide the next jpeg to p, or, the timelapse duration has completed
// in which case we don't read and provide io.EOF error.
func (r *Raspistill) read(p []byte) (int, error) {
r.log.Debug("reading from test raspistill")
if !r.running() {
return 0, errNotStarted
}
r.mu.Lock()
defer r.mu.Unlock()
if r.buf == nil {
return 0, io.EOF
}
n := copy(p, r.buf)
r.log.Debug("copied", "p[:2]", p[:2], "p[n-2:n]", p[n-2:n])
r.buf = r.buf[n:]
return n, nil
}
func (r *Raspistill) setRunning(s bool) {
r.mu.Lock()
r.isRunning = s
r.mu.Unlock()
}
func (r *Raspistill) running() bool {
r.mu.Lock()
ir := r.isRunning
r.mu.Unlock()
return ir
}