jim800121chen c54f16fca0 Initial commit: visionA monorepo with local-tool subproject
local-tool/: visionA-local desktop app
- M1: Wails shell + Go server + Next.js UI + Mock mode (macOS dmg ready)
- M2: i18n (zh-TW/en) + Settings 4-tab refactor
- M3: Embedded Python 3.12 runtime (python-build-standalone) + KneronPLUS wheels
- M4: Windows Inno Setup script (build on Windows runner)
- M5: Linux AppImage script + udev rule (build on Linux runner)
- M6: ffmpeg (GPL, pending legal review) + yt-dlp bundled
- Lifecycle: watchServer health check, fatal native dialog,
            Wails IPC raise endpoint, stale process cleanup

.autoflow/: full PRD / Design Spec / Architecture / Testing docs
            (4 rounds tri-party discussion + cross review)
.github/workflows/: macOS / Windows / Linux build CI

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 22:10:38 +08:00

108 lines
2.2 KiB
Go

package camera
import (
"fmt"
"sync"
)
type CameraInfo struct {
ID string `json:"id"`
Name string `json:"name"`
Index int `json:"index"`
Width int `json:"width"`
Height int `json:"height"`
}
type Manager struct {
mockMode bool
mockCamera *MockCamera
ffmpegCam *FFmpegCamera
isOpen bool
mu sync.Mutex
}
func NewManager(mockMode bool) *Manager {
return &Manager{mockMode: mockMode}
}
func (m *Manager) ListCameras() []CameraInfo {
if m.mockMode {
return []CameraInfo{
{ID: "mock-cam-0", Name: "Mock Camera 0", Index: 0, Width: 640, Height: 480},
}
}
// Try to detect real cameras via ffmpeg (auto-detects OS)
devices := ListFFmpegDevices()
if len(devices) > 0 {
return devices
}
if !DetectFFmpeg() {
fmt.Println("[WARN] ffmpeg not found — install with: brew install ffmpeg (macOS) or winget install ffmpeg (Windows)")
} else {
fmt.Println("[WARN] No video devices detected by ffmpeg")
}
return []CameraInfo{}
}
func (m *Manager) Open(index, width, height int) error {
m.mu.Lock()
defer m.mu.Unlock()
if m.mockMode {
m.mockCamera = NewMockCamera(width, height)
m.isOpen = true
return nil
}
// Try real camera via ffmpeg
if !DetectFFmpeg() {
return fmt.Errorf("ffmpeg not found — install with: brew install ffmpeg (macOS) or winget install ffmpeg (Windows)")
}
cam, err := NewFFmpegCamera(index, width, height, 30)
if err != nil {
return fmt.Errorf("failed to open camera (index=%d): %w", index, err)
}
m.ffmpegCam = cam
m.isOpen = true
fmt.Printf("[INFO] Opened real camera (index=%d) via ffmpeg\n", index)
return nil
}
func (m *Manager) Close() error {
m.mu.Lock()
defer m.mu.Unlock()
if m.ffmpegCam != nil {
_ = m.ffmpegCam.Close()
m.ffmpegCam = nil
}
m.mockCamera = nil
m.isOpen = false
return nil
}
func (m *Manager) ReadFrame() ([]byte, error) {
m.mu.Lock()
defer m.mu.Unlock()
if !m.isOpen {
return nil, fmt.Errorf("camera not open")
}
if m.ffmpegCam != nil {
return m.ffmpegCam.ReadFrame()
}
if m.mockCamera != nil {
return m.mockCamera.ReadFrame()
}
return nil, fmt.Errorf("no camera available")
}
func (m *Manager) IsOpen() bool {
m.mu.Lock()
defer m.mu.Unlock()
return m.isOpen
}