Add libmegapixels

This commit is contained in:
Martijn Braam
2023-07-11 23:43:23 +02:00
parent b5161db18e
commit 3ee314a68d
25 changed files with 540 additions and 2509 deletions

View File

@@ -1,9 +1,9 @@
#include "camera.h"
#include "mode.h"
#include <assert.h>
#include <errno.h>
#include <glib.h>
#include <libmegapixels.h>
#include <linux/v4l2-subdev.h>
#include <stdio.h>
#include <sys/ioctl.h>
@@ -11,9 +11,6 @@
#include <sys/wait.h>
#include <unistd.h>
#define MAX_VIDEO_BUFFERS 20
#define MAX_BG_TASKS 8
static void
errno_printerr(const char *s)
{
@@ -37,12 +34,7 @@ struct video_buffer {
};
struct _MPCamera {
int video_fd;
int subdev_fd;
int bridge_fd;
bool has_set_mode;
MPMode current_mode;
libmegapixels_camera *camera;
struct video_buffer buffers[MAX_VIDEO_BUFFERS];
uint32_t num_buffers;
@@ -54,13 +46,12 @@ struct _MPCamera {
};
MPCamera *
mp_camera_new(int video_fd, int subdev_fd, int bridge_fd)
mp_camera_new(libmegapixels_camera *camera)
{
g_return_val_if_fail(video_fd != -1, NULL);
libmegapixels_open(camera);
// Query capabilities
struct v4l2_capability cap;
if (xioctl(video_fd, VIDIOC_QUERYCAP, &cap) == -1) {
if (xioctl(camera->video_fd, VIDIOC_QUERYCAP, &cap) == -1) {
return NULL;
}
@@ -73,31 +64,14 @@ mp_camera_new(int video_fd, int subdev_fd, int bridge_fd)
} else {
return NULL;
}
libmegapixels_close(camera);
MPCamera *camera = malloc(sizeof(MPCamera));
camera->video_fd = video_fd;
camera->subdev_fd = subdev_fd;
camera->bridge_fd = bridge_fd;
camera->has_set_mode = false;
camera->num_buffers = 0;
camera->use_mplane = use_mplane;
memset(camera->child_bg_pids,
0,
sizeof(camera->child_bg_pids[0]) * MAX_BG_TASKS);
return camera;
}
void
mp_camera_free(MPCamera *camera)
{
mp_camera_wait_bg_tasks(camera);
g_warn_if_fail(camera->num_buffers == 0);
if (camera->num_buffers != 0) {
mp_camera_stop_capture(camera);
}
free(camera);
MPCamera *cam = malloc(sizeof(MPCamera));
cam->camera = camera;
cam->num_buffers = 0;
cam->use_mplane = use_mplane;
memset(cam->child_bg_pids, 0, sizeof(cam->child_bg_pids[0]) * MAX_BG_TASKS);
return cam;
}
void
@@ -180,22 +154,10 @@ mp_camera_check_task_complete(MPCamera *camera, pid_t pid)
}
}
bool
mp_camera_is_subdev(MPCamera *camera)
{
return camera->subdev_fd != -1;
}
int
mp_camera_get_video_fd(MPCamera *camera)
{
return camera->video_fd;
}
int
mp_camera_get_subdev_fd(MPCamera *camera)
{
return camera->subdev_fd;
return camera->camera->video_fd;
}
static enum v4l2_buf_type
@@ -207,151 +169,9 @@ get_buf_type(MPCamera *camera)
return V4L2_BUF_TYPE_VIDEO_CAPTURE;
}
static bool
camera_mode_impl(MPCamera *camera, int request, MPMode *mode)
{
uint32_t pixfmt = mp_pixel_format_to_v4l_pixel_format(mode->pixel_format);
struct v4l2_format fmt = {};
if (camera->use_mplane) {
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
fmt.fmt.pix_mp.width = mode->width;
fmt.fmt.pix_mp.height = mode->height;
fmt.fmt.pix_mp.pixelformat = pixfmt;
fmt.fmt.pix_mp.field = V4L2_FIELD_ANY;
} else {
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = mode->width;
fmt.fmt.pix.height = mode->height;
fmt.fmt.pix.pixelformat = pixfmt;
fmt.fmt.pix.field = V4L2_FIELD_ANY;
}
if (xioctl(camera->video_fd, request, &fmt) == -1) {
return false;
}
if (camera->use_mplane) {
mode->width = fmt.fmt.pix_mp.width;
mode->height = fmt.fmt.pix_mp.height;
mode->pixel_format = mp_pixel_format_from_v4l_pixel_format(
fmt.fmt.pix_mp.pixelformat);
} else {
mode->width = fmt.fmt.pix.width;
mode->height = fmt.fmt.pix.height;
mode->pixel_format = mp_pixel_format_from_v4l_pixel_format(
fmt.fmt.pix.pixelformat);
}
return true;
}
bool
mp_camera_try_mode(MPCamera *camera, MPMode *mode)
{
if (!camera_mode_impl(camera, VIDIOC_TRY_FMT, mode)) {
errno_printerr("VIDIOC_S_FMT");
return false;
}
return true;
}
const MPMode *
mp_camera_get_mode(const MPCamera *camera)
{
return &camera->current_mode;
}
bool
mp_camera_set_mode(MPCamera *camera, MPMode *mode)
{
// Set the mode in the subdev the camera is one
if (mp_camera_is_subdev(camera)) {
struct v4l2_subdev_frame_interval interval = {};
interval.pad = 0;
interval.interval = mode->frame_interval;
if (xioctl(camera->subdev_fd,
VIDIOC_SUBDEV_S_FRAME_INTERVAL,
&interval) == -1) {
errno_printerr("VIDIOC_SUBDEV_S_FRAME_INTERVAL");
}
bool did_set_frame_rate = interval.interval.numerator ==
mode->frame_interval.numerator &&
interval.interval.denominator ==
mode->frame_interval.denominator;
struct v4l2_subdev_format fmt = {};
fmt.pad = 0;
fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
fmt.format.width = mode->width;
fmt.format.height = mode->height;
fmt.format.code =
mp_pixel_format_to_v4l_bus_code(mode->pixel_format);
fmt.format.field = V4L2_FIELD_ANY;
if (xioctl(camera->subdev_fd, VIDIOC_SUBDEV_S_FMT, &fmt) == -1) {
errno_printerr("VIDIOC_SUBDEV_S_FMT");
return false;
}
// sun6i-csi-bridge will return EINVAL when trying to read
// frames if the resolution it's configured with doesn't match
// the resolution of its input.
if (camera->bridge_fd > 0) {
struct v4l2_subdev_format bridge_fmt = fmt;
if (xioctl(camera->bridge_fd,
VIDIOC_SUBDEV_S_FMT,
&bridge_fmt) == -1) {
errno_printerr("VIDIOC_SUBDEV_S_FMT");
return false;
}
if (fmt.format.width != bridge_fmt.format.width ||
fmt.format.height != bridge_fmt.format.height) {
g_printerr("Bridge format resolution mismatch\n");
return false;
}
}
// Some drivers like ov5640 don't allow you to set the frame format
// with too high a frame-rate, but that means the frame-rate won't be
// set after the format change. So we need to try again here if we
// didn't succeed before. Ideally we'd be able to set both at once.
if (!did_set_frame_rate) {
interval.interval = mode->frame_interval;
if (xioctl(camera->subdev_fd,
VIDIOC_SUBDEV_S_FRAME_INTERVAL,
&interval) == -1) {
errno_printerr("VIDIOC_SUBDEV_S_FRAME_INTERVAL");
}
}
// Update the mode
mode->pixel_format =
mp_pixel_format_from_v4l_bus_code(fmt.format.code);
mode->frame_interval = interval.interval;
mode->width = fmt.format.width;
mode->height = fmt.format.height;
}
// Set the mode for the video device
{
if (!camera_mode_impl(camera, VIDIOC_S_FMT, mode)) {
errno_printerr("VIDIOC_S_FMT");
return false;
}
}
camera->has_set_mode = true;
camera->current_mode = *mode;
return true;
}
bool
mp_camera_start_capture(MPCamera *camera)
{
g_return_val_if_fail(camera->has_set_mode, false);
g_return_val_if_fail(camera->num_buffers == 0, false);
const enum v4l2_buf_type buftype = get_buf_type(camera);
@@ -362,7 +182,7 @@ mp_camera_start_capture(MPCamera *camera)
req.type = buftype;
req.memory = V4L2_MEMORY_MMAP;
if (xioctl(camera->video_fd, VIDIOC_REQBUFS, &req) == -1) {
if (xioctl(camera->camera->video_fd, VIDIOC_REQBUFS, &req) == -1) {
errno_printerr("VIDIOC_REQBUFS");
return false;
}
@@ -388,7 +208,7 @@ mp_camera_start_capture(MPCamera *camera)
buf.length = 1;
}
if (xioctl(camera->video_fd, VIDIOC_QUERYBUF, &buf) == -1) {
if (xioctl(camera->camera->video_fd, VIDIOC_QUERYBUF, &buf) == -1) {
errno_printerr("VIDIOC_QUERYBUF");
break;
}
@@ -399,7 +219,7 @@ mp_camera_start_capture(MPCamera *camera)
planes[0].length,
PROT_READ,
MAP_SHARED,
camera->video_fd,
camera->camera->video_fd,
planes[0].m.mem_offset);
} else {
camera->buffers[i].length = buf.length;
@@ -407,7 +227,7 @@ mp_camera_start_capture(MPCamera *camera)
buf.length,
PROT_READ,
MAP_SHARED,
camera->video_fd,
camera->camera->video_fd,
buf.m.offset);
}
@@ -420,7 +240,7 @@ mp_camera_start_capture(MPCamera *camera)
.type = buftype,
.index = i,
};
if (xioctl(camera->video_fd, VIDIOC_EXPBUF, &expbuf) == -1) {
if (xioctl(camera->camera->video_fd, VIDIOC_EXPBUF, &expbuf) == -1) {
errno_printerr("VIDIOC_EXPBUF");
break;
}
@@ -449,7 +269,7 @@ mp_camera_start_capture(MPCamera *camera)
}
// Queue the buffer for capture
if (xioctl(camera->video_fd, VIDIOC_QBUF, &buf) == -1) {
if (xioctl(camera->camera->video_fd, VIDIOC_QBUF, &buf) == -1) {
errno_printerr("VIDIOC_QBUF");
goto error;
}
@@ -457,7 +277,7 @@ mp_camera_start_capture(MPCamera *camera)
// Start capture
enum v4l2_buf_type type = buftype;
if (xioctl(camera->video_fd, VIDIOC_STREAMON, &type) == -1) {
if (xioctl(camera->camera->video_fd, VIDIOC_STREAMON, &type) == -1) {
errno_printerr("VIDIOC_STREAMON");
goto error;
}
@@ -485,7 +305,7 @@ error:
req.type = buftype;
req.memory = V4L2_MEMORY_MMAP;
if (xioctl(camera->video_fd, VIDIOC_REQBUFS, &req) == -1) {
if (xioctl(camera->camera->video_fd, VIDIOC_REQBUFS, &req) == -1) {
errno_printerr("VIDIOC_REQBUFS");
}
}
@@ -501,7 +321,7 @@ mp_camera_stop_capture(MPCamera *camera)
const enum v4l2_buf_type buftype = get_buf_type(camera);
enum v4l2_buf_type type = buftype;
if (xioctl(camera->video_fd, VIDIOC_STREAMOFF, &type) == -1) {
if (xioctl(camera->camera->video_fd, VIDIOC_STREAMOFF, &type) == -1) {
errno_printerr("VIDIOC_STREAMOFF");
}
@@ -523,7 +343,7 @@ mp_camera_stop_capture(MPCamera *camera)
req.count = 0;
req.type = buftype;
req.memory = V4L2_MEMORY_MMAP;
if (xioctl(camera->video_fd, VIDIOC_REQBUFS, &req) == -1) {
if (xioctl(camera->camera->video_fd, VIDIOC_REQBUFS, &req) == -1) {
errno_printerr("VIDIOC_REQBUFS");
}
@@ -551,7 +371,7 @@ mp_camera_capture_buffer(MPCamera *camera, MPBuffer *buffer)
buf.length = 1;
}
if (xioctl(camera->video_fd, VIDIOC_DQBUF, &buf) == -1) {
if (xioctl(camera->camera->video_fd, VIDIOC_DQBUF, &buf) == -1) {
switch (errno) {
case EAGAIN:
return true;
@@ -565,9 +385,9 @@ mp_camera_capture_buffer(MPCamera *camera, MPBuffer *buffer)
}
}
uint32_t pixel_format = camera->current_mode.pixel_format;
uint32_t width = camera->current_mode.width;
uint32_t height = camera->current_mode.height;
int format = camera->camera->current_mode->format;
uint32_t width = camera->camera->current_mode->width;
uint32_t height = camera->camera->current_mode->height;
uint32_t bytesused;
if (camera->use_mplane) {
@@ -576,8 +396,8 @@ mp_camera_capture_buffer(MPCamera *camera, MPBuffer *buffer)
bytesused = buf.bytesused;
}
assert(bytesused == (mp_pixel_format_width_to_bytes(pixel_format, width) +
mp_pixel_format_width_to_padding(pixel_format, width)) *
assert(bytesused == (libmegapixels_mode_width_to_bytes(format, width) +
libmegapixels_mode_width_to_padding(format, width)) *
height);
assert(bytesused == camera->buffers[buf.index].length);
@@ -604,243 +424,13 @@ mp_camera_release_buffer(MPCamera *camera, uint32_t buffer_index)
buf.length = 1;
}
if (xioctl(camera->video_fd, VIDIOC_QBUF, &buf) == -1) {
if (xioctl(camera->camera->video_fd, VIDIOC_QBUF, &buf) == -1) {
errno_printerr("VIDIOC_QBUF");
return false;
}
return true;
}
static MPModeList *
get_subdev_modes(MPCamera *camera, bool (*check)(MPCamera *, MPMode *))
{
MPModeList *item = NULL;
for (uint32_t fmt_index = 0;; ++fmt_index) {
struct v4l2_subdev_mbus_code_enum fmt = {};
fmt.index = fmt_index;
fmt.pad = 0;
fmt.which = V4L2_SUBDEV_FORMAT_TRY;
if (xioctl(camera->subdev_fd, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &fmt) ==
-1) {
if (errno != EINVAL) {
errno_printerr("VIDIOC_SUBDEV_ENUM_MBUS_CODE");
}
break;
}
// Skip unsupported formats
uint32_t format = mp_pixel_format_from_v4l_bus_code(fmt.code);
if (format == MP_PIXEL_FMT_UNSUPPORTED) {
continue;
}
for (uint32_t frame_index = 0;; ++frame_index) {
struct v4l2_subdev_frame_size_enum frame = {};
frame.index = frame_index;
frame.pad = 0;
frame.code = fmt.code;
frame.which = V4L2_SUBDEV_FORMAT_TRY;
if (xioctl(camera->subdev_fd,
VIDIOC_SUBDEV_ENUM_FRAME_SIZE,
&frame) == -1) {
if (errno != EINVAL) {
errno_printerr(
"VIDIOC_SUBDEV_ENUM_FRAME_SIZE");
}
break;
}
// TODO: Handle other types
if (frame.min_width != frame.max_width ||
frame.min_height != frame.max_height) {
break;
}
for (uint32_t interval_index = 0;; ++interval_index) {
struct v4l2_subdev_frame_interval_enum interval = {};
interval.index = interval_index;
interval.pad = 0;
interval.code = fmt.code;
interval.width = frame.max_width;
interval.height = frame.max_height;
interval.which = V4L2_SUBDEV_FORMAT_TRY;
if (xioctl(camera->subdev_fd,
VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL,
&interval) == -1) {
if (errno != EINVAL) {
errno_printerr(
"VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL");
}
break;
}
MPMode mode = {
.pixel_format = format,
.frame_interval = interval.interval,
.width = frame.max_width,
.height = frame.max_height,
};
if (!check(camera, &mode)) {
continue;
}
MPModeList *new_item = malloc(sizeof(MPModeList));
new_item->mode = mode;
new_item->next = item;
item = new_item;
}
}
}
return item;
}
static MPModeList *
get_video_modes(MPCamera *camera, bool (*check)(MPCamera *, MPMode *))
{
const enum v4l2_buf_type buftype = get_buf_type(camera);
MPModeList *item = NULL;
for (uint32_t fmt_index = 0;; ++fmt_index) {
struct v4l2_fmtdesc fmt = {};
fmt.index = fmt_index;
fmt.type = buftype;
if (xioctl(camera->video_fd, VIDIOC_ENUM_FMT, &fmt) == -1) {
if (errno != EINVAL) {
errno_printerr("VIDIOC_ENUM_FMT");
}
break;
}
// Skip unsupported formats
uint32_t format =
mp_pixel_format_from_v4l_pixel_format(fmt.pixelformat);
if (format == MP_PIXEL_FMT_UNSUPPORTED) {
continue;
}
for (uint32_t frame_index = 0;; ++frame_index) {
struct v4l2_frmsizeenum frame = {};
frame.index = frame_index;
frame.pixel_format = fmt.pixelformat;
if (xioctl(camera->video_fd,
VIDIOC_ENUM_FRAMESIZES,
&frame) == -1) {
if (errno != EINVAL) {
errno_printerr("VIDIOC_ENUM_FRAMESIZES");
}
break;
}
// TODO: Handle other types
if (frame.type != V4L2_FRMSIZE_TYPE_DISCRETE) {
break;
}
for (uint32_t interval_index = 0;; ++interval_index) {
struct v4l2_frmivalenum interval = {};
interval.index = interval_index;
interval.pixel_format = fmt.pixelformat;
interval.width = frame.discrete.width;
interval.height = frame.discrete.height;
if (xioctl(camera->video_fd,
VIDIOC_ENUM_FRAMEINTERVALS,
&interval) == -1) {
if (errno != EINVAL) {
errno_printerr(
"VIDIOC_ENUM_FRAMESIZES");
}
break;
}
// TODO: Handle other types
if (interval.type != V4L2_FRMIVAL_TYPE_DISCRETE) {
break;
}
MPMode mode = {
.pixel_format = format,
.frame_interval = interval.discrete,
.width = frame.discrete.width,
.height = frame.discrete.height,
};
if (!check(camera, &mode)) {
continue;
}
MPModeList *new_item = malloc(sizeof(MPModeList));
new_item->mode = mode;
new_item->next = item;
item = new_item;
}
}
}
return item;
}
static bool
all_modes(MPCamera *camera, MPMode *mode)
{
return true;
}
static bool
available_modes(MPCamera *camera, MPMode *mode)
{
MPMode attempt = *mode;
return mp_camera_try_mode(camera, &attempt) &&
mp_mode_is_equivalent(mode, &attempt);
}
MPModeList *
mp_camera_list_supported_modes(MPCamera *camera)
{
if (mp_camera_is_subdev(camera)) {
return get_subdev_modes(camera, all_modes);
} else {
return get_video_modes(camera, all_modes);
}
}
MPModeList *
mp_camera_list_available_modes(MPCamera *camera)
{
if (mp_camera_is_subdev(camera)) {
return get_subdev_modes(camera, available_modes);
} else {
return get_video_modes(camera, available_modes);
}
}
MPMode *
mp_camera_mode_list_get(MPModeList *list)
{
g_return_val_if_fail(list, NULL);
return &list->mode;
}
MPModeList *
mp_camera_mode_list_next(MPModeList *list)
{
g_return_val_if_fail(list, NULL);
return list->next;
}
void
mp_camera_mode_list_free(MPModeList *list)
{
while (list) {
MPModeList *tmp = list;
list = tmp->next;
free(tmp);
}
}
struct int_str_pair {
uint32_t value;
const char *str;
@@ -1010,24 +600,15 @@ struct _MPControlList {
MPControlList *next;
};
static int
control_fd(MPCamera *camera)
{
if (camera->subdev_fd != -1) {
return camera->subdev_fd;
}
return camera->video_fd;
}
MPControlList *
mp_camera_list_controls(MPCamera *camera)
mp_camera_list_controls(libmegapixels_camera *camera)
{
MPControlList *item = NULL;
struct v4l2_query_ext_ctrl ctrl = {};
ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND;
while (true) {
if (xioctl(control_fd(camera), VIDIOC_QUERY_EXT_CTRL, &ctrl) == -1) {
if (xioctl(camera->sensor_fd, VIDIOC_QUERY_EXT_CTRL, &ctrl) == -1) {
if (errno != EINVAL) {
errno_printerr("VIDIOC_QUERY_EXT_CTRL");
}
@@ -1090,11 +671,13 @@ mp_control_list_free(MPControlList *list)
}
bool
mp_camera_query_control(MPCamera *camera, uint32_t id, MPControl *control)
mp_camera_query_control(libmegapixels_camera *camera,
uint32_t id,
MPControl *control)
{
struct v4l2_query_ext_ctrl ctrl = {};
ctrl.id = id;
if (xioctl(control_fd(camera), VIDIOC_QUERY_EXT_CTRL, &ctrl) == -1) {
if (xioctl(camera->sensor_fd, VIDIOC_QUERY_EXT_CTRL, &ctrl) == -1) {
if (errno != EINVAL) {
errno_printerr("VIDIOC_QUERY_EXT_CTRL");
}
@@ -1121,7 +704,10 @@ mp_camera_query_control(MPCamera *camera, uint32_t id, MPControl *control)
}
static bool
control_impl_int32(MPCamera *camera, uint32_t id, int request, int32_t *value)
control_impl_int32(libmegapixels_camera *camera,
uint32_t id,
int request,
int32_t *value)
{
struct v4l2_ext_control ctrl = {};
ctrl.id = id;
@@ -1133,7 +719,7 @@ control_impl_int32(MPCamera *camera, uint32_t id, int request, int32_t *value)
.count = 1,
.controls = &ctrl,
};
if (xioctl(control_fd(camera), request, &ctrls) == -1) {
if (xioctl(camera->sensor_fd, request, &ctrls) == -1) {
return false;
}
@@ -1142,7 +728,7 @@ control_impl_int32(MPCamera *camera, uint32_t id, int request, int32_t *value)
}
pid_t
mp_camera_control_set_int32_bg(MPCamera *camera, uint32_t id, int32_t v)
mp_camera_control_set_int32_bg(libmegapixels_camera *camera, uint32_t id, int32_t v)
{
struct v4l2_ext_control ctrl = {};
ctrl.id = id;
@@ -1155,7 +741,7 @@ mp_camera_control_set_int32_bg(MPCamera *camera, uint32_t id, int32_t v)
.controls = &ctrl,
};
int fd = control_fd(camera);
int fd = camera->sensor_fd;
// fork only after all the memory has been read
pid_t pid = fork();
@@ -1174,19 +760,19 @@ mp_camera_control_set_int32_bg(MPCamera *camera, uint32_t id, int32_t v)
}
bool
mp_camera_control_try_int32(MPCamera *camera, uint32_t id, int32_t *v)
mp_camera_control_try_int32(libmegapixels_camera *camera, uint32_t id, int32_t *v)
{
return control_impl_int32(camera, id, VIDIOC_TRY_EXT_CTRLS, v);
}
bool
mp_camera_control_set_int32(MPCamera *camera, uint32_t id, int32_t v)
mp_camera_control_set_int32(libmegapixels_camera *camera, uint32_t id, int32_t v)
{
return control_impl_int32(camera, id, VIDIOC_S_EXT_CTRLS, &v);
}
int32_t
mp_camera_control_get_int32(MPCamera *camera, uint32_t id)
mp_camera_control_get_int32(libmegapixels_camera *camera, uint32_t id)
{
int32_t v = 0;
control_impl_int32(camera, id, VIDIOC_G_EXT_CTRLS, &v);
@@ -1194,7 +780,7 @@ mp_camera_control_get_int32(MPCamera *camera, uint32_t id)
}
bool
mp_camera_control_try_boolean(MPCamera *camera, uint32_t id, bool *v)
mp_camera_control_try_boolean(libmegapixels_camera *camera, uint32_t id, bool *v)
{
int32_t value = *v;
bool s = control_impl_int32(camera, id, VIDIOC_TRY_EXT_CTRLS, &value);
@@ -1203,14 +789,14 @@ mp_camera_control_try_boolean(MPCamera *camera, uint32_t id, bool *v)
}
bool
mp_camera_control_set_bool(MPCamera *camera, uint32_t id, bool v)
mp_camera_control_set_bool(libmegapixels_camera *camera, uint32_t id, bool v)
{
int32_t value = v;
return control_impl_int32(camera, id, VIDIOC_S_EXT_CTRLS, &value);
}
bool
mp_camera_control_get_bool(MPCamera *camera, uint32_t id)
mp_camera_control_get_bool(libmegapixels_camera *camera, uint32_t id)
{
int32_t v = false;
control_impl_int32(camera, id, VIDIOC_G_EXT_CTRLS, &v);
@@ -1218,7 +804,7 @@ mp_camera_control_get_bool(MPCamera *camera, uint32_t id)
}
pid_t
mp_camera_control_set_bool_bg(MPCamera *camera, uint32_t id, bool v)
mp_camera_control_set_bool_bg(libmegapixels_camera *camera, uint32_t id, bool v)
{
int32_t value = v;
return mp_camera_control_set_int32_bg(camera, id, value);