Files
webrtc/vendor/github.com/blackjack/webcam/webcam.go
T
2024-03-29 16:00:35 +01:00

352 lines
7.5 KiB
Go

// Library for working with webcams and other video capturing devices.
// It depends entirely on v4l2 framework, thus will compile and work
// only on Linux machine
package webcam
import (
"errors"
"reflect"
"unsafe"
"golang.org/x/sys/unix"
)
// Webcam object
type Webcam struct {
fd uintptr
bufcount uint32
buffers [][]byte
streaming bool
pollFds []unix.PollFd
}
type ControlID uint32
type Control struct {
Name string
Min int32
Max int32
Type int32
Step int32
}
// Open a webcam with a given path
// Checks if device is a v4l2 device and if it is
// capable to stream video
func Open(path string) (*Webcam, error) {
handle, err := unix.Open(path, unix.O_RDWR|unix.O_NONBLOCK, 0666)
fd := uintptr(handle)
if fd < 0 || err != nil {
return nil, err
}
supportsVideoCapture, supportsVideoStreaming, err := checkCapabilities(fd)
if err != nil {
return nil, err
}
if !supportsVideoCapture {
return nil, errors.New("Not a video capture device")
}
if !supportsVideoStreaming {
return nil, errors.New("Device does not support the streaming I/O method")
}
w := new(Webcam)
w.fd = fd
w.bufcount = 256
w.pollFds = []unix.PollFd{{Fd: int32(fd), Events: unix.POLLIN}}
return w, nil
}
// Returns image formats supported by the device alongside with
// their text description
// Note that this function is somewhat experimental. Frames are not ordered in
// any meaning, also duplicates can occur so it's up to developer to clean it up.
// See http://linuxtv.org/downloads/v4l-dvb-apis/vidioc-enum-framesizes.html
// for more information
func (w *Webcam) GetSupportedFormats() map[PixelFormat]string {
result := make(map[PixelFormat]string)
var err error
var code uint32
var desc string
var index uint32
for index = 0; err == nil; index++ {
code, desc, err = getPixelFormat(w.fd, index)
if err != nil {
break
}
result[PixelFormat(code)] = desc
}
return result
}
// GetName returns the human-readable name of the device
func (w *Webcam) GetName() (string, error) {
return getName(w.fd)
}
// GetBusInfo returns the location of the device in the system
func (w *Webcam) GetBusInfo() (string, error) {
return getBusInfo(w.fd)
}
// SelectInput selects the current video input.
func (w *Webcam) SelectInput(index uint32) error {
return selectInput(w.fd, index)
}
// GetInput queries the current video input.
func (w *Webcam) GetInput() (int32, error) {
return getInput(w.fd)
}
// Returns supported frame sizes for a given image format
func (w *Webcam) GetSupportedFrameSizes(f PixelFormat) []FrameSize {
result := make([]FrameSize, 0)
var index uint32
var err error
for index = 0; err == nil; index++ {
s, err := getFrameSize(w.fd, index, uint32(f))
if err != nil {
break
}
result = append(result, s)
}
return result
}
// GetSupportedFramerates returns supported frame rates for a given image format and frame size.
func (w *Webcam) GetSupportedFramerates(fp PixelFormat, width uint32, height uint32) []FrameRate {
var result []FrameRate
var index uint32
var err error
// keep incrementing the index value until we get an EINVAL error
index = 0
for err == nil {
r, err := getFrameInterval(w.fd, index, uint32(fp), width, height)
if err != nil {
break
}
result = append(result, r)
index++
}
return result
}
// Sets desired image format and frame size
// Note, that device driver can change that values.
// Resulting values are returned by a function
// alongside with an error if any
func (w *Webcam) SetImageFormat(f PixelFormat, width, height uint32) (PixelFormat, uint32, uint32, error) {
code := uint32(f)
cw := width
ch := height
err := setImageFormat(w.fd, &code, &width, &height)
if err != nil {
return 0, 0, 0, err
} else {
return PixelFormat(code), cw, ch, nil
}
}
// Set the number of frames to be buffered.
// Not allowed if streaming is already on.
func (w *Webcam) SetBufferCount(count uint32) error {
if w.streaming {
return errors.New("Cannot set buffer count when streaming")
}
w.bufcount = count
return nil
}
// Get a map of available controls.
func (w *Webcam) GetControls() map[ControlID]Control {
cmap := make(map[ControlID]Control)
for _, c := range queryControls(w.fd) {
cmap[ControlID(c.id)] = Control{
Name: c.name,
Min: c.min,
Max: c.max,
Type: int32(c.c_type),
Step: c.step,
}
}
return cmap
}
// Get the value of a control.
func (w *Webcam) GetControl(id ControlID) (int32, error) {
return getControl(w.fd, uint32(id))
}
// Set a control.
func (w *Webcam) SetControl(id ControlID, value int32) error {
return setControl(w.fd, uint32(id), value)
}
// Get the framerate.
func (w *Webcam) GetFramerate() (float32, error) {
return getFramerate(w.fd)
}
// Set FPS
func (w *Webcam) SetFramerate(fps float32) error {
return setFramerate(w.fd, 1000, uint32(1000*(fps)))
}
// Start streaming process
func (w *Webcam) StartStreaming() error {
if w.streaming {
return errors.New("Already streaming")
}
err := mmapRequestBuffers(w.fd, &w.bufcount)
if err != nil {
return errors.New("Failed to map request buffers: " + string(err.Error()))
}
w.buffers = make([][]byte, w.bufcount, w.bufcount)
for index, _ := range w.buffers {
var length uint32
buffer, err := mmapQueryBuffer(w.fd, uint32(index), &length)
if err != nil {
return errors.New("Failed to map memory: " + string(err.Error()))
}
w.buffers[index] = buffer
}
for index, _ := range w.buffers {
err := mmapEnqueueBuffer(w.fd, uint32(index))
if err != nil {
return errors.New("Failed to enqueue buffer: " + string(err.Error()))
}
}
err = startStreaming(w.fd)
if err != nil {
return errors.New("Failed to start streaming: " + string(err.Error()))
}
w.streaming = true
return nil
}
// Read a single frame from the webcam
// If frame cannot be read at the moment
// function will return empty slice
func (w *Webcam) ReadFrame() ([]byte, error) {
result, index, err := w.GetFrame()
if err == nil {
w.ReleaseFrame(index)
}
return result, err
}
// Get a single frame from the webcam and return the frame and
// the buffer index. To return the buffer, ReleaseFrame must be called.
// If frame cannot be read at the moment
// function will return empty slice
func (w *Webcam) GetFrame() ([]byte, uint32, error) {
var index uint32
var length uint32
err := mmapDequeueBuffer(w.fd, &index, &length)
if err != nil {
return nil, 0, err
}
return w.buffers[int(index)][:length], index, nil
}
// Release the frame buffer that was obtained via GetFrame
func (w *Webcam) ReleaseFrame(index uint32) error {
return mmapEnqueueBuffer(w.fd, index)
}
// Wait until frame could be read
func (w *Webcam) WaitForFrame(timeout uint32) error {
count, err := waitForFrame(w.pollFds, timeout)
if count < 0 || err != nil {
return err
} else if count == 0 {
return new(Timeout)
} else {
return nil
}
}
func (w *Webcam) StopStreaming() error {
if !w.streaming {
return errors.New("Request to stop streaming when not streaming")
}
w.streaming = false
for _, buffer := range w.buffers {
err := mmapReleaseBuffer(buffer)
if err != nil {
return err
}
}
return stopStreaming(w.fd)
}
// Close the device
func (w *Webcam) Close() error {
if w.streaming {
w.StopStreaming()
}
err := unix.Close(int(w.fd))
return err
}
// Sets automatic white balance correction
func (w *Webcam) SetAutoWhiteBalance(val bool) error {
v := int32(0)
if val {
v = 1
}
return setControl(w.fd, V4L2_CID_AUTO_WHITE_BALANCE, v)
}
func gobytes(p unsafe.Pointer, n int) []byte {
h := reflect.SliceHeader{uintptr(p), n, n}
s := *(*[]byte)(unsafe.Pointer(&h))
return s
}