Zig bindings for libcamera
|
felixdrp
9bba16a5a6
fix: list camera formats and add YUYV support
- Add lc_camera_list_formats() C API with LcFormatDesc struct to enumerate supported pixel formats and resolutions per camera - Fix StreamFormats iteration to use pixelformats()/sizes() API - Replace CameraManager::get(string_view) with manual cameras() loop to avoid libc++ vs libstdc++ ABI symbol mismatch at runtime - Switch build from zig c++ to g++ so wrapper uses the same libstdc++ ABI as the system libcamera - Add LC_FMT_YUYV / .yuyv pixel format throughout - Make LibCameraSource.open and .close pub for direct callers - Update README with format enumeration feature and example Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| src | fix: list camera formats and add YUYV support | |
| .gitignore | Initial commit | |
| build.zig | fix: list camera formats and add YUYV support | |
| build.zig.zon | fix: list camera formats and add YUYV support | |
| LICENSE | Initial commit | |
| README.md | fix: list camera formats and add YUYV support | |
libcamera-zig
Zig bindings for libcamera, inspired by libcamera-rs.
Features
| # | Feature | Status |
|---|---|---|
| 1 | Zero-copy frame capture via DMA-BUF mapping | ✅ Implemented |
| 2 | C++ wrapper for the libcamera C++ API (opaque LcCamera pointer) |
✅ Implemented |
| 3 | Simple Zig VideoSource vtable interface |
✅ Implemented |
| 4 | Camera controls — brightness, contrast, saturation, sharpness, exposure time, analogue gain | ✅ Implemented |
| 5 | AE/AWB enable & lock — AeEnable, AwbEnable controls (AeLocked absent in libcamera 0.7) |
✅ Implemented |
| 6 | AWB mode — Auto, Incandescent, Tungsten, Fluorescent, Indoor, Daylight, Cloudy, Custom | ✅ Implemented |
| 7 | AE metering mode — CentreWeighted, Spot, Matrix, Custom | ✅ Implemented |
| 8 | Stream roles — VideoRecording, StillCapture, Viewfinder, Raw | ✅ Implemented |
| 9 | Raw Bayer output — SGRBG10, SRGGB10, SBGGR10, SGBRG10; LC_ROLE_RAW |
✅ Implemented |
| 10 | Camera properties — sensor model string, pixel array size, focal length | ✅ Implemented |
| 11 | Per-frame metadata — actual exposure time, analogue gain, focus FoM, lux | ✅ Implemented |
| 12 | Hotplug events — callbacks on camera add/remove via persistent global CameraManager |
✅ Implemented |
| 13 | Stub builds (no libcamera headers required) — all new functions have no-op stubs | ✅ Implemented |
| 14 | Multi-stream (simultaneous preview + capture) | ✅ Implemented |
| 15 | Digital zoom / crop rectangle control | ✅ Implemented |
| 16 | Format enumeration — list supported pixel formats and resolutions (listFormats) |
✅ Implemented |
| 17 | YUYV pixel format — LC_FMT_YUYV / .yuyv |
✅ Implemented |
Usage
Prerequisites
This library requires g++, libcamera, and libcamera-base development headers installed on your system.
# Debian/Ubuntu/Raspberry Pi OS
sudo apt install g++ libcamera-dev libcamera-base-dev
As a dependency
You can add libcamera-zig to your project using zig fetch:
zig fetch --save https://codeberg.org/felixdrp/libcamera-zig/archive/main.tar.gz
This will update your build.zig.zon:
.{.fingerprint=0x7725543516074afb,// Example fingerprint.name=.my_project,.version="0.1.0",.dependencies=.{.libcamera_zig=.{.url="https://codeberg.org/felixdrp/libcamera-zig/archive/main.tar.gz",.hash="...",},},}And in your build.zig:
constlibcamera_dep=b.dependency("libcamera_zig",.{.target=target,.optimize=optimize,.libcamera=true,// Set to false to build with stubs only});constlibcamera_mod=libcamera_dep.module("libcamera-zig");// Use the link helper from the dependency to compile and link the C++ wrapperconstlibcamera_lib=@import("libcamera_zig");libcamera_lib.link(b,libcamera_dep,exe);exe.root_module.addImport("libcamera",libcamera_mod);Compiling your project
Once configured, you can build your project with the standard Zig command:
zig build
If you implemented a build option to toggle libcamera support (e.g., -Dlibcamera=true), use:
zig build -Dlibcamera=true
Camera controls example
constlc=@import("libcamera");varsrc=lc.LibCameraSource.init(0);varvs=src.videoSource();tryvs.open(.{.width=1920,.height=1080,.fps=30});// Set brightness and lock AE_=src.setControls(.{.brightness=0.2,.ae_locked=1,});Raw Bayer capture example
tryvs.open(.{.width=4056,.height=3040,.fps=10,.pixel_format=.sgrbg10,.stream_role=.raw,});Camera properties example
if(src.getProperties())|props|{std.debug.print("Sensor: {s} ({d}x{d})\n",.{props.model,props.pixel_array_width,props.pixel_array_height,});}Per-frame metadata example
constframe=(tryvs.nextFrame())orelsereturn;std.debug.print("exp={}μs gain={d:.2} lux={d:.1}\n",.{frame.actual_exposure_time_us,frame.actual_gain,frame.lux,});Hotplug example
constlc=@import("libcamera");fncameraAdded(id:[*:0]constu8,_:?*anyopaque)callconv(.C)void{std.debug.print("Camera added: {s}\n",.{id});}fncameraRemoved(id:[*:0]constu8,_:?*anyopaque)callconv(.C)void{std.debug.print("Camera removed: {s}\n",.{id});}// ..._=lc.setHotplugCallbacks(cameraAdded,cameraRemoved,null);// ... application runs ...lc.stopHotplug();Format enumeration example
varsrc=lc.LibCameraSource.init(0);trysrc.open(.{});defersrc.close();constformats=trysrc.listFormats(allocator);defer{for(formats)|f|allocator.free(f.fps);allocator.free(formats);}for(formats)|f|{std.debug.print("{s} {d}x{d}\n",.{@tagName(f.pixel_format),f.width,f.height});}Digital zoom example
// 2x digital zoom (centered crop)_=src.setControls(.{.zoom_factor=2.0});// Raw ScalerCrop rectangle override (overrides zoom_factor)_=src.setControls(.{.crop=.{.x=100,.y=100,.w=1920,.h=1080}});Multi-stream example
tryvs.open(.{.width=1920,.height=1080,.stream_count=2,.extra_streams=.{.{.width=640,.height=480,.pixel_format=.nv12,.stream_role=.viewfinder},.{},.{},},});// Primary stream (stream 0)constframe0=trysrc.nextFrameFromStream(0);// Secondary stream (stream 1)constframe1=trysrc.nextFrameFromStream(1);