//go:build windows package client import ( "fmt" "os/exec" "strings" ) func handleCameraAction(action, device string) (handled bool, success bool, message string) { switch action { case "camera_snapshot": raw, err := captureWindowsCameraJPEG(strings.TrimSpace(device)) if err != nil { return true, false, err.Error() } b64, err := encodeCameraSnapshotJPEG(raw) if err != nil { return true, false, err.Error() } return true, true, b64 case "camera_list": devs, err := listWindowsCameraDevices() if err != nil { return true, false, err.Error() } if len(devs) == 0 { return true, false, "no DirectShow video capture devices found (install ffmpeg and connect a camera)" } return true, true, strings.Join(devs, "\n") default: return false, false, "" } } func ffmpegOnPath() (string, error) { path, err := exec.LookPath("ffmpeg") if err != nil { return "", fmt.Errorf("ffmpeg not found on PATH — install ffmpeg to capture USB/built-in camera frames") } return path, nil } func listWindowsCameraDevices() ([]string, error) { ff, err := ffmpegOnPath() if err != nil { return nil, err } out, err := silentCombinedOutput(ff, "-hide_banner", "-list_devices", "true", "-f", "dshow", "-i", "dummy") // ffmpeg exits non-zero for -list_devices; output is on stderr merged in CombinedOutput _ = err return parseDShowVideoDevices(string(out)), nil } func captureWindowsCameraJPEG(preferred string) ([]byte, error) { ff, err := ffmpegOnPath() if err != nil { return nil, err } devs, err := listWindowsCameraDevices() if err != nil { return nil, err } if len(devs) == 0 { return nil, fmt.Errorf("no video capture devices found") } device := devs[0] if preferred != "" { for _, d := range devs { if d == preferred { device = d break } } } out, err := silentCombinedOutput(ff, "-hide_banner", "-loglevel", "error", "-f", "dshow", "-i", dshowVideoInput(device), "-frames:v", "1", "-q:v", "2", "-f", "image2", "pipe:1", ) if err != nil { hint := strings.TrimSpace(string(out)) if hint != "" { return nil, fmt.Errorf("ffmpeg capture failed: %v (%s)", err, hint) } return nil, fmt.Errorf("ffmpeg capture failed: %v (device %q)", err, device) } return out, nil } func dshowVideoInput(name string) string { return `video="` + strings.ReplaceAll(name, `"`, `\"`) + `"` }