1 /*****************************************************************************
2 * controls.c : Video4Linux2 device controls for vlc
3 *****************************************************************************
4 * Copyright (C) 2002-2011 the VideoLAN team
6 * Authors: Benjamin Pracht <bigben at videolan dot org>
7 * Richard Hosking <richard at hovis dot net>
8 * Antoine Cellerier <dionoea at videolan d.t org>
9 * Dennis Lou <dlou99 at yahoo dot com>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
32 #include <sys/ioctl.h>
34 typedef struct vlc_v4l2_ctrl_name
38 } vlc_v4l2_ctrl_name_t;
40 /* NOTE: must be sorted by ID */
41 static const vlc_v4l2_ctrl_name_t controls[] =
43 { "brightness", V4L2_CID_BRIGHTNESS },
44 { "contrast", V4L2_CID_CONTRAST },
45 { "saturation", V4L2_CID_SATURATION },
46 { "hue", V4L2_CID_HUE },
47 { "audio-volume", V4L2_CID_AUDIO_VOLUME },
48 { "audio-balance", V4L2_CID_AUDIO_BALANCE },
49 { "audio-bass", V4L2_CID_AUDIO_BASS },
50 { "audio-treble", V4L2_CID_AUDIO_TREBLE },
51 { "audio-mute", V4L2_CID_AUDIO_MUTE },
52 { "audio-loudness", V4L2_CID_AUDIO_LOUDNESS },
53 { "black-level", V4L2_CID_BLACK_LEVEL },
54 { "auto-white-balance", V4L2_CID_AUTO_WHITE_BALANCE },
55 { "do-white-balance", V4L2_CID_DO_WHITE_BALANCE },
56 { "red-balance", V4L2_CID_RED_BALANCE },
57 { "blue-balance", V4L2_CID_BLUE_BALANCE },
58 { "gamma", V4L2_CID_GAMMA },
59 { "exposure", V4L2_CID_EXPOSURE },
60 { "autogain", V4L2_CID_AUTOGAIN },
61 { "gain", V4L2_CID_GAIN },
62 { "hflip", V4L2_CID_HFLIP },
63 { "vflip", V4L2_CID_VFLIP },
64 { "hcenter", V4L2_CID_HCENTER },
65 { "vcenter", V4L2_CID_VCENTER },
66 /* TODO: add more standardized controls */
67 #define CTRL_CID_KNOWN(cid) \
68 ((((uint32_t)cid) - V4L2_CID_BRIGHTNESS) \
69 <= (V4L2_CID_VCENTER - V4L2_CID_BRIGHTNESS))
72 typedef struct vlc_v4l2_ctrl
76 enum v4l2_ctrl_type type;
78 int32_t default_value;
79 struct vlc_v4l2_ctrl *next;
82 static int ControlSet (const vlc_v4l2_ctrl_t *c, int_fast32_t value)
84 struct v4l2_control ctrl = {
88 if (v4l2_ioctl (c->fd, VIDIOC_S_CTRL, &ctrl) < 0)
93 static int ControlSetCallback (vlc_object_t *obj, const char *var,
94 vlc_value_t old, vlc_value_t cur, void *data)
96 const vlc_v4l2_ctrl_t *ctrl = data;
98 if (ControlSet (ctrl, cur.i_int))
100 msg_Err (obj, "cannot set control %s: %m", var);
107 static void ControlsReset (vlc_v4l2_ctrl_t *list)
111 if (list->type != V4L2_CTRL_TYPE_BUTTON)
112 ControlSet (list, list->default_value);
117 static int ControlsResetCallback (vlc_object_t *obj, const char *var,
118 vlc_value_t old, vlc_value_t cur, void *data)
120 ControlsReset (data);
121 (void) obj; (void) var; (void) old; (void) cur;
125 static void ControlsSetFromString (vlc_object_t *obj,
126 const vlc_v4l2_ctrl_t *list)
128 char *buf = var_InheritString (obj, CFG_PREFIX"set-ctrls");
136 char *end = strchr (p, '}');
140 while (p != NULL && *p)
142 const char *name, *value;
144 p += strspn (p, ", ");
146 end = strchr (p, ',');
149 p = end; /* next name/value pair */
151 end = strchr (name, '=');
154 /* TODO? support button controls that way? */
155 msg_Err (obj, "syntax error in \"%s\": missing '='", name);
161 long val = strtol (value, &end, 0);
164 msg_Err (obj, "syntax error in \"%s\": not an integer", value);
168 for (const vlc_v4l2_ctrl_t *c = list; c != NULL; c = c->next)
169 if (!strcasecmp (name, c->name))
175 msg_Err (obj, "control \"%s\" not available", name);
180 static int cidcmp (const void *a, const void *b)
182 const uint32_t *id = a;
183 const vlc_v4l2_ctrl_name_t *name = b;
185 return (int32_t)(*id - name->cid);
189 * Creates a VLC-V4L2 control structure:
190 * In particular, determines a name suitable for a VLC object variable.
191 * \param query V4L2 control query structure [IN]
192 * \return NULL on error
194 static vlc_v4l2_ctrl_t *ControlCreate (int fd,
195 const struct v4l2_queryctrl *query)
197 vlc_v4l2_ctrl_t *ctrl = malloc (sizeof (*ctrl));
198 if (unlikely(ctrl == NULL))
202 ctrl->id = query->id;
203 ctrl->type = query->type;
205 /* Search for a well-known control */
206 const vlc_v4l2_ctrl_name_t *known;
207 known = bsearch (&query->id, controls, sizeof (controls) / sizeof (*known),
208 sizeof (*known), cidcmp);
210 strcpy (ctrl->name, known->name);
212 /* Fallback to automatically-generated control name */
215 for (i = 0; query->name[i]; i++)
217 unsigned char c = query->name[i];
224 ctrl->name[i] = '\0';
227 ctrl->default_value = query->default_value;
232 #ifndef V4L2_CTRL_FLAG_VOLATILE
233 # define V4L2_CTRL_FLAG_VOLATILE 0x0080
234 # warning Please update V4L2 kernel headers!
237 #define CTRL_FLAGS_IGNORE \
238 (V4L2_CTRL_FLAG_DISABLED /* not implemented at all */ \
239 |V4L2_CTRL_FLAG_READ_ONLY /* value is constant */ \
240 |V4L2_CTRL_FLAG_VOLATILE /* value is (variable but) read-only */)
242 static vlc_v4l2_ctrl_t *ControlAddInteger (vlc_object_t *obj, int fd,
243 const struct v4l2_queryctrl *query)
245 msg_Dbg (obj, " integer %s (%08"PRIX32")", query->name, query->id);
246 if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
249 vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
250 if (unlikely(c == NULL))
253 if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND))
260 struct v4l2_control ctrl = { .id = query->id };
262 if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
264 msg_Dbg (obj, " current: %3"PRId32", default: %3"PRId32,
265 ctrl.value, query->default_value);
266 val.i_int = ctrl.value;
267 var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
269 val.i_int = query->minimum;
270 var_Change (obj, c->name, VLC_VAR_SETMIN, &val, NULL);
271 val.i_int = query->maximum;
272 var_Change (obj, c->name, VLC_VAR_SETMAX, &val, NULL);
273 if (query->step != 1)
275 val.i_int = query->step;
276 var_Change (obj, c->name, VLC_VAR_SETSTEP, &val, NULL);
278 val.i_int = query->default_value;
279 var_Change (obj, c->name, VLC_VAR_SETDEFAULT, &val, NULL);
283 static vlc_v4l2_ctrl_t *ControlAddBoolean (vlc_object_t *obj, int fd,
284 const struct v4l2_queryctrl *query)
286 msg_Dbg (obj, " boolean %s (%08"PRIX32")", query->name, query->id);
287 if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
290 vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
291 if (unlikely(c == NULL))
294 if (var_Create (obj, c->name, VLC_VAR_BOOL | VLC_VAR_ISCOMMAND))
301 struct v4l2_control ctrl = { .id = query->id };
303 if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
305 msg_Dbg (obj, " current: %s, default: %s",
306 ctrl.value ? " true" : "false",
307 query->default_value ? " true" : "false");
308 val.b_bool = ctrl.value;
309 var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
311 val.b_bool = query->default_value;
312 var_Change (obj, c->name, VLC_VAR_SETDEFAULT, &val, NULL);
316 static vlc_v4l2_ctrl_t *ControlAddMenu (vlc_object_t *obj, int fd,
317 const struct v4l2_queryctrl *query)
319 msg_Dbg (obj, " menu %s (%08"PRIX32")", query->name, query->id);
320 if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
323 vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
324 if (unlikely(c == NULL))
327 if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_HASCHOICE
328 | VLC_VAR_ISCOMMAND))
335 struct v4l2_control ctrl = { .id = query->id };
337 if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
339 msg_Dbg (obj, " current: %"PRId32", default: %"PRId32,
340 ctrl.value, query->default_value);
341 val.i_int = ctrl.value;
342 var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
344 val.b_bool = query->default_value;
345 var_Change (obj, c->name, VLC_VAR_SETDEFAULT, &val, NULL);
346 val.i_int = query->minimum;
347 var_Change (obj, c->name, VLC_VAR_SETMIN, &val, NULL);
348 val.i_int = query->maximum;
349 var_Change (obj, c->name, VLC_VAR_SETMAX, &val, NULL);
351 /* Import menu choices */
352 for (uint_fast32_t idx = query->minimum;
353 idx <= (uint_fast32_t)query->maximum;
356 struct v4l2_querymenu menu = { .id = query->id, .index = idx };
358 if (v4l2_ioctl (fd, VIDIOC_QUERYMENU, &menu) < 0)
360 msg_Dbg (obj, " choice %"PRIu32") %s", menu.index, menu.name);
363 val.i_int = menu.index;
364 text.psz_string = (char *)menu.name;
365 var_Change (obj, c->name, VLC_VAR_ADDCHOICE, &val, &text);
370 static vlc_v4l2_ctrl_t *ControlAddButton (vlc_object_t *obj, int fd,
371 const struct v4l2_queryctrl *query)
373 msg_Dbg (obj, " button %s (%08"PRIX32")", query->name, query->id);
374 if (query->flags & CTRL_FLAGS_IGNORE)
377 vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
378 if (unlikely(c == NULL))
381 if (var_Create (obj, c->name, VLC_VAR_VOID | VLC_VAR_ISCOMMAND))
387 static vlc_v4l2_ctrl_t *ControlAddClass (vlc_object_t *obj, int fd,
388 const struct v4l2_queryctrl *query)
390 msg_Dbg (obj, "control class %s:", query->name);
395 static vlc_v4l2_ctrl_t *ControlAddUnknown (vlc_object_t *obj, int fd,
396 const struct v4l2_queryctrl *query)
398 msg_Dbg (obj, " unknown %s (%08"PRIX32")", query->name, query->id);
399 msg_Warn (obj, " unknown control type %u", (unsigned)query->type);
404 typedef vlc_v4l2_ctrl_t *(*ctrl_type_cb) (vlc_object_t *, int,
405 const struct v4l2_queryctrl *);
408 * Lists all user-class v4l2 controls, sets them to the user specified
409 * value and create the relevant variables to enable run-time changes.
411 vlc_v4l2_ctrl_t *ControlsInit (vlc_object_t *obj, int fd)
413 /* A list of controls that can be modified at run-time is stored in the
414 * "controls" variable. The V4L2 controls dialog can be built from this. */
415 var_Create (obj, "controls", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE);
417 static const ctrl_type_cb handlers[] =
419 [V4L2_CTRL_TYPE_INTEGER] = ControlAddInteger,
420 [V4L2_CTRL_TYPE_BOOLEAN] = ControlAddBoolean,
421 [V4L2_CTRL_TYPE_MENU] = ControlAddMenu,
422 [V4L2_CTRL_TYPE_BUTTON] = ControlAddButton,
423 [V4L2_CTRL_TYPE_CTRL_CLASS] = ControlAddClass,
426 vlc_v4l2_ctrl_t *list = NULL;
427 struct v4l2_queryctrl query;
429 query.id = V4L2_CTRL_FLAG_NEXT_CTRL;
430 while (v4l2_ioctl (fd, VIDIOC_QUERYCTRL, &query) >= 0)
432 ctrl_type_cb handler = NULL;
433 if (query.type < (sizeof (handlers) / sizeof (handlers[0])))
434 handler = handlers[query.type];
436 handler = ControlAddUnknown;
438 vlc_v4l2_ctrl_t *c = handler (obj, fd, &query);
441 vlc_value_t val, text;
443 var_AddCallback (obj, c->name, ControlSetCallback, c);
444 text.psz_string = (char *)query.name;
445 var_Change (obj, c->name, VLC_VAR_SETTEXT, &text, NULL);
446 val.i_int = query.id;
447 text.psz_string = (char *)c->name;
448 var_Change (obj, "controls", VLC_VAR_ADDCHOICE, &val, &text);
453 query.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
456 /* Set well-known controls from VLC configuration */
457 for (vlc_v4l2_ctrl_t *ctrl = list; ctrl != NULL; ctrl = ctrl->next)
459 if (!CTRL_CID_KNOWN (ctrl->id))
462 char varname[sizeof (CFG_PREFIX) + sizeof (ctrl->name) - 1];
463 sprintf (varname, CFG_PREFIX"%s", ctrl->name);
465 int64_t val = var_InheritInteger (obj, varname);
467 continue; /* the VLC default value: "do not modify" */
468 ControlSet (ctrl, val);
471 /* Set any control from the VLC configuration control string */
472 ControlsSetFromString (obj, list);
474 /* Add a control to reset all controls to their default values */
476 vlc_value_t val, text;
478 var_Create (obj, "reset", VLC_VAR_VOID | VLC_VAR_ISCOMMAND);
479 val.psz_string = _("Reset defaults");
480 var_Change (obj, "reset", VLC_VAR_SETTEXT, &val, NULL);
483 text.psz_string = (char *)"reset";
484 var_Change (obj, "controls", VLC_VAR_ADDCHOICE, &val, &text);
485 var_AddCallback (obj, "reset", ControlsResetCallback, list);
487 if (var_InheritBool (obj, CFG_PREFIX"controls-reset"))
488 ControlsReset (list);
493 void ControlsDeinit (vlc_object_t *obj, vlc_v4l2_ctrl_t *list)
495 var_DelCallback (obj, "reset", ControlsResetCallback, list);
496 var_Destroy (obj, "reset");
500 vlc_v4l2_ctrl_t *next = list->next;
502 var_DelCallback (obj, list->name, ControlSetCallback, list);
503 var_Destroy (obj, list->name);
508 var_Destroy (obj, "controls");