Added error returns, removed panics
This commit is contained in:
parent
5de0d207f5
commit
3f4ae6eb23
7 changed files with 232 additions and 98 deletions
71
camera.go
71
camera.go
|
@ -1,6 +1,7 @@
|
|||
package vidio
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
@ -52,7 +53,7 @@ func (camera *Camera) FrameBuffer() []byte {
|
|||
|
||||
// Returns the webcam device name.
|
||||
// On windows, ffmpeg output from the -list_devices command is parsed to find the device name.
|
||||
func getDevicesWindows() []string {
|
||||
func getDevicesWindows() ([]string, error) {
|
||||
// Run command to get list of devices.
|
||||
cmd := exec.Command(
|
||||
"ffmpeg",
|
||||
|
@ -63,10 +64,12 @@ func getDevicesWindows() []string {
|
|||
)
|
||||
pipe, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
pipe.Close()
|
||||
return nil, err
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
panic(err)
|
||||
cmd.Process.Kill()
|
||||
return nil, err
|
||||
}
|
||||
// Read list devices from Stdout.
|
||||
buffer := make([]byte, 2<<10)
|
||||
|
@ -80,28 +83,34 @@ func getDevicesWindows() []string {
|
|||
}
|
||||
cmd.Wait()
|
||||
devices := parseDevices(buffer)
|
||||
return devices
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
// Get camera meta data such as width, height, fps and codec.
|
||||
func getCameraData(device string, camera *Camera) {
|
||||
func getCameraData(device string, camera *Camera) error {
|
||||
// Run command to get camera data.
|
||||
// Webcam will turn on and then off in quick succession.
|
||||
webcamDeviceName, err := webcam()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := exec.Command(
|
||||
"ffmpeg",
|
||||
"-hide_banner",
|
||||
"-f", webcam(),
|
||||
"-f", webcamDeviceName,
|
||||
"-i", device,
|
||||
)
|
||||
// The command will fail since we do not give a file to write to, therefore
|
||||
// it will write the meta data to Stderr.
|
||||
pipe, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
pipe.Close()
|
||||
return err
|
||||
}
|
||||
// Start the command.
|
||||
if err := cmd.Start(); err != nil {
|
||||
panic(err)
|
||||
cmd.Process.Kill()
|
||||
return err
|
||||
}
|
||||
// Read ffmpeg output from Stdout.
|
||||
buffer := make([]byte, 2<<11)
|
||||
|
@ -117,51 +126,61 @@ func getCameraData(device string, camera *Camera) {
|
|||
cmd.Wait()
|
||||
|
||||
parseWebcamData(buffer[:total], camera)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Creates a new camera struct that can read from the device with the given stream index.
|
||||
func NewCamera(stream int) *Camera {
|
||||
func NewCamera(stream int) (*Camera, error) {
|
||||
// Check if ffmpeg is installed on the users machine.
|
||||
checkExists("ffmpeg")
|
||||
if err := checkExists("ffmpeg"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var device string
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
device = "/dev/video" + strconv.Itoa(stream)
|
||||
break
|
||||
case "darwin":
|
||||
device = strconv.Itoa(stream)
|
||||
break
|
||||
case "windows":
|
||||
// If OS is windows, we need to parse the listed devices to find which corresponds to the
|
||||
// given "stream" index.
|
||||
devices := getDevicesWindows()
|
||||
devices, err := getDevicesWindows()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if stream >= len(devices) {
|
||||
panic("Could not find devices with index: " + strconv.Itoa(stream))
|
||||
return nil, errors.New("Could not find device with index: " + strconv.Itoa(stream))
|
||||
}
|
||||
device = "video=" + devices[stream]
|
||||
break
|
||||
default:
|
||||
panic("Unsupported OS: " + runtime.GOOS)
|
||||
return nil, errors.New("Unsupported OS: " + runtime.GOOS)
|
||||
}
|
||||
|
||||
camera := Camera{name: device, depth: 3}
|
||||
getCameraData(device, &camera)
|
||||
return &camera
|
||||
if err := getCameraData(device, &camera); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &camera, nil
|
||||
}
|
||||
|
||||
// Once the user calls Read() for the first time on a Camera struct,
|
||||
// the ffmpeg command which is used to read the camera device is started.
|
||||
func initCamera(camera *Camera) {
|
||||
func initCamera(camera *Camera) error {
|
||||
// If user exits with Ctrl+C, stop ffmpeg process.
|
||||
camera.cleanup()
|
||||
|
||||
webcamDeviceName, err := webcam()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Use ffmpeg to pipe webcam to stdout.
|
||||
cmd := exec.Command(
|
||||
"ffmpeg",
|
||||
"-hide_banner",
|
||||
"-loglevel", "quiet",
|
||||
"-f", webcam(),
|
||||
"-f", webcamDeviceName,
|
||||
"-i", camera.name,
|
||||
"-f", "image2pipe",
|
||||
"-pix_fmt", "rgb24",
|
||||
|
@ -171,21 +190,27 @@ func initCamera(camera *Camera) {
|
|||
camera.cmd = cmd
|
||||
pipe, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
pipe.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
camera.pipe = &pipe
|
||||
if err := cmd.Start(); err != nil {
|
||||
panic(err)
|
||||
cmd.Process.Kill()
|
||||
return err
|
||||
}
|
||||
|
||||
camera.framebuffer = make([]byte, camera.width*camera.height*camera.depth)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reads the next frame from the webcam and stores in the framebuffer.
|
||||
func (camera *Camera) Read() bool {
|
||||
// If cmd is nil, video reading has not been initialized.
|
||||
if camera.cmd == nil {
|
||||
initCamera(camera)
|
||||
if err := initCamera(camera); err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
total := 0
|
||||
for total < camera.width*camera.height*camera.depth {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue