瀏覽代碼

Merge pull request #10 from vladimirvivien/cgo-refactor

Refactor project to use cgo-generated types and values
Vladimir Vivien 3 年之前
父節點
當前提交
1a3878df42

+ 113 - 5
README.md

@@ -1,25 +1,133 @@
 # go4vl
-A Go library for the Video for Linux user API (V4L2).
+A Go library for the `Video for Linux 2`  (v4l2) user API.
 
 ----
 
-`go4vl` hides all the complexities of working with V4L2 and 
-provides idiomatic Go types, like channels, to consume and process captured video frames.
+The `go4vl` project is for working with the Video for Linux 2 API for real-time video. 
+It hides all the complexities of working with V4L2 and provides idiomatic Go types, like channels, to consume and process captured video frames.
+
+> This project is designed to work with Linux and the Linux Video API.  
+> It is *NOT* meant to be a portable/cross-platform capable package for real-time video processing.
 
 ## Features
 * Capture and control video data from your Go programs
 * Idiomatic Go API for device access and video capture
+* Use cgo-generated types for correct data representation in Go
 * Use familiar types such as channels to stream video data
 * Exposes device enumeration and information
 * Provides device capture control
 * Access to video format information
-* Streaming support using memory map (other methods coming soon)
+* Streaming support using memory map (other methods coming later)
+
+### Not working/supported yet
+* Inherent support for video output
+* Only support MMap memory stream (user pointers, DMA not working)
+* Device control not implemented yet
+
+## Prerequisites
+* Go compiler/tools
+* Linux OS (32- or 64-bit)
+* Kernel minimum v5.10.x
+* A locally configured C compiler (i.e. gcc)
+* Header files for V4L2 (i.e. /usr/include/linux/videodev2.h)
+
+All examples have been tested using a Rasperry PI 3, running 32-bit Raspberry PI OS.
+The package should work with no problem on your 64-bit Linux OS.
 
 ## Getting started
+To avoid issues with old header files on your machine, upgrade your system to pull down the latest OS packages
+with something similar to the following (follow directions for your system to properly upgrade):
+
+```shell
+sudo apt update
+sudo apt full-upgrade
+```
+
 To include `go4vl` in your own code, pull the package
 
 ```bash
 go get github.com/vladimirvivien/go4vl/v4l2
 ```
 
-## Example
+## Examples
+The following is a simple example that captures video data from an attached camera device to
+and saves them as JPEG files. The example assumes the attached device supports JPEG (MJPEG) output format inherently.
+
+```go
+package main
+
+import (
+    ...
+    "github.com/vladimirvivien/go4vl/v4l2"
+)
+
+func main() {
+	// open device
+	device, err := v4l2.Open("/dev/video0")
+	if err != nil {
+		log.Fatalf("failed to open device: %s", err)
+	}
+	defer device.Close()
+
+	// configure device with preferred fmt
+	if err := device.SetPixFormat(v4l2.PixFormat{
+		Width:       640,
+		Height:      480,
+		PixelFormat: v4l2.PixelFmtMJPEG,
+		Field:       v4l2.FieldNone,
+	}); err != nil {
+		log.Fatalf("failed to set format: %s", err)
+	}
+
+	// start a device stream with 3 video buffers
+	if err := device.StartStream(3); err != nil {
+		log.Fatalf("failed to start stream: %s", err)
+	}
+
+	ctx, cancel := context.WithCancel(context.TODO())
+	// capture video data at 15 fps
+	frameChan, err := device.Capture(ctx, 15)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// grab 10 frames from frame channel and save them as files
+	totalFrames := 10
+	count := 0
+	for frame := range frameChan {
+		fileName := fmt.Sprintf("capture_%d.jpg", count)
+		file, err := os.Create(fileName)
+		if err != nil {
+			log.Printf("failed to create file %s: %s", fileName, err)
+			continue
+		}
+		if _, err := file.Write(frame); err != nil {
+			log.Printf("failed to write file %s: %s", fileName, err)
+			continue
+		}
+		if err := file.Close(); err != nil {
+			log.Printf("failed to close file %s: %s", fileName, err)
+		}
+		count++
+		if count >= totalFrames {
+			break
+		}
+	}
+
+	cancel() // stop capture
+	if err := device.StopStream(); err != nil {
+		log.Fatal(err)
+	}
+	fmt.Println("Done.")
+}
+```
+
+### Other examples
+The [./examples](./examples) directory contains additional examples including:
+
+* [device_info](./examples/device_info) - queries and prints devince information
+* [webcam](./examples/webcam) - uses the v4l2 package to create a simple webcam that streams images from an attached camera accessible via a web page.
+
+## Roadmap
+There is no defined roadmap. The main goal is to port as much functionlities as possible so that 
+adopters can use Go to create cool video-based tools on platforms such as the Raspberry Pi.

+ 1 - 0
TODO.md

@@ -7,3 +7,4 @@ A general list (of no implied order) of high level tasks for the project.
 * [ ] Support for YUYV conversion
 * [ ] Set and query device controls
 * [ ] Support for User pointer and other stream mode
+* [x] Use cgo-generated Go types to avoid alignment bugs

+ 32 - 9
examples/capture/capture.go

@@ -23,9 +23,9 @@ func main() {
 	defer device.Close()
 
 	// helper function to search for format descriptions
-	findPreferredFmt := func(fmts []v4l2.FormatDescription, pixEncoding v4l2.FourCCEncoding) *v4l2.FormatDescription {
+	findPreferredFmt := func(fmts []v4l2.FormatDescription, pixEncoding v4l2.FourCCType) *v4l2.FormatDescription {
 		for _, desc := range fmts {
-			if desc.GetPixelFormat() == pixEncoding{
+			if desc.PixelFormat == pixEncoding{
 				return &desc
 			}
 		}
@@ -39,7 +39,7 @@ func main() {
 	}
 
 	// search for preferred formats
-	preferredFmts := []v4l2.FourCCEncoding{v4l2.PixelFmtMPEG, v4l2.PixelFmtMJPEG, v4l2.PixelFmtJPEG, v4l2.PixelFmtYUYV}
+	preferredFmts := []v4l2.FourCCType{v4l2.PixelFmtMPEG, v4l2.PixelFmtMJPEG, v4l2.PixelFmtJPEG, v4l2.PixelFmtYUYV}
 	var fmtDesc *v4l2.FormatDescription
 	for _, preferredFmt := range preferredFmts{
 		fmtDesc = findPreferredFmt(fmtDescs, preferredFmt)
@@ -52,24 +52,45 @@ func main() {
 	if fmtDesc == nil {
 		log.Fatalf("device does not support any of %#v", preferredFmts)
 	}
-
-	frameSize, err := fmtDesc.GetFrameSize()
+	log.Printf("Found preferred fmt: %s", fmtDesc)
+	frameSizes, err := v4l2.GetFormatFrameSizes(device.GetFileDescriptor(), fmtDesc.PixelFormat)
 	if err!=nil{
 		log.Fatalf("failed to get framesize info: %s", err)
 	}
 
+	// select size 640x480 for format
+	var frmSize v4l2.FrameSize
+	for _, size := range frameSizes {
+		if size.Width == 640 && size.Height == 480 {
+			frmSize = size
+			break
+		}
+	}
+
+	if frmSize.Width == 0 {
+		log.Fatalf("Size 640x480 not supported for fmt: %s", fmtDesc)
+	}
+
 	// configure device with preferred fmt
+
 	if err := device.SetPixFormat(v4l2.PixFormat{
-		Width:       frameSize.Width,
-		Height:      frameSize.Height,
-		PixelFormat: fmtDesc.GetPixelFormat(),
+		Width:       frmSize.Width,
+		Height:      frmSize.Height,
+		PixelFormat: fmtDesc.PixelFormat,
 		Field:       v4l2.FieldNone,
 	}); err != nil {
 		log.Fatalf("failed to set format: %s", err)
 	}
 
+	pixFmt, err := device.GetPixFormat()
+	if err != nil {
+		log.Fatalf("failed to get format: %s", err)
+	}
+	log.Printf("Pixel format set to [%s]", pixFmt)
+
 	// start stream
-	if err := device.StartStream(10); err != nil {
+	log.Println("Start capturing...")
+	if err := device.StartStream(3); err != nil {
 		log.Fatalf("failed to start stream: %s", err)
 	}
 
@@ -82,6 +103,7 @@ func main() {
 	// process frames from capture channel
 	totalFrames := 10
 	count := 0
+	log.Println("Streaming frames from device...")
 	for frame := range frameChan {
 		fileName := fmt.Sprintf("capture_%d.jpg", count)
 		file, err := os.Create(fileName)
@@ -93,6 +115,7 @@ func main() {
 			log.Printf("failed to write file %s: %s", fileName, err)
 			continue
 		}
+		log.Printf("Saved file: %s", fileName)
 		if err := file.Close(); err != nil {
 			log.Printf("failed to close file %s: %s", fileName, err)
 		}

+ 17 - 0
examples/cgo_types/README.md

@@ -0,0 +1,17 @@
+# Example: capture with cgo-generated types
+:warning:
+
+This example illustrates how to use ioctl directly
+to communicate to device using cgo-generated types.
+
+## Do not use it ## 
+
+Use package `v4l2` to do realtime image capture, as shown in example
+[examples/capture](../capture).
+
+:warning:
+
+The example in this directory shows most of the moving pieces that make
+the V4L2 API works using Go.  It illustrates the steps, in detail, that
+are required to communicate with a device driver to configure, initiate,
+and capture images without using the Go v4l2 device type provided.

+ 0 - 48
examples/native_c_types/capture.go → examples/cgo_types/cgo_capture.go

@@ -16,51 +16,6 @@ import (
 	sys "golang.org/x/sys/unix"
 )
 
-// ========================= V4L2 command encoding =====================
-// https://elixir.bootlin.com/linux/v5.13-rc6/source/include/uapi/asm-generic/ioctl.h
-
-const (
-	//ioctl command layout
-	iocNone  = 0 // no op
-	iocWrite = 1 // userland app is writing, kernel reading
-	iocRead  = 2 // userland app is reading, kernel writing
-
-	iocTypeBits   = 8
-	iocNumberBits = 8
-	iocSizeBits   = 14
-	iocOpBits     = 2
-
-	numberPos = 0
-	typePos   = numberPos + iocNumberBits
-	sizePos   = typePos + iocTypeBits
-	opPos     = sizePos + iocSizeBits
-)
-
-// ioctl command encoding funcs
-func ioEnc(iocMode, iocType, number, size uintptr) uintptr {
-	return (iocMode << opPos) |
-		(iocType << typePos) |
-		(number << numberPos) |
-		(size << sizePos)
-}
-
-func ioEncR(iocType, number, size uintptr) uintptr {
-	return ioEnc(iocRead, iocType, number, size)
-}
-
-func ioEncW(iocType, number, size uintptr) uintptr {
-	return ioEnc(iocWrite, iocType, number, size)
-}
-
-func ioEncRW(iocType, number, size uintptr) uintptr {
-	return ioEnc(iocRead|iocWrite, iocType, number, size)
-}
-
-// four character pixel format encoding
-func fourcc(a, b, c, d uint32) uint32 {
-	return (a | b<<8) | c<<16 | d<<24
-}
-
 // wrapper for ioctl system call
 func ioctl(fd, req, arg uintptr) (err error) {
 	if _, _, errno := sys.Syscall(sys.SYS_IOCTL, fd, req, arg); errno != 0 {
@@ -115,9 +70,6 @@ func setFormat(fd uintptr, pixFmt PixFormat) error {
 	v4l2Fmt._type = C.uint(BufTypeVideoCapture)
 	*(*C.struct_v4l2_pix_format)(unsafe.Pointer(&v4l2Fmt.fmt[0])) = *(*C.struct_v4l2_pix_format)(unsafe.Pointer(&pixFmt))
 
-	// encode command to send
-	// vidiocSetFormat := ioEncRW('V', 5, uintptr(unsafe.Sizeof(v4l2Fmt)))
-
 	// send command
 	if err := ioctl(fd, C.VIDIOC_S_FMT, uintptr(unsafe.Pointer(&v4l2Fmt))); err != nil {
 		return err

+ 8 - 11
examples/device_info/devinfo.go

@@ -50,20 +50,18 @@ func printDeviceDriverInfo(dev *v4l2.Device) error {
 
 	// print driver info
 	fmt.Println("Device Info:")
-	fmt.Printf(template, "Driver name", caps.DriverName())
-	fmt.Printf(template, "Card name", caps.CardName())
-	fmt.Printf(template, "Bus info", caps.BusInfo())
+	fmt.Printf(template, "Driver name", caps.Driver)
+	fmt.Printf(template, "Card name", caps.Card)
+	fmt.Printf(template, "Bus info", caps.BusInfo)
 
-	verVal := caps.GetVersion()
-	version := fmt.Sprintf("%d.%d.%d", verVal>>16, (verVal>>8)&0xff, verVal&0xff)
-	fmt.Printf(template, "Driver version", version)
+	fmt.Printf(template, "Driver version", caps.GetVersionInfo())
 
-	fmt.Printf("\t%-16s : %0x\n", "Driver capabilities", caps.GetCapabilities())
+	fmt.Printf("\t%-16s : %0x\n", "Driver capabilities", caps.Capabilities)
 	for _, desc := range caps.GetDriverCapDescriptions() {
 		fmt.Printf("\t\t%s\n", desc.Desc)
 	}
 
-	fmt.Printf("\t%-16s : %0x\n", "Device capabilities", caps.GetCapabilities())
+	fmt.Printf("\t%-16s : %0x\n", "Device capabilities", caps.Capabilities)
 	for _, desc := range caps.GetDeviceCapDescriptions() {
 		fmt.Printf("\t\t%s\n", desc.Desc)
 	}
@@ -141,7 +139,7 @@ func printFormatDesc(dev *v4l2.Device) error {
 	}
 	fmt.Println("Supported formats:")
 	for i, desc := range descs{
-		frmSizes, err := desc.GetFrameSizes()
+		frmSizes, err := v4l2.GetFormatFrameSizes(dev.GetFileDescriptor(), desc.PixelFormat)
 		if err != nil {
 			return fmt.Errorf("format desc: %w", err)
 		}
@@ -150,8 +148,7 @@ func printFormatDesc(dev *v4l2.Device) error {
 		for _, size := range frmSizes{
 			sizeStr.WriteString(fmt.Sprintf("[%dx%d] ", size.Width, size.Height))
 		}
-		fmt.Printf(template, fmt.Sprintf("[%0d] %s", i, desc.GetDescription()), sizeStr.String())
-
+		fmt.Printf(template, fmt.Sprintf("[%0d] %s", i, desc.Description), sizeStr.String())
 	}
 	return nil
 }

+ 5 - 2
examples/v4l2_direct/README.md → examples/manual_types/README.md

@@ -1,7 +1,10 @@
-# Example: capture with V4L2 directly
+# Example: using handcrafted types (Deprecated) 
 :warning: 
 
-This example is here to illustrate the complexity of v4l2.
+This example is here to illustrate the complexity of v4l2 when using hand-crafted 
+Go types to communicate with the driver.  This approach was abandoned in favor of 
+cgo-generated types (see v4l2 package) for stability.
+
 Do not use it. 
 
 If you want to play around with image capture, use the 

+ 0 - 0
examples/v4l2_direct/v4l2_capture.go → examples/manual_types/v4l2_capture.go


+ 1 - 1
examples/webcam/webcam.go

@@ -18,7 +18,7 @@ import (
 var (
 	frames <-chan []byte
 	fps    uint32 = 30
-	pixfmt v4l2.FourCCEncoding
+	pixfmt v4l2.FourCCType
 )
 
 // servePage reads templated HTML

+ 82 - 99
v4l2/capability.go

@@ -1,5 +1,8 @@
 package v4l2
 
+// #include <linux/videodev2.h>
+import "C"
+
 import (
 	"fmt"
 	"unsafe"
@@ -9,42 +12,41 @@ import (
 // see https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L451
 
 const (
-	CapVideoCapture       = 0x00000001 // V4L2_CAP_VIDEO_CAPTURE
-	CapVideoOutput        = 0x00000002 // V4L2_CAP_VIDEO_OUTPUT
-	CapVideoOverlay       = 0x00000004 // V4L2_CAP_VIDEO_OVERLAY
-	CapVBICapture         = 0x00000010 // V4L2_CAP_VBI_CAPTURE
-	CapVBIOutput          = 0x00000020 // V4L2_CAP_VBI_OUTPUT
-	CapSlicedVBICapture   = 0x00000040 // V4L2_CAP_SLICED_VBI_CAPTURE
-	CapSlicedVBIOutput    = 0x00000080 // V4L2_CAP_SLICED_VBI_OUTPUT
-	CapRDSCapture         = 0x00000100 // V4L2_CAP_RDS_CAPTURE
-	CapVideoOutputOverlay = 0x00000200 // V4L2_CAP_VIDEO_OUTPUT_OVERLAY
-	CapHWFrequencySeek    = 0x00000400 // V4L2_CAP_HW_FREQ_SEEK
-	CapRDSOutput          = 0x00000800 // V4L2_CAP_RDS_OUTPUT
-
-	CapVideoCaptureMPlane = 0x00001000 // V4L2_CAP_VIDEO_CAPTURE_MPLANE
-	CapVideoOutputMPlane  = 0x00002000 // V4L2_CAP_VIDEO_OUTPUT_MPLANE
-	CapVideoMem2MemMPlane = 0x00004000 // V4L2_CAP_VIDEO_M2M_MPLANE
-	CapVideoMem2Mem       = 0x00008000 // V4L2_CAP_VIDEO_M2M
-
-	CapTuner     = 0x00010000 // V4L2_CAP_TUNER
-	CapAudio     = 0x00020000 // V4L2_CAP_AUDIO
-	CapRadio     = 0x00040000 // V4L2_CAP_RADIO
-	CapModulator = 0x00080000 // V4L2_CAP_MODULATOR
-
-	CapSDRCapture        = 0x00100000 // V4L2_CAP_SDR_CAPTURE
-	CapExtendedPixFormat = 0x00200000 // V4L2_CAP_EXT_PIX_FORMAT
-	CapSDROutput         = 0x00400000 // V4L2_CAP_SDR_OUTPUT
-	CapMetadataCapture   = 0x00800000 // V4L2_CAP_META_CAPTURE
-
-	CapReadWrite      = 0x01000000 // V4L2_CAP_READWRITE
-	CapAsyncIO        = 0x02000000 // V4L2_CAP_ASYNCIO
-	CapStreaming      = 0x04000000 // V4L2_CAP_STREAMING
-	CapMetadataOutput = 0x08000000 // V4L2_CAP_META_OUTPUT
-
-	CapTouch             = 0x10000000 // V4L2_CAP_TOUCH
-	CapIOMediaController = 0x20000000 // V4L2_CAP_IO_MC
-
-	CapDeviceCapabilities = 0x80000000 // V4L2_CAP_DEVICE_CAPS
+	CapVideoCapture       uint32 = C.V4L2_CAP_VIDEO_CAPTURE
+	CapVideoOutput        uint32 = C.V4L2_CAP_VIDEO_OUTPUT
+	CapVideoOverlay       uint32 = C.V4L2_CAP_VIDEO_OVERLAY
+	CapVBICapture         uint32 = C.V4L2_CAP_VBI_CAPTURE
+	CapVBIOutput          uint32 = C.V4L2_CAP_VBI_OUTPUT
+	CapSlicedVBICapture   uint32 = C.V4L2_CAP_SLICED_VBI_CAPTURE
+	CapSlicedVBIOutput    uint32 = C.V4L2_CAP_SLICED_VBI_OUTPUT
+	CapRDSCapture         uint32 = C.V4L2_CAP_RDS_CAPTURE
+	CapVideoOutputOverlay uint32 = C.V4L2_CAP_VIDEO_OUTPUT_OVERLAY
+	CapHWFrequencySeek    uint32 = C.V4L2_CAP_HW_FREQ_SEEK
+	CapRDSOutput          uint32 = C.V4L2_CAP_RDS_OUTPUT
+
+	CapVideoCaptureMPlane uint32 = C.V4L2_CAP_VIDEO_CAPTURE_MPLANE
+	CapVideoOutputMPlane  uint32 = C.V4L2_CAP_VIDEO_OUTPUT_MPLANE
+	CapVideoMem2MemMPlane uint32 = C.V4L2_CAP_VIDEO_M2M_MPLANE
+	CapVideoMem2Mem       uint32 = C.V4L2_CAP_VIDEO_M2M
+
+	CapTuner     uint32 = C.V4L2_CAP_TUNER
+	CapAudio     uint32 = C.V4L2_CAP_AUDIO
+	CapRadio     uint32 = C.V4L2_CAP_RADIO
+	CapModulator uint32 = C.V4L2_CAP_MODULATOR
+
+	CapSDRCapture        uint32 = C.V4L2_CAP_SDR_CAPTURE
+	CapExtendedPixFormat uint32 = C.V4L2_CAP_EXT_PIX_FORMAT
+	CapSDROutput         uint32 = C.V4L2_CAP_SDR_OUTPUT
+	CapMetadataCapture   uint32 = C.V4L2_CAP_META_CAPTURE
+
+	CapReadWrite uint32 = C.V4L2_CAP_READWRITE
+	CapAsyncIO   uint32 = C.V4L2_CAP_ASYNCIO
+	CapStreaming uint32 = C.V4L2_CAP_STREAMING
+
+	CapMetadataOutput     uint32 = C.V4L2_CAP_META_OUTPUT
+	CapTouch              uint32 = C.V4L2_CAP_TOUCH
+	CapIOMediaController  uint32 = C.V4L2_CAP_IO_MC
+	CapDeviceCapabilities uint32 = C.V4L2_CAP_DEVICE_CAPS
 )
 
 type CapabilityDesc struct {
@@ -93,87 +95,84 @@ var (
 	}
 )
 
-// v4l2Capability type for device (see v4l2_capability)
-// This type stores the capability information returned by the device.
-// https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-querycap.html#c.V4L.v4l2_capability
-// See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L440
-type v4l2Capability struct {
-	driver       [16]uint8
-	card         [32]uint8
-	busInfo      [32]uint8
-	version      uint32
-	capabilities uint32
-	deviceCaps   uint32
-	reserved     [3]uint32
-}
-
-// Capability represents capabilities retrieved for the device.
+// Capability represents capabilities retrieved for the device (see v4l2_capability).
 // Use attached methods on this type to access capabilities.
-// See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-querycap.html#c.V4L.v4l2_capability
+// https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L440
+// https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-querycap.html#c.V4L.v4l2_capability
 type Capability struct {
-	v4l2Cap v4l2Capability
+	// Driver name of the driver module
+	Driver string
+
+	// Card name of the device card
+	Card string
+
+	// BusInfo is the name of the device bus
+	BusInfo string 
+
+	// Version is the kernel version
+	Version uint32
+
+	// Capabilities returns all exported capabilities for the physical device (opened or not)
+	Capabilities uint32
+
+	// DeviceCapabilities is the capability for this particular (opened) device or node
+	DeviceCapabilities uint32 
 }
 
 // GetCapability retrieves capability info for device
 func GetCapability(fd uintptr) (Capability, error) {
-	v4l2Cap := v4l2Capability{}
-	if err := Send(fd, VidiocQueryCap, uintptr(unsafe.Pointer(&v4l2Cap))); err != nil {
+	var v4l2Cap C.struct_v4l2_capability
+	if err := send(fd, C.VIDIOC_QUERYCAP, uintptr(unsafe.Pointer(&v4l2Cap))); err != nil {
 		return Capability{}, fmt.Errorf("capability: %w", err)
 	}
-	return Capability{v4l2Cap: v4l2Cap}, nil
-}
-
-// GetCapabilities returns the capability mask as a union of
-// all exported capabilities for the physical device (opened or not).
-// Use this method to access capabilities.
-func (c Capability) GetCapabilities() uint32 {
-	return c.v4l2Cap.capabilities
-}
-
-// GetDeviceCaps returns the capability mask for the open device.
-// This is a subset of capabilities returned by GetCapabilities.
-func (c Capability) GetDeviceCaps() uint32 {
-	return c.v4l2Cap.deviceCaps
+	return Capability{
+		Driver:             C.GoString((*C.char)(&v4l2Cap.driver[0])),
+		Card:               C.GoString((*C.char)(&v4l2Cap.card[0])),
+		BusInfo:            C.GoString((*C.char)(&v4l2Cap.bus_info[0])),
+		Version:            uint32(v4l2Cap.version),
+		Capabilities:       uint32(v4l2Cap.capabilities),
+		DeviceCapabilities: uint32(v4l2Cap.device_caps),
+	}, nil
 }
 
 // IsVideoCaptureSupported returns caps & CapVideoCapture
 func (c Capability) IsVideoCaptureSupported() bool {
-	return (c.v4l2Cap.capabilities & CapVideoCapture) != 0
+	return (uint32(c.Capabilities) & CapVideoCapture) != 0
 }
 
 // IsVideoOutputSupported returns caps & CapVideoOutput
 func (c Capability) IsVideoOutputSupported() bool {
-	return (c.v4l2Cap.capabilities & CapVideoOutput) != 0
+	return (uint32(c.Capabilities) & CapVideoOutput) != 0
 }
 
 // IsVideoOverlaySupported returns caps & CapVideoOverlay
 func (c Capability) IsVideoOverlaySupported() bool {
-	return (c.v4l2Cap.capabilities & CapVideoOverlay) != 0
+	return (uint32(c.Capabilities) & CapVideoOverlay) != 0
 }
 
 // IsVideoOutputOverlaySupported returns caps & CapVideoOutputOverlay
 func (c Capability) IsVideoOutputOverlaySupported() bool {
-	return (c.v4l2Cap.capabilities & CapVideoOutputOverlay) != 0
+	return (uint32(c.Capabilities) & CapVideoOutputOverlay) != 0
 }
 
 // IsVideoCaptureMultiplanarSupported returns caps & CapVideoCaptureMPlane
 func (c Capability) IsVideoCaptureMultiplanarSupported() bool {
-	return (c.v4l2Cap.capabilities & CapVideoCaptureMPlane) != 0
+	return (uint32(c.Capabilities) & CapVideoCaptureMPlane) != 0
 }
 
 // IsVideoOutputMultiplanerSupported returns caps & CapVideoOutputMPlane
 func (c Capability) IsVideoOutputMultiplanerSupported() bool {
-	return (c.v4l2Cap.capabilities & CapVideoOutputMPlane) != 0
+	return (uint32(c.Capabilities) & CapVideoOutputMPlane) != 0
 }
 
 // IsReadWriteSupported returns caps & CapReadWrite
 func (c Capability) IsReadWriteSupported() bool {
-	return (c.v4l2Cap.capabilities & CapReadWrite) != 0
+	return (uint32(c.Capabilities) & CapReadWrite) != 0
 }
 
 // IsStreamingSupported returns caps & CapStreaming
 func (c Capability) IsStreamingSupported() bool {
-	return (c.v4l2Cap.capabilities & CapStreaming) != 0
+	return (uint32(c.Capabilities) & CapStreaming) != 0
 }
 
 // IsDeviceCapabilitiesProvided returns true if the device returns
@@ -181,14 +180,14 @@ func (c Capability) IsStreamingSupported() bool {
 // See notes on VL42_CAP_DEVICE_CAPS:
 // https://linuxtv.org/downloads/v4l-dvb-apis/userspace-api/v4l/vidioc-querycap.html?highlight=v4l2_cap_device_caps
 func (c Capability) IsDeviceCapabilitiesProvided() bool {
-	return (c.v4l2Cap.capabilities & CapDeviceCapabilities) != 0
+	return (uint32(c.Capabilities) & CapDeviceCapabilities) != 0
 }
 
 // GetDriverCapDescriptions return textual descriptions of driver capabilities
 func (c Capability) GetDriverCapDescriptions() []CapabilityDesc {
 	var result []CapabilityDesc
 	for _, cap := range Capabilities {
-		if c.GetCapabilities() & cap.Cap == cap.Cap {
+		if c.Capabilities&cap.Cap == cap.Cap {
 			result = append(result, cap)
 		}
 	}
@@ -199,34 +198,18 @@ func (c Capability) GetDriverCapDescriptions() []CapabilityDesc {
 func (c Capability) GetDeviceCapDescriptions() []CapabilityDesc {
 	var result []CapabilityDesc
 	for _, cap := range Capabilities {
-		if c.GetDeviceCaps() & cap.Cap == cap.Cap {
+		if c.DeviceCapabilities&cap.Cap == cap.Cap {
 			result = append(result, cap)
 		}
 	}
 	return result
 }
 
-// DriverName returns a string value for the driver name
-func (c Capability) DriverName() string {
-	return toGoString(c.v4l2Cap.driver[:])
-}
-
-// CardName returns a string value for device's card
-func (c Capability) CardName() string {
-	return toGoString(c.v4l2Cap.card[:])
-}
-
-// BusInfo returns the device's bus info
-func (c Capability) BusInfo() string {
-	return toGoString(c.v4l2Cap.busInfo[:])
-}
-
-// GetVersion returns the device's version
-func (c Capability) GetVersion() uint32 {
-	return c.v4l2Cap.version
+func (c Capability) GetVersionInfo() VersionInfo {
+	return VersionInfo{value: c.Version}
 }
 
 // String returns a string value representing driver information
 func (c Capability) String() string {
-	return fmt.Sprintf("driver: %s; card: %s; bus info: %s", c.DriverName(), c.CardName(), c.BusInfo())
+	return fmt.Sprintf("driver: %s; card: %s; bus info: %s", c.Driver, c.Card, c.BusInfo)
 }

+ 16 - 15
v4l2/crop.go

@@ -1,5 +1,8 @@
 package v4l2
 
+// #include <linux/videodev2.h>
+import "C"
+
 import (
 	"fmt"
 	"unsafe"
@@ -24,39 +27,37 @@ type Fract struct {
 }
 
 // CropCapability (v4l2_cropcap)
-// https://www.kernel.org/doc/html/v4.14/media/uapi/v4l/vidioc-cropcap.html#c.v4l2_cropcap
+// https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-cropcap.html#c.v4l2_cropcap
 // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L1221
 type CropCapability struct {
 	StreamType  uint32
 	Bounds      Rect
 	DefaultRect Rect
 	PixelAspect Fract
-}
-
-// Crop (v4l2_crop)
-// https://www.kernel.org/doc/html/v4.14/media/uapi/v4l/vidioc-g-crop.html#c.v4l2_crop
-// https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L1228
-type Crop struct {
-	StreamType uint32
-	Rect       Rect
+	_           [4]uint32
 }
 
 // GetCropCapability  retrieves cropping info for specified device
 // See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-cropcap.html#ioctl-vidioc-cropcap
 func GetCropCapability(fd uintptr) (CropCapability, error) {
-	cropCap := CropCapability{}
-	cropCap.StreamType = BufTypeVideoCapture
-	if err := Send(fd, VidiocCropCap, uintptr(unsafe.Pointer(&cropCap))); err != nil {
+	var cap C.struct_v4l2_cropcap
+	cap._type = C.uint(BufTypeVideoCapture)
+
+	if err := send(fd, C.VIDIOC_CROPCAP, uintptr(unsafe.Pointer(&cap))); err != nil {
 		return CropCapability{}, fmt.Errorf("crop capability: %w", err)
 	}
-	return cropCap, nil
+
+	return *(*CropCapability)(unsafe.Pointer(&cap)), nil
 }
 
 // SetCropRect sets the cropping dimension for specified device
 // See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-g-crop.html#ioctl-vidioc-g-crop-vidioc-s-crop
 func SetCropRect(fd uintptr, r Rect) error {
-	crop := Crop{Rect: r, StreamType: BufTypeVideoCapture}
-	if err := Send(fd, VidiocSetCrop, uintptr(unsafe.Pointer(&crop))); err != nil {
+	var crop C.struct_v4l2_crop
+	crop._type = C.uint(BufTypeVideoCapture)
+	crop.c = *(*C.struct_v4l2_rect)(unsafe.Pointer(&r))
+
+	if err := send(fd, C.VIDIOC_S_CROP, uintptr(unsafe.Pointer(&crop))); err != nil {
 		return fmt.Errorf("set crop: %w", err)
 	}
 	return nil

+ 8 - 8
v4l2/device.go

@@ -165,7 +165,7 @@ func (d *Device) StartStream(buffSize uint32) error {
 	}
 
 	// allocate device buffers
-	bufReq, err := AllocateBuffers(d.fd, buffSize)
+	bufReq, err := InitBuffers(d.fd, buffSize)
 	if err != nil {
 		return fmt.Errorf("device: start stream: %w", err)
 	}
@@ -175,16 +175,16 @@ func (d *Device) StartStream(buffSize uint32) error {
 	bufCount := int(d.requestedBuf.Count)
 	d.buffers = make([][]byte, d.requestedBuf.Count)
 	for i := 0; i < bufCount; i++ {
-		bufInfo, err := GetBufferInfo(d.fd, uint32(i))
+		buffer, err := GetBuffer(d.fd, uint32(i))
 		if err != nil {
-			return fmt.Errorf("device: start stream: %w", err)
+			return fmt.Errorf("device start stream: %w", err)
 		}
 
-		offset := bufInfo.GetService().Offset
-		length := bufInfo.Length
+		offset := buffer.Info.Offset
+		length := buffer.Length
 		mappedBuf, err := MapMemoryBuffer(d.fd, int64(offset), int(length))
 		if err != nil {
-			return fmt.Errorf("device: start stream: %w", err)
+			return fmt.Errorf("device start stream: %w", err)
 		}
 		d.buffers[i] = mappedBuf
 	}
@@ -193,13 +193,13 @@ func (d *Device) StartStream(buffSize uint32) error {
 	for i := 0; i < bufCount; i++ {
 		_, err := QueueBuffer(d.fd, uint32(i))
 		if err != nil {
-			return fmt.Errorf("device: start stream: %w", err)
+			return fmt.Errorf("device start stream: %w", err)
 		}
 	}
 
 	// turn on device stream
 	if err := StreamOn(d.fd); err != nil {
-		return fmt.Errorf("device: start stream: %w", err)
+		return fmt.Errorf("device start stream: %w", err)
 	}
 
 	d.streaming = true

+ 96 - 115
v4l2/format.go

@@ -1,41 +1,36 @@
 package v4l2
 
+// #include <linux/videodev2.h>
+import "C"
+
 import (
 	"fmt"
 	"unsafe"
 )
 
-// FourCCEncoding represents the four character encoding value
-type FourCCEncoding = uint32
+// FourCCType represents the four character encoding value
+type FourCCType = uint32
 
 // Some Predefined pixel format definitions
 // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/pixfmt.html
 // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L518
 var (
-	PixFmtRGB24   = fourcc('R', 'G', 'B', '3') // V4L2_PIX_FMT_RGB24
-	PixFmtGrey    = fourcc('G', 'R', 'E', 'Y') // V4L2_PIX_FMT_GREY
-	PixelFmtYUYV  = fourcc('Y', 'U', 'Y', 'V') // V4L2_PIX_FMT_YUYV
-	PixelFmtYYUV  = fourcc('Y', 'Y', 'U', 'V') // V4L2_PIX_FMT_YYUV
-	PixelFmtYVYU  = fourcc('Y', 'V', 'Y', 'U') // V4L2_PIX_FMT_YVYU
-	PixelFmtUYVY  = fourcc('U', 'Y', 'V', 'Y') // V4L2_PIX_FMT_UYVY
-	PixelFmtVYUY  = fourcc('V', 'Y', 'U', 'Y') // V4L2_PIX_FMT_VYUY
-	PixelFmtMJPEG = fourcc('M', 'J', 'P', 'G') // V4L2_PIX_FMT_MJPEG
-	PixelFmtJPEG  = fourcc('J', 'P', 'E', 'G') // V4L2_PIX_FMT_JPEG
-	PixelFmtMPEG  = fourcc('M', 'P', 'E', 'G') // V4L2_PIX_FMT_MPEG
-	PixelFmtH264  = fourcc('H', '2', '6', '4') // V4L2_PIX_FMT_H264
-	PixelFmtMPEG4 = fourcc('M', 'P', 'G', '4') // V4L2_PIX_FMT_MPEG4
+	PixFmtRGB24   FourCCType = C.V4L2_PIX_FMT_RGB24
+	PixFmtGrey    FourCCType = C.V4L2_PIX_FMT_GREY
+	PixelFmtYUYV  FourCCType = C.V4L2_PIX_FMT_YUYV
+	PixelFmtYYUV  FourCCType = C.V4L2_PIX_FMT_YYUV
+	PixelFmtYVYU  FourCCType = C.V4L2_PIX_FMT_YVYU
+	PixelFmtUYVY  FourCCType = C.V4L2_PIX_FMT_UYVY
+	PixelFmtVYUY  FourCCType = C.V4L2_PIX_FMT_VYUY
+	PixelFmtMJPEG FourCCType = C.V4L2_PIX_FMT_MJPEG
+	PixelFmtJPEG  FourCCType = C.V4L2_PIX_FMT_JPEG
+	PixelFmtMPEG  FourCCType = C.V4L2_PIX_FMT_MPEG
+	PixelFmtH264  FourCCType = C.V4L2_PIX_FMT_H264
+	PixelFmtMPEG4 FourCCType = C.V4L2_PIX_FMT_MPEG4
 )
 
-// fourcc implements the four character code encoding found
-// https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L81
-// #define v4l2_fourcc(a, b, c, d)\
-// 	 ((__u32)(a) | ((__u32)(b) << 8) | ((__u32)(c) << 16) | ((__u32)(d) << 24))
-func fourcc(a, b, c, d uint32) FourCCEncoding {
-	return (a | b<<8) | c<<16 | d<<24
-}
-
-// PixelFormats provides a map of FourCC encoding description
-var PixelFormats = map[FourCCEncoding]string{
+// PixelFormats provides a map of FourCCType encoding description
+var PixelFormats = map[FourCCType]string{
 	PixFmtRGB24:   "24-bit RGB 8-8-8",
 	PixFmtGrey:    "8-bit Greyscale",
 	PixelFmtYUYV:  "YUYV 4:2:2",
@@ -47,7 +42,7 @@ var PixelFormats = map[FourCCEncoding]string{
 }
 
 // IsPixYUVEncoded returns true if the pixel format is a chrome+luminance YUV format
-func IsPixYUVEncoded(pixFmt FourCCEncoding) bool {
+func IsPixYUVEncoded(pixFmt FourCCType) bool {
 	switch pixFmt {
 	case
 		PixelFmtYUYV,
@@ -66,24 +61,24 @@ func IsPixYUVEncoded(pixFmt FourCCEncoding) bool {
 type ColorspaceType = uint32
 
 const (
-	ColorspaceTypeDefault ColorspaceType = iota //V4L2_COLORSPACE_DEFAULT
-	ColorspaceSMPTE170M                         //V4L2_COLORSPACE_SMPTE170M
-	ColorspaceSMPTE240M                         // V4L2_COLORSPACE_SMPTE240M
-	ColorspaceREC709                            // V4L2_COLORSPACE_REC709
-	ColorspaceBT878                             // V4L2_COLORSPACE_BT878 (absolete)
-	Colorspace470SystemM                        // V4L2_COLORSPACE_470_SYSTEM_M (absolete)
-	Colorspace470SystemBG                       // V4L2_COLORSPACE_470_SYSTEM_BG
-	ColorspaceJPEG                              // V4L2_COLORSPACE_JPEG
-	ColorspaceSRGB                              // V4L2_COLORSPACE_SRGB
-	ColorspaceOPRGB                             // V4L2_COLORSPACE_OPRGB
-	ColorspaceBT2020                            // V4L2_COLORSPACE_BT2020
-	ColorspaceRaw                               // V4L2_COLORSPACE_RAW
-	ColorspaceDCIP3                             // V4L2_COLORSPACE_DCI_P3
+	ColorspaceDefault     ColorspaceType = C.V4L2_COLORSPACE_DEFAULT
+	ColorspaceSMPTE170M   ColorspaceType = C.V4L2_COLORSPACE_SMPTE170M
+	ColorspaceSMPTE240M   ColorspaceType = C.V4L2_COLORSPACE_SMPTE240M
+	ColorspaceREC709      ColorspaceType = C.V4L2_COLORSPACE_REC709
+	ColorspaceBT878       ColorspaceType = C.V4L2_COLORSPACE_BT878        //(absolete)
+	Colorspace470SystemM  ColorspaceType = C.V4L2_COLORSPACE_470_SYSTEM_M //(absolete)
+	Colorspace470SystemBG ColorspaceType = C.V4L2_COLORSPACE_470_SYSTEM_BG
+	ColorspaceJPEG        ColorspaceType = C.V4L2_COLORSPACE_JPEG
+	ColorspaceSRGB        ColorspaceType = C.V4L2_COLORSPACE_SRGB
+	ColorspaceOPRGB       ColorspaceType = C.V4L2_COLORSPACE_OPRGB
+	ColorspaceBT2020      ColorspaceType = C.V4L2_COLORSPACE_BT2020
+	ColorspaceRaw         ColorspaceType = C.V4L2_COLORSPACE_RAW
+	ColorspaceDCIP3       ColorspaceType = C.V4L2_COLORSPACE_DCI_P3
 )
 
 // Colorspaces is a map of colorspace to its respective description
 var Colorspaces = map[ColorspaceType]string{
-	ColorspaceTypeDefault: "Default",
+	ColorspaceDefault:     "Default",
 	ColorspaceREC709:      "Rec. 709",
 	Colorspace470SystemBG: "470 System BG",
 	ColorspaceJPEG:        "JPEG",
@@ -100,14 +95,14 @@ var Colorspaces = map[ColorspaceType]string{
 type YCbCrEncodingType = uint32
 
 const (
-	YCbCrEncodingDefault        YCbCrEncodingType = iota // V4L2_YCBCR_ENC_DEFAULT
-	YCbCrEncoding601                                     // V4L2_YCBCR_ENC_601
-	YCbCrEncoding709                                     // V4L2_YCBCR_ENC_709
-	YCbCrEncodingXV601                                   // V4L2_YCBCR_ENC_XV601
-	YCbCrEncodingXV709                                   // V4L2_YCBCR_ENC_XV709
-	_                                                    // V4L2_YCBCR_ENC_SYCC (absolete)
-	YCbCrEncodingBT2020                                  // V4L2_YCBCR_ENC_BT2020
-	YCbCrEncodingBT2020ConstLum                          // V4L2_YCBCR_ENC_BT2020_CONST_LUM
+	YCbCrEncodingDefault        YCbCrEncodingType = C.V4L2_YCBCR_ENC_DEFAULT
+	YCbCrEncoding601            YCbCrEncodingType = C.V4L2_YCBCR_ENC_601
+	YCbCrEncoding709            YCbCrEncodingType = C.V4L2_YCBCR_ENC_709
+	YCbCrEncodingXV601          YCbCrEncodingType = C.V4L2_YCBCR_ENC_XV601
+	YCbCrEncodingXV709          YCbCrEncodingType = C.V4L2_YCBCR_ENC_XV709
+	_                           YCbCrEncodingType = C.V4L2_YCBCR_ENC_SYCC //(absolete)
+	YCbCrEncodingBT2020         YCbCrEncodingType = C.V4L2_YCBCR_ENC_BT2020
+	YCbCrEncodingBT2020ConstLum YCbCrEncodingType = C.V4L2_YCBCR_ENC_BT2020_CONST_LUM
 )
 
 var YCbCrEncodings = map[YCbCrEncodingType]string{
@@ -139,8 +134,8 @@ func ColorspaceToYCbCrEnc(cs ColorspaceType) YCbCrEncodingType {
 type HSVEncodingType = YCbCrEncodingType
 
 const (
-	HSVEncoding180 = HSVEncodingType(128) // V4L2_HSV_ENC_180
-	HSVEncoding256 = HSVEncodingType(129) // V4L2_HSV_ENC_256
+	HSVEncoding180 HSVEncodingType = C.V4L2_HSV_ENC_180
+	HSVEncoding256 HSVEncodingType = C.V4L2_HSV_ENC_256
 )
 
 // QuantizationType (v4l2_quantization)
@@ -149,9 +144,9 @@ const (
 type QuantizationType = uint32
 
 const (
-	QuantizationDefault      QuantizationType = iota // V4L2_QUANTIZATION_DEFAULT
-	QuantizationFullRange                            // V4L2_QUANTIZATION_FULL_RANGE
-	QuantizationLimitedRange                         // V4L2_QUANTIZATION_LIM_RANGE
+	QuantizationDefault      QuantizationType = C.V4L2_QUANTIZATION_DEFAULT
+	QuantizationFullRange    QuantizationType = C.V4L2_QUANTIZATION_FULL_RANGE
+	QuantizationLimitedRange QuantizationType = C.V4L2_QUANTIZATION_LIM_RANGE
 )
 
 var Quantizations = map[QuantizationType]string{
@@ -176,14 +171,14 @@ func ColorspaceToQuantization(cs ColorspaceType) QuantizationType {
 type XferFunctionType = uint32
 
 const (
-	XferFuncDefault   XferFunctionType = iota // V4L2_XFER_FUNC_DEFAULT     = 0
-	XferFunc709                               // V4L2_XFER_FUNC_709         = 1,
-	XferFuncSRGB                              // V4L2_XFER_FUNC_SRGB        = 2,
-	XferFuncOpRGB                             // V4L2_XFER_FUNC_OPRGB       = 3,
-	XferFuncSMPTE240M                         // V4L2_XFER_FUNC_SMPTE240M   = 4,
-	XferFuncNone                              // V4L2_XFER_FUNC_NONE        = 5,
-	XferFuncDCIP3                             // V4L2_XFER_FUNC_DCI_P3      = 6,
-	XferFuncSMPTE2084                         // V4L2_XFER_FUNC_SMPTE2084   = 7,
+	XferFuncDefault   XferFunctionType = C.V4L2_XFER_FUNC_DEFAULT
+	XferFunc709       XferFunctionType = C.V4L2_XFER_FUNC_709
+	XferFuncSRGB      XferFunctionType = C.V4L2_XFER_FUNC_SRGB
+	XferFuncOpRGB     XferFunctionType = C.V4L2_XFER_FUNC_OPRGB
+	XferFuncSMPTE240M XferFunctionType = C.V4L2_XFER_FUNC_SMPTE240M
+	XferFuncNone      XferFunctionType = C.V4L2_XFER_FUNC_NONE
+	XferFuncDCIP3     XferFunctionType = C.V4L2_XFER_FUNC_DCI_P3
+	XferFuncSMPTE2084 XferFunctionType = C.V4L2_XFER_FUNC_SMPTE2084
 )
 
 var XferFunctions = map[XferFunctionType]string{
@@ -223,16 +218,16 @@ func ColorspaceToXferFunc(cs ColorspaceType) XferFunctionType {
 type FieldType = uint32
 
 const (
-	FieldAny                 FieldType = iota // V4L2_FIELD_ANY
-	FieldNone                                 // V4L2_FIELD_NONE
-	FieldTop                                  // V4L2_FIELD_TOP
-	FieldBottom                               // V4L2_FIELD_BOTTOM
-	FieldInterlaced                           // V4L2_FIELD_INTERLACED
-	FieldSequentialTopBottom                  // V4L2_FIELD_SEQ_TB
-	FieldSequentialBottomTop                  // V4L2_FIELD_SEQ_BT
-	FieldAlternate                            // V4L2_FIELD_ALTERNATE
-	FieldInterlacedTopBottom                  // V4L2_FIELD_INTERLACED_TB
-	FieldInterlacedBottomTop                  // V4L2_FIELD_INTERLACED_BT
+	FieldAny                 FieldType = C.V4L2_FIELD_ANY
+	FieldNone                FieldType = C.V4L2_FIELD_NONE
+	FieldTop                 FieldType = C.V4L2_FIELD_TOP
+	FieldBottom              FieldType = C.V4L2_FIELD_BOTTOM
+	FieldInterlaced          FieldType = C.V4L2_FIELD_INTERLACED
+	FieldSequentialTopBottom FieldType = C.V4L2_FIELD_SEQ_TB
+	FieldSequentialBottomTop FieldType = C.V4L2_FIELD_SEQ_BT
+	FieldAlternate           FieldType = C.V4L2_FIELD_ALTERNATE
+	FieldInterlacedTopBottom FieldType = C.V4L2_FIELD_INTERLACED_TB
+	FieldInterlacedBottomTop FieldType = C.V4L2_FIELD_INTERLACED_BT
 )
 
 // Fields is a map of FieldType description
@@ -249,13 +244,13 @@ var Fields = map[FieldType]string{
 	FieldInterlacedBottomTop: "interlaced bottom-top",
 }
 
-// PixFormat (v4l2_pix_format)
+// PixFormat contains video image format from v4l2_pix_format.
 // https://www.kernel.org/doc/html/v4.9/media/uapi/v4l/pixfmt-002.html?highlight=v4l2_pix_format
 // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L496
 type PixFormat struct {
 	Width        uint32
 	Height       uint32
-	PixelFormat  FourCCEncoding
+	PixelFormat  FourCCType
 	Field        FieldType
 	BytesPerLine uint32
 	SizeImage    uint32
@@ -263,6 +258,7 @@ type PixFormat struct {
 	Priv         uint32
 	Flags        uint32
 	YcbcrEnc     YCbCrEncodingType
+	HSVEnc       HSVEncodingType
 	Quantization QuantizationType
 	XferFunc     XferFunctionType
 }
@@ -282,58 +278,43 @@ func (f PixFormat) String() string {
 	)
 }
 
-// v4l2Format (v4l2_format)
-// https://www.kernel.org/doc/html/v4.9/media/uapi/v4l/vidioc-g-fmt.html?highlight=v4l2_format
-// https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L2331
-//
-// field fmt is a union, thus it's constructed as an appropriately sized array:
-//
-// struct v4l2_format {
-// 	__u32	 type;
-// 	union {
-// 		struct v4l2_pix_format		    pix;
-// 		struct v4l2_pix_format_mplane	pix_mp;
-// 		struct v4l2_window		        win;
-// 		struct v4l2_vbi_format		    vbi;
-// 		struct v4l2_sliced_vbi_format	sliced;
-// 		struct v4l2_sdr_format	 	    sdr;
-// 		struct v4l2_meta_format		    meta;
-// 		__u8	raw_data[200];   /* user-defined */
-// 	} fmt;
-// };
-type v4l2Format struct {
-	StreamType uint32
-	fmt        [200]byte
-}
-
-// getPixFormat returns the PixFormat by casting the pointer to the union type
-func (f *v4l2Format) getPixFormat() PixFormat {
-	pixfmt := (*PixFormat)(unsafe.Pointer(&f.fmt[0]))
-	return *pixfmt
-}
-
-// setPixFormat sets the PixFormat by casting the pointer to the fmt union and set its value
-func (f *v4l2Format) setPixFormat(newPix PixFormat) {
-	f.fmt = *(*[200]byte)(unsafe.Pointer(&newPix))
-}
-
-// GetPixFormat retrieves pixel information for the specified driver
-// See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-g-fmt.html#ioctl-vidioc-g-fmt-vidioc-s-fmt-vidioc-try-fmt
+// GetPixFormat retrieves pixel information for the specified driver (via v4l2_format and v4l2_pix_format)
+// See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L2331
+// and https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-g-fmt.html#ioctl-vidioc-g-fmt-vidioc-s-fmt-vidioc-try-fmt
 func GetPixFormat(fd uintptr) (PixFormat, error) {
-	format := &v4l2Format{StreamType: BufTypeVideoCapture}
-	if err := Send(fd, VidiocGetFormat, uintptr(unsafe.Pointer(format))); err != nil {
+	var v4l2Format C.struct_v4l2_format
+	v4l2Format._type = C.uint(BufTypeVideoCapture)
+
+	if err := send(fd, C.VIDIOC_G_FMT, uintptr(unsafe.Pointer(&v4l2Format))); err != nil {
 		return PixFormat{}, fmt.Errorf("pix format failed: %w", err)
 	}
 
-	return format.getPixFormat(), nil
+	v4l2PixFmt := *(*C.struct_v4l2_pix_format)(unsafe.Pointer(&v4l2Format.fmt[0]))
+	return PixFormat{
+		Width:        uint32(v4l2PixFmt.width),
+		Height:       uint32(v4l2PixFmt.height),
+		PixelFormat:  uint32(v4l2PixFmt.pixelformat),
+		Field:        uint32(v4l2PixFmt.field),
+		BytesPerLine: uint32(v4l2PixFmt.bytesperline),
+		SizeImage:    uint32(v4l2PixFmt.sizeimage),
+		Colorspace:   uint32(v4l2PixFmt.colorspace),
+		Priv:         uint32(v4l2PixFmt.priv),
+		Flags:        uint32(v4l2PixFmt.flags),
+		YcbcrEnc:     *(*uint32)(unsafe.Pointer(&v4l2PixFmt.anon0[0])),
+		HSVEnc:       *(*uint32)(unsafe.Pointer(uintptr(unsafe.Pointer(&v4l2PixFmt.anon0[0])) + unsafe.Sizeof(&v4l2PixFmt.anon0[0]))),
+		Quantization: uint32(v4l2PixFmt.quantization),
+		XferFunc:     uint32(v4l2PixFmt.xfer_func),
+	}, nil
 }
 
 // SetPixFormat sets the pixel format information for the specified driver
 // See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-g-fmt.html#ioctl-vidioc-g-fmt-vidioc-s-fmt-vidioc-try-fmt
 func SetPixFormat(fd uintptr, pixFmt PixFormat) error {
-	format := &v4l2Format{StreamType: BufTypeVideoCapture}
-	format.setPixFormat(pixFmt)
-	if err := Send(fd, VidiocSetFormat, uintptr(unsafe.Pointer(format))); err != nil {
+	var v4l2Format C.struct_v4l2_format
+	v4l2Format._type = C.uint(BufTypeVideoCapture)
+	*(*C.struct_v4l2_pix_format)(unsafe.Pointer(&v4l2Format.fmt[0])) = *(*C.struct_v4l2_pix_format)(unsafe.Pointer(&pixFmt))
+
+	if err := send(fd, C.VIDIOC_S_FMT, uintptr(unsafe.Pointer(&v4l2Format))); err != nil {
 		return fmt.Errorf("pix format failed: %w", err)
 	}
 	return nil

+ 59 - 68
v4l2/format_desc.go

@@ -1,5 +1,10 @@
 package v4l2
 
+/*
+#include <linux/videodev2.h>
+*/
+import "C"
+
 import (
 	"errors"
 	"fmt"
@@ -12,16 +17,16 @@ import (
 type FmtDescFlag = uint32
 
 const (
-	FmtDescFlagCompressed                  FmtDescFlag = 0x0001                    // V4L2_FMT_FLAG_COMPRESSED
-	FmtDescFlagEmulated                    FmtDescFlag = 0x0002                    // V4L2_FMT_FLAG_EMULATED
-	FmtDescFlagContinuousBytestream        FmtDescFlag = 0x0004                    // V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM
-	FmtDescFlagDynResolution               FmtDescFlag = 0x0008                    // V4L2_FMT_FLAG_DYN_RESOLUTION
-	FmtDescFlagEncodedCaptureFrameInterval FmtDescFlag = 0x0010                    //  V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL
-	FmtDescFlagConfigColorspace            FmtDescFlag = 0x0020                    //  V4L2_FMT_FLAG_CSC_COLORSPACE
-	FmtDescFlagConfigXferFunc              FmtDescFlag = 0x0040                    // V4L2_FMT_FLAG_CSC_XFER_FUNC
-	FmtDescFlagConfigYcbcrEnc              FmtDescFlag = 0x0080                    //  V4L2_FMT_FLAG_CSC_YCBCR_ENC
-	FmtDescFlagConfigHsvEnc                FmtDescFlag = FmtDescFlagConfigYcbcrEnc // V4L2_FMT_FLAG_CSC_HSV_ENC
-	FmtDescFlagConfigQuantization          FmtDescFlag = 0x0100                    // V4L2_FMT_FLAG_CSC_QUANTIZATION
+	FmtDescFlagCompressed                  FmtDescFlag = C.V4L2_FMT_FLAG_COMPRESSED
+	FmtDescFlagEmulated                    FmtDescFlag = C.V4L2_FMT_FLAG_EMULATED
+	FmtDescFlagContinuousBytestream        FmtDescFlag = C.V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM
+	FmtDescFlagDynResolution               FmtDescFlag = C.V4L2_FMT_FLAG_DYN_RESOLUTION
+	FmtDescFlagEncodedCaptureFrameInterval FmtDescFlag = C.V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL
+	FmtDescFlagConfigColorspace            FmtDescFlag = C.V4L2_FMT_FLAG_CSC_COLORSPACE
+	FmtDescFlagConfigXferFunc              FmtDescFlag = C.V4L2_FMT_FLAG_CSC_XFER_FUNC
+	FmtDescFlagConfigYcbcrEnc              FmtDescFlag = C.V4L2_FMT_FLAG_CSC_YCBCR_ENC
+	FmtDescFlagConfigHsvEnc                FmtDescFlag = C.V4L2_FMT_FLAG_CSC_HSV_ENC
+	FmtDescFlagConfigQuantization          FmtDescFlag = C.V4L2_FMT_FLAG_CSC_QUANTIZATION
 )
 
 var FormatDescriptionFlags = map[FmtDescFlag]string{
@@ -36,72 +41,55 @@ var FormatDescriptionFlags = map[FmtDescFlag]string{
 	FmtDescFlagConfigQuantization:          "Quantization update supported",
 }
 
-// v4l2FormatDesc  (v4l2_fmtdesc)
+// FormatDescription  (v4l2_fmtdesc) provides access to the device format description
 // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L784
 // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-enum-fmt.html
-type v4l2FormatDesc struct {
-	index       uint32  // format number
-	bufType     BufType // stream type BufType
-	flags       FmtDescFlag
-	description [32]uint8      // string description
-	pixelFormat FourCCEncoding // Format fourcc value
-	mbusCode    uint32         // media bus code
-	reserved    [3]uint32
-}
-
-// FormatDescription provides access to the device format description
-// See v4l2FormatDesc
 type FormatDescription struct {
-	fd uintptr
-	v4l2FormatDesc
-}
-
-// GetIndex returns the format number
-func (d FormatDescription) GetIndex() uint32 {
-	return d.index
-}
-
-// GetBufType returns the type for the buffer (see v4l2_buf_type)
-func (d FormatDescription) GetBufType() BufType {
-	return d.bufType
-}
-
-// GetFlags returns image description flags (see FmtDescFlag)
-func (d FormatDescription) GetFlags() FmtDescFlag {
-	return d.flags
+	// Index returns the format number
+	Index uint32
+	// StreamType type for the buffer (see v4l2_buf_type)
+	StreamType BufType
+	// Flags is the image description flags (see FmtDescFlag)
+	Flags FmtDescFlag
+	// Description is a string value for the format description
+	Description string
+	// PixelFormat stores the four character encoding for the format
+	PixelFormat FourCCType
+	// MBusCode is the media bus code for drivers that advertise v4l2_cap_io_mc
+	MBusCode uint32
 }
 
-// GetDescription returns a string value for the format description
-func (d FormatDescription) GetDescription() string {
-	return toGoString(d.description[:])
+func (d FormatDescription) String() string {
+	return fmt.Sprintf(
+		"Format: %s [index: %d, flags: %s, format:%s]",
+		d.Description,
+		d.Index,
+		FormatDescriptionFlags[d.Flags],
+		PixelFormats[d.PixelFormat],
+	)
 }
-
-// GetPixelFormat returns the four character encoding for the format
-func (d FormatDescription) GetPixelFormat() FourCCEncoding {
-	return d.pixelFormat
-}
-
-// GetBusCode returns the media bus code for drivers that advertise v4l2_cap_io_mc
-func (d FormatDescription) GetBusCode() uint32 {
-	return d.mbusCode
-}
-
-// GetFrameSizes return all supported frame sizes for the format description.
-func (d FormatDescription) GetFrameSizes() ([]FrameSize, error) {
-	if d.fd == 0 {
-		return nil, fmt.Errorf("invalid file descriptor")
+func makeFormatDescription(fmtDesc C.struct_v4l2_fmtdesc) FormatDescription {
+	return FormatDescription{
+		Index:       uint32(fmtDesc.index),
+		StreamType:  uint32(fmtDesc._type),
+		Flags:       uint32(fmtDesc.flags),
+		Description: C.GoString((*C.char)(&fmtDesc.description[0])),
+		PixelFormat: uint32(fmtDesc.pixelformat),
+		MBusCode:    uint32(fmtDesc.mbus_code),
 	}
-	return GetFormatFrameSizes(d.fd, d.pixelFormat)
 }
 
 // GetFormatDescription returns a device format description at index
 func GetFormatDescription(fd uintptr, index uint32) (FormatDescription, error) {
-	desc := v4l2FormatDesc{index: index, bufType: BufTypeVideoCapture}
-	if err := Send(fd, VidiocEnumFmt, uintptr(unsafe.Pointer(&desc))); err != nil {
+	var fmtDesc C.struct_v4l2_fmtdesc
+	fmtDesc.index = C.uint(index)
+	fmtDesc._type = C.uint(BufTypeVideoCapture)
+
+	if err := send(fd, C.VIDIOC_ENUM_FMT, uintptr(unsafe.Pointer(&fmtDesc))); err != nil {
 		return FormatDescription{}, fmt.Errorf("format desc: index %d: %w", index, err)
 
 	}
-	return FormatDescription{fd: fd, v4l2FormatDesc: desc}, nil
+	return makeFormatDescription(fmtDesc), nil
 }
 
 // GetAllFormatDescriptions attempts to retrieve all device format descriptions by
@@ -111,30 +99,33 @@ func GetFormatDescription(fd uintptr, index uint32) (FormatDescription, error) {
 func GetAllFormatDescriptions(fd uintptr) (result []FormatDescription, err error) {
 	index := uint32(0)
 	for {
-		desc := v4l2FormatDesc{index: index, bufType: BufTypeVideoCapture}
-		if err = Send(fd, VidiocEnumFmt, uintptr(unsafe.Pointer(&desc))); err != nil {
+		var fmtDesc C.struct_v4l2_fmtdesc
+		fmtDesc.index = C.uint(index)
+		fmtDesc._type = C.uint(BufTypeVideoCapture)
+
+		if err = send(fd, C.VIDIOC_ENUM_FMT, uintptr(unsafe.Pointer(&fmtDesc))); err != nil {
 			if errors.Is(err, ErrorBadArgument) && len(result) > 0 {
 				break
 			}
 			return result, fmt.Errorf("format desc: all: %w", err)
 		}
-		result = append(result, FormatDescription{fd: fd, v4l2FormatDesc: desc})
+		result = append(result, makeFormatDescription(fmtDesc))
 		index++
 	}
 	return result, nil
 }
 
 // GetFormatDescriptionByEncoding returns a FormatDescription that matches the specified encoded pixel format
-func GetFormatDescriptionByEncoding(fd uintptr, enc FourCCEncoding)(FormatDescription, error) {
+func GetFormatDescriptionByEncoding(fd uintptr, enc FourCCType) (FormatDescription, error) {
 	descs, err := GetAllFormatDescriptions(fd)
 	if err != nil {
 		return FormatDescription{}, fmt.Errorf("format desc: encoding %s: %s", PixelFormats[enc], err)
 	}
 	for _, desc := range descs {
-		if desc.GetPixelFormat() == enc{
+		if desc.PixelFormat == enc {
 			return desc, nil
 		}
 	}
 
 	return FormatDescription{}, fmt.Errorf("format desc: driver does not support encoding %d", enc)
-}
+}

+ 38 - 68
v4l2/format_framesizes.go

@@ -1,59 +1,31 @@
 package v4l2
 
+//#include <linux/videodev2.h>
+import "C"
+
 import (
 	"errors"
 	"fmt"
 	"unsafe"
 )
 
-//enum v4l2_frmsizetypes {
-//V4L2_FRMSIZE_TYPE_DISCRETE	= 1,
-//V4L2_FRMSIZE_TYPE_CONTINUOUS	= 2,
-//V4L2_FRMSIZE_TYPE_STEPWISE	= 3,
-//};
-//
-//struct v4l2_frmsize_discrete {
-//__u32			width;		/* Frame width [pixel] */
-//__u32			height;		/* Frame height [pixel] */
-//};
-//
-//struct v4l2_frmsize_stepwise {
-//__u32			min_width;	/* Minimum frame width [pixel] */
-//__u32			max_width;	/* Maximum frame width [pixel] */
-//__u32			step_width;	/* Frame width step size [pixel] */
-//__u32			min_height;	/* Minimum frame height [pixel] */
-//__u32			max_height;	/* Maximum frame height [pixel] */
-//__u32			step_height;	/* Frame height step size [pixel] */
-//};
-//
-//struct v4l2_frmsizeenum {
-//__u32			index;		/* Frame size number */
-//__u32			pixel_format;	/* Pixel format */
-//__u32			type;		/* Frame size type the device supports. */
-//
-//union {					/* Frame size */
-//struct v4l2_frmsize_discrete	discrete;
-//struct v4l2_frmsize_stepwise	stepwise;
-//};
-//
-//__u32   reserved[2];			/* Reserved space for future use */
-//};
-
 type FrameSizeType = uint32
 
 const (
-	FrameSizeTypeDiscrete   FrameSizeType = iota + 1 // V4L2_FRMSIZE_TYPE_DISCRETE	 = 1
-	FrameSizeTypeContinuous                          // V4L2_FRMSIZE_TYPE_CONTINUOUS = 2
-	FrameSizeTypeStepwise                            // V4L2_FRMSIZE_TYPE_STEPWISE	 = 3
+	FrameSizeTypeDiscrete   FrameSizeType = C.V4L2_FRMSIZE_TYPE_DISCRETE
+	FrameSizeTypeContinuous FrameSizeType = C.V4L2_FRMSIZE_TYPE_CONTINUOUS
+	FrameSizeTypeStepwise   FrameSizeType = C.V4L2_FRMSIZE_TYPE_STEPWISE
 )
 
-// FrameSize is the frame size supported by the driver for the pixel format.
+// FrameSize uses v4l2_frmsizeenum to get supporeted frame size for the driver based for the pixel format.
 // Use FrameSizeType to determine which sizes the driver support.
+// https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L829
+// https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-enum-framesizes.html
 type FrameSize struct {
 	FrameSizeType
 	FrameSizeDiscrete
 	FrameSizeStepwise
-	PixelFormat FourCCEncoding
+	PixelFormat FourCCType
 }
 
 // FrameSizeDiscrete (v4l2_frmsize_discrete)
@@ -74,30 +46,19 @@ type FrameSizeStepwise struct {
 	StepHeight uint32 // Frame height step size [pixel]
 }
 
-// v4l2FrameSizes (v4l2_frmsizeenum)
-// https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L829
-// https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-enum-framesizes.html
-type v4l2FrameSizeEnum struct {
-	index         uint32
-	pixelFormat   FourCCEncoding
-	frameSizeType FrameSizeType
-	frameSize     [24]byte // union sized for largest struct: stepwise
-	reserved      [2]uint32
-}
-
-// getFrameSize retrieves the supported frame size
-func (fs v4l2FrameSizeEnum) getFrameSize() FrameSize {
-	frameSize := FrameSize{FrameSizeType: fs.frameSizeType, PixelFormat: fs.pixelFormat}
-	switch fs.frameSizeType {
+// getFrameSize retrieves the supported frame size based on the type
+func getFrameSize(frmSizeEnum C.struct_v4l2_frmsizeenum) FrameSize {
+	frameSize := FrameSize{FrameSizeType: FrameSizeType(frmSizeEnum._type), PixelFormat: FourCCType(frmSizeEnum.pixel_format)}
+	switch frameSize.FrameSizeType {
 	case FrameSizeTypeDiscrete:
-		fsDiscrete := (*FrameSizeDiscrete)(unsafe.Pointer(&fs.frameSize[0]))
+		fsDiscrete := (*FrameSizeDiscrete)(unsafe.Pointer(&frmSizeEnum.anon0[0]))
 		frameSize.FrameSizeDiscrete = *fsDiscrete
 		frameSize.FrameSizeStepwise.MinWidth = frameSize.FrameSizeDiscrete.Width
 		frameSize.FrameSizeStepwise.MinHeight = frameSize.FrameSizeDiscrete.Height
 		frameSize.FrameSizeStepwise.MaxWidth = frameSize.FrameSizeDiscrete.Width
 		frameSize.FrameSizeStepwise.MaxHeight = frameSize.FrameSizeDiscrete.Height
 	case FrameSizeTypeStepwise, FrameSizeTypeContinuous:
-		fsStepwise := (*FrameSizeStepwise)(unsafe.Pointer(&fs.frameSize[0]))
+		fsStepwise := (*FrameSizeStepwise)(unsafe.Pointer(&frmSizeEnum.anon0[0]))
 		frameSize.FrameSizeStepwise = *fsStepwise
 		frameSize.FrameSizeDiscrete.Width = frameSize.FrameSizeStepwise.MaxWidth
 		frameSize.FrameSizeDiscrete.Height = frameSize.FrameSizeStepwise.MaxHeight
@@ -106,20 +67,26 @@ func (fs v4l2FrameSizeEnum) getFrameSize() FrameSize {
 }
 
 // GetFormatFrameSize returns a supported device frame size for a specified encoding at index
-func GetFormatFrameSize(fd uintptr, index uint32, encoding FourCCEncoding) (FrameSize, error) {
-	fsEnum := v4l2FrameSizeEnum{index: index, pixelFormat: encoding}
-	if err := Send(fd, VidiocEnumFrameSizes, uintptr(unsafe.Pointer(&fsEnum))); err != nil {
+func GetFormatFrameSize(fd uintptr, index uint32, encoding FourCCType) (FrameSize, error) {
+	var frmSizeEnum C.struct_v4l2_frmsizeenum
+	frmSizeEnum.index = C.uint(index)
+	frmSizeEnum.pixel_format = C.uint(encoding)
+
+	if err := send(fd, C.VIDIOC_ENUM_FRAMESIZES, uintptr(unsafe.Pointer(&frmSizeEnum))); err != nil {
 		return FrameSize{}, fmt.Errorf("frame size: index %d: %w", index, err)
 	}
-	return fsEnum.getFrameSize(), nil
+	return getFrameSize(frmSizeEnum), nil
 }
 
 // GetFormatFrameSizes returns all supported device frame sizes for a specified encoding
-func GetFormatFrameSizes(fd uintptr, encoding FourCCEncoding) (result []FrameSize, err error) {
+func GetFormatFrameSizes(fd uintptr, encoding FourCCType) (result []FrameSize, err error) {
 	index := uint32(0)
 	for {
-		fsEnum := v4l2FrameSizeEnum{index: index, pixelFormat: encoding}
-		if err = Send(fd, VidiocEnumFrameSizes, uintptr(unsafe.Pointer(&fsEnum))); err != nil {
+		var frmSizeEnum C.struct_v4l2_frmsizeenum
+		frmSizeEnum.index = C.uint(index)
+		frmSizeEnum.pixel_format = C.uint(encoding)
+
+		if err = send(fd, C.VIDIOC_ENUM_FRAMESIZES, uintptr(unsafe.Pointer(&frmSizeEnum))); err != nil {
 			if errors.Is(err, ErrorBadArgument) && len(result) > 0 {
 				break
 			}
@@ -128,8 +95,8 @@ func GetFormatFrameSizes(fd uintptr, encoding FourCCEncoding) (result []FrameSiz
 
 		// At index 0, check the frame type, if not discrete exit loop.
 		// See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-enum-framesizes.html
-		result = append(result, fsEnum.getFrameSize())
-		if index == 0 && fsEnum.frameSizeType != FrameSizeTypeDiscrete {
+		result = append(result, getFrameSize(frmSizeEnum))
+		if index == 0 && uint32(frmSizeEnum._type) != FrameSizeTypeDiscrete {
 			break
 		}
 		index++
@@ -150,8 +117,11 @@ func GetAllFormatFrameSizes(fd uintptr) (result []FrameSize, err error) {
 	for _, format := range formats {
 		index := uint32(0)
 		for {
-			fsEnum := v4l2FrameSizeEnum{index: index, pixelFormat: format.GetPixelFormat()}
-			if err = Send(fd, VidiocEnumFrameSizes, uintptr(unsafe.Pointer(&fsEnum))); err != nil {
+			var frmSizeEnum C.struct_v4l2_frmsizeenum
+			frmSizeEnum.index = C.uint(index)
+			frmSizeEnum.pixel_format = C.uint(format.PixelFormat)
+
+			if err = send(fd, C.VIDIOC_ENUM_FRAMESIZES, uintptr(unsafe.Pointer(&frmSizeEnum))); err != nil {
 				if errors.Is(err, ErrorBadArgument) && len(result) > 0 {
 					break
 				}
@@ -160,8 +130,8 @@ func GetAllFormatFrameSizes(fd uintptr) (result []FrameSize, err error) {
 
 			// At index 0, check the frame type, if not discrete exit loop.
 			// See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-enum-framesizes.html
-			result = append(result, fsEnum.getFrameSize())
-			if index == 0 && fsEnum.frameSizeType != FrameSizeTypeDiscrete {
+			result = append(result, getFrameSize(frmSizeEnum))
+			if index == 0 && uint32(frmSizeEnum._type) != FrameSizeTypeDiscrete {
 				break
 			}
 			index++

+ 2 - 82
v4l2/ioctl.go

@@ -1,66 +1,9 @@
 package v4l2
 
 import (
-	"unsafe"
-
 	sys "golang.org/x/sys/unix"
 )
 
-// ioctl uses a 32-bit value to encode commands sent to the kernel for device control.
-// Requests sent via ioctl uses the following layout:
-// - lower 16 bits: ioctl command
-// - Upper 14 bits: size of the parameter structure
-// - MSB 2 bits: are reserved for indicating the ``access mode''.
-// https://elixir.bootlin.com/linux/latest/source/include/uapi/asm-generic/ioctl.h
-
-const (
-	// ioctl command bit sizes
-	// See https://elixir.bootlin.com/linux/latest/source/include/uapi/asm-generic/ioctl.h#L23
-	iocNumberBits = 8
-	iocTypeBits   = 8
-	iocSizeBits   = 14
-	iocDirBits    = 2
-
-	// ioctl bit layout positions
-	// see https://elixir.bootlin.com/linux/latest/source/include/uapi/asm-generic/ioctl.h#L44
-	numShift  = 0
-	typeShift = numShift + iocNumberBits
-	sizeShift = typeShift + iocTypeBits
-	dirShift  = sizeShift + iocSizeBits
-
-	// ioctl direction bits
-	// These values are from the ioctl.h in linux.
-	// See https://elixir.bootlin.com/linux/latest/source/include/uapi/asm-generic/ioctl.h#L57
-	iocNone  = 0 // no op
-	iocWrite = 1 // userland app is writing, kernel reading
-	iocRead  = 2 // userland app is reading, kernel writing
-
-)
-
-// iocEnc encodes V42L API command as value.
-// See https://elixir.bootlin.com/linux/latest/source/include/uapi/asm-generic/ioctl.h#L69
-func iocEnc(dir, iocType, number, size uintptr) uintptr {
-	return (dir << dirShift) | (iocType << typeShift) | (number << numShift) | (size << sizeShift)
-}
-
-// iocEncR encodes ioctl command where program reads result from kernel.
-// See https://elixir.bootlin.com/linux/latest/source/include/uapi/asm-generic/ioctl.h#L86
-func iocEncR(iocType, number, size uintptr) uintptr {
-	return iocEnc(iocRead, iocType, number, size)
-}
-
-// iocEncW encodes ioctl command where program writes values read by the kernel.
-// See https://elixir.bootlin.com/linux/latest/source/include/uapi/asm-generic/ioctl.h#L87
-func iocEncW(iocType, number, size uintptr) uintptr {
-	return iocEnc(iocWrite, iocType, number, size)
-}
-
-// iocEncRW encodes ioctl command for program reads and program writes.
-// See https://elixir.bootlin.com/linux/latest/source/include/uapi/asm-generic/ioctl.h#L88
-func iocEncRW(iocType, number, size uintptr) uintptr {
-	return iocEnc(iocRead|iocWrite, iocType, number, size)
-}
-
 // ioctl is a wrapper for Syscall(SYS_IOCTL)
 func ioctl(fd, req, arg uintptr) (err sys.Errno) {
 	if _, _, errno := sys.Syscall(sys.SYS_IOCTL, fd, req, arg); errno != 0 {
@@ -72,31 +15,8 @@ func ioctl(fd, req, arg uintptr) (err sys.Errno) {
 	return 0
 }
 
-// V4L2 command request values for ioctl
-// https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L2510
-// https://www.kernel.org/doc/html/v4.14/media/uapi/v4l/user-func.html
-
-var (
-	VidiocQueryCap       = iocEncR('V', 0, unsafe.Sizeof(v4l2Capability{}))      // Represents command VIDIOC_QUERYCAP
-	VidiocEnumFmt        = iocEncRW('V', 2, unsafe.Sizeof(v4l2FormatDesc{}))     // Represents command VIDIOC_ENUM_FMT
-	VidiocGetFormat      = iocEncRW('V', 4, unsafe.Sizeof(v4l2Format{}))         // Represents command VIDIOC_G_FMT
-	VidiocSetFormat      = iocEncRW('V', 5, unsafe.Sizeof(v4l2Format{}))         // Represents command VIDIOC_S_FMT
-	VidiocReqBufs        = iocEncRW('V', 8, unsafe.Sizeof(RequestBuffers{}))     // Represents command VIDIOC_REQBUFS
-	VidiocQueryBuf       = iocEncRW('V', 9, unsafe.Sizeof(BufferInfo{}))         // Represents command VIDIOC_QUERYBUF
-	VidiocQueueBuf       = iocEncRW('V', 15, unsafe.Sizeof(BufferInfo{}))        // Represents command VIDIOC_QBUF
-	VidiocDequeueBuf     = iocEncRW('V', 17, unsafe.Sizeof(BufferInfo{}))        // Represents command VIDIOC_DQBUF
-	VidiocStreamOn       = iocEncW('V', 18, unsafe.Sizeof(int32(0)))             // Represents command VIDIOC_STREAMON
-	VidiocStreamOff      = iocEncW('V', 19, unsafe.Sizeof(int32(0)))             // Represents command VIDIOC_STREAMOFF
-	VidiocGetParam       = iocEncRW('V', 21, unsafe.Sizeof(v4l2StreamParam{}))   // Represents command VIDIOC_G_PARM
-	VidiocEnumInput      = iocEncRW('V', 26, unsafe.Sizeof(v4l2InputInfo{}))     // Represents command VIDIOC_ENUMINPUT
-	VidiocGetVideoInput  = iocEncR('V', 38, unsafe.Sizeof(int32(0)))             // Represents command VIDIOC_G_INPUT
-	VidiocCropCap        = iocEncRW('V', 58, unsafe.Sizeof(CropCapability{}))    // Represents command VIDIOC_CROPCAP
-	VidiocSetCrop        = iocEncW('V', 60, unsafe.Sizeof(Crop{}))               // Represents command VIDIOC_S_CROP
-	VidiocEnumFrameSizes = iocEncRW('V', 74, unsafe.Sizeof(v4l2FrameSizeEnum{})) // Represents command VIDIOC_ENUM_FRAMESIZES
-)
-
-// Send sends a request to the kernel (via ioctl syscall)
-func Send(fd, req, arg uintptr) error {
+// send sends a request to the kernel (via ioctl syscall)
+func send(fd, req, arg uintptr) error {
 	errno := ioctl(fd, req, arg)
 	if errno == 0 {
 		return nil

+ 13 - 35
v4l2/stream_param.go

@@ -1,5 +1,8 @@
 package v4l2
 
+// #include <linux/videodev2.h>
+import "C"
+
 import (
 	"fmt"
 	"unsafe"
@@ -11,8 +14,8 @@ import (
 type StreamParamFlag = uint32
 
 const (
-	StreamParamModeHighQuality StreamParamFlag = 0x0001 // V4L2_MODE_HIGHQUALITY
-	StreamParamTimePerFrame    StreamParamFlag = 0x1000 // V4L2_CAP_TIMEPERFRAME
+	StreamParamModeHighQuality StreamParamFlag = C.V4L2_MODE_HIGHQUALITY
+	StreamParamTimePerFrame    StreamParamFlag = C.V4L2_CAP_TIMEPERFRAME
 )
 
 // CaptureParam (v4l2_captureparam)
@@ -24,44 +27,19 @@ type CaptureParam struct {
 	TimePerFrame Fract
 	ExtendedMode uint32
 	ReadBuffers  uint32
-	reserved     [4]uint32
+	_            [4]uint32
 }
 
-// v4l2StreamParam (v4l2_streamparam)
+// GetStreamCaptureParam returns streaming capture parameter for the driver (v4l2_streamparm).
 // https://linuxtv.org/downloads/v4l-dvb-apis/userspace-api/v4l/vidioc-g-parm.html
 // See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L2347
-//
-// Field param points to an embedded union, therefore,  it points to an array sized
-// as the largest knwon member of the embedded struct.  See below:
-//
-// struct v4l2_streamparm {
-//	__u32	 type;
-//	union {
-//		struct v4l2_captureparm	capture;
-//		struct v4l2_outputparm	output;
-//		__u8	raw_data[200];
-//	} parm;
-//};
-type v4l2StreamParam struct {
-	streamType StreamMemoryType
-	param      [200]byte // embedded union
-}
 
-// getCaptureParam returns CaptureParam value from v4l2StreamParam embedded union
-// if p.streamType = BufTypeVideoCapture.
-func (p v4l2StreamParam) getCaptureParam() CaptureParam {
-	var param CaptureParam
-	if p.streamType == BufTypeVideoCapture {
-		param = *(*CaptureParam)(unsafe.Pointer(&p.param[0]))
-	}
-	return param
-}
+func GetStreamCaptureParam(fd uintptr) (CaptureParam, error) {
+	var param C.struct_v4l2_streamparm
+	param._type = C.uint(BufTypeVideoCapture)
 
-// GetStreamCaptureParam returns streaming capture parameter for the driver.
-func GetStreamCaptureParam (fd uintptr)(CaptureParam, error){
-	param := v4l2StreamParam{streamType: BufTypeVideoCapture}
-	if err := Send(fd, VidiocGetParam, uintptr(unsafe.Pointer(&param))); err != nil {
+	if err := send(fd, C.VIDIOC_G_PARM, uintptr(unsafe.Pointer(&param))); err != nil {
 		return CaptureParam{}, fmt.Errorf("stream param: %w", err)
 	}
-	return param.getCaptureParam(), nil
-}
+	return *(*CaptureParam)(unsafe.Pointer(&param.parm[0])), nil
+}

+ 94 - 121
v4l2/streaming.go

@@ -1,5 +1,8 @@
 package v4l2
 
+// #include <linux/videodev2.h>
+import "C"
+
 import (
 	"errors"
 	"fmt"
@@ -18,28 +21,27 @@ import (
 type BufType = uint32
 
 const (
-	BufTypeVideoCapture BufType = iota + 1 // V4L2_BUF_TYPE_VIDEO_CAPTURE = 1
-	BufTypeVideoOutput                     // V4L2_BUF_TYPE_VIDEO_OUTPUT  = 2
-	BufTypeOverlay                         // V4L2_BUF_TYPE_VIDEO_OVERLAY = 3
+	BufTypeVideoCapture BufType = C.V4L2_BUF_TYPE_VIDEO_CAPTURE
+	BufTypeVideoOutput  BufType = C.V4L2_BUF_TYPE_VIDEO_OUTPUT
+	BufTypeOverlay      BufType = C.V4L2_BUF_TYPE_VIDEO_OVERLAY
 )
 
-// StreamMemoryType (v4l2_memory)
+// StreamType (v4l2_memory)
 // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/mmap.html?highlight=v4l2_memory_mmap
 // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L188
-type StreamMemoryType = uint32
+type StreamType = uint32
 
 const (
-	StreamMemoryTypeMMAP    StreamMemoryType = iota + 1 // V4L2_MEMORY_MMAP             = 1,
-	StreamMemoryTypeUserPtr                             // V4L2_MEMORY_USERPTR          = 2,
-	StreamMemoryTypeOverlay                             // V4L2_MEMORY_OVERLAY          = 3,
-	StreamMemoryTypeDMABuf                              // V4L2_MEMORY_DMABUF           = 4,
+	StreamTypeMMAP    StreamType = C.V4L2_MEMORY_MMAP
+	StreamTypeUserPtr StreamType = C.V4L2_MEMORY_USERPTR
+	StreamTypeOverlay StreamType = C.V4L2_MEMORY_OVERLAY
+	StreamTypeDMABuf  StreamType = C.V4L2_MEMORY_DMABUF
 )
 
 // TODO implement vl42_create_buffers
 
-// RequestBuffers (v4l2_requestbuffers)
-// This type is used to allocate buffer/io resources when initializing streaming io for
-// memory mapped, user pointer, or DMA buffer access.
+// RequestBuffers (v4l2_requestbuffers) is used to request buffer allocation initializing
+// streaming for memory mapped, user pointer, or DMA buffer access.
 // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L949
 // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-reqbufs.html?highlight=v4l2_requestbuffers#c.V4L.v4l2_requestbuffers
 type RequestBuffers struct {
@@ -47,43 +49,15 @@ type RequestBuffers struct {
 	StreamType   uint32
 	Memory       uint32
 	Capabilities uint32
-	Reserved     [1]uint32
+	_            [1]uint32
 }
 
-// BufferInfo (v4l2_buffer)
-// This type is used to send buffers management info between application and driver
+// Buffer (v4l2_buffer) is used to send buffers info between application and driver
 // after streaming IO has been initialized.
 // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/buffer.html#c.V4L.v4l2_buffer
 // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L1037
 //
-// BufferInfo represents type v4l2_buffer which contains unions as shown below.
-// Remember, the union is represented as an arry of bytes sized as the largest
-// member in bytes.
-//
-//   struct v4l2_buffer {
-// 	   __u32			index;
-// 	   __u32			type;
-// 	   __u32			bytesused;
-// 	   __u32			flags;
-// 	   __u32			field;
-// 	   struct timeval		timestamp;
-// 	   struct v4l2_timecode	timecode;
-// 	   __u32			sequence;
-// 	   __u32			memory;
-// 	   union {
-// 		  __u32             offset;
-// 		  unsigned long     userptr;
-// 		  struct v4l2_plane *planes;
-// 		  __s32		fd;
-// 	   } m;
-// 	   __u32	length;
-// 	   __u32	reserved2;
-// 	   union {
-// 		  __s32		request_fd;
-// 		  __u32		reserved;
-// 	   };
-// };
-type BufferInfo struct {
+type Buffer struct {
 	Index      uint32
 	StreamType uint32
 	BytesUsed  uint32
@@ -93,61 +67,56 @@ type BufferInfo struct {
 	Timecode   Timecode
 	Sequence   uint32
 	Memory     uint32
-	m          [unsafe.Sizeof(&BufferService{})]byte // union m, cast to BufferService
+	Info       BufferInfo // union m
 	Length     uint32
 	Reserved2  uint32
 	RequestFD  int32
 }
 
-func (b BufferInfo) GetService() BufferService {
-	m := (*BufferService)(unsafe.Pointer(&b.m[0]))
-	return *m
+// makeBuffer makes a Buffer value from C.struct_v4l2_buffer
+func makeBuffer(v4l2Buf C.struct_v4l2_buffer) Buffer {
+	return Buffer{
+		Index:      uint32(v4l2Buf.index),
+		StreamType: uint32(v4l2Buf._type),
+		BytesUsed:  uint32(v4l2Buf.bytesused),
+		Flags:      uint32(v4l2Buf.flags),
+		Field:      uint32(v4l2Buf.field),
+		Timestamp:  *(*sys.Timeval)(unsafe.Pointer(&v4l2Buf.timestamp)),
+		Timecode:   *(*Timecode)(unsafe.Pointer(&v4l2Buf.timecode)),
+		Sequence:   uint32(v4l2Buf.sequence),
+		Memory:     uint32(v4l2Buf.memory),
+		Info:       *(*BufferInfo)(unsafe.Pointer(&v4l2Buf.m[0])),
+		Length:     uint32(v4l2Buf.length),
+		Reserved2:  uint32(v4l2Buf.reserved2),
+		RequestFD:  *(*int32)(unsafe.Pointer(&v4l2Buf.anon0[0])),
+	}
 }
 
-// BufferService represents Union of several values in type Buffer
+// BufferInfo represents Union of several values in type Buffer
 // that are used to service the stream depending on the type of streaming
 // selected (MMap, User pointer, planar, file descriptor for DMA)
-type BufferService struct {
+type BufferInfo struct {
 	Offset  uint32
 	UserPtr uintptr
-	Planes  *PlaneInfo
+	Planes  *Plane
 	FD      int32
 }
 
-// PlaneInfo (v4l2_plane)
-// Represents a plane in a multi-planar buffers
+// Plane (see struct v4l2_plane) represents a plane in a multi-planar buffers
 // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/buffer.html#c.V4L.v4l2_plane
 // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L990
 //
-// PlaneInfo includes a uinion of types as shown below:
-// struct v4l2_plane {
-// 	__u32			bytesused;
-// 	__u32			length;
-// 	union {
-// 		__u32		mem_offset;
-// 		unsigned long	userptr;
-// 		__s32		fd;
-// 	} m;
-// 	__u32			data_offset;
-// 	__u32			reserved[11];
-// };
-type PlaneInfo struct {
+type Plane struct {
 	BytesUsed  uint32
 	Length     uint32
-	m          [unsafe.Sizeof(uintptr(0))]byte // union m, cast to BufferPlaneService
+	Info       PlaneInfo // union m
 	DataOffset uint32
-	Reserved   [11]uint32
-}
-
-func (p PlaneInfo) GetService() PlaneService {
-	m := (*PlaneService)(unsafe.Pointer(&p.m[0]))
-	return *m
 }
 
-// PlaneService representes the combination of type
+// PlaneInfo representes the combination of type
 // of type of memory stream that can be serviced for the
 // associated plane.
-type PlaneService struct {
+type PlaneInfo struct {
 	MemOffset uint32
 	UserPtr   uintptr
 	FD        int32
@@ -158,7 +127,7 @@ type PlaneService struct {
 // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-streamon.html
 func StreamOn(fd uintptr) error {
 	bufType := BufTypeVideoCapture
-	if err := Send(fd, VidiocStreamOn, uintptr(unsafe.Pointer(&bufType))); err != nil {
+	if err := send(fd, C.VIDIOC_STREAMON, uintptr(unsafe.Pointer(&bufType))); err != nil {
 		return fmt.Errorf("stream on: %w", err)
 	}
 	return nil
@@ -169,91 +138,95 @@ func StreamOn(fd uintptr) error {
 // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-streamon.html
 func StreamOff(fd uintptr) error {
 	bufType := BufTypeVideoCapture
-	if err := Send(fd, VidiocStreamOff, uintptr(unsafe.Pointer(&bufType))); err != nil {
+	if err := send(fd, C.VIDIOC_STREAMOFF, uintptr(unsafe.Pointer(&bufType))); err != nil {
 		return fmt.Errorf("stream off: %w", err)
 	}
 	return nil
 }
 
-// AllocateBuffers sends buffer allocation request to underlying driver
+// InitBuffers sends buffer allocation request to initialize buffer IO
 // for video capture when using either mem map, user pointer, or DMA buffers.
-func AllocateBuffers(fd uintptr, buffSize uint32) (RequestBuffers, error) {
-	req := RequestBuffers{
-		Count:      buffSize,
-		StreamType: BufTypeVideoCapture,
-		Memory:     StreamMemoryTypeMMAP,
-	}
-
-	if err := Send(fd, VidiocReqBufs, uintptr(unsafe.Pointer(&req))); err != nil {
+// See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-reqbufs.html#vidioc-reqbufs
+func InitBuffers(fd uintptr, buffSize uint32) (RequestBuffers, error) {
+	var req C.struct_v4l2_requestbuffers
+	req.count = C.uint(buffSize)
+	req._type = C.uint(BufTypeVideoCapture)
+	req.memory = C.uint(StreamTypeMMAP)
+
+	if err := send(fd, C.VIDIOC_REQBUFS, uintptr(unsafe.Pointer(&req))); err != nil {
 		return RequestBuffers{}, fmt.Errorf("request buffers: %w", err)
 	}
-	if req.Count < 2 {
+	if req.count < 2 {
 		return RequestBuffers{}, errors.New("request buffers: insufficient memory on device")
 	}
 
-	return req, nil
+	return *(*RequestBuffers)(unsafe.Pointer(&req)), nil
 }
 
-// GetBuffersInfo retrieves information for allocated buffers at provided index.
+// GetBuffer retrieves bunffer info for allocated buffers at provided index.
 // This call should take place after buffers are allocated (for mmap for instance).
-func GetBufferInfo(fd uintptr, index uint32) (BufferInfo, error) {
-	buf := BufferInfo{
-		StreamType: BufTypeVideoCapture,
-		Memory:     StreamMemoryTypeMMAP,
-		Index:      index,
-	}
-
-	if err := Send(fd, VidiocQueryBuf, uintptr(unsafe.Pointer(&buf))); err != nil {
-		return BufferInfo{}, fmt.Errorf("buffer info: %w", err)
+func GetBuffer(fd uintptr, index uint32) (Buffer, error) {
+	var v4l2Buf C.struct_v4l2_buffer
+	v4l2Buf._type = C.uint(BufTypeVideoCapture)
+	v4l2Buf.memory = C.uint(StreamTypeMMAP)
+	v4l2Buf.index = C.uint(index)
+
+	if err := send(fd, C.VIDIOC_QUERYBUF, uintptr(unsafe.Pointer(&v4l2Buf))); err != nil {
+		return Buffer{}, fmt.Errorf("query buffer: %w", err)
 	}
 
-	return buf, nil
+	return makeBuffer(v4l2Buf), nil
 }
 
 // MapMemoryBuffer creates a local buffer mapped to the address space of the device specified by fd.
 func MapMemoryBuffer(fd uintptr, offset int64, len int) ([]byte, error) {
-	return sys.Mmap(int(fd), offset, len, sys.PROT_READ|sys.PROT_WRITE, sys.MAP_SHARED)
+	data, err := sys.Mmap(int(fd), offset, len, sys.PROT_READ|sys.PROT_WRITE, sys.MAP_SHARED)
+	if err != nil {
+		return nil, fmt.Errorf("map memory buffer: %w", err)
+	}
+	return data, nil
 }
 
 // UnmapMemoryBuffer removes the buffer that was previously mapped.
 func UnmapMemoryBuffer(buf []byte) error {
-	return sys.Munmap(buf)
+	if err := sys.Munmap(buf); err != nil {
+		return fmt.Errorf("unmap memory buffer: %w", err)
+	}
+	return nil
 }
 
-// QueueBuffer enqueues a buffer in the device driver (empty for capturing, filled for video output)
-// when using either memory map, user pointer, or DMA buffers. BufferInfo is returned with
+// QueueBuffer enqueues a buffer in the device driver (as empty for capturing, or filled for video output)
+// when using either memory map, user pointer, or DMA buffers. Buffer is returned with
 // additional information about the queued buffer.
 // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-qbuf.html#vidioc-qbuf
-func QueueBuffer(fd uintptr, index uint32) (BufferInfo, error) {
-	buf := BufferInfo{
-		StreamType: BufTypeVideoCapture,
-		Memory:     StreamMemoryTypeMMAP,
-		Index:      index,
-	}
-
-	if err := Send(fd, VidiocQueueBuf, uintptr(unsafe.Pointer(&buf))); err != nil {
-		return BufferInfo{}, fmt.Errorf("buffer queue: %w", err)
+func QueueBuffer(fd uintptr, index uint32) (Buffer, error) {
+	var v4l2Buf C.struct_v4l2_buffer
+	v4l2Buf._type = C.uint(BufTypeVideoCapture)
+	v4l2Buf.memory = C.uint(StreamTypeMMAP)
+	v4l2Buf.index = C.uint(index)
+
+	if err := send(fd, C.VIDIOC_QBUF, uintptr(unsafe.Pointer(&v4l2Buf))); err != nil {
+		return Buffer{}, fmt.Errorf("buffer queue: %w", err)
 	}
 
-	return buf, nil
+	return makeBuffer(v4l2Buf), nil
 }
 
 // DequeueBuffer dequeues a buffer in the device driver, marking it as consumed by the application,
-// when using either memory map, user pointer, or DMA buffers. BufferInfo is returned with
+// when using either memory map, user pointer, or DMA buffers. Buffer is returned with
 // additional information about the dequeued buffer.
 // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-qbuf.html#vidioc-qbuf
-func DequeueBuffer(fd uintptr) (BufferInfo, error) {
-	buf := BufferInfo{
-		StreamType: BufTypeVideoCapture,
-		Memory:     StreamMemoryTypeMMAP,
-	}
+func DequeueBuffer(fd uintptr) (Buffer, error) {
+	var v4l2Buf C.struct_v4l2_buffer
+	v4l2Buf._type = C.uint(BufTypeVideoCapture)
+	v4l2Buf.memory = C.uint(StreamTypeMMAP)
 
-	if err := Send(fd, VidiocDequeueBuf, uintptr(unsafe.Pointer(&buf))); err != nil {
-		return BufferInfo{}, fmt.Errorf("buffer dequeue: %w", err)
+	if err := send(fd, C.VIDIOC_DQBUF, uintptr(unsafe.Pointer(&v4l2Buf))); err != nil {
+		return Buffer{}, fmt.Errorf("buffer dequeue: %w", err)
 
 	}
 
-	return buf, nil
+	return makeBuffer(v4l2Buf), nil
 }
 
 // WaitForDeviceRead blocks until the specified device is

+ 17 - 14
v4l2/timing.go

@@ -1,16 +1,19 @@
 package v4l2
 
+// #include <linux/videodev2.h>
+import "C"
+
 // TimecodeType
 // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/buffer.html?highlight=v4l2_timecode#timecode-type
 // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L886
 type TimecodeType = uint32
 
 const (
-	TimecodeType24FPS TimecodeType = iota + 1 // V4L2_TC_TYPE_24FPS
-	TimecodeType25FPS                         // V4L2_TC_TYPE_25FPS
-	TimecodeType30FPS                         // V4L2_TC_TYPE_30FPS
-	TimecodeType50FPS                         // V4L2_TC_TYPE_50FPS
-	TimecodeType60FPS                         // V4L2_TC_TYPE_60FPS
+	TimecodeType24FPS TimecodeType = C.V4L2_TC_TYPE_24FPS
+	TimecodeType25FPS TimecodeType = C.V4L2_TC_TYPE_25FPS
+	TimecodeType30FPS TimecodeType = C.V4L2_TC_TYPE_30FPS
+	TimecodeType50FPS TimecodeType = C.V4L2_TC_TYPE_50FPS
+	TimecodeType60FPS TimecodeType = C.V4L2_TC_TYPE_60FPS
 )
 
 // TimecodeFlag
@@ -19,19 +22,19 @@ const (
 type TimecodeFlag = uint32
 
 const (
-	TimecodeFlagDropFrame  TimecodeFlag = 0x0001 // V4L2_TC_FLAG_DROPFRAME	0x0001
-	TimecodeFlagColorFrame TimecodeFlag = 0x0002 // V4L2_TC_FLAG_COLORFRAME	0x0002
+	TimecodeFlagDropFrame  TimecodeFlag = C.V4L2_TC_FLAG_DROPFRAME
+	TimecodeFlagColorFrame TimecodeFlag = C.V4L2_TC_FLAG_COLORFRAME
 )
 
 // Timecode (v4l2_timecode)
 // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/buffer.html?highlight=v4l2_timecode#c.V4L.v4l2_timecode
 // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L875
 type Timecode struct {
-	Type     TimecodeType
-	Flags    TimecodeFlag
-	Frames   uint8
-	Seconds  uint8
-	Minutes  uint8
-	Hours    uint8
-	Userbits [4]uint8
+	Type    TimecodeType
+	Flags   TimecodeFlag
+	Frames  uint8
+	Seconds uint8
+	Minutes uint8
+	Hours   uint8
+	_       [4]uint8 // userbits
 }

+ 23 - 0
v4l2/types.go

@@ -1,2 +1,25 @@
 package v4l2
 
+import (
+	"fmt"
+)
+
+type VersionInfo struct {
+	value uint32
+}
+
+func (v VersionInfo) Major() uint32{
+	return v.value >> 16
+}
+
+func (v VersionInfo) Minor() uint32{
+	return (v.value>>8)&0xff
+}
+
+func (v VersionInfo) Patch() uint32{
+	return v.value&0xff
+}
+
+func (v VersionInfo) String() string {
+	return fmt.Sprintf("v%d.%d.%d", v.Major(), v.Minor(), v.Patch())
+}

+ 26 - 33
v4l2/video_info.go

@@ -1,9 +1,13 @@
 package v4l2
 
+// #include <linux/videodev2.h>
+import "C"
+
 import (
 	"fmt"
-	sys "golang.org/x/sys/unix"
 	"unsafe"
+
+	sys "golang.org/x/sys/unix"
 )
 
 // InputStatus
@@ -11,9 +15,9 @@ import (
 type InputStatus = uint32
 
 var (
-	InputStatusNoPower  = InputStatus(0x00000001) // V4L2_IN_ST_NO_POWER
-	InputStatusNoSignal = InputStatus(0x00000002) // V4L2_IN_ST_NO_SIGNAL
-	InputStatusNoColor  = InputStatus(0x00000004) // V4L2_IN_ST_NO_COLOR
+	InputStatusNoPower  InputStatus = C.V4L2_IN_ST_NO_POWER
+	InputStatusNoSignal InputStatus = C.V4L2_IN_ST_NO_SIGNAL
+	InputStatusNoColor  InputStatus  = C.V4L2_IN_ST_NO_COLOR
 )
 
 var InputStatuses = map[InputStatus]string{
@@ -33,63 +37,50 @@ const (
 
 type StandardId = uint64
 
-type v4l2InputInfo struct {
-	index        uint32
-	name         [32]uint8
-	inputType    InputType
-	audioset     uint32
-	tuner        uint32
-	std          StandardId
-	status       InputStatus
-	capabilities uint32
-	reserved     [3]uint32
-	_            [4]uint8 // go compiler alignment adjustment for 32-bit platforms (Raspberry pi's, etc)
-}
-
 // InputInfo (v4l2_input)
 // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L1649
 // https://linuxtv.org/downloads/v4l-dvb-apis/userspace-api/v4l/vidioc-enuminput.html
 type InputInfo struct {
-	v4l2InputInfo
+	v4l2Input C.struct_v4l2_input
 }
 
 func (i InputInfo) GetIndex() uint32 {
-	return i.index
+	return uint32(i.v4l2Input.index)
 }
 
 func (i InputInfo) GetName() string {
-	return toGoString(i.name[:])
+	return C.GoString((*C.char)(&i.v4l2Input.name[0]))
 }
 
 func (i InputInfo) GetInputType() InputType {
-	return i.inputType
+	return InputType(i.v4l2Input._type)
 }
 
 func (i InputInfo) GetAudioset() uint32 {
-	return i.audioset
+	return uint32(i.v4l2Input.audioset)
 }
 
 func (i InputInfo) GetTuner() uint32 {
-	return i.tuner
+	return uint32(i.v4l2Input.tuner)
 }
 
 func (i InputInfo) GetStandardId() StandardId {
-	return i.std
+	return StandardId(i.v4l2Input.std)
 }
 
 func (i InputInfo) GetStatus() uint32 {
-	return i.status
+	return uint32(i.v4l2Input.status)
 }
 
 func (i InputInfo) GetCapabilities() uint32 {
-	return i.capabilities
+	return uint32(i.v4l2Input.capabilities)
 }
 
 // GetCurrentVideoInputIndex returns the currently selected video input index
 // See https://linuxtv.org/downloads/v4l-dvb-apis/userspace-api/v4l/vidioc-g-input.html
 func GetCurrentVideoInputIndex(fd uintptr) (int32, error) {
 	var index int32
-	if err := Send(fd, VidiocGetVideoInput, uintptr(unsafe.Pointer(&index))); err != nil {
+	if err := send(fd, C.VIDIOC_G_INPUT, uintptr(unsafe.Pointer(&index))); err != nil {
 		return -1, fmt.Errorf("video input get: %w", err)
 	}
 	return index, nil
@@ -98,11 +89,12 @@ func GetCurrentVideoInputIndex(fd uintptr) (int32, error) {
 // GetVideoInputInfo returns specified input information for video device
 // See https://linuxtv.org/downloads/v4l-dvb-apis/userspace-api/v4l/vidioc-enuminput.html
 func GetVideoInputInfo(fd uintptr, index uint32) (InputInfo, error) {
-	input := v4l2InputInfo{index: index}
-	if err := Send(fd, VidiocEnumInput, uintptr(unsafe.Pointer(&input))); err != nil {
+	var input C.struct_v4l2_input
+	input.index = C.uint(index)
+	if err := send(fd, C.VIDIOC_ENUMINPUT, uintptr(unsafe.Pointer(&input))); err != nil {
 		return InputInfo{}, fmt.Errorf("video input info: index %d: %w", index, err)
 	}
-	return InputInfo{v4l2InputInfo: input}, nil
+	return InputInfo{v4l2Input: input}, nil
 }
 
 // GetAllVideoInputInfo returns all input information for device by
@@ -110,15 +102,16 @@ func GetVideoInputInfo(fd uintptr, index uint32) (InputInfo, error) {
 func GetAllVideoInputInfo(fd uintptr) (result []InputInfo, err error) {
 	index := uint32(0)
 	for {
-		input := v4l2InputInfo{index: index}
-		if err = Send(fd, VidiocEnumInput, uintptr(unsafe.Pointer(&input))); err != nil {
+		var input C.struct_v4l2_input
+		input.index = C.uint(index)
+		if err = send(fd, C.VIDIOC_ENUMINPUT, uintptr(unsafe.Pointer(&input))); err != nil {
 			errno := err.(sys.Errno)
 			if errno.Is(sys.EINVAL) && len(result) > 0 {
 				break
 			}
 			return result, fmt.Errorf("all video info: %w", err)
 		}
-		result = append(result, InputInfo{v4l2InputInfo: input})
+		result = append(result, InputInfo{v4l2Input: input})
 		index++
 	}
 	return result, err