]> git.sesse.net Git - vlc/blob - modules/access/v4l2/controls.c
Retrieve current value of V4L2 64-bits control
[vlc] / modules / access / v4l2 / controls.c
1 /*****************************************************************************
2  * controls.c : Video4Linux2 device controls for vlc
3  *****************************************************************************
4  * Copyright (C) 2002-2011 the VideoLAN team
5  *
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>
10  *
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.
15  *
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.
20  *
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  *****************************************************************************/
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include "v4l2.h"
31 #include <ctype.h>
32 #include <assert.h>
33 #include <sys/ioctl.h>
34
35 typedef struct vlc_v4l2_ctrl_name
36 {
37     const char name[28];
38     uint32_t cid;
39 } vlc_v4l2_ctrl_name_t;
40
41 /* NOTE: must be sorted by ID */
42 static const vlc_v4l2_ctrl_name_t controls[] =
43 {
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     { "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     { "power-line-frequency", V4L2_CID_POWER_LINE_FREQUENCY },
65     { "hue-auto", V4L2_CID_HUE_AUTO },
66     { "white-balance-temperature", V4L2_CID_WHITE_BALANCE_TEMPERATURE },
67     { "sharpness", V4L2_CID_SHARPNESS },
68     { "backlight-compensation", V4L2_CID_BACKLIGHT_COMPENSATION },
69     { "chroma-gain-auto", V4L2_CID_CHROMA_AGC },
70     { "color-killer", V4L2_CID_COLOR_KILLER },
71     { "color-effect", V4L2_CID_COLORFX },
72     { "rotate", V4L2_CID_ROTATE },
73     { "bg-color", V4L2_CID_BG_COLOR }, // NOTE: output only
74     { "chroma-gain", V4L2_CID_CHROMA_GAIN },
75
76     { "illuminators-1", V4L2_CID_ILLUMINATORS_1 }, // NOTE: don't care?
77     { "illuminators-2", V4L2_CID_ILLUMINATORS_2 },
78 #define CTRL_CID_KNOWN(cid) \
79     ((((uint32_t)cid) - V4L2_CID_BRIGHTNESS) \
80         <= (V4L2_CID_VCENTER - V4L2_CID_BRIGHTNESS))
81 };
82
83 struct vlc_v4l2_ctrl
84 {
85     int                   fd;
86     uint32_t              id;
87     uint8_t               type;
88     char                  name[32];
89     int32_t               default_value;
90     struct vlc_v4l2_ctrl *next;
91 };
92
93 static int ControlSet (const vlc_v4l2_ctrl_t *c, int_fast32_t value)
94 {
95     struct v4l2_control ctrl = {
96         .id = c->id,
97         .value = value,
98     };
99     if (v4l2_ioctl (c->fd, VIDIOC_S_CTRL, &ctrl) < 0)
100         return -1;
101     return 0;
102 }
103
104 static int ControlSet64 (const vlc_v4l2_ctrl_t *c, int64_t value)
105 {
106     struct v4l2_ext_control ext_ctrl = {
107         .id = c->id,
108         .size = 0,
109     };
110     ext_ctrl.value64 = value;
111     struct v4l2_ext_controls ext_ctrls = {
112         .ctrl_class = V4L2_CTRL_ID2CLASS(c->id),
113         .count = 1,
114         .error_idx = 0,
115         .controls = &ext_ctrl,
116     };
117
118     if (v4l2_ioctl (c->fd, VIDIOC_S_EXT_CTRLS, &ext_ctrls) < 0)
119         return -1;
120     return 0;
121 }
122
123 static int ControlSetCallback (vlc_object_t *obj, const char *var,
124                                vlc_value_t old, vlc_value_t cur, void *data)
125 {
126     const vlc_v4l2_ctrl_t *ctrl = data;
127     int ret;
128
129     switch (ctrl->type)
130     {
131         case V4L2_CTRL_TYPE_INTEGER:
132         case V4L2_CTRL_TYPE_MENU:
133         case V4L2_CTRL_TYPE_BITMASK:
134             ret = ControlSet (ctrl, cur.i_int);
135             break;
136         case V4L2_CTRL_TYPE_BOOLEAN:
137             ret = ControlSet (ctrl, cur.b_bool);
138             break;
139         case V4L2_CTRL_TYPE_BUTTON:
140             ret = ControlSet (ctrl, 0);
141             break;
142         case V4L2_CTRL_TYPE_INTEGER64:
143             ret = ControlSet64 (ctrl, cur.i_int);
144             break;
145         default:
146             assert (0);
147     }
148
149     if (ret)
150     {
151         msg_Err (obj, "cannot set control %s: %m", var);
152         return VLC_EGENERIC;
153     }
154     (void) old;
155     return VLC_SUCCESS;
156 }
157
158 static void ControlsReset (vlc_object_t *obj, vlc_v4l2_ctrl_t *list)
159 {
160     while (list != NULL)
161     {
162         switch (list->type)
163         {
164             case V4L2_CTRL_TYPE_INTEGER:
165             case V4L2_CTRL_TYPE_MENU:
166                 var_SetInteger (obj, list->name, list->default_value);
167                 break;
168             case V4L2_CTRL_TYPE_BOOLEAN:
169                 var_SetBool (obj, list->name, list->default_value);
170                 break;
171             default:;
172         }
173         list = list->next;
174     }
175 }
176
177 static int ControlsResetCallback (vlc_object_t *obj, const char *var,
178                                   vlc_value_t old, vlc_value_t cur, void *data)
179 {
180     ControlsReset (obj, data);
181     (void) var; (void) old; (void) cur;
182     return VLC_SUCCESS;
183 }
184
185 static void ControlsSetFromString (vlc_object_t *obj,
186                                    const vlc_v4l2_ctrl_t *list)
187 {
188     char *buf = var_InheritString (obj, CFG_PREFIX"set-ctrls");
189     if (buf == NULL)
190         return;
191
192     char *p = buf;
193     if (*p == '{')
194         p++;
195
196     char *end = strchr (p, '}');
197     if (end != NULL)
198         *end = '\0';
199 next:
200     while (p != NULL && *p)
201     {
202         const char *name, *value;
203
204         p += strspn (p, ", ");
205         name = p;
206         end = strchr (p, ',');
207         if (end != NULL)
208             *(end++) = '\0';
209         p = end; /* next name/value pair */
210
211         end = strchr (name, '=');
212         if (end == NULL)
213         {
214             /* TODO? support button controls that way? */
215             msg_Err (obj, "syntax error in \"%s\": missing '='", name);
216             continue;
217         }
218         *(end++) = '\0';
219         value = end;
220
221         for (const vlc_v4l2_ctrl_t *c = list; c != NULL; c = c->next)
222             if (!strcasecmp (name, c->name))
223                 switch (c->type)
224                 {
225                     case V4L2_CTRL_TYPE_INTEGER:
226                     case V4L2_CTRL_TYPE_BOOLEAN:
227                     case V4L2_CTRL_TYPE_MENU:
228                     {
229                         long val = strtol (value, &end, 0);
230                         if (*end)
231                         {
232                             msg_Err (obj, "syntax error in \"%s\": "
233                                      " not an integer", value);
234                             goto next;
235                         }
236                         ControlSet (c, val);
237                         break;
238                     }
239
240                     case V4L2_CTRL_TYPE_INTEGER64:
241                     {
242                         long long val = strtoll (value, &end, 0);
243                         if (*end)
244                         {
245                             msg_Err (obj, "syntax error in \"%s\": "
246                                      " not an integer", value);
247                             goto next;
248                         }
249                         ControlSet64 (c, val);
250                         break;
251                     }
252
253                     case V4L2_CTRL_TYPE_BITMASK:
254                     {
255                         unsigned long val = strtoul (value, &end, 0);
256                         if (*end)
257                         {
258                             msg_Err (obj, "syntax error in \"%s\": "
259                                      " not an integer", value);
260                             goto next;
261                         }
262                         ControlSet (c, val);
263                         break;
264                     }
265
266                     default:
267                         msg_Err (obj, "setting \"%s\" not supported", name);
268                         goto next;
269                 }
270
271         msg_Err (obj, "control \"%s\" not available", name);
272     }
273     free (buf);
274 }
275
276 static int cidcmp (const void *a, const void *b)
277 {
278     const uint32_t *id = a;
279     const vlc_v4l2_ctrl_name_t *name = b;
280
281     return (int32_t)(*id - name->cid);
282 }
283
284 /**
285  * Creates a VLC-V4L2 control structure:
286  * In particular, determines a name suitable for a VLC object variable.
287  * \param query V4L2 control query structure [IN]
288  * \return NULL on error
289  */
290 static vlc_v4l2_ctrl_t *ControlCreate (int fd,
291                                        const struct v4l2_queryctrl *query)
292 {
293     vlc_v4l2_ctrl_t *ctrl = malloc (sizeof (*ctrl));
294     if (unlikely(ctrl == NULL))
295         return NULL;
296
297     ctrl->fd = fd;
298     ctrl->id = query->id;
299     ctrl->type = query->type;
300
301     /* Search for a well-known control */
302     const vlc_v4l2_ctrl_name_t *known;
303     known = bsearch (&query->id, controls, sizeof (controls) / sizeof (*known),
304                      sizeof (*known), cidcmp);
305     if (known != NULL)
306         strcpy (ctrl->name, known->name);
307     else
308     /* Fallback to automatically-generated control name */
309     {
310         size_t i;
311         for (i = 0; query->name[i]; i++)
312         {
313             unsigned char c = query->name[i];
314             if (c == ' ')
315                 c = '_';
316             if (c < 128)
317                 c = tolower (c);
318             ctrl->name[i] = c;
319         }
320         ctrl->name[i] = '\0';
321     }
322
323     ctrl->default_value = query->default_value;
324     return ctrl;
325 }
326
327
328 #define CTRL_FLAGS_IGNORE \
329     (V4L2_CTRL_FLAG_DISABLED /* not implemented at all */ \
330     |V4L2_CTRL_FLAG_READ_ONLY /* value is constant */ \
331     |V4L2_CTRL_FLAG_VOLATILE /* value is (variable but) read-only */)
332
333 static vlc_v4l2_ctrl_t *ControlAddInteger (vlc_object_t *obj, int fd,
334                                            const struct v4l2_queryctrl *query)
335 {
336     msg_Dbg (obj, " integer  %s (%08"PRIX32")", query->name, query->id);
337     if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
338         return NULL;
339
340     vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
341     if (unlikely(c == NULL))
342         return NULL;
343
344     if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND))
345     {
346         free (c);
347         return NULL;
348     }
349
350     vlc_value_t val;
351     struct v4l2_control ctrl = { .id = query->id };
352
353     if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
354     {
355         msg_Dbg (obj, "  current: %3"PRId32", default: %3"PRId32,
356                  ctrl.value, query->default_value);
357         val.i_int = ctrl.value;
358         var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
359     }
360     val.i_int = query->minimum;
361     var_Change (obj, c->name, VLC_VAR_SETMIN, &val, NULL);
362     val.i_int = query->maximum;
363     var_Change (obj, c->name, VLC_VAR_SETMAX, &val, NULL);
364     if (query->step != 1)
365     {
366         val.i_int = query->step;
367         var_Change (obj, c->name, VLC_VAR_SETSTEP, &val, NULL);
368     }
369     val.i_int = query->default_value;
370     var_Change (obj, c->name, VLC_VAR_SETDEFAULT, &val, NULL);
371     return c;
372 }
373
374 static vlc_v4l2_ctrl_t *ControlAddBoolean (vlc_object_t *obj, int fd,
375                                            const struct v4l2_queryctrl *query)
376 {
377     msg_Dbg (obj, " boolean  %s (%08"PRIX32")", query->name, query->id);
378     if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
379         return NULL;
380
381     vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
382     if (unlikely(c == NULL))
383         return NULL;
384
385     if (var_Create (obj, c->name, VLC_VAR_BOOL | VLC_VAR_ISCOMMAND))
386     {
387         free (c);
388         return NULL;
389     }
390
391     vlc_value_t val;
392     struct v4l2_control ctrl = { .id = query->id };
393
394     if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
395     {
396         msg_Dbg (obj, "  current: %s, default: %s",
397                  ctrl.value ? " true" : "false",
398                  query->default_value ? " true" : "false");
399         val.b_bool = ctrl.value;
400         var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
401     }
402     val.b_bool = query->default_value;
403     var_Change (obj, c->name, VLC_VAR_SETDEFAULT, &val, NULL);
404     return c;
405 }
406
407 static vlc_v4l2_ctrl_t *ControlAddMenu (vlc_object_t *obj, int fd,
408                                         const struct v4l2_queryctrl *query)
409 {
410     msg_Dbg (obj, " menu     %s (%08"PRIX32")", query->name, query->id);
411     if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
412         return NULL;
413
414     vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
415     if (unlikely(c == NULL))
416         return NULL;
417
418     if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_HASCHOICE
419                                                   | VLC_VAR_ISCOMMAND))
420     {
421         free (c);
422         return NULL;
423     }
424
425     vlc_value_t val;
426     struct v4l2_control ctrl = { .id = query->id };
427
428     if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
429     {
430         msg_Dbg (obj, "  current: %"PRId32", default: %"PRId32,
431                  ctrl.value, query->default_value);
432         val.i_int = ctrl.value;
433         var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
434     }
435     val.b_bool = query->default_value;
436     var_Change (obj, c->name, VLC_VAR_SETDEFAULT, &val, NULL);
437     val.i_int = query->minimum;
438     var_Change (obj, c->name, VLC_VAR_SETMIN, &val, NULL);
439     val.i_int = query->maximum;
440     var_Change (obj, c->name, VLC_VAR_SETMAX, &val, NULL);
441
442     /* Import menu choices */
443     for (uint_fast32_t idx = query->minimum;
444          idx <= (uint_fast32_t)query->maximum;
445          idx++)
446     {
447         struct v4l2_querymenu menu = { .id = query->id, .index = idx };
448
449         if (v4l2_ioctl (fd, VIDIOC_QUERYMENU, &menu) < 0)
450             continue;
451         msg_Dbg (obj, "  choice %"PRIu32") %s", menu.index, menu.name);
452
453         vlc_value_t text;
454         val.i_int = menu.index;
455         text.psz_string = (char *)menu.name;
456         var_Change (obj, c->name, VLC_VAR_ADDCHOICE, &val, &text);
457     }
458     return c;
459 }
460
461 static vlc_v4l2_ctrl_t *ControlAddButton (vlc_object_t *obj, int fd,
462                                           const struct v4l2_queryctrl *query)
463 {
464     msg_Dbg (obj, " button   %s (%08"PRIX32")", query->name, query->id);
465     if (query->flags & CTRL_FLAGS_IGNORE)
466         return NULL;
467
468     vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
469     if (unlikely(c == NULL))
470         return NULL;
471
472     if (var_Create (obj, c->name, VLC_VAR_VOID | VLC_VAR_ISCOMMAND))
473     {
474         free (c);
475         return NULL;
476     }
477     return c;
478 }
479
480 static vlc_v4l2_ctrl_t *ControlAddInteger64 (vlc_object_t *obj, int fd,
481                                             const struct v4l2_queryctrl *query)
482 {
483     msg_Dbg (obj, " 64-bits  %s (%08"PRIX32")", query->name, query->id);
484     if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
485         return NULL;
486
487     vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
488     if (unlikely(c == NULL))
489         return NULL;
490
491     if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND))
492     {
493         free (c);
494         return NULL;
495     }
496
497     struct v4l2_ext_control ext_ctrl = { .id = c->id, .size = 0, };
498     struct v4l2_ext_controls ext_ctrls = {
499         .ctrl_class = V4L2_CTRL_ID2CLASS(c->id),
500         .count = 1,
501         .error_idx = 0,
502         .controls = &ext_ctrl,
503     };
504
505     if (v4l2_ioctl (c->fd, VIDIOC_G_EXT_CTRLS, &ext_ctrls) >= 0)
506     {
507         vlc_value_t val = { .i_int = ext_ctrl.value64 };
508
509         msg_Dbg (obj, "  current: %"PRId64, val.i_int);
510         var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
511     }
512
513     return c;
514 }
515
516 static vlc_v4l2_ctrl_t *ControlAddClass (vlc_object_t *obj, int fd,
517                                          const struct v4l2_queryctrl *query)
518 {
519     msg_Dbg (obj, "control class %s:", query->name);
520     (void) fd;
521     return NULL;
522 }
523
524 static vlc_v4l2_ctrl_t *ControlAddBitMask (vlc_object_t *obj, int fd,
525                                            const struct v4l2_queryctrl *query)
526 {
527     msg_Dbg (obj, " bit mask %s (%08"PRIX32")", query->name, query->id);
528     if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
529         return NULL;
530
531     vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
532     if (unlikely(c == NULL))
533         return NULL;
534
535     if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND))
536     {
537         free (c);
538         return NULL;
539     }
540
541     vlc_value_t val;
542     struct v4l2_control ctrl = { .id = query->id };
543
544     if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
545     {
546         msg_Dbg (obj, "  current: 0x%08"PRIX32", default: 0x%08"PRIX32,
547                  ctrl.value, query->default_value);
548         val.i_int = ctrl.value;
549         var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
550     }
551     val.i_int = 0;
552     var_Change (obj, c->name, VLC_VAR_SETMIN, &val, NULL);
553     val.i_int = (uint32_t)query->maximum;
554     var_Change (obj, c->name, VLC_VAR_SETMAX, &val, NULL);
555     val.i_int = query->default_value;
556     var_Change (obj, c->name, VLC_VAR_SETDEFAULT, &val, NULL);
557     return c;
558 }
559
560 static vlc_v4l2_ctrl_t *ControlAddUnknown (vlc_object_t *obj, int fd,
561                                            const struct v4l2_queryctrl *query)
562 {
563     msg_Dbg (obj, " unknown %s (%08"PRIX32")", query->name, query->id);
564     msg_Warn (obj, "  unknown control type %u", (unsigned)query->type);
565     (void) fd;
566     return NULL;
567 }
568
569 typedef vlc_v4l2_ctrl_t *(*ctrl_type_cb) (vlc_object_t *, int,
570                                           const struct v4l2_queryctrl *);
571
572 /**
573  * Lists all user-class v4l2 controls, sets them to the user specified
574  * value and create the relevant variables to enable run-time changes.
575  */
576 vlc_v4l2_ctrl_t *ControlsInit (vlc_object_t *obj, int fd)
577 {
578     /* A list of controls that can be modified at run-time is stored in the
579      * "controls" variable. The V4L2 controls dialog can be built from this. */
580     var_Create (obj, "controls", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE);
581
582     static const ctrl_type_cb handlers[] =
583     {
584         [V4L2_CTRL_TYPE_INTEGER] = ControlAddInteger,
585         [V4L2_CTRL_TYPE_BOOLEAN] = ControlAddBoolean,
586         [V4L2_CTRL_TYPE_MENU] = ControlAddMenu,
587         [V4L2_CTRL_TYPE_BUTTON] = ControlAddButton,
588         [V4L2_CTRL_TYPE_INTEGER64] = ControlAddInteger64,
589         [V4L2_CTRL_TYPE_CTRL_CLASS] = ControlAddClass,
590         [V4L2_CTRL_TYPE_BITMASK] = ControlAddBitMask,
591     };
592
593     vlc_v4l2_ctrl_t *list = NULL;
594     struct v4l2_queryctrl query;
595
596     query.id = V4L2_CTRL_FLAG_NEXT_CTRL;
597     while (v4l2_ioctl (fd, VIDIOC_QUERYCTRL, &query) >= 0)
598     {
599         ctrl_type_cb handler = NULL;
600         if (query.type < (sizeof (handlers) / sizeof (handlers[0])))
601             handler = handlers[query.type];
602         if (handler == NULL)
603             handler = ControlAddUnknown;
604
605         vlc_v4l2_ctrl_t *c = handler (obj, fd, &query);
606         if (c != NULL)
607         {
608             vlc_value_t val, text;
609
610             var_AddCallback (obj, c->name, ControlSetCallback, c);
611             text.psz_string = (char *)query.name;
612             var_Change (obj, c->name, VLC_VAR_SETTEXT, &text, NULL);
613             val.i_int = query.id;
614             text.psz_string = (char *)c->name;
615             var_Change (obj, "controls", VLC_VAR_ADDCHOICE, &val, &text);
616
617             c->next = list;
618             list = c;
619         }
620         query.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
621     }
622
623     /* Set well-known controls from VLC configuration */
624     for (vlc_v4l2_ctrl_t *ctrl = list; ctrl != NULL; ctrl = ctrl->next)
625     {
626         if (!CTRL_CID_KNOWN (ctrl->id))
627             continue;
628
629         char varname[sizeof (CFG_PREFIX) + sizeof (ctrl->name) - 1];
630         sprintf (varname, CFG_PREFIX"%s", ctrl->name);
631
632         int64_t val = var_InheritInteger (obj, varname);
633         if (val == -1)
634             continue; /* the VLC default value: "do not modify" */
635         ControlSet (ctrl, val); /* NOTE: all known are integers or booleans */
636     }
637
638     /* Set any control from the VLC configuration control string */
639     ControlsSetFromString (obj, list);
640
641     /* Add a control to reset all controls to their default values */
642     {
643         vlc_value_t val, text;
644
645         var_Create (obj, "reset", VLC_VAR_VOID | VLC_VAR_ISCOMMAND);
646         val.psz_string = _("Reset defaults");
647         var_Change (obj, "reset", VLC_VAR_SETTEXT, &val, NULL);
648         val.i_int = -1;
649
650         text.psz_string = (char *)"reset";
651         var_Change (obj, "controls", VLC_VAR_ADDCHOICE, &val, &text);
652         var_AddCallback (obj, "reset", ControlsResetCallback, list);
653     }
654     if (var_InheritBool (obj, CFG_PREFIX"controls-reset"))
655         ControlsReset (obj, list);
656
657     return list;
658 }
659
660 void ControlsDeinit (vlc_object_t *obj, vlc_v4l2_ctrl_t *list)
661 {
662     var_DelCallback (obj, "reset", ControlsResetCallback, list);
663     var_Destroy (obj, "reset");
664
665     while (list != NULL)
666     {
667         vlc_v4l2_ctrl_t *next = list->next;
668
669         var_DelCallback (obj, list->name, ControlSetCallback, list);
670         var_Destroy (obj, list->name);
671         free (list);
672         list = next;
673     }
674
675     var_Destroy (obj, "controls");
676 }