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 *****************************************************************************/
33 #include <sys/ioctl.h>
35 typedef struct vlc_v4l2_ctrl_name
39 } vlc_v4l2_ctrl_name_t;
41 /* NOTE: must be sorted by ID */
42 static const vlc_v4l2_ctrl_name_t controls[] =
44 { "brightness", V4L2_CID_BRIGHTNESS },
45 { "contrast", V4L2_CID_CONTRAST },
46 { "saturation", V4L2_CID_SATURATION },
47 { "hue", V4L2_CID_HUE },
48 { "audio-volume", V4L2_CID_AUDIO_VOLUME },
49 { "audio-balance", V4L2_CID_AUDIO_BALANCE },
50 { "audio-bass", V4L2_CID_AUDIO_BASS },
51 { "audio-treble", V4L2_CID_AUDIO_TREBLE },
52 { "audio-mute", V4L2_CID_AUDIO_MUTE },
53 { "audio-loudness", V4L2_CID_AUDIO_LOUDNESS },
54 { "black-level", V4L2_CID_BLACK_LEVEL },
55 { "auto-white-balance", V4L2_CID_AUTO_WHITE_BALANCE },
56 { "do-white-balance", V4L2_CID_DO_WHITE_BALANCE },
57 { "red-balance", V4L2_CID_RED_BALANCE },
58 { "blue-balance", V4L2_CID_BLUE_BALANCE },
59 { "gamma", V4L2_CID_GAMMA },
60 { "exposure", V4L2_CID_EXPOSURE },
61 { "autogain", V4L2_CID_AUTOGAIN },
62 { "gain", V4L2_CID_GAIN },
63 { "hflip", V4L2_CID_HFLIP },
64 { "vflip", V4L2_CID_VFLIP },
65 { "hcenter", V4L2_CID_HCENTER },
66 { "vcenter", V4L2_CID_VCENTER },
67 /* TODO: add more standardized controls */
68 #define CTRL_CID_KNOWN(cid) \
69 ((((uint32_t)cid) - V4L2_CID_BRIGHTNESS) \
70 <= (V4L2_CID_VCENTER - V4L2_CID_BRIGHTNESS))
73 typedef struct vlc_v4l2_ctrl
77 enum v4l2_ctrl_type type;
79 int32_t default_value;
80 struct vlc_v4l2_ctrl *next;
83 static int ControlSet (const vlc_v4l2_ctrl_t *c, int_fast32_t value)
85 struct v4l2_control ctrl = {
89 if (v4l2_ioctl (c->fd, VIDIOC_S_CTRL, &ctrl) < 0)
94 static int ControlSet64 (const vlc_v4l2_ctrl_t *c, int64_t value)
96 struct v4l2_ext_control ext_ctrl = {
101 struct v4l2_ext_controls ext_ctrls = {
102 .ctrl_class = V4L2_CTRL_ID2CLASS(c->id),
105 .controls = &ext_ctrl,
108 if (v4l2_ioctl (c->fd, VIDIOC_S_EXT_CTRLS, &ext_ctrls) < 0)
113 static int ControlSetCallback (vlc_object_t *obj, const char *var,
114 vlc_value_t old, vlc_value_t cur, void *data)
116 const vlc_v4l2_ctrl_t *ctrl = data;
121 case V4L2_CTRL_TYPE_INTEGER:
122 case V4L2_CTRL_TYPE_MENU:
123 case V4L2_CTRL_TYPE_BITMASK:
124 ret = ControlSet (ctrl, cur.i_int);
126 case V4L2_CTRL_TYPE_BOOLEAN:
127 ret = ControlSet (ctrl, cur.b_bool);
129 case V4L2_CTRL_TYPE_BUTTON:
130 ret = ControlSet (ctrl, 0);
132 case V4L2_CTRL_TYPE_INTEGER64:
133 ret = ControlSet64 (ctrl, cur.i_int);
141 msg_Err (obj, "cannot set control %s: %m", var);
148 static void ControlsReset (vlc_object_t *obj, vlc_v4l2_ctrl_t *list)
154 case V4L2_CTRL_TYPE_INTEGER:
155 case V4L2_CTRL_TYPE_MENU:
156 var_SetInteger (obj, list->name, list->default_value);
158 case V4L2_CTRL_TYPE_BOOLEAN:
159 var_SetBool (obj, list->name, list->default_value);
167 static int ControlsResetCallback (vlc_object_t *obj, const char *var,
168 vlc_value_t old, vlc_value_t cur, void *data)
170 ControlsReset (obj, data);
171 (void) var; (void) old; (void) cur;
175 static void ControlsSetFromString (vlc_object_t *obj,
176 const vlc_v4l2_ctrl_t *list)
178 char *buf = var_InheritString (obj, CFG_PREFIX"set-ctrls");
186 char *end = strchr (p, '}');
190 while (p != NULL && *p)
192 const char *name, *value;
194 p += strspn (p, ", ");
196 end = strchr (p, ',');
199 p = end; /* next name/value pair */
201 end = strchr (name, '=');
204 /* TODO? support button controls that way? */
205 msg_Err (obj, "syntax error in \"%s\": missing '='", name);
211 for (const vlc_v4l2_ctrl_t *c = list; c != NULL; c = c->next)
212 if (!strcasecmp (name, c->name))
215 case V4L2_CTRL_TYPE_INTEGER:
216 case V4L2_CTRL_TYPE_BOOLEAN:
217 case V4L2_CTRL_TYPE_MENU:
219 long val = strtol (value, &end, 0);
222 msg_Err (obj, "syntax error in \"%s\": "
223 " not an integer", value);
230 case V4L2_CTRL_TYPE_INTEGER64:
232 long long val = strtoll (value, &end, 0);
235 msg_Err (obj, "syntax error in \"%s\": "
236 " not an integer", value);
239 ControlSet64 (c, val);
243 case V4L2_CTRL_TYPE_BITMASK:
245 unsigned long val = strtoul (value, &end, 0);
248 msg_Err (obj, "syntax error in \"%s\": "
249 " not an integer", value);
257 msg_Err (obj, "setting \"%s\" not supported", name);
261 msg_Err (obj, "control \"%s\" not available", name);
266 static int cidcmp (const void *a, const void *b)
268 const uint32_t *id = a;
269 const vlc_v4l2_ctrl_name_t *name = b;
271 return (int32_t)(*id - name->cid);
275 * Creates a VLC-V4L2 control structure:
276 * In particular, determines a name suitable for a VLC object variable.
277 * \param query V4L2 control query structure [IN]
278 * \return NULL on error
280 static vlc_v4l2_ctrl_t *ControlCreate (int fd,
281 const struct v4l2_queryctrl *query)
283 vlc_v4l2_ctrl_t *ctrl = malloc (sizeof (*ctrl));
284 if (unlikely(ctrl == NULL))
288 ctrl->id = query->id;
289 ctrl->type = query->type;
291 /* Search for a well-known control */
292 const vlc_v4l2_ctrl_name_t *known;
293 known = bsearch (&query->id, controls, sizeof (controls) / sizeof (*known),
294 sizeof (*known), cidcmp);
296 strcpy (ctrl->name, known->name);
298 /* Fallback to automatically-generated control name */
301 for (i = 0; query->name[i]; i++)
303 unsigned char c = query->name[i];
310 ctrl->name[i] = '\0';
313 ctrl->default_value = query->default_value;
318 #define CTRL_FLAGS_IGNORE \
319 (V4L2_CTRL_FLAG_DISABLED /* not implemented at all */ \
320 |V4L2_CTRL_FLAG_READ_ONLY /* value is constant */ \
321 |V4L2_CTRL_FLAG_VOLATILE /* value is (variable but) read-only */)
323 static vlc_v4l2_ctrl_t *ControlAddInteger (vlc_object_t *obj, int fd,
324 const struct v4l2_queryctrl *query)
326 msg_Dbg (obj, " integer %s (%08"PRIX32")", query->name, query->id);
327 if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
330 vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
331 if (unlikely(c == NULL))
334 if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND))
341 struct v4l2_control ctrl = { .id = query->id };
343 if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
345 msg_Dbg (obj, " current: %3"PRId32", default: %3"PRId32,
346 ctrl.value, query->default_value);
347 val.i_int = ctrl.value;
348 var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
350 val.i_int = query->minimum;
351 var_Change (obj, c->name, VLC_VAR_SETMIN, &val, NULL);
352 val.i_int = query->maximum;
353 var_Change (obj, c->name, VLC_VAR_SETMAX, &val, NULL);
354 if (query->step != 1)
356 val.i_int = query->step;
357 var_Change (obj, c->name, VLC_VAR_SETSTEP, &val, NULL);
359 val.i_int = query->default_value;
360 var_Change (obj, c->name, VLC_VAR_SETDEFAULT, &val, NULL);
364 static vlc_v4l2_ctrl_t *ControlAddBoolean (vlc_object_t *obj, int fd,
365 const struct v4l2_queryctrl *query)
367 msg_Dbg (obj, " boolean %s (%08"PRIX32")", query->name, query->id);
368 if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
371 vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
372 if (unlikely(c == NULL))
375 if (var_Create (obj, c->name, VLC_VAR_BOOL | VLC_VAR_ISCOMMAND))
382 struct v4l2_control ctrl = { .id = query->id };
384 if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
386 msg_Dbg (obj, " current: %s, default: %s",
387 ctrl.value ? " true" : "false",
388 query->default_value ? " true" : "false");
389 val.b_bool = ctrl.value;
390 var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
392 val.b_bool = query->default_value;
393 var_Change (obj, c->name, VLC_VAR_SETDEFAULT, &val, NULL);
397 static vlc_v4l2_ctrl_t *ControlAddMenu (vlc_object_t *obj, int fd,
398 const struct v4l2_queryctrl *query)
400 msg_Dbg (obj, " menu %s (%08"PRIX32")", query->name, query->id);
401 if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
404 vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
405 if (unlikely(c == NULL))
408 if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_HASCHOICE
409 | VLC_VAR_ISCOMMAND))
416 struct v4l2_control ctrl = { .id = query->id };
418 if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
420 msg_Dbg (obj, " current: %"PRId32", default: %"PRId32,
421 ctrl.value, query->default_value);
422 val.i_int = ctrl.value;
423 var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
425 val.b_bool = query->default_value;
426 var_Change (obj, c->name, VLC_VAR_SETDEFAULT, &val, NULL);
427 val.i_int = query->minimum;
428 var_Change (obj, c->name, VLC_VAR_SETMIN, &val, NULL);
429 val.i_int = query->maximum;
430 var_Change (obj, c->name, VLC_VAR_SETMAX, &val, NULL);
432 /* Import menu choices */
433 for (uint_fast32_t idx = query->minimum;
434 idx <= (uint_fast32_t)query->maximum;
437 struct v4l2_querymenu menu = { .id = query->id, .index = idx };
439 if (v4l2_ioctl (fd, VIDIOC_QUERYMENU, &menu) < 0)
441 msg_Dbg (obj, " choice %"PRIu32") %s", menu.index, menu.name);
444 val.i_int = menu.index;
445 text.psz_string = (char *)menu.name;
446 var_Change (obj, c->name, VLC_VAR_ADDCHOICE, &val, &text);
451 static vlc_v4l2_ctrl_t *ControlAddButton (vlc_object_t *obj, int fd,
452 const struct v4l2_queryctrl *query)
454 msg_Dbg (obj, " button %s (%08"PRIX32")", query->name, query->id);
455 if (query->flags & CTRL_FLAGS_IGNORE)
458 vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
459 if (unlikely(c == NULL))
462 if (var_Create (obj, c->name, VLC_VAR_VOID | VLC_VAR_ISCOMMAND))
468 static vlc_v4l2_ctrl_t *ControlAddInteger64 (vlc_object_t *obj, int fd,
469 const struct v4l2_queryctrl *query)
471 msg_Dbg (obj, " 64-bits %s (%08"PRIX32")", query->name, query->id);
472 if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
475 vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
476 if (unlikely(c == NULL))
479 if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND))
488 static vlc_v4l2_ctrl_t *ControlAddClass (vlc_object_t *obj, int fd,
489 const struct v4l2_queryctrl *query)
491 msg_Dbg (obj, "control class %s:", query->name);
496 static vlc_v4l2_ctrl_t *ControlAddBitMask (vlc_object_t *obj, int fd,
497 const struct v4l2_queryctrl *query)
499 msg_Dbg (obj, " bit mask %s (%08"PRIX32")", query->name, query->id);
500 if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
503 vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
504 if (unlikely(c == NULL))
507 if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND))
514 struct v4l2_control ctrl = { .id = query->id };
516 if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
518 msg_Dbg (obj, " current: 0x%08"PRIX32", default: 0x%08"PRIX32,
519 ctrl.value, query->default_value);
520 val.i_int = ctrl.value;
521 var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
524 var_Change (obj, c->name, VLC_VAR_SETMIN, &val, NULL);
525 val.i_int = (uint32_t)query->maximum;
526 var_Change (obj, c->name, VLC_VAR_SETMAX, &val, NULL);
527 val.i_int = query->default_value;
528 var_Change (obj, c->name, VLC_VAR_SETDEFAULT, &val, NULL);
532 static vlc_v4l2_ctrl_t *ControlAddUnknown (vlc_object_t *obj, int fd,
533 const struct v4l2_queryctrl *query)
535 msg_Dbg (obj, " unknown %s (%08"PRIX32")", query->name, query->id);
536 msg_Warn (obj, " unknown control type %u", (unsigned)query->type);
541 typedef vlc_v4l2_ctrl_t *(*ctrl_type_cb) (vlc_object_t *, int,
542 const struct v4l2_queryctrl *);
545 * Lists all user-class v4l2 controls, sets them to the user specified
546 * value and create the relevant variables to enable run-time changes.
548 vlc_v4l2_ctrl_t *ControlsInit (vlc_object_t *obj, int fd)
550 /* A list of controls that can be modified at run-time is stored in the
551 * "controls" variable. The V4L2 controls dialog can be built from this. */
552 var_Create (obj, "controls", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE);
554 static const ctrl_type_cb handlers[] =
556 [V4L2_CTRL_TYPE_INTEGER] = ControlAddInteger,
557 [V4L2_CTRL_TYPE_BOOLEAN] = ControlAddBoolean,
558 [V4L2_CTRL_TYPE_MENU] = ControlAddMenu,
559 [V4L2_CTRL_TYPE_BUTTON] = ControlAddButton,
560 [V4L2_CTRL_TYPE_INTEGER64] = ControlAddInteger64,
561 [V4L2_CTRL_TYPE_CTRL_CLASS] = ControlAddClass,
562 [V4L2_CTRL_TYPE_BITMASK] = ControlAddBitMask,
565 vlc_v4l2_ctrl_t *list = NULL;
566 struct v4l2_queryctrl query;
568 query.id = V4L2_CTRL_FLAG_NEXT_CTRL;
569 while (v4l2_ioctl (fd, VIDIOC_QUERYCTRL, &query) >= 0)
571 ctrl_type_cb handler = NULL;
572 if (query.type < (sizeof (handlers) / sizeof (handlers[0])))
573 handler = handlers[query.type];
575 handler = ControlAddUnknown;
577 vlc_v4l2_ctrl_t *c = handler (obj, fd, &query);
580 vlc_value_t val, text;
582 var_AddCallback (obj, c->name, ControlSetCallback, c);
583 text.psz_string = (char *)query.name;
584 var_Change (obj, c->name, VLC_VAR_SETTEXT, &text, NULL);
585 val.i_int = query.id;
586 text.psz_string = (char *)c->name;
587 var_Change (obj, "controls", VLC_VAR_ADDCHOICE, &val, &text);
592 query.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
595 /* Set well-known controls from VLC configuration */
596 for (vlc_v4l2_ctrl_t *ctrl = list; ctrl != NULL; ctrl = ctrl->next)
598 if (!CTRL_CID_KNOWN (ctrl->id))
601 char varname[sizeof (CFG_PREFIX) + sizeof (ctrl->name) - 1];
602 sprintf (varname, CFG_PREFIX"%s", ctrl->name);
604 int64_t val = var_InheritInteger (obj, varname);
606 continue; /* the VLC default value: "do not modify" */
607 ControlSet (ctrl, val); /* NOTE: all known are integers or booleans */
610 /* Set any control from the VLC configuration control string */
611 ControlsSetFromString (obj, list);
613 /* Add a control to reset all controls to their default values */
615 vlc_value_t val, text;
617 var_Create (obj, "reset", VLC_VAR_VOID | VLC_VAR_ISCOMMAND);
618 val.psz_string = _("Reset defaults");
619 var_Change (obj, "reset", VLC_VAR_SETTEXT, &val, NULL);
622 text.psz_string = (char *)"reset";
623 var_Change (obj, "controls", VLC_VAR_ADDCHOICE, &val, &text);
624 var_AddCallback (obj, "reset", ControlsResetCallback, list);
626 if (var_InheritBool (obj, CFG_PREFIX"controls-reset"))
627 ControlsReset (obj, list);
632 void ControlsDeinit (vlc_object_t *obj, vlc_v4l2_ctrl_t *list)
634 var_DelCallback (obj, "reset", ControlsResetCallback, list);
635 var_Destroy (obj, "reset");
639 vlc_v4l2_ctrl_t *next = list->next;
641 var_DelCallback (obj, list->name, ControlSetCallback, list);
642 var_Destroy (obj, list->name);
647 var_Destroy (obj, "controls");