From 1e4a9afb9189bc2efad1f4496a5baf3ee17aa4e9 Mon Sep 17 00:00:00 2001 From: Tai Groot Date: Fri, 31 Jul 2026 10:33:40 +0000 Subject: [PATCH] fix(v4l2): detect multi-planar capture devices --- vidnumerator.go | 10 ++++---- vidnumerator_test.go | 56 ++++++++++++++++++++++++-------------------- 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/vidnumerator.go b/vidnumerator.go index 7e4290a..df09a43 100644 --- a/vidnumerator.go +++ b/vidnumerator.go @@ -30,9 +30,10 @@ const ( (0 << IOCNrShift) | (unsafe.Sizeof(cap{}) << IOCSizeShift) - V4L2CapVideoCapture uint32 = 0x00000001 - V4L2CapStreaming uint32 = 0x04000000 - V4L2CapDeviceCaps uint32 = 0x80000000 + V4L2CapVideoCapture uint32 = 0x00000001 + V4L2CapVideoCaptureMPlane uint32 = 0x00001000 + V4L2CapStreaming uint32 = 0x04000000 + V4L2CapDeviceCaps uint32 = 0x80000000 ) type cap struct { @@ -70,7 +71,8 @@ func (r cap) videoCaptureCaps() uint32 { func (r cap) isVideoCapture() bool { caps := r.videoCaptureCaps() - return caps&V4L2CapVideoCapture != 0 && caps&V4L2CapStreaming != 0 + isCapture := caps&(V4L2CapVideoCapture|V4L2CapVideoCaptureMPlane) != 0 + return isCapture && caps&V4L2CapStreaming != 0 } // IsVideoCapture checks the ioctl for VIDIOC_QUERYCAP to see if the device is a video capture device. diff --git a/vidnumerator_test.go b/vidnumerator_test.go index d482b6d..ed01573 100644 --- a/vidnumerator_test.go +++ b/vidnumerator_test.go @@ -92,44 +92,48 @@ func TestCapVideoCaptureCapsFallsBackToCapabilities(t *testing.T) { } } -func TestCapIsVideoCaptureAcceptsAdditionalFlags(t *testing.T) { - ic := cap{ - capabilities: V4L2CapDeviceCaps, - deviceCaps: V4L2CapVideoCapture | - V4L2CapStreaming | - 0x00000002, - } - - if !ic.isVideoCapture() { - t.Fatal("expected device with capture and streaming flags to be detected") - } -} - -func TestCapIsVideoCaptureRequiresCaptureAndStreaming(t *testing.T) { +func TestCapIsVideoCapture(t *testing.T) { tests := []struct { name string - ic cap + caps uint32 + want bool }{ { - name: "missing capture", - ic: cap{ - capabilities: V4L2CapDeviceCaps, - deviceCaps: V4L2CapStreaming, - }, + name: "single-planar capture with streaming", + caps: V4L2CapVideoCapture | V4L2CapStreaming, + want: true, + }, + { + name: "multi-planar capture with streaming", + caps: V4L2CapVideoCaptureMPlane | V4L2CapStreaming, + want: true, + }, + { + name: "capture with additional flags", + caps: V4L2CapVideoCapture | V4L2CapStreaming | 0x00000002, + want: true, }, { name: "missing streaming", - ic: cap{ - capabilities: V4L2CapDeviceCaps, - deviceCaps: V4L2CapVideoCapture, - }, + caps: V4L2CapVideoCapture, + want: false, + }, + { + name: "missing capture", + caps: V4L2CapStreaming, + want: false, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - if test.ic.isVideoCapture() { - t.Fatal("expected non-capture device") + ic := cap{ + capabilities: V4L2CapDeviceCaps, + deviceCaps: test.caps, + } + + if got := ic.isVideoCapture(); got != test.want { + t.Fatalf("isVideoCapture() = %v, want %v", got, test.want) } }) }