From f53103588746df3ad3b2b8e20ab4ba5745082dfe Mon Sep 17 00:00:00 2001 From: Harshit-Dhanwalkar Date: Fri, 19 Jun 2026 12:16:57 +0530 Subject: [PATCH] Add system level camera parameter controles like hue, sat, expo, etc --- C/include/capture.h | 16 +++++ C/lib/nl_alloc.c | 30 ++++++--- C/src/capture_linux.c | 153 ++++++++++++++++++++++++++++++++++++++++++ C/src/capture_macos.c | 81 ++++++++++++++++++++++ 4 files changed, 272 insertions(+), 8 deletions(-) diff --git a/C/include/capture.h b/C/include/capture.h index 4de3775..279b79d 100644 --- a/C/include/capture.h +++ b/C/include/capture.h @@ -31,4 +31,20 @@ int webcam_requeue_buffer(webcam_t *cam); // Stop streaming and clean up resources void webcam_cleanup(webcam_t *cam); +// Hardware camera controls. +int webcam_set_auto_exposure(webcam_t *cam, int enable); +int webcam_set_auto_white_balance(webcam_t *cam, int enable); + +int webcam_adjust_exposure(webcam_t *cam, int delta, int *out_value); +int webcam_adjust_contrast(webcam_t *cam, int delta, int *out_value); +int webcam_adjust_white_balance(webcam_t *cam, int delta, int *out_value); + +int webcam_get_exposure(webcam_t *cam, int *value); +int webcam_get_contrast(webcam_t *cam, int *value); +int webcam_get_white_balance(webcam_t *cam, int *value); + +int webcam_get_exposure_range(webcam_t *cam, int *min, int *max); +int webcam_get_contrast_range(webcam_t *cam, int *min, int *max); +int webcam_get_white_balance_range(webcam_t *cam, int *min, int *max); + #endif diff --git a/C/lib/nl_alloc.c b/C/lib/nl_alloc.c index 42198ae..aef5a1d 100644 --- a/C/lib/nl_alloc.c +++ b/C/lib/nl_alloc.c @@ -56,18 +56,19 @@ void *nl_malloc(size_t n) { } p += sizeof(block_hdr_t) + h->size; } - return (void *)0; /* OOM */ + return (void *)0; // OOM } void *nl_calloc(size_t nmemb, size_t size) { + if (nmemb != 0 && size > SIZE_MAX / nmemb) + return NULL; + size_t total = nmemb * size; void *p = nl_malloc(total); if (p) { - // uint8_t *b = (uint8_t *)p; - // for (size_t i = 0; i < total; i++) - // b[i] = 0; - if (nmemb && size > SIZE_MAX / nmemb) - return NULL; + uint8_t *b = (uint8_t *)p; + for (size_t i = 0; i < total; i++) + b[i] = 0; } return p; } @@ -91,6 +92,19 @@ void nl_free(void *ptr) { } } - // Coalesce Backward - // TODO: + // Coalesce backward + unsigned char *p = _arena; + unsigned char *target = (unsigned char *)h; + block_hdr_t *prev = NULL; + while (p < target) { + block_hdr_t *cur = (block_hdr_t *)p; + if (cur->magic != HDR_MAGIC) + break; // heap corruption guard + prev = cur; + p += sizeof(block_hdr_t) + cur->size; + } + if (prev && prev->free && p == target) { + prev->size += sizeof(block_hdr_t) + h->size; + h->magic = 0; // h is now absorbed into prev + } } diff --git a/C/src/capture_linux.c b/C/src/capture_linux.c index e2de05a..cb749a1 100644 --- a/C/src/capture_linux.c +++ b/C/src/capture_linux.c @@ -13,6 +13,8 @@ typedef struct webcam_impl webcam_impl_t; struct webcam_impl { struct v4l2_buffer buf_info; + int auto_exposure_disabled; + int auto_wb_disabled; }; static webcam_impl_t _impl_storage; @@ -127,4 +129,155 @@ void webcam_cleanup(webcam_t *cam) { cam->impl = (webcam_impl_t *)0; } +// Hardware controls (V4L2_CID_*) +static int v4l2_query_range(int fd, unsigned int id, int *min, int *max) { + struct v4l2_queryctrl q; + nl_memset(&q, 0, sizeof(q)); + q.id = id; + if (ioctl(fd, VIDIOC_QUERYCTRL, &q) < 0) + return -1; + if (q.flags & V4L2_CTRL_FLAG_DISABLED) + return -1; + if (min) + *min = q.minimum; + if (max) + *max = q.maximum; + return 0; +} + +static int v4l2_get_value(int fd, unsigned int id, int *value) { + struct v4l2_control c; + nl_memset(&c, 0, sizeof(c)); + c.id = id; + if (ioctl(fd, VIDIOC_G_CTRL, &c) < 0) + return -1; + *value = c.value; + return 0; +} + +static int v4l2_set_value(int fd, unsigned int id, int value) { + struct v4l2_control c; + nl_memset(&c, 0, sizeof(c)); + c.id = id; + c.value = value; + return ioctl(fd, VIDIOC_S_CTRL, &c); +} + +static int v4l2_clamp(int v, int lo, int hi) { + return (v < lo) ? lo : (v > hi) ? hi : v; +} + +int webcam_set_auto_exposure(webcam_t *cam, int enable) { + if (!cam || cam->fd < 0) + return -1; + // NOTE: UVC drivers expose V4L2_CID_EXPOSURE_AUTO as a menu (0=manual, + // 1=aperture priority, 3=auto, driver-dependent which subset exists). + // Some webcam drivers instead/also expose the simpler boolean + // V4L2_CID_AUTOGAIN. Try the proper one first, fall back to the boolean. + if (v4l2_set_value(cam->fd, V4L2_CID_EXPOSURE_AUTO, + enable ? V4L2_EXPOSURE_AUTO : V4L2_EXPOSURE_MANUAL) == 0) + return 0; + return v4l2_set_value(cam->fd, V4L2_CID_AUTOGAIN, enable ? 1 : 0); +} + +int webcam_set_auto_white_balance(webcam_t *cam, int enable) { + if (!cam || cam->fd < 0) + return -1; + return v4l2_set_value(cam->fd, V4L2_CID_AUTO_WHITE_BALANCE, enable ? 1 : 0); +} + +int webcam_get_exposure(webcam_t *cam, int *value) { + if (!cam || cam->fd < 0 || !value) + return -1; + return v4l2_get_value(cam->fd, V4L2_CID_EXPOSURE_ABSOLUTE, value); +} + +int webcam_get_contrast(webcam_t *cam, int *value) { + if (!cam || cam->fd < 0 || !value) + return -1; + return v4l2_get_value(cam->fd, V4L2_CID_CONTRAST, value); +} + +int webcam_get_white_balance(webcam_t *cam, int *value) { + if (!cam || cam->fd < 0 || !value) + return -1; + return v4l2_get_value(cam->fd, V4L2_CID_WHITE_BALANCE_TEMPERATURE, value); +} + +int webcam_get_exposure_range(webcam_t *cam, int *min, int *max) { + if (!cam || cam->fd < 0) + return -1; + return v4l2_query_range(cam->fd, V4L2_CID_EXPOSURE_ABSOLUTE, min, max); +} + +int webcam_get_contrast_range(webcam_t *cam, int *min, int *max) { + if (!cam || cam->fd < 0) + return -1; + return v4l2_query_range(cam->fd, V4L2_CID_CONTRAST, min, max); +} + +int webcam_get_white_balance_range(webcam_t *cam, int *min, int *max) { + if (!cam || cam->fd < 0) + return -1; + return v4l2_query_range(cam->fd, V4L2_CID_WHITE_BALANCE_TEMPERATURE, min, + max); +} + +int webcam_adjust_exposure(webcam_t *cam, int delta, int *out_value) { + if (!cam || cam->fd < 0) + return -1; + if (!cam->impl->auto_exposure_disabled) { + webcam_set_auto_exposure(cam, 0); + cam->impl->auto_exposure_disabled = 1; + } + int min, max, cur; + if (v4l2_query_range(cam->fd, V4L2_CID_EXPOSURE_ABSOLUTE, &min, &max) < 0) + return -1; + if (v4l2_get_value(cam->fd, V4L2_CID_EXPOSURE_ABSOLUTE, &cur) < 0) + cur = min; + int next = v4l2_clamp(cur + delta, min, max); + if (v4l2_set_value(cam->fd, V4L2_CID_EXPOSURE_ABSOLUTE, next) < 0) + return -1; + if (out_value) + *out_value = next; + return 0; +} + +int webcam_adjust_contrast(webcam_t *cam, int delta, int *out_value) { + if (!cam || cam->fd < 0) + return -1; + int min, max, cur; + if (v4l2_query_range(cam->fd, V4L2_CID_CONTRAST, &min, &max) < 0) + return -1; + if (v4l2_get_value(cam->fd, V4L2_CID_CONTRAST, &cur) < 0) + cur = min; + int next = v4l2_clamp(cur + delta, min, max); + if (v4l2_set_value(cam->fd, V4L2_CID_CONTRAST, next) < 0) + return -1; + if (out_value) + *out_value = next; + return 0; +} + +int webcam_adjust_white_balance(webcam_t *cam, int delta, int *out_value) { + if (!cam || cam->fd < 0) + return -1; + if (!cam->impl->auto_wb_disabled) { + webcam_set_auto_white_balance(cam, 0); + cam->impl->auto_wb_disabled = 1; + } + int min, max, cur; + if (v4l2_query_range(cam->fd, V4L2_CID_WHITE_BALANCE_TEMPERATURE, &min, + &max) < 0) + return -1; + if (v4l2_get_value(cam->fd, V4L2_CID_WHITE_BALANCE_TEMPERATURE, &cur) < 0) + cur = min; + int next = v4l2_clamp(cur + delta, min, max); + if (v4l2_set_value(cam->fd, V4L2_CID_WHITE_BALANCE_TEMPERATURE, next) < 0) + return -1; + if (out_value) + *out_value = next; + return 0; +} + #endif diff --git a/C/src/capture_macos.c b/C/src/capture_macos.c index 8372cb9..03b64d7 100644 --- a/C/src/capture_macos.c +++ b/C/src/capture_macos.c @@ -294,4 +294,85 @@ void webcam_cleanup(webcam_t *cam) { cam->buffer = NULL; } +// Hardware controls +// TODO: : not implemented on macOS yet. +// AVFoundation expose the equivalent knobs on AVCaptureDevice +// (exposureMode/setExposureModeCustomWithDuration:ISO:, whiteBalanceMode/ +// setWhiteBalanceModeLocked:..., per-key-value-observed lockForConfiguration +// dance), it needs its own implementation rather than +// a thin wrapper +// HACK: "unsupported" for now so callers (main.c) don't. +// TODO: implement via AVCaptureDevice exposure/white-balance APIs. +int webcam_set_auto_exposure(webcam_t *cam, int enable) { + (void)cam; + (void)enable; + return -1; +} + +int webcam_set_auto_white_balance(webcam_t *cam, int enable) { + (void)cam; + (void)enable; + return -1; +} + +int webcam_adjust_exposure(webcam_t *cam, int delta, int *out_value) { + (void)cam; + (void)delta; + (void)out_value; + return -1; +} + +int webcam_adjust_contrast(webcam_t *cam, int delta, int *out_value) { + (void)cam; + (void)delta; + (void)out_value; + return -1; +} + +int webcam_adjust_white_balance(webcam_t *cam, int delta, int *out_value) { + (void)cam; + (void)delta; + (void)out_value; + return -1; +} + +int webcam_get_exposure(webcam_t *cam, int *value) { + (void)cam; + (void)value; + return -1; +} + +int webcam_get_contrast(webcam_t *cam, int *value) { + (void)cam; + (void)value; + return -1; +} + +int webcam_get_white_balance(webcam_t *cam, int *value) { + (void)cam; + (void)value; + return -1; +} + +int webcam_get_exposure_range(webcam_t *cam, int *min, int *max) { + (void)cam; + (void)min; + (void)max; + return -1; +} + +int webcam_get_contrast_range(webcam_t *cam, int *min, int *max) { + (void)cam; + (void)min; + (void)max; + return -1; +} + +int webcam_get_white_balance_range(webcam_t *cam, int *min, int *max) { + (void)cam; + (void)min; + (void)max; + return -1; +} + #endif /* PLATFORM_MACOS */