2021-01-20 04:19:01 +03:00
|
|
|
/*
|
|
|
|
DESCRIPTION
|
|
|
|
raspistill.go provides an implementation of the AVDevice interface for the
|
|
|
|
raspistill raspberry pi camera interfacing utility. This allows for the
|
|
|
|
capture of single frames over time, i.e. a timelapse form of capture.
|
|
|
|
|
|
|
|
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 rovides an implementation of the AVDevice interface for the
|
|
|
|
// raspistill raspberry pi camera interfacing utility. This allows for the
|
|
|
|
// capture of single frames over time, i.e. a timelapse form of capture.
|
|
|
|
package raspistill
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"bitbucket.org/ausocean/av/device"
|
|
|
|
"bitbucket.org/ausocean/av/revid/config"
|
|
|
|
)
|
|
|
|
|
|
|
|
// To indicate package when logging.
|
|
|
|
const pkg = "raspistill: "
|
|
|
|
|
|
|
|
// Config field validation bounds.
|
|
|
|
const (
|
2021-02-03 04:47:23 +03:00
|
|
|
minTimelapseDuration = 10 * time.Second // s
|
2021-01-20 04:19:01 +03:00
|
|
|
maxTimelapseDuration = 86400 * time.Second // s = 24 hours
|
|
|
|
minTimelapseInterval = 1 * time.Second // s
|
|
|
|
maxTimelapseInterval = 86400 * time.Second // s = 24 hours
|
|
|
|
)
|
|
|
|
|
2021-01-27 06:11:29 +03:00
|
|
|
// Raspistill configuration defaults.
|
2021-01-20 04:19:01 +03:00
|
|
|
const (
|
|
|
|
defaultRotation = 0 // degrees
|
|
|
|
defaultWidth = 1280 // pixels
|
|
|
|
defaultHeight = 720 // pixels
|
2021-01-27 06:11:29 +03:00
|
|
|
defaultJPEGQuality = 75 // %
|
2021-01-20 04:19:01 +03:00
|
|
|
defaultTimelapseDuration = maxTimelapseDuration // ms
|
|
|
|
defaultTimelapseInterval = 3600 * time.Second // ms
|
|
|
|
)
|
|
|
|
|
|
|
|
// Configuration errors.
|
|
|
|
var (
|
2021-01-27 06:11:29 +03:00
|
|
|
errBadRotation = fmt.Errorf("Rotation bad or unset, defaulting to: %v", defaultRotation)
|
|
|
|
errBadWidth = fmt.Errorf("Width bad or unset, defaulting to: %v", defaultWidth)
|
|
|
|
errBadHeight = fmt.Errorf("Height bad or unset, defaulting to: %v", defaultHeight)
|
|
|
|
errBadJPEGQuality = fmt.Errorf("JPEGQuality bad or unset, defaulting to: %v", defaultJPEGQuality)
|
2021-01-20 04:19:01 +03:00
|
|
|
errBadTimelapseDuration = fmt.Errorf("TimelapseDuration bad or unset, defaulting to: %v", defaultTimelapseDuration)
|
|
|
|
errBadTimelapseInterval = fmt.Errorf("TimelapseInterval bad or unset, defaulting to: %v", defaultTimelapseInterval)
|
|
|
|
)
|
|
|
|
|
2021-01-27 06:11:29 +03:00
|
|
|
// Misc errors.
|
2021-01-20 04:19:01 +03:00
|
|
|
var errNotStarted = errors.New("cannot read, raspistill not started")
|
|
|
|
|
|
|
|
// Raspistill is an implementation of AVDevice that provides control over the
|
|
|
|
// raspistill utility for using the raspberry pi camera for the capture of
|
|
|
|
// singular images.
|
|
|
|
type Raspistill struct{ raspistill }
|
|
|
|
|
2021-01-27 06:11:29 +03:00
|
|
|
// New returns a new Raspistill.
|
2021-01-20 04:19:01 +03:00
|
|
|
func New(l config.Logger) *Raspistill { return &Raspistill{raspistill: new(l)} }
|
|
|
|
|
|
|
|
// Start will prepare the arguments for the raspistill command using the
|
|
|
|
// configuration set using the Set method then call the raspistill command,
|
|
|
|
// piping the image output from which the Read method will read from.
|
|
|
|
func (r *Raspistill) Start() error { return r.start() }
|
|
|
|
|
|
|
|
// Read implements io.Reader. Calling read before Start has been called will
|
|
|
|
// result in 0 bytes read and an error.
|
|
|
|
func (r *Raspistill) Read(p []byte) (int, error) { return r.read(p) }
|
|
|
|
|
|
|
|
// Stop will terminate the raspistill process and close the output pipe.
|
|
|
|
func (r *Raspistill) Stop() error { return r.stop() }
|
|
|
|
|
|
|
|
// IsRunning is used to determine if the pi's camera is running.
|
|
|
|
func (r *Raspistill) IsRunning() bool { return r.isRunning }
|
|
|
|
|
|
|
|
// Name returns the name of the device.
|
|
|
|
func (r *Raspistill) Name() string { return "Raspistill" }
|
|
|
|
|
|
|
|
// Set will take a Config struct, check the validity of the relevant fields
|
|
|
|
// and then performs any configuration necessary. If fields are not valid,
|
|
|
|
// an error is added to the multiError and a default value is used.
|
|
|
|
func (r *Raspistill) Set(c config.Config) error {
|
|
|
|
var errs device.MultiError
|
|
|
|
|
|
|
|
if c.Rotation > 359 || c.Rotation < 0 {
|
|
|
|
c.Rotation = defaultRotation
|
|
|
|
errs = append(errs, errBadRotation)
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.Width == 0 {
|
|
|
|
c.Width = defaultWidth
|
|
|
|
errs = append(errs, errBadWidth)
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.Height == 0 {
|
|
|
|
c.Height = defaultHeight
|
|
|
|
errs = append(errs, errBadHeight)
|
|
|
|
}
|
|
|
|
|
2021-01-27 06:11:29 +03:00
|
|
|
if c.JPEGQuality < 0 || c.JPEGQuality > 100 {
|
|
|
|
c.JPEGQuality = defaultJPEGQuality
|
|
|
|
errs = append(errs, errBadJPEGQuality)
|
2021-01-20 04:19:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if c.TimelapseDuration > maxTimelapseDuration || c.TimelapseDuration < minTimelapseDuration {
|
|
|
|
c.TimelapseDuration = defaultTimelapseDuration
|
|
|
|
errs = append(errs, errBadTimelapseDuration)
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.TimelapseInterval > maxTimelapseInterval || c.TimelapseInterval < minTimelapseInterval {
|
|
|
|
c.TimelapseInterval = defaultTimelapseInterval
|
|
|
|
errs = append(errs, errBadTimelapseInterval)
|
|
|
|
}
|
|
|
|
|
|
|
|
r.cfg = c
|
|
|
|
return errs
|
|
|
|
}
|