initial commit

This commit is contained in:
2023-06-27 16:06:07 -07:00
commit 5d726183d5
6 changed files with 121 additions and 0 deletions

3
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,3 @@
# These are supported funding model platforms
github: taigrr # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]

12
LICENSE Normal file
View File

@@ -0,0 +1,12 @@
Copyright (C) 2023 by Tai Groot <tai@taigrr.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

7
README.md Normal file
View File

@@ -0,0 +1,7 @@
# VidNumerator
This is a tiny library that only exports one function: `EnumerateVideoDevices`.
This function uses syscalls to efficiently determine which `/dev/videoN` devices
are webcams and which are the additional metadata control handles.
The list of strings returned are the full filepaths to valid devices.

5
go.mod Normal file
View File

@@ -0,0 +1,5 @@
module github.com/taigrr/vidnumerator
go 1.20
require golang.org/x/sys v0.9.0

2
go.sum Normal file
View File

@@ -0,0 +1,2 @@
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

92
vidnumerator.go Normal file
View File

@@ -0,0 +1,92 @@
package vidnumerator
import (
"fmt"
"os"
"path/filepath"
"strings"
"unsafe"
"golang.org/x/sys/unix"
)
const (
IOCNrBits = 8
IOCTypeBits = 8
IOCSizeBits = 14
IOCDirBits = 2
IOCNone = 0
IOCWrite = 1
IOCRead = 2
IOCNrShift = 0
IOCTypeShift = (IOCNrShift + IOCNrBits)
IOCSizeShift = (IOCTypeShift + IOCTypeBits)
IOCDirShift = (IOCSizeShift + IOCSizeBits)
VidIOCQueryCap = (IOCRead << IOCDirShift) |
(uintptr('V') << IOCTypeShift) |
(0 << IOCNrShift) |
(unsafe.Sizeof(cap{}) << IOCSizeShift)
)
type cap struct {
driver [16]uint8
card [32]uint8
busInfo [32]uint8
version uint32
capabilities uint32
deviceCaps uint32
reserved [3]uint32
}
func (r *cap) QueryFd(fileDesciptor int) error {
if r == nil {
return fmt.Errorf("nil receiver")
}
_, _, errorNumber := unix.Syscall(
unix.SYS_IOCTL,
uintptr(fileDesciptor),
VidIOCQueryCap,
uintptr(unsafe.Pointer(r)),
)
if errorNumber != 0 {
return errorNumber
}
return nil
}
// this function checks the ioctl for VIDIOC_QUERYCAP to see if the device is a video capture device
func EnumeratedVideoDevices() []string {
// list all files in the /dev directory
d, err := os.ReadDir("/dev")
if err != nil {
return []string{}
}
// iterate over the files in the directory
devNames := []string{}
for _, file := range d {
if file.IsDir() {
continue
}
fname := file.Name()
if !strings.HasPrefix(fname, "video") {
continue
}
fname = filepath.Join("/dev/", fname)
f, err := os.OpenFile(fname, os.O_RDONLY, 0o755)
if err != nil {
continue
}
fd := f.Fd()
ic := cap{}
err = ic.QueryFd(int(fd))
if err != nil {
continue
}
if ic.deviceCaps == 69206017 {
devNames = append(devNames, fname)
}
}
return devNames
}