/*****************************************************************************
* controls.c : Video4Linux2 device controls for vlc
*****************************************************************************
- * Copyright (C) 2002-2011 the VideoLAN team
+ * Copyright (C) 2002-2011 VLC authors and VideoLAN
*
* Authors: Benjamin Pracht <bigben at videolan dot org>
* Richard Hosking <richard at hovis dot net>
* Antoine Cellerier <dionoea at videolan d.t org>
* Dennis Lou <dlou99 at yahoo dot com>
*
- * 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
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
-#include "v4l2.h"
+#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#include <sys/ioctl.h>
+#include <vlc_common.h>
+
+#include "v4l2.h"
+
typedef struct vlc_v4l2_ctrl_name
{
- const char name[20];
+ const char name[28];
uint32_t cid;
} vlc_v4l2_ctrl_name_t;
{ "audio-treble", V4L2_CID_AUDIO_TREBLE },
{ "audio-mute", V4L2_CID_AUDIO_MUTE },
{ "audio-loudness", V4L2_CID_AUDIO_LOUDNESS },
- { "black-level", V4L2_CID_BLACK_LEVEL },
{ "auto-white-balance", V4L2_CID_AUTO_WHITE_BALANCE },
{ "do-white-balance", V4L2_CID_DO_WHITE_BALANCE },
{ "red-balance", V4L2_CID_RED_BALANCE },
{ "blue-balance", V4L2_CID_BLUE_BALANCE },
{ "gamma", V4L2_CID_GAMMA },
- { "exposure", V4L2_CID_EXPOSURE },
{ "autogain", V4L2_CID_AUTOGAIN },
{ "gain", V4L2_CID_GAIN },
{ "hflip", V4L2_CID_HFLIP },
{ "vflip", V4L2_CID_VFLIP },
- { "hcenter", V4L2_CID_HCENTER },
- { "vcenter", V4L2_CID_VCENTER },
- /* TODO: add more standardized controls */
+ { "power-line-frequency", V4L2_CID_POWER_LINE_FREQUENCY },
+ { "hue-auto", V4L2_CID_HUE_AUTO },
+ { "white-balance-temperature", V4L2_CID_WHITE_BALANCE_TEMPERATURE },
+ { "sharpness", V4L2_CID_SHARPNESS },
+ { "backlight-compensation", V4L2_CID_BACKLIGHT_COMPENSATION },
+ { "chroma-gain-auto", V4L2_CID_CHROMA_AGC },
+ { "color-killer", V4L2_CID_COLOR_KILLER },
+ { "color-effect", V4L2_CID_COLORFX },
+ { "rotate", V4L2_CID_ROTATE },
+ { "bg-color", V4L2_CID_BG_COLOR }, // NOTE: output only
+ { "chroma-gain", V4L2_CID_CHROMA_GAIN },
+ { "brightness-auto", V4L2_CID_AUTOBRIGHTNESS },
+ { "band-stop-filter", V4L2_CID_BAND_STOP_FILTER },
+
+ { "illuminators-1", V4L2_CID_ILLUMINATORS_1 }, // NOTE: don't care?
+ { "illuminators-2", V4L2_CID_ILLUMINATORS_2 },
#define CTRL_CID_KNOWN(cid) \
((((uint32_t)cid) - V4L2_CID_BRIGHTNESS) \
- <= (V4L2_CID_VCENTER - V4L2_CID_BRIGHTNESS))
+ <= (V4L2_CID_BAND_STOP_FILTER - V4L2_CID_BRIGHTNESS))
};
-typedef struct vlc_v4l2_ctrl
+struct vlc_v4l2_ctrl
{
int fd;
uint32_t id;
- enum v4l2_ctrl_type type;
+ uint8_t type;
char name[32];
int32_t default_value;
struct vlc_v4l2_ctrl *next;
-} vlc_v4l2_ctrl_t;
+};
static int ControlSet (const vlc_v4l2_ctrl_t *c, int_fast32_t value)
{
struct v4l2_ext_control ext_ctrl = {
.id = c->id,
.size = 0,
- .value64 = value,
};
+ ext_ctrl.value64 = value;
+ struct v4l2_ext_controls ext_ctrls = {
+ .ctrl_class = V4L2_CTRL_ID2CLASS(c->id),
+ .count = 1,
+ .error_idx = 0,
+ .controls = &ext_ctrl,
+ };
+
+ if (v4l2_ioctl (c->fd, VIDIOC_S_EXT_CTRLS, &ext_ctrls) < 0)
+ return -1;
+ return 0;
+}
+
+static int ControlSetStr (const vlc_v4l2_ctrl_t *c, const char *restrict value)
+{
+ struct v4l2_ext_control ext_ctrl = {
+ .id = c->id,
+ .size = strlen (value) + 1,
+ };
+ ext_ctrl.string = (char *)value;
struct v4l2_ext_controls ext_ctrls = {
.ctrl_class = V4L2_CTRL_ID2CLASS(c->id),
.count = 1,
case V4L2_CTRL_TYPE_INTEGER:
case V4L2_CTRL_TYPE_MENU:
case V4L2_CTRL_TYPE_BITMASK:
+ case V4L2_CTRL_TYPE_INTEGER_MENU:
ret = ControlSet (ctrl, cur.i_int);
break;
case V4L2_CTRL_TYPE_BOOLEAN:
case V4L2_CTRL_TYPE_INTEGER64:
ret = ControlSet64 (ctrl, cur.i_int);
break;
+ case V4L2_CTRL_TYPE_STRING:
+ ret = ControlSetStr (ctrl, cur.psz_string);
+ break;
default:
assert (0);
}
{
case V4L2_CTRL_TYPE_INTEGER:
case V4L2_CTRL_TYPE_MENU:
+ case V4L2_CTRL_TYPE_INTEGER_MENU:
var_SetInteger (obj, list->name, list->default_value);
break;
case V4L2_CTRL_TYPE_BOOLEAN:
case V4L2_CTRL_TYPE_INTEGER:
case V4L2_CTRL_TYPE_BOOLEAN:
case V4L2_CTRL_TYPE_MENU:
+ case V4L2_CTRL_TYPE_INTEGER_MENU:
{
long val = strtol (value, &end, 0);
if (*end)
break;
}
+ case V4L2_CTRL_TYPE_STRING:
+ ControlSetStr (c, value);
+ break;
+
case V4L2_CTRL_TYPE_BITMASK:
{
unsigned long val = strtoul (value, &end, 0);
for (i = 0; query->name[i]; i++)
{
unsigned char c = query->name[i];
- if (c == ' ')
+ if (c == ' ' || c == ',')
c = '_';
if (c < 128)
c = tolower (c);
const struct v4l2_queryctrl *query)
{
msg_Dbg (obj, " integer %s (%08"PRIX32")", query->name, query->id);
- if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
+ if (query->flags & CTRL_FLAGS_IGNORE)
return NULL;
vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
const struct v4l2_queryctrl *query)
{
msg_Dbg (obj, " boolean %s (%08"PRIX32")", query->name, query->id);
- if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
+ if (query->flags & CTRL_FLAGS_IGNORE)
return NULL;
vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
const struct v4l2_queryctrl *query)
{
msg_Dbg (obj, " menu %s (%08"PRIX32")", query->name, query->id);
- if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
+ if (query->flags & CTRL_FLAGS_IGNORE)
return NULL;
vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
val.i_int = ctrl.value;
var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
}
- val.b_bool = query->default_value;
- var_Change (obj, c->name, VLC_VAR_SETDEFAULT, &val, NULL);
val.i_int = query->minimum;
var_Change (obj, c->name, VLC_VAR_SETMIN, &val, NULL);
val.i_int = query->maximum;
var_Change (obj, c->name, VLC_VAR_SETMAX, &val, NULL);
+ val.i_int = query->default_value;
+ var_Change (obj, c->name, VLC_VAR_SETDEFAULT, &val, NULL);
/* Import menu choices */
for (uint_fast32_t idx = query->minimum;
return NULL;
if (var_Create (obj, c->name, VLC_VAR_VOID | VLC_VAR_ISCOMMAND))
+ {
+ free (c);
return NULL;
- (void) fd;
+ }
return c;
}
const struct v4l2_queryctrl *query)
{
msg_Dbg (obj, " 64-bits %s (%08"PRIX32")", query->name, query->id);
- if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
+ if (query->flags & CTRL_FLAGS_IGNORE)
return NULL;
vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
return NULL;
}
+ struct v4l2_ext_control ext_ctrl = { .id = c->id, .size = 0, };
+ struct v4l2_ext_controls ext_ctrls = {
+ .ctrl_class = V4L2_CTRL_ID2CLASS(c->id),
+ .count = 1,
+ .error_idx = 0,
+ .controls = &ext_ctrl,
+ };
+
+ if (v4l2_ioctl (c->fd, VIDIOC_G_EXT_CTRLS, &ext_ctrls) >= 0)
+ {
+ vlc_value_t val = { .i_int = ext_ctrl.value64 };
+
+ msg_Dbg (obj, " current: %"PRId64, val.i_int);
+ var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
+ }
+
return c;
}
return NULL;
}
+static vlc_v4l2_ctrl_t *ControlAddString (vlc_object_t *obj, int fd,
+ const struct v4l2_queryctrl *query)
+{
+ msg_Dbg (obj, " string %s (%08"PRIX32")", query->name, query->id);
+ if ((query->flags & CTRL_FLAGS_IGNORE) || query->maximum > 65535)
+ return NULL;
+
+ vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
+ if (unlikely(c == NULL))
+ return NULL;
+
+ if (var_Create (obj, c->name, VLC_VAR_STRING | VLC_VAR_ISCOMMAND))
+ {
+ free (c);
+ return NULL;
+ }
+
+ /* Get current value */
+ char *buf = malloc (query->maximum + 1);
+ if (likely(buf != NULL))
+ {
+ struct v4l2_ext_control ext_ctrl = {
+ .id = c->id,
+ .size = query->maximum + 1,
+ };
+ ext_ctrl.string = buf;
+ struct v4l2_ext_controls ext_ctrls = {
+ .ctrl_class = V4L2_CTRL_ID2CLASS(c->id),
+ .count = 1,
+ .error_idx = 0,
+ .controls = &ext_ctrl,
+ };
+
+ if (v4l2_ioctl (c->fd, VIDIOC_G_EXT_CTRLS, &ext_ctrls) >= 0)
+ {
+ vlc_value_t val = { .psz_string = buf };
+
+ msg_Dbg (obj, " current: \"%s\"", buf);
+ var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
+ }
+ free (buf);
+ }
+
+ return c;
+}
+
static vlc_v4l2_ctrl_t *ControlAddBitMask (vlc_object_t *obj, int fd,
const struct v4l2_queryctrl *query)
{
msg_Dbg (obj, " bit mask %s (%08"PRIX32")", query->name, query->id);
- if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
+ if (query->flags & CTRL_FLAGS_IGNORE)
return NULL;
vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
return c;
}
+static vlc_v4l2_ctrl_t *ControlAddIntMenu (vlc_object_t *obj, int fd,
+ const struct v4l2_queryctrl *query)
+{
+ msg_Dbg (obj, " int menu %s (%08"PRIX32")", query->name, query->id);
+ if (query->flags & CTRL_FLAGS_IGNORE)
+ return NULL;
+
+ vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
+ if (unlikely(c == NULL))
+ return NULL;
+
+ if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_HASCHOICE
+ | VLC_VAR_ISCOMMAND))
+ {
+ free (c);
+ return NULL;
+ }
+
+ vlc_value_t val;
+ struct v4l2_control ctrl = { .id = query->id };
+
+ if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
+ {
+ msg_Dbg (obj, " current: %"PRId32", default: %"PRId32,
+ ctrl.value, query->default_value);
+ val.i_int = ctrl.value;
+ var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
+ }
+ val.i_int = query->minimum;
+ var_Change (obj, c->name, VLC_VAR_SETMIN, &val, NULL);
+ val.i_int = query->maximum;
+ var_Change (obj, c->name, VLC_VAR_SETMAX, &val, NULL);
+ val.i_int = query->default_value;
+ var_Change (obj, c->name, VLC_VAR_SETDEFAULT, &val, NULL);
+
+ /* Import menu choices */
+ for (uint_fast32_t idx = query->minimum;
+ idx <= (uint_fast32_t)query->maximum;
+ idx++)
+ {
+ struct v4l2_querymenu menu = { .id = query->id, .index = idx };
+ char name[sizeof ("-9223372036854775808")];
+
+ if (v4l2_ioctl (fd, VIDIOC_QUERYMENU, &menu) < 0)
+ continue;
+ msg_Dbg (obj, " choice %"PRIu32") %"PRId64, menu.index, menu.value);
+
+ vlc_value_t text;
+ val.i_int = menu.index;
+ sprintf (name, "%"PRId64, menu.value);
+ text.psz_string = name;
+ var_Change (obj, c->name, VLC_VAR_ADDCHOICE, &val, &text);
+ }
+ return c;
+}
+
static vlc_v4l2_ctrl_t *ControlAddUnknown (vlc_object_t *obj, int fd,
const struct v4l2_queryctrl *query)
{
[V4L2_CTRL_TYPE_BUTTON] = ControlAddButton,
[V4L2_CTRL_TYPE_INTEGER64] = ControlAddInteger64,
[V4L2_CTRL_TYPE_CTRL_CLASS] = ControlAddClass,
+ [V4L2_CTRL_TYPE_STRING] = ControlAddString,
[V4L2_CTRL_TYPE_BITMASK] = ControlAddBitMask,
+ [V4L2_CTRL_TYPE_INTEGER_MENU] = ControlAddIntMenu,
};
vlc_v4l2_ctrl_t *list = NULL;