fix webcam
This commit is contained in:
@@ -96,6 +96,23 @@ var (
|
|||||||
webcamActive *webcamState
|
webcamActive *webcamState
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func activeWebcamFrameFPS(requested int, captureFPS float64) int {
|
||||||
|
actual := int(captureFPS + 0.5)
|
||||||
|
if requested < 1 {
|
||||||
|
requested = actual
|
||||||
|
}
|
||||||
|
if actual > 0 && requested > actual {
|
||||||
|
requested = actual
|
||||||
|
}
|
||||||
|
if requested < 1 {
|
||||||
|
requested = 30
|
||||||
|
}
|
||||||
|
if requested > 120 {
|
||||||
|
requested = 120
|
||||||
|
}
|
||||||
|
return requested
|
||||||
|
}
|
||||||
|
|
||||||
func NowWebcam(ctx context.Context, env *rt.Env) error {
|
func NowWebcam(ctx context.Context, env *rt.Env) error {
|
||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -122,6 +139,8 @@ func NowWebcam(ctx context.Context, env *rt.Env) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
frameFPS := activeWebcamFrameFPS(env.WebcamFPS, state.captureFPS)
|
||||||
|
|
||||||
quality := env.WebcamQuality
|
quality := env.WebcamQuality
|
||||||
codec := env.WebcamCodec
|
codec := env.WebcamCodec
|
||||||
outFormat := format
|
outFormat := format
|
||||||
@@ -158,7 +177,7 @@ func NowWebcam(ctx context.Context, env *rt.Env) error {
|
|||||||
Type: "frame",
|
Type: "frame",
|
||||||
Header: wire.FrameHeader{
|
Header: wire.FrameHeader{
|
||||||
Monitor: deviceIndex,
|
Monitor: deviceIndex,
|
||||||
FPS: 0,
|
FPS: frameFPS,
|
||||||
Format: outFormat,
|
Format: outFormat,
|
||||||
Webcam: true,
|
Webcam: true,
|
||||||
},
|
},
|
||||||
@@ -168,14 +187,7 @@ func NowWebcam(ctx context.Context, env *rt.Env) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if outFormat == "h264" && webrtcpub.IsActive(webrtcpub.KindWebcam) {
|
if outFormat == "h264" && webrtcpub.IsActive(webrtcpub.KindWebcam) {
|
||||||
fps := env.WebcamFPS
|
dur := time.Second / time.Duration(frameFPS)
|
||||||
if fps <= 0 {
|
|
||||||
fps = 30
|
|
||||||
}
|
|
||||||
dur := time.Second / time.Duration(fps)
|
|
||||||
if dur <= 0 {
|
|
||||||
dur = 33 * time.Millisecond
|
|
||||||
}
|
|
||||||
if werr := webrtcpub.WriteH264(webrtcpub.KindWebcam, frameBytes, dur); werr != nil {
|
if werr := webrtcpub.WriteH264(webrtcpub.KindWebcam, frameBytes, dur); werr != nil {
|
||||||
log.Printf("webrtc: write webcam h264 failed: %v", werr)
|
log.Printf("webrtc: write webcam h264 failed: %v", werr)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
//go:build windows && cgo
|
||||||
|
|
||||||
|
package capture
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
rt "overlord-client/cmd/agent/runtime"
|
||||||
|
"overlord-client/cmd/agent/wire"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestActiveWebcamFrameFPS(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
requested int
|
||||||
|
captureFPS float64
|
||||||
|
want int
|
||||||
|
}{
|
||||||
|
{name: "requested rate", requested: 15, captureFPS: 30, want: 15},
|
||||||
|
{name: "camera limit", requested: 60, captureFPS: 30, want: 30},
|
||||||
|
{name: "camera default", captureFPS: 29.97, want: 30},
|
||||||
|
{name: "safe default", want: 30},
|
||||||
|
{name: "protocol limit", requested: 240, captureFPS: 240, want: 120},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
if got := activeWebcamFrameFPS(test.requested, test.captureFPS); got != test.want {
|
||||||
|
t.Fatalf("activeWebcamFrameFPS(%d, %.2f) = %d, want %d", test.requested, test.captureFPS, got, test.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWebcamCaptureSmoke(t *testing.T) {
|
||||||
|
if os.Getenv("OVERLORD_WEBCAM_SMOKE") != "1" {
|
||||||
|
t.Skip("set OVERLORD_WEBCAM_SMOKE=1 to exercise an attached camera")
|
||||||
|
}
|
||||||
|
|
||||||
|
writer := &recordingWriter{}
|
||||||
|
env := &rt.Env{
|
||||||
|
Conn: writer,
|
||||||
|
WebcamDeviceIndex: 0,
|
||||||
|
WebcamFPS: 30,
|
||||||
|
WebcamQuality: 90,
|
||||||
|
WebcamCodec: "jpeg",
|
||||||
|
}
|
||||||
|
t.Cleanup(CleanupWebcam)
|
||||||
|
|
||||||
|
deadline := time.Now().Add(8 * time.Second)
|
||||||
|
for time.Now().Before(deadline) {
|
||||||
|
if err := NowWebcam(context.Background(), env); err != nil {
|
||||||
|
t.Fatalf("capture webcam frame: %v", err)
|
||||||
|
}
|
||||||
|
if len(writer.msgs) > 0 {
|
||||||
|
var frame wire.Frame
|
||||||
|
if err := msgpack.Unmarshal(writer.msgs[0], &frame); err != nil {
|
||||||
|
t.Fatalf("decode webcam frame: %v", err)
|
||||||
|
}
|
||||||
|
if !frame.Header.Webcam || frame.Header.FPS <= 0 || len(frame.Data) == 0 {
|
||||||
|
t.Fatalf("invalid live webcam frame: header=%+v bytes=%d", frame.Header, len(frame.Data))
|
||||||
|
}
|
||||||
|
t.Logf("captured webcam format=%s fps=%d bytes=%d", frame.Header.Format, frame.Header.FPS, len(frame.Data))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
time.Sleep(25 * time.Millisecond)
|
||||||
|
}
|
||||||
|
t.Fatal("timed out waiting for a live webcam frame")
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user