Skip to content

Commit

Permalink
Improved Controls support (closes: #21)
Browse files Browse the repository at this point in the history
  • Loading branch information
aamcrae authored and Oleksandr Senkovych committed Jan 20, 2019
1 parent c9c96d5 commit 26d4afb
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 12 deletions.
40 changes: 40 additions & 0 deletions examples/getcontrols/getcontrols.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Example program that reads the list of available controls and prints them.
package main

import (
"flag"
"fmt"

"github.com/blackjack/webcam"
)

var device = flag.String("input", "/dev/video0", "Input video device")

func main() {
flag.Parse()
cam, err := webcam.Open(*device)
if err != nil {
panic(err.Error())
}
defer cam.Close()

fmap := cam.GetSupportedFormats()
fmt.Println("Available Formats: ")
for p, s := range fmap {
var pix []byte
for i := 0; i < 4; i++ {
pix = append(pix, byte(p>>uint(i*8)))
}
fmt.Printf("ID:%08x ('%s') %s\n ", p, pix, s)
for _, fs := range cam.GetSupportedFrameSizes(p) {
fmt.Printf(" %s", fs.GetString())
}
fmt.Printf("\n")
}

cmap := cam.GetControls()
fmt.Println("Available controls: ")
for id, c := range cmap {
fmt.Printf("ID:%08x %-32s Min: %4d Max: %5d\n", id, c.Name, c.Min, c.Max)
}
}
86 changes: 74 additions & 12 deletions v4l2.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@ import (
"golang.org/x/sys/unix"
)

type controlType int

const (
c_int controlType = iota
c_bool
c_menu
)

type control struct {
id uint32
name string
c_type controlType
min int32
max int32
}

const (
V4L2_CAP_VIDEO_CAPTURE uint32 = 0x00000001
V4L2_CAP_STREAMING uint32 = 0x04000000
Expand All @@ -29,6 +45,28 @@ const (
V4L2_CID_PRIVATE_BASE uint32 = 0x08000000
)

const (
V4L2_CTRL_TYPE_INTEGER uint32 = 1
V4L2_CTRL_TYPE_BOOLEAN uint32 = 2
V4L2_CTRL_TYPE_MENU uint32 = 3
V4L2_CTRL_TYPE_BUTTON uint32 = 4
V4L2_CTRL_TYPE_INTEGER64 uint32 = 5
V4L2_CTRL_TYPE_CTRL_CLASS uint32 = 6
V4L2_CTRL_TYPE_STRING uint32 = 7
V4L2_CTRL_TYPE_BITMASK uint32 = 8
V4L2_CTRL_TYPE_INTEGER_MENU uint32 = 9

V4L2_CTRL_COMPOUND_TYPES uint32 = 0x0100
V4L2_CTRL_TYPE_U8 uint32 = 0x0100
V4L2_CTRL_TYPE_U16 uint32 = 0x0101
V4L2_CTRL_TYPE_U32 uint32 = 0x0102
)

const (
V4L2_CTRL_FLAG_DISABLED uint32 = 0x00000001
V4L2_CTRL_FLAG_NEXT_CTRL uint32 = 0x80000000
)

var (
VIDIOC_QUERYCAP = ioctl.IoR(uintptr('V'), 0, unsafe.Sizeof(v4l2_capability{}))
VIDIOC_ENUM_FMT = ioctl.IoRW(uintptr('V'), 2, unsafe.Sizeof(v4l2_fmtdesc{}))
Expand All @@ -37,6 +75,7 @@ var (
VIDIOC_QUERYBUF = ioctl.IoRW(uintptr('V'), 9, unsafe.Sizeof(v4l2_buffer{}))
VIDIOC_QBUF = ioctl.IoRW(uintptr('V'), 15, unsafe.Sizeof(v4l2_buffer{}))
VIDIOC_DQBUF = ioctl.IoRW(uintptr('V'), 17, unsafe.Sizeof(v4l2_buffer{}))
VIDIOC_G_CTRL = ioctl.IoRW(uintptr('V'), 27, unsafe.Sizeof(v4l2_control{}))
VIDIOC_S_CTRL = ioctl.IoRW(uintptr('V'), 28, unsafe.Sizeof(v4l2_control{}))
VIDIOC_QUERYCTRL = ioctl.IoRW(uintptr('V'), 36, unsafe.Sizeof(v4l2_queryctrl{}))
//sizeof int32
Expand Down Expand Up @@ -418,28 +457,51 @@ func waitForFrame(fd uintptr, timeout uint32) (count int, err error) {

}

func getControl(fd uintptr, id uint32) (int32, error) {
ctrl := &v4l2_control{}
ctrl.id = id
err := ioctl.Ioctl(fd, VIDIOC_G_CTRL, uintptr(unsafe.Pointer(ctrl)))
return ctrl.value, err
}

func setControl(fd uintptr, id uint32, val int32) error {
ctrl := &v4l2_control{}
ctrl.id = id
ctrl.value = val
return ioctl.Ioctl(fd, VIDIOC_S_CTRL, uintptr(unsafe.Pointer(ctrl)))
}

func getControls(fd uintptr) map[uint32]string {
query := &v4l2_queryctrl{}
var controls map[uint32]string
func queryControls(fd uintptr) []control {
controls := []control{}
var err error
for query.id = V4L2_CID_BASE; err == nil; query.id++ {
err = ioctl.Ioctl(fd, VIDIOC_QUERYCTRL, uintptr(unsafe.Pointer(query)))
if err == nil {
controls[query.id] = CToGoString(query.name[:])
}
}
err = nil
for query.id = V4L2_CID_PRIVATE_BASE; err == nil; query.id++ {
// Don't use V42L_CID_BASE since it is the same as brightness.
var id uint32
for err == nil {
id |= V4L2_CTRL_FLAG_NEXT_CTRL
query := &v4l2_queryctrl{}
query.id = id
err = ioctl.Ioctl(fd, VIDIOC_QUERYCTRL, uintptr(unsafe.Pointer(query)))
id = query.id
if err == nil {
controls[query.id] = CToGoString(query.name[:])
if (query.flags & V4L2_CTRL_FLAG_DISABLED) != 0 {
continue
}
var c control
switch query._type {
default:
continue
case V4L2_CTRL_TYPE_INTEGER, V4L2_CTRL_TYPE_INTEGER64:
c.c_type = c_int
case V4L2_CTRL_TYPE_BOOLEAN:
c.c_type = c_bool
case V4L2_CTRL_TYPE_MENU:
c.c_type = c_menu
}
c.id = id
c.name = CToGoString(query.name[:])
c.min = query.minimum
c.max = query.maximum
controls = append(controls, c)
}
}
return controls
Expand Down
27 changes: 27 additions & 0 deletions webcam.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ type Webcam struct {
streaming bool
}

type ControlID uint32

type Control struct {
Name string
Min int32
Max int32
}

// Open a webcam with a given path
// Checks if device is a v4l2 device and if it is
// capable to stream video
Expand Down Expand Up @@ -126,6 +134,25 @@ func (w *Webcam) SetBufferCount(count uint32) error {
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{c.name, c.min, c.max}
}
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)
}

// Start streaming process
func (w *Webcam) StartStreaming() error {
if w.streaming {
Expand Down

0 comments on commit 26d4afb

Please sign in to comment.