From cbe1791362bc25a3598e6eb058757da378fd055c Mon Sep 17 00:00:00 2001 From: Gary Kramlich Date: Sat, 16 Mar 2019 23:15:44 -0500 Subject: [PATCH] linux-v4l2: Add support for controls Video4Linux exposes controls for capture devices. This commit adds those controls to the properties window for v4l2-input devices. --- plugins/linux-v4l2/CMakeLists.txt | 1 + plugins/linux-v4l2/v4l2-controls.c | 155 +++++++++++++++++++++++++++++ plugins/linux-v4l2/v4l2-controls.h | 40 ++++++++ plugins/linux-v4l2/v4l2-input.c | 2 + 4 files changed, 198 insertions(+) create mode 100644 plugins/linux-v4l2/v4l2-controls.c create mode 100644 plugins/linux-v4l2/v4l2-controls.h diff --git a/plugins/linux-v4l2/CMakeLists.txt b/plugins/linux-v4l2/CMakeLists.txt index a310d5def..fbddd69ff 100644 --- a/plugins/linux-v4l2/CMakeLists.txt +++ b/plugins/linux-v4l2/CMakeLists.txt @@ -31,6 +31,7 @@ include_directories( set(linux-v4l2_SOURCES linux-v4l2.c + v4l2-controls.c v4l2-input.c v4l2-helpers.c ${linux-v4l2-udev_SOURCES} diff --git a/plugins/linux-v4l2/v4l2-controls.c b/plugins/linux-v4l2/v4l2-controls.c new file mode 100644 index 000000000..0cf30849e --- /dev/null +++ b/plugins/linux-v4l2/v4l2-controls.c @@ -0,0 +1,155 @@ +/* +Copyright (C) 2019 by Gary Kramlich + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include + +#include +#include + +#include "v4l2-controls.h" + +#define blog(level, msg, ...) blog(level, "v4l2-controls: " msg, ##__VA_ARGS__) + +#if defined(__i386__) +#define UINT_TO_POINTER(val) ((void *)(unsigned int)(val)) +#define POINTER_TO_UINT(p) ((unsigned int)(unsigned int)(p)) +#elif defined(__x86_64__) +#define UINT_TO_POINTER(val) ((void *)(unsigned long)(val)) +#define POINTER_TO_UINT(p) ((unsigned int)(unsigned long)(p)) +#else +#error "unknown platform, this code won't work" +#endif + +static bool v4l2_control_changed(void *data, obs_properties_t *props, + obs_property_t *prop, obs_data_t *settings) +{ + int dev = v4l2_open(obs_data_get_string(settings, "device_id"), + O_RDWR | O_NONBLOCK); + bool ret = false; + + (void)props; + + if (dev == -1) + return false; + + struct v4l2_control control; + control.id = POINTER_TO_UINT(data); + control.value = obs_data_get_int(settings, obs_property_name(prop)); + + if (0 != v4l2_ioctl(dev, VIDIOC_S_CTRL, &control)) { + ret = true; + } + + v4l2_close(dev); + + return ret; +} + +static int_fast32_t v4l2_update_controls_menu(int_fast32_t dev, + obs_properties_t *props, + struct v4l2_queryctrl *qctrl) +{ + obs_property_t *prop; + struct v4l2_querymenu qmenu; + + prop = obs_properties_add_list(props, (char *)qctrl->name, + (char *)qctrl->name, OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_INT); + + obs_property_set_modified_callback2(prop, v4l2_control_changed, + UINT_TO_POINTER(qctrl->id)); + + memset(&qmenu, 0, sizeof(qmenu)); + qmenu.id = qctrl->id; + + for (qmenu.index = qctrl->minimum; + qmenu.index <= (uint32_t)qctrl->maximum; + qmenu.index += qctrl->step) { + if (0 == v4l2_ioctl(dev, VIDIOC_QUERYMENU, &qmenu)) { + obs_property_list_add_int(prop, (char *)qmenu.name, + qmenu.value); + } + } + + return 0; +} + +int_fast32_t v4l2_update_controls(int_fast32_t dev, obs_properties_t *props, + obs_data_t *settings) +{ + struct v4l2_queryctrl qctrl; + obs_property_t *prop = NULL; + + if (!dev || !props) + return -1; + + memset(&qctrl, 0, sizeof(qctrl)); + qctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL; + while (0 == v4l2_ioctl(dev, VIDIOC_QUERYCTRL, &qctrl)) { + if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) { + blog(LOG_INFO, "found control %s but it is disabled", + qctrl.name); + continue; + } + + if (qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY) { + blog(LOG_INFO, "found control %s but it is readonly", + qctrl.name); + continue; + } + + if (qctrl.flags & V4L2_CTRL_FLAG_VOLATILE) { + blog(LOG_INFO, "found control %s but it is volatile", + qctrl.name); + continue; + } + + switch (qctrl.type) { + case V4L2_CTRL_TYPE_INTEGER: + prop = obs_properties_add_int_slider( + props, (char *)qctrl.name, (char *)qctrl.name, + qctrl.minimum, qctrl.maximum, qctrl.step); + obs_data_set_default_int(settings, (char *)qctrl.name, + qctrl.default_value); + obs_property_set_modified_callback2( + prop, v4l2_control_changed, + UINT_TO_POINTER(qctrl.id)); + break; + case V4L2_CTRL_TYPE_BOOLEAN: + prop = obs_properties_add_bool( + props, (char *)qctrl.name, (char *)qctrl.name); + obs_data_set_default_bool(settings, (char *)qctrl.name, + qctrl.default_value); + obs_property_set_modified_callback2( + prop, v4l2_control_changed, + UINT_TO_POINTER(qctrl.id)); + break; + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER_MENU: + v4l2_update_controls_menu(dev, props, &qctrl); + obs_data_set_default_int(settings, (char *)qctrl.name, + qctrl.default_value); + blog(LOG_INFO, "setting default for %s to %d", + (char *)qctrl.name, qctrl.default_value); + break; + } + + qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; + } + + return 0; +} diff --git a/plugins/linux-v4l2/v4l2-controls.h b/plugins/linux-v4l2/v4l2-controls.h new file mode 100644 index 000000000..8f082c448 --- /dev/null +++ b/plugins/linux-v4l2/v4l2-controls.h @@ -0,0 +1,40 @@ +/* +Copyright (C) 2019 by Gary Kramlich + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Update a devices properties to include all v4l controls that it supports. + * + * @param dev handle to the v4l2 device + * @param props properties for the device + * @param data the settings for the source + * + * @return negative on failure + */ +int_fast32_t v4l2_update_controls(int_fast32_t dev, obs_properties_t *props, + obs_data_t *settings); + +#ifdef __cplusplus +} +#endif diff --git a/plugins/linux-v4l2/v4l2-input.c b/plugins/linux-v4l2/v4l2-input.c index 302209f8c..5c7957bb5 100644 --- a/plugins/linux-v4l2/v4l2-input.c +++ b/plugins/linux-v4l2/v4l2-input.c @@ -35,6 +35,7 @@ along with this program. If not, see . #include #include +#include "v4l2-controls.h" #include "v4l2-helpers.h" #if HAVE_UDEV @@ -579,6 +580,7 @@ static bool device_selected(obs_properties_t *props, obs_property_t *p, obs_property_t *prop = obs_properties_get(props, "input"); v4l2_input_list(dev, prop); + v4l2_update_controls(dev, props, settings); v4l2_close(dev); obs_property_modified(prop, settings);