Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ListDevices() and exposed more device capabilities #42

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions examples/list_devices/list_devices.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package main

import (
"flag"
"fmt"

"github.com/blackjack/webcam"
)

func main() {
flag.Parse()

devices, err := webcam.ListDevices()
if err != nil {
panic(err.Error())
}
if len(devices) == 0 {
fmt.Printf("No valid video devices found in %q\n", webcam.VIDEO4LINUX_DIR)
} else {
fmt.Println("Video devices found:")
for devPath, name := range devices {
fmt.Printf(" %q located in %s\n", name, devPath)
}
}
}

9 changes: 3 additions & 6 deletions v4l2.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,18 +203,15 @@ type v4l2_control struct {
value int32
}

func checkCapabilities(fd uintptr) (supportsVideoCapture bool, supportsVideoStreaming bool, err error) {

caps := &v4l2_capability{}
func checkCapabilities(fd uintptr) (caps *v4l2_capability, err error) {

caps = &v4l2_capability{}
err = ioctl.Ioctl(fd, VIDIOC_QUERYCAP, uintptr(unsafe.Pointer(caps)))

if err != nil {
return
return nil, err
}

supportsVideoCapture = (caps.capabilities & V4L2_CAP_VIDEO_CAPTURE) != 0
supportsVideoStreaming = (caps.capabilities & V4L2_CAP_STREAMING) != 0
return

}
Expand Down
90 changes: 82 additions & 8 deletions webcam.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ package webcam

import (
"errors"
"fmt"
"golang.org/x/sys/unix"
"os"
"path"
"path/filepath"
"reflect"
"strings"
"unsafe"
)

Expand All @@ -16,6 +21,7 @@ type Webcam struct {
bufcount uint32
buffers [][]byte
streaming bool
capabilities *v4l2_capability
}

type ControlID uint32
Expand All @@ -38,26 +44,48 @@ func Open(path string) (*Webcam, error) {
return nil, err
}

supportsVideoCapture, supportsVideoStreaming, err := checkCapabilities(fd)

caps, err := checkCapabilities(fd)
if err != nil {
return nil, err
}

if !supportsVideoCapture {
return nil, errors.New("Not a video capture device")
w := &Webcam{
fd: uintptr(fd),
bufcount: 256,
capabilities: caps,
}

if !supportsVideoStreaming {
// Makes sure it supports some form of video capture capability.
if !w.SupportsVideoCapture() {
return nil, errors.New("Not a video capture device")
}
if !w.SupportsVideoStreaming() {
return nil, errors.New("Device does not support the streaming I/O method")
}

w := new(Webcam)
w.fd = uintptr(fd)
w.bufcount = 256
return w, nil
}

func (w *Webcam) SupportsVideoCapture() bool {
return (w.capabilities.capabilities & V4L2_CAP_VIDEO_CAPTURE) != 0
}

func (w *Webcam) SupportsVideoStreaming() bool {
return (w.capabilities.capabilities & V4L2_CAP_STREAMING) != 0
}

func (w *Webcam) Card() string {
return CToGoString(w.capabilities.card[:])
}

func (w *Webcam) Driver() string {
return CToGoString(w.capabilities.driver[:])
}

func (w *Webcam) BusInfo() string {
return CToGoString(w.capabilities.bus_info[:])
}

// Returns image formats supported by the device alongside with
// their text description
// Not that this function is somewhat experimental. Frames are not ordered in
Expand Down Expand Up @@ -281,10 +309,56 @@ func (w *Webcam) SetAutoWhiteBalance(val bool) error {
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
}

// VIDEO4LINUX_DIR path to kernel known list of videos devices.
var VIDEO4LINUX_DIR string = "/sys/class/video4linux"

// ListDevices enumerates video devices present in the system. It returns a map of
// of path names to the "human readable" device name (the "card name").
func ListDevices() (devices map[string]string, err error) {
devices = make(map[string]string)
if _, err = os.Stat(VIDEO4LINUX_DIR); err != nil {
if os.IsNotExist(err) {
// No devices present, make error nil and return an empty list.
err = nil
}
return
}
err = filepath.Walk(VIDEO4LINUX_DIR, func(_ string, info os.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("failure accessing %q: %v", VIDEO4LINUX_DIR, err)
}
if info.IsDir() { return nil } // Root directory.
if !strings.HasPrefix(info.Name(), "video") && !strings.HasPrefix(info.Name(), "subdev") {
return nil
}
devPath := path.Join("/dev", info.Name())
w, err := Open(devPath)
if err != nil{
return fmt.Errorf("Failed to open device %q: %v", devPath, err)
}
defer w.Close()

// For some reason the kernel creates more than one path per actual physical device,
// one of which has no supported formats and can't be used for streaming.
formats := w.GetSupportedFormats()
if len(formats) == 0 {
return nil
}
devices[devPath] = w.Card()
return nil
})
if err != nil {
return nil, err
}
return
}