]> git.sesse.net Git - vlc/blob - modules/access/v4l2/controls.c
Add V4L2 64-bits integer controls (refs #5302)
[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[20];
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     { "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))
71 };
72
73 typedef struct vlc_v4l2_ctrl
74 {
75     int                   fd;
76     uint32_t              id;
77     enum v4l2_ctrl_type   type;
78     char                  name[32];
79     int32_t               default_value;
80     struct vlc_v4l2_ctrl *next;
81 } vlc_v4l2_ctrl_t;
82
83 static int ControlSet (const vlc_v4l2_ctrl_t *c, int_fast32_t value)
84 {
85     struct v4l2_control ctrl = {
86         .id = c->id,
87         .value = value,
88     };
89     if (v4l2_ioctl (c->fd, VIDIOC_S_CTRL, &ctrl) < 0)
90         return -1;
91     return 0;
92 }
93
94 static int ControlSet64 (const vlc_v4l2_ctrl_t *c, int64_t value)
95 {
96     struct v4l2_ext_control ext_ctrl = {
97         .id = c->id,
98         .size = 0,
99         .value64 = value,
100     };
101     struct v4l2_ext_controls ext_ctrls = {
102         .ctrl_class = V4L2_CTRL_ID2CLASS(c->id),
103         .count = 1,
104         .error_idx = 0,
105         .controls = &ext_ctrl,
106     };
107
108     if (v4l2_ioctl (c->fd, VIDIOC_S_EXT_CTRLS, &ext_ctrls) < 0)
109         return -1;
110     return 0;
111 }
112
113 static int ControlSetCallback (vlc_object_t *obj, const char *var,
114                                vlc_value_t old, vlc_value_t cur, void *data)
115 {
116     const vlc_v4l2_ctrl_t *ctrl = data;
117     int ret;
118
119     switch (ctrl->type)
120     {
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);
125             break;
126         case V4L2_CTRL_TYPE_BOOLEAN:
127             ret = ControlSet (ctrl, cur.b_bool);
128             break;
129         case V4L2_CTRL_TYPE_BUTTON:
130             ret = ControlSet (ctrl, 0);
131             break;
132         case V4L2_CTRL_TYPE_INTEGER64:
133             ret = ControlSet64 (ctrl, cur.i_int);
134             break;
135         default:
136             assert (0);
137     }
138
139     if (ret)
140     {
141         msg_Err (obj, "cannot set control %s: %m", var);
142         return VLC_EGENERIC;
143     }
144     (void) old;
145     return VLC_SUCCESS;
146 }
147
148 static void ControlsReset (vlc_object_t *obj, vlc_v4l2_ctrl_t *list)
149 {
150     while (list != NULL)
151     {
152         switch (list->type)
153         {
154             case V4L2_CTRL_TYPE_INTEGER:
155             case V4L2_CTRL_TYPE_MENU:
156                 var_SetInteger (obj, list->name, list->default_value);
157                 break;
158             case V4L2_CTRL_TYPE_BOOLEAN:
159                 var_SetBool (obj, list->name, list->default_value);
160                 break;
161             default:;
162         }
163         list = list->next;
164     }
165 }
166
167 static int ControlsResetCallback (vlc_object_t *obj, const char *var,
168                                   vlc_value_t old, vlc_value_t cur, void *data)
169 {
170     ControlsReset (obj, data);
171     (void) var; (void) old; (void) cur;
172     return VLC_SUCCESS;
173 }
174
175 static void ControlsSetFromString (vlc_object_t *obj,
176                                    const vlc_v4l2_ctrl_t *list)
177 {
178     char *buf = var_InheritString (obj, CFG_PREFIX"set-ctrls");
179     if (buf == NULL)
180         return;
181
182     char *p = buf;
183     if (*p == '{')
184         p++;
185
186     char *end = strchr (p, '}');
187     if (end != NULL)
188         *end = '\0';
189 next:
190     while (p != NULL && *p)
191     {
192         const char *name, *value;
193
194         p += strspn (p, ", ");
195         name = p;
196         end = strchr (p, ',');
197         if (end != NULL)
198             *(end++) = '\0';
199         p = end; /* next name/value pair */
200
201         end = strchr (name, '=');
202         if (end == NULL)
203         {
204             /* TODO? support button controls that way? */
205             msg_Err (obj, "syntax error in \"%s\": missing '='", name);
206             continue;
207         }
208         *(end++) = '\0';
209         value = end;
210
211         for (const vlc_v4l2_ctrl_t *c = list; c != NULL; c = c->next)
212             if (!strcasecmp (name, c->name))
213                 switch (c->type)
214                 {
215                     case V4L2_CTRL_TYPE_INTEGER:
216                     case V4L2_CTRL_TYPE_BOOLEAN:
217                     case V4L2_CTRL_TYPE_MENU:
218                     {
219                         long val = strtol (value, &end, 0);
220                         if (*end)
221                         {
222                             msg_Err (obj, "syntax error in \"%s\": "
223                                      " not an integer", value);
224                             goto next;
225                         }
226                         ControlSet (c, val);
227                         break;
228                     }
229
230                     case V4L2_CTRL_TYPE_INTEGER64:
231                     {
232                         long long val = strtoll (value, &end, 0);
233                         if (*end)
234                         {
235                             msg_Err (obj, "syntax error in \"%s\": "
236                                      " not an integer", value);
237                             goto next;
238                         }
239                         ControlSet64 (c, val);
240                         break;
241                     }
242
243                     case V4L2_CTRL_TYPE_BITMASK:
244                     {
245                         unsigned long val = strtoul (value, &end, 0);
246                         if (*end)
247                         {
248                             msg_Err (obj, "syntax error in \"%s\": "
249                                      " not an integer", value);
250                             goto next;
251                         }
252                         ControlSet (c, val);
253                         break;
254                     }
255
256                     default:
257                         msg_Err (obj, "setting \"%s\" not supported", name);
258                         goto next;
259                 }
260
261         msg_Err (obj, "control \"%s\" not available", name);
262     }
263     free (buf);
264 }
265
266 static int cidcmp (const void *a, const void *b)
267 {
268     const uint32_t *id = a;
269     const vlc_v4l2_ctrl_name_t *name = b;
270
271     return (int32_t)(*id - name->cid);
272 }
273
274 /**
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
279  */
280 static vlc_v4l2_ctrl_t *ControlCreate (int fd,
281                                        const struct v4l2_queryctrl *query)
282 {
283     vlc_v4l2_ctrl_t *ctrl = malloc (sizeof (*ctrl));
284     if (unlikely(ctrl == NULL))
285         return NULL;
286
287     ctrl->fd = fd;
288     ctrl->id = query->id;
289     ctrl->type = query->type;
290
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);
295     if (known != NULL)
296         strcpy (ctrl->name, known->name);
297     else
298     /* Fallback to automatically-generated control name */
299     {
300         size_t i;
301         for (i = 0; query->name[i]; i++)
302         {
303             unsigned char c = query->name[i];
304             if (c == ' ')
305                 c = '_';
306             if (c < 128)
307                 c = tolower (c);
308             ctrl->name[i] = c;
309         }
310         ctrl->name[i] = '\0';
311     }
312
313     ctrl->default_value = query->default_value;
314     return ctrl;
315 }
316
317
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 */)
322
323 static vlc_v4l2_ctrl_t *ControlAddInteger (vlc_object_t *obj, int fd,
324                                            const struct v4l2_queryctrl *query)
325 {
326     msg_Dbg (obj, " integer  %s (%08"PRIX32")", query->name, query->id);
327     if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
328         return NULL;
329
330     vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
331     if (unlikely(c == NULL))
332         return NULL;
333
334     if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND))
335     {
336         free (c);
337         return NULL;
338     }
339
340     vlc_value_t val;
341     struct v4l2_control ctrl = { .id = query->id };
342
343     if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
344     {
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);
349     }
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)
355     {
356         val.i_int = query->step;
357         var_Change (obj, c->name, VLC_VAR_SETSTEP, &val, NULL);
358     }
359     val.i_int = query->default_value;
360     var_Change (obj, c->name, VLC_VAR_SETDEFAULT, &val, NULL);
361     return c;
362 }
363
364 static vlc_v4l2_ctrl_t *ControlAddBoolean (vlc_object_t *obj, int fd,
365                                            const struct v4l2_queryctrl *query)
366 {
367     msg_Dbg (obj, " boolean  %s (%08"PRIX32")", query->name, query->id);
368     if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
369         return NULL;
370
371     vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
372     if (unlikely(c == NULL))
373         return NULL;
374
375     if (var_Create (obj, c->name, VLC_VAR_BOOL | VLC_VAR_ISCOMMAND))
376     {
377         free (c);
378         return NULL;
379     }
380
381     vlc_value_t val;
382     struct v4l2_control ctrl = { .id = query->id };
383
384     if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
385     {
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);
391     }
392     val.b_bool = query->default_value;
393     var_Change (obj, c->name, VLC_VAR_SETDEFAULT, &val, NULL);
394     return c;
395 }
396
397 static vlc_v4l2_ctrl_t *ControlAddMenu (vlc_object_t *obj, int fd,
398                                         const struct v4l2_queryctrl *query)
399 {
400     msg_Dbg (obj, " menu     %s (%08"PRIX32")", query->name, query->id);
401     if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
402         return NULL;
403
404     vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
405     if (unlikely(c == NULL))
406         return NULL;
407
408     if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_HASCHOICE
409                                                   | VLC_VAR_ISCOMMAND))
410     {
411         free (c);
412         return NULL;
413     }
414
415     vlc_value_t val;
416     struct v4l2_control ctrl = { .id = query->id };
417
418     if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
419     {
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);
424     }
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);
431
432     /* Import menu choices */
433     for (uint_fast32_t idx = query->minimum;
434          idx <= (uint_fast32_t)query->maximum;
435          idx++)
436     {
437         struct v4l2_querymenu menu = { .id = query->id, .index = idx };
438
439         if (v4l2_ioctl (fd, VIDIOC_QUERYMENU, &menu) < 0)
440             continue;
441         msg_Dbg (obj, "  choice %"PRIu32") %s", menu.index, menu.name);
442
443         vlc_value_t text;
444         val.i_int = menu.index;
445         text.psz_string = (char *)menu.name;
446         var_Change (obj, c->name, VLC_VAR_ADDCHOICE, &val, &text);
447     }
448     return c;
449 }
450
451 static vlc_v4l2_ctrl_t *ControlAddButton (vlc_object_t *obj, int fd,
452                                           const struct v4l2_queryctrl *query)
453 {
454     msg_Dbg (obj, " button   %s (%08"PRIX32")", query->name, query->id);
455     if (query->flags & CTRL_FLAGS_IGNORE)
456         return NULL;
457
458     vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
459     if (unlikely(c == NULL))
460         return NULL;
461
462     if (var_Create (obj, c->name, VLC_VAR_VOID | VLC_VAR_ISCOMMAND))
463         return NULL;
464     (void) fd;
465     return c;
466 }
467
468 static vlc_v4l2_ctrl_t *ControlAddInteger64 (vlc_object_t *obj, int fd,
469                                             const struct v4l2_queryctrl *query)
470 {
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))
473         return NULL;
474
475     vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
476     if (unlikely(c == NULL))
477         return NULL;
478
479     if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND))
480     {
481         free (c);
482         return NULL;
483     }
484
485     return c;
486 }
487
488 static vlc_v4l2_ctrl_t *ControlAddClass (vlc_object_t *obj, int fd,
489                                          const struct v4l2_queryctrl *query)
490 {
491     msg_Dbg (obj, "control class %s:", query->name);
492     (void) fd;
493     return NULL;
494 }
495
496 static vlc_v4l2_ctrl_t *ControlAddBitMask (vlc_object_t *obj, int fd,
497                                            const struct v4l2_queryctrl *query)
498 {
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))
501         return NULL;
502
503     vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
504     if (unlikely(c == NULL))
505         return NULL;
506
507     if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND))
508     {
509         free (c);
510         return NULL;
511     }
512
513     vlc_value_t val;
514     struct v4l2_control ctrl = { .id = query->id };
515
516     if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
517     {
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);
522     }
523     val.i_int = 0;
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);
529     return c;
530 }
531
532 static vlc_v4l2_ctrl_t *ControlAddUnknown (vlc_object_t *obj, int fd,
533                                            const struct v4l2_queryctrl *query)
534 {
535     msg_Dbg (obj, " unknown %s (%08"PRIX32")", query->name, query->id);
536     msg_Warn (obj, "  unknown control type %u", (unsigned)query->type);
537     (void) fd;
538     return NULL;
539 }
540
541 typedef vlc_v4l2_ctrl_t *(*ctrl_type_cb) (vlc_object_t *, int,
542                                           const struct v4l2_queryctrl *);
543
544 /**
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.
547  */
548 vlc_v4l2_ctrl_t *ControlsInit (vlc_object_t *obj, int fd)
549 {
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);
553
554     static const ctrl_type_cb handlers[] =
555     {
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,
563     };
564
565     vlc_v4l2_ctrl_t *list = NULL;
566     struct v4l2_queryctrl query;
567
568     query.id = V4L2_CTRL_FLAG_NEXT_CTRL;
569     while (v4l2_ioctl (fd, VIDIOC_QUERYCTRL, &query) >= 0)
570     {
571         ctrl_type_cb handler = NULL;
572         if (query.type < (sizeof (handlers) / sizeof (handlers[0])))
573             handler = handlers[query.type];
574         if (handler == NULL)
575             handler = ControlAddUnknown;
576
577         vlc_v4l2_ctrl_t *c = handler (obj, fd, &query);
578         if (c != NULL)
579         {
580             vlc_value_t val, text;
581
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);
588
589             c->next = list;
590             list = c;
591         }
592         query.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
593     }
594
595     /* Set well-known controls from VLC configuration */
596     for (vlc_v4l2_ctrl_t *ctrl = list; ctrl != NULL; ctrl = ctrl->next)
597     {
598         if (!CTRL_CID_KNOWN (ctrl->id))
599             continue;
600
601         char varname[sizeof (CFG_PREFIX) + sizeof (ctrl->name) - 1];
602         sprintf (varname, CFG_PREFIX"%s", ctrl->name);
603
604         int64_t val = var_InheritInteger (obj, varname);
605         if (val == -1)
606             continue; /* the VLC default value: "do not modify" */
607         ControlSet (ctrl, val); /* NOTE: all known are integers or booleans */
608     }
609
610     /* Set any control from the VLC configuration control string */
611     ControlsSetFromString (obj, list);
612
613     /* Add a control to reset all controls to their default values */
614     {
615         vlc_value_t val, text;
616
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);
620         val.i_int = -1;
621
622         text.psz_string = (char *)"reset";
623         var_Change (obj, "controls", VLC_VAR_ADDCHOICE, &val, &text);
624         var_AddCallback (obj, "reset", ControlsResetCallback, list);
625     }
626     if (var_InheritBool (obj, CFG_PREFIX"controls-reset"))
627         ControlsReset (obj, list);
628
629     return list;
630 }
631
632 void ControlsDeinit (vlc_object_t *obj, vlc_v4l2_ctrl_t *list)
633 {
634     var_DelCallback (obj, "reset", ControlsResetCallback, list);
635     var_Destroy (obj, "reset");
636
637     while (list != NULL)
638     {
639         vlc_v4l2_ctrl_t *next = list->next;
640
641         var_DelCallback (obj, list->name, ControlSetCallback, list);
642         var_Destroy (obj, list->name);
643         free (list);
644         list = next;
645     }
646
647     var_Destroy (obj, "controls");
648 }