go4vl

repository·main·Indexed 43 days ago

https://github.com/vladimirvivien/go4vl

A Go-centric abstraction for the Video for Linux 2 (v4l2) API, providing idiomatic Go interfaces such as channels and buffer pooling for real-time video capture and control on Linux systems.

Tokens
35K
Snippets
112
Records
172
Agent score
55%

What's inside go4vl

  1. Manage the Frame Lifecycle and Buffer Pooling

    main

    To benefit from the performance improvements of GetFrames(), you must strictly follow the frame lifecycle to prevent pool exhaustion:

    1. Receive: Iterate over the channel returned by dev.GetFrames().
    2. Process: Use frame.Data to perform your work.
    3. Release: You MUST call frame.Release() when finished with the frame. This returns the buffer to the pool for reuse.

    Critical Warnings:

    • Do not use frame.Data after calling Release(): The buffer is immediately invalid and may be overwritten by the next captured frame.
    • Copy if you need to persist data: If you need to use the frame data outside the capture loop, you must copy it to a new slice before calling Release().
    • Failure to call Release(): If you forget to release frames, the buffer pool will eventually exhaust, causing the application to stall or crash.
    // GOOD: Copy before Release() to use data later
    frame := <-dev.GetFrames()
    saved := make([]byte, len(frame.Data))
    copy(saved, frame.Data)
    frame.Release()
    processLater(saved) // OK: using copy
  2. How device management works in go4vl

    main

    Device management is handled via the device package. You can enumerate all available devices using device.GetAllDevices(). To interact with a specific device, use device.Open() with functional options like device.WithBufferSize(n) or device.WithPixFormat(v4l2.PixFormat{...}) to configure the capture settings immediately upon opening.

    // List available devices
    devices := device.GetAllDevices()
    
    // Open device with options
    dev, err := device.Open("/dev/video0",
        device.WithBufferSize(4),
        device.WithPixFormat(v4l2.PixFormat{PixelFormat: v4l2.PixelFmtMJPEG}),
    )
    defer dev.Close()
  3. How to control video formats and device settings

    main

    Once a device is open, you can query and modify its state.

    • Formats: Use GetFormatDescriptions() to see what the device supports and GetPixFormat()/SetPixFormat() to change the current pixel format.
    • Controls: Use GetControl(controlID) and SetControl(controlID, value) to adjust hardware parameters like brightness or contrast using constants from the v4l2 package (e.g., v4l2.CtrlBrightness).
    // Query supported formats
    formats := dev.GetFormatDescriptions()
    
    // Set pixel format
    currFmt, err := dev.GetPixFormat()
    currFmt.PixelFormat = v4l2.PixelFmtYUYV
    if err := dev.SetPixFormat(currFmt); err != nil {
        log.Fatal(err)
    }
    
    // Get/Set control value
    brightness := dev.GetControl(v4l2.CtrlBrightness)
    dev.SetControl(v4l2.CtrlBrightness, 128)
  4. Optimize frame processing performance with FramePool

    main

    The go4vl library implements a FramePool using sync.Pool to eliminate memory allocation bottlenecks during high-throughput video capture. This optimization significantly reduces garbage collection (GC) pressure and improves buffer allocation speed.

    Performance Benefits (Phase 1):

    • ~1,200x faster buffer allocation (22ns vs 28,000ns per 614KB frame).
    • 99.996% reduction in memory allocated per operation.
    • Effective elimination of allocations after the pool has warmed up.

    Best Practices:

    • Use the provided pooling mechanisms to handle high-resolution, high-frame-rate scenarios (e.g., 1080p @ 60fps or 4K @ 30fps) without dropping frames.
    • Ensure proper goroutine synchronization when stopping devices to avoid race conditions.
  5. Use type-safe helpers for codec controls

    main

    For stateless codec parameters (H.264, MPEG2, VP8, FWHT), go4vl provides type-safe helper methods. Instead of manually marshaling byte slices, you can use Go structs and specialized Add... and Get... methods. This provides compile-time checking and better IDE support.

    Supported Codec Helpers

    H.264 Stateless Codec:

    • AddH264SPS() / GetH264SPS()
    • AddH264PPS() / GetH264PPS()
    • AddH264ScalingMatrix() / GetH264ScalingMatrix()
    • AddH264SliceParams() / GetH264SliceParams()
    • AddH264DecodeParams() / GetH264DecodeParams()
    • AddH264PredWeights() / GetH264PredWeights()

    MPEG2 Stateless Codec:

    • AddMPEG2Sequence() / GetMPEG2Sequence()
    • AddMPEG2Picture() / GetMPEG2Picture()
    • AddMPEG2Quantization() / GetMPEG2Quantization()

    VP8 Stateless Codec:

    • AddVP8Frame() / GetVP8Frame()

    FWHT Stateless Codec:

    • AddFWHTParams() / GetFWHTParams()
    // Example: Setting H.264 SPS using type-safe API
    dev, _ := device.Open("/dev/video0")
    defer dev.Close()
    
    ctrls := v4l2.NewExtControls()
    
    sps := &v4l2.ControlH264SPS{
        ProfileIDC:              100, // High Profile
        LevelIDC:                51,  // Level 5.1
        ChromaFormatIDC:         1,   // 4:2:0
        Log2MaxFrameNumMinus4:   0,
        PicOrderCntType:         0,
        Log2MaxPicOrderCntLsbMinus4: 2,
        MaxNumRefFrames:         4,
        PicWidthInMbsMinus1:     119, // 1920 pixels
        PicHeightInMapUnitsMinus1: 67, // 1080 pixels
    }
    ctrls.AddH264SPS(sps)
    
    err := dev.SetExtControls(ctrls)
    if err != nil {
        log.Fatalf("Failed to set H.264 controls: %v", err)
    }
  6. V4L2 Control Classes and Types

    main

    V4L2 organizes device parameters into specific classes. Understanding these helps in targeting the correct controls.

    Control Classes

    ClassDescriptionCommon Controls
    UserBasic picture controlsBrightness, Contrast, Saturation, Hue
    CameraCamera controlsExposure, Focus, Zoom, Pan, Tilt, Iris
    FlashFlash and LED controlsFlash Mode, Intensity, Timeout, Strobe
    JPEGJPEG compressionCompression Quality, Markers, Restart Interval
    Image SourceImage sensor controlsAnalog Gain, Blanking, Test Patterns
    Image ProcessingImage processingPixel Rate, Link Frequency, Deinterlacing
    CodecVideo codec controlsBitrate, GOP Size, Profiles, Levels
    Codec StatelessStateless codec controlsH.264, VP8, VP9, MPEG2 parameters
    Digital VideoDigital video timingRX/TX mode, Timings, Aspect Ratio
    DetectionDetection controlsMotion Detection, Face Detection
    ColorimetryColor space and HDRColor Space, Transfer Function, HDR

    Control Types

    Controls can be of several types:

    • Integer: Numeric value with range and step.
    • Boolean: True/false (0/1).
    • Menu: Selection from named options.
    • Button: Triggers an action.
    • Integer64: 64-bit numeric value.
    • String: Text value.
    • Bitmask: Bitfield value.
    • IntegerMenu: Selection from numeric options.
    • U8/U16/U32: Array controls.
  7. How to stream video using the go4vl API

    main

    The webcam example demonstrates a pattern for streaming video frames to a web page using the go4vl API:

    1. Open the device: Use device.Open with configuration options like WithIOType, WithPixFormat, and WithFPS.
    2. Start capture: Call device.Start(ctx) to begin streaming. Use defer device.Close() to ensure resources are released.
    3. Retrieve frames: Access the stream of video buffers via device.GetOutput(), which returns a <-chan []byte.
    4. Serve via HTTP: Use an HTTP handler to stream the frames using the multipart/x-mixed-replace content type. This allows the browser to render the sequence of JPEG buffers as a continuous video stream.
    // 1. Open device
    device, err := device.Open(devName,
        device.WithIOType(v4l2.IOTypeMMAP),
        device.WithPixFormat(v4l2.PixFormat{PixelFormat: getFormatType(format), Width: uint32(width), Height: uint32(height)}),
        device.WithFPS(uint32(frameRate)), 
    )
    
    // 2. Start capture
    ctx, cancel := context.WithCancel(context.TODO())
    if err := device.Start(ctx); err != nil {
        log.Fatalf("stream capture: %s", err)
    }
    defer func() {
        cancel()
        device.Close()
    }()
    
    // 3. Get output channel
    frames := device.GetOutput()
    
    // 4. Serve via HTTP (simplified logic)
    for frame := range frames {
        // Write multipart boundary and JPEG headers, then the frame bytes
        io.WriteString(w, fmt.Sprintf("--%s\n", boundaryName))
        io.WriteString(w, "Content-Type: image/jpeg\n")
        io.WriteString(w, fmt.Sprintf("Content-Length: %d\n\n", len(frame)))
        w.Write(frame)
        io.WriteString(w, "\n")
    }
  8. Understand MMAP vs USERPTR streaming modes

    main

    The go4vl project supports two primary streaming I/O modes for frame capture. While the consumer API (Start, GetFrames, Stop) remains identical for both, they differ in how memory is managed:

    • MMAP (Default): The kernel allocates the buffers and maps them into userspace via mmap(). This provides zero-copy performance from the kernel to the application and is suitable for general-purpose use.
    • USERPTR: The application allocates the buffers and provides direct pointers to the kernel. This involves one copy per frame but is ideal for scenarios requiring custom allocators or shared memory.
  9. Calculate frequency units for V4L2 modulators

    main

    V4L2 uses two different frequency unit systems depending on the modulator capability. You must provide the frequency in the specific units required by your device.

    High Frequency Units (Normal)

    • Unit: 1/16 MHz = 62.5 kHz
    • Common for: TV modulators
    • Calculation: Frequency (MHz) * 16
    • Example: To transmit on 474 MHz (UHF TV), use 474 * 16 = 7584.

    Low Frequency Units (TunerCapLow)

    • Unit: 1/16000 kHz = 62.5 Hz
    • Common for: FM/AM radio modulators
    • Calculation: Frequency (kHz) * 16
    • Example: To transmit on 100.5 MHz (100,500 kHz) FM, use 100,500 * 16 = 1,608,000.
  10. Concept: DMA-BUF Export vs Import

    main

    DMA-BUF is a Linux kernel mechanism for sharing buffers between devices via file descriptors. In the context of V4L2, there are two primary workflows:

    1. Export: Sharing V4L2 capture buffers with other subsystems (like GPU/DRM) using VIDIOC_EXPBUF.
    2. Import: Using external DMA-BUF file descriptors as the source for V4L2 capture using V4L2_MEMORY_DMABUF.
  11. Understand audio output capabilities in V4L2

    main

    When enumerating audio outputs, go4vl identifies specific capabilities for each output. Common capabilities include:

    • Stereo: Indicates the output supports left/right channels.
    • AVL: Indicates Automatic Volume Level control is available.

    In the command-line output, these are often represented with a checkmark (e.g., ✓ Stereo) or via a capability bitmask (e.g., Capability: 0x00000001).

  12. Understand the low-level cgo-generated types approach

    main

    The examples/cgo_types directory provides a low-level demonstration of how to communicate with a V4L2 device driver using ioctl calls and cgo-generated types.

    Warning: This approach is intended for educational purposes to illustrate the underlying mechanics of the V4L2 API in Go (configuration, initiation, and capture steps). It is not recommended for production real-time image capture.