Files
2026-03-09 01:09:51 -05:00

162 lines
3.5 KiB
Go

//go:build windows && cgo
package capture
import (
"fmt"
"math"
cam "github.com/Kirizu-Official/windows-camera-go/camera/v1"
)
type WebcamDeviceInfo struct {
Index int
Name string
MaxFPS int
}
func ListWebcams() ([]WebcamDeviceInfo, error) {
webcamMu.Lock()
defer webcamMu.Unlock()
temporaryInit := false
if !webcamInitDone {
if err := cam.Init(); err != nil {
return nil, fmt.Errorf("webcam init failed: %w", err)
}
webcamInitDone = true
temporaryInit = true
}
devices, err := cam.EnumDevice()
if err != nil {
if temporaryInit && webcamActive == nil {
cam.Shutdown()
webcamInitDone = false
}
return nil, fmt.Errorf("webcam enumerate failed: %w", err)
}
out := make([]WebcamDeviceInfo, 0, len(devices))
for i, dev := range devices {
name := dev.Name
if name == "" {
name = fmt.Sprintf("Camera %d", i+1)
}
maxFPS := 30
if webcamActive != nil && webcamActive.deviceIndex == i && webcamActive.captureFPS > 0 {
maxFPS = int(math.Round(webcamActive.captureFPS))
} else if fps, fpsErr := webcamMaxSupportedFPSNoLock(dev.SymbolLink); fpsErr == nil && fps > 0 {
maxFPS = fps
}
out = append(out, WebcamDeviceInfo{Index: i, Name: name, MaxFPS: maxFPS})
}
if temporaryInit && webcamActive == nil {
cam.Shutdown()
webcamInitDone = false
}
return out, nil
}
func ClampWebcamFPS(deviceIndex int, requestedFPS int, useMax bool) (int, error) {
if requestedFPS < 1 {
requestedFPS = 30
}
if requestedFPS > 120 {
requestedFPS = 120
}
maxFPS, err := webcamMaxSupportedFPS(deviceIndex)
if err != nil {
return requestedFPS, err
}
if maxFPS <= 0 {
return requestedFPS, nil
}
if useMax || requestedFPS > maxFPS {
return maxFPS, nil
}
return requestedFPS, nil
}
func webcamMaxSupportedFPS(deviceIndex int) (int, error) {
webcamMu.Lock()
defer webcamMu.Unlock()
if webcamActive != nil && webcamActive.deviceIndex == deviceIndex && webcamActive.captureFPS > 0 {
return int(math.Round(webcamActive.captureFPS)), nil
}
temporaryInit := false
if !webcamInitDone {
if err := cam.Init(); err != nil {
return 0, fmt.Errorf("webcam init failed: %w", err)
}
webcamInitDone = true
temporaryInit = true
}
devices, err := cam.EnumDevice()
if err != nil {
if temporaryInit && webcamActive == nil {
cam.Shutdown()
webcamInitDone = false
}
return 0, fmt.Errorf("webcam enumerate failed: %w", err)
}
if len(devices) == 0 {
if temporaryInit && webcamActive == nil {
cam.Shutdown()
webcamInitDone = false
}
return 0, fmt.Errorf("no webcam devices detected")
}
if deviceIndex < 0 || deviceIndex >= len(devices) {
deviceIndex = 0
}
maxFPS, err := webcamMaxSupportedFPSNoLock(devices[deviceIndex].SymbolLink)
if err != nil {
if temporaryInit && webcamActive == nil {
cam.Shutdown()
webcamInitDone = false
}
return 0, err
}
if temporaryInit && webcamActive == nil {
cam.Shutdown()
webcamInitDone = false
}
return maxFPS, nil
}
func webcamMaxSupportedFPSNoLock(symbolLink string) (int, error) {
device, err := cam.OpenDevice(symbolLink)
if err != nil {
return 0, fmt.Errorf("open webcam failed: %w", err)
}
defer device.CloseDevice()
formats, err := device.EnumerateCaptureFormats()
if err != nil {
return 0, fmt.Errorf("enumerate webcam formats failed: %w", err)
}
maxFPS := 0
for _, format := range formats {
if !isJPEGSubtype(format) && !isH264Subtype(format) {
continue
}
fps := int(math.Round(format.Fps))
if fps > maxFPS {
maxFPS = fps
}
}
return maxFPS, nil
}