mirror of https://bitbucket.org/ausocean/av.git
139 lines
3.2 KiB
Go
139 lines
3.2 KiB
Go
// +build !test
|
|
|
|
/*
|
|
DESCRIPTION
|
|
release.go provides implementations for the Raspistill struct for release
|
|
conditions, i.e. we're running on a raspberry pi with access to the actual
|
|
raspistill utility with a pi camera connected. The code here runs a raspistill
|
|
background process and reads captured images from the camera.
|
|
|
|
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 (
|
|
"bufio"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
"bitbucket.org/ausocean/av/revid/config"
|
|
"bitbucket.org/ausocean/utils/logging"
|
|
)
|
|
|
|
type raspistill struct {
|
|
cfg config.Config
|
|
cmd *exec.Cmd
|
|
out io.ReadCloser
|
|
log logging.Logger
|
|
done chan struct{}
|
|
isRunning bool
|
|
}
|
|
|
|
func new(l logging.Logger) raspistill {
|
|
return raspistill{
|
|
log: l,
|
|
done: make(chan struct{}),
|
|
}
|
|
}
|
|
|
|
func (r *Raspistill) stop() error {
|
|
if r.isRunning == false {
|
|
return nil
|
|
}
|
|
close(r.done)
|
|
if r.cmd == nil || r.cmd.Process == nil {
|
|
return errors.New("raspistill process was never started")
|
|
}
|
|
err := r.cmd.Process.Kill()
|
|
if err != nil {
|
|
return fmt.Errorf("could not kill raspistill process: %w", err)
|
|
}
|
|
r.isRunning = false
|
|
return r.out.Close()
|
|
}
|
|
|
|
func (r *Raspistill) start() error {
|
|
args := []string{
|
|
"--output", "-",
|
|
"--nopreview",
|
|
"--width", fmt.Sprint(r.cfg.Width),
|
|
"--height", fmt.Sprint(r.cfg.Height),
|
|
"--rotation", fmt.Sprint(r.cfg.Rotation),
|
|
"--timeout", fmt.Sprint(r.cfg.TimelapseDuration),
|
|
"--timelapse", fmt.Sprint(r.cfg.TimelapseInterval),
|
|
"--quality", fmt.Sprint(r.cfg.JPEGQuality),
|
|
}
|
|
|
|
r.log.Info(pkg+"raspistill args", "args", strings.Join(args, " "))
|
|
r.cmd = exec.Command("raspistill", args...)
|
|
|
|
var err error
|
|
r.out, err = r.cmd.StdoutPipe()
|
|
if err != nil {
|
|
return fmt.Errorf("could not pipe command output: %w", err)
|
|
}
|
|
|
|
stderr, err := r.cmd.StderrPipe()
|
|
if err != nil {
|
|
return fmt.Errorf("could not pipe command error: %w", err)
|
|
}
|
|
|
|
go func() {
|
|
errScnr := bufio.NewScanner(stderr)
|
|
for {
|
|
select {
|
|
case <-r.done:
|
|
r.log.Info("raspistill.Stop() called, finished checking stderr")
|
|
return
|
|
default:
|
|
}
|
|
|
|
if errScnr.Scan() {
|
|
r.log.Error("error line from raspistill stderr", "error", errScnr.Text())
|
|
continue
|
|
}
|
|
|
|
err := errScnr.Err()
|
|
if err != nil {
|
|
r.log.Error("error from stderr scan", "error", err)
|
|
}
|
|
}
|
|
}()
|
|
|
|
err = r.cmd.Start()
|
|
if err != nil {
|
|
return fmt.Errorf("could not start raspistill process: %w", err)
|
|
}
|
|
r.isRunning = true
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *Raspistill) read(p []byte) (int, error) {
|
|
if r.out != nil {
|
|
return r.out.Read(p)
|
|
}
|
|
return 0, errNotStarted
|
|
}
|