mirror of
https://github.com/taigrr/vidnumerator.git
synced 2026-04-02 05:08:52 -07:00
initial commit
This commit is contained in:
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal 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
12
LICENSE
Normal 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
7
README.md
Normal 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
5
go.mod
Normal 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
2
go.sum
Normal 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
92
vidnumerator.go
Normal 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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user