1 /*****************************************************************************
2 * controls.c : Video4Linux2 device controls for vlc
3 *****************************************************************************
4 * Copyright (C) 2002-2011 VLC authors and VideoLAN
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 it
12 * under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program; if not, write to the Free Software Foundation,
23 * 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 <stdio.h>
31 #include <ctype.h>
32 #include <assert.h>
33 #include <errno.h>
34 #include <sys/ioctl.h>
35
36 #include <vlc_common.h>
37
38 #include "v4l2.h"
39
40 typedef struct vlc_v4l2_ctrl_name
41 {
42 const char name[28];
43 uint32_t cid;
44 } vlc_v4l2_ctrl_name_t;
45
46 /* NOTE: must be sorted by ID */
47 static const vlc_v4l2_ctrl_name_t controls[] =
48 {
49 { "brightness", V4L2_CID_BRIGHTNESS },
50 { "contrast", V4L2_CID_CONTRAST },
51 { "saturation", V4L2_CID_SATURATION },
52 { "hue", V4L2_CID_HUE },
53 { "audio-volume", V4L2_CID_AUDIO_VOLUME },
54 { "audio-balance", V4L2_CID_AUDIO_BALANCE },
55 { "audio-bass", V4L2_CID_AUDIO_BASS },
56 { "audio-treble", V4L2_CID_AUDIO_TREBLE },
57 { "audio-mute", V4L2_CID_AUDIO_MUTE },
58 { "audio-loudness", V4L2_CID_AUDIO_LOUDNESS },
59 { "auto-white-balance", V4L2_CID_AUTO_WHITE_BALANCE },
60 { "do-white-balance", V4L2_CID_DO_WHITE_BALANCE },
61 { "red-balance", V4L2_CID_RED_BALANCE },
62 { "blue-balance", V4L2_CID_BLUE_BALANCE },
63 { "gamma", V4L2_CID_GAMMA },
64 { "autogain", V4L2_CID_AUTOGAIN },
65 { "gain", V4L2_CID_GAIN },
66 { "hflip", V4L2_CID_HFLIP },
67 { "vflip", V4L2_CID_VFLIP },
68 { "power-line-frequency", V4L2_CID_POWER_LINE_FREQUENCY },
69 { "hue-auto", V4L2_CID_HUE_AUTO },
70 { "white-balance-temperature", V4L2_CID_WHITE_BALANCE_TEMPERATURE },
71 { "sharpness", V4L2_CID_SHARPNESS },
72 { "backlight-compensation", V4L2_CID_BACKLIGHT_COMPENSATION },
73 { "chroma-gain-auto", V4L2_CID_CHROMA_AGC },
74 { "color-killer", V4L2_CID_COLOR_KILLER },
75 { "color-effect", V4L2_CID_COLORFX },
76 { "rotate", V4L2_CID_ROTATE },
77 { "bg-color", V4L2_CID_BG_COLOR }, // NOTE: output only
78 { "chroma-gain", V4L2_CID_CHROMA_GAIN },
79 { "brightness-auto", V4L2_CID_AUTOBRIGHTNESS },
80 { "band-stop-filter", V4L2_CID_BAND_STOP_FILTER },
81
82 { "illuminators-1", V4L2_CID_ILLUMINATORS_1 }, // NOTE: don't care?
83 { "illuminators-2", V4L2_CID_ILLUMINATORS_2 },
84 #define CTRL_CID_KNOWN(cid) \
85 ((((uint32_t)cid) - V4L2_CID_BRIGHTNESS) \
86 <= (V4L2_CID_BAND_STOP_FILTER - V4L2_CID_BRIGHTNESS))
87 };
88
89 struct vlc_v4l2_ctrl
90 {
91 int fd;
92 uint32_t id;
93 uint8_t type;
94 char name[32];
95 int32_t default_value;
96 struct vlc_v4l2_ctrl *next;
97 };
98
ControlSet(const vlc_v4l2_ctrl_t * c,int_fast32_t value)99 static int ControlSet (const vlc_v4l2_ctrl_t *c, int_fast32_t value)
100 {
101 struct v4l2_control ctrl = {
102 .id = c->id,
103 .value = value,
104 };
105 if (v4l2_ioctl (c->fd, VIDIOC_S_CTRL, &ctrl) < 0)
106 return -1;
107 return 0;
108 }
109
ControlSet64(const vlc_v4l2_ctrl_t * c,int64_t value)110 static int ControlSet64 (const vlc_v4l2_ctrl_t *c, int64_t value)
111 {
112 struct v4l2_ext_control ext_ctrl = {
113 .id = c->id,
114 .size = 0,
115 };
116 ext_ctrl.value64 = value;
117 struct v4l2_ext_controls ext_ctrls = {
118 .ctrl_class = V4L2_CTRL_ID2CLASS(c->id),
119 .count = 1,
120 .error_idx = 0,
121 .controls = &ext_ctrl,
122 };
123
124 if (v4l2_ioctl (c->fd, VIDIOC_S_EXT_CTRLS, &ext_ctrls) < 0)
125 return -1;
126 return 0;
127 }
128
ControlSetStr(const vlc_v4l2_ctrl_t * c,const char * restrict value)129 static int ControlSetStr (const vlc_v4l2_ctrl_t *c, const char *restrict value)
130 {
131 struct v4l2_ext_control ext_ctrl = {
132 .id = c->id,
133 .size = strlen (value) + 1,
134 };
135 ext_ctrl.string = (char *)value;
136 struct v4l2_ext_controls ext_ctrls = {
137 .ctrl_class = V4L2_CTRL_ID2CLASS(c->id),
138 .count = 1,
139 .error_idx = 0,
140 .controls = &ext_ctrl,
141 };
142
143 if (v4l2_ioctl (c->fd, VIDIOC_S_EXT_CTRLS, &ext_ctrls) < 0)
144 return -1;
145 return 0;
146 }
147
ControlSetCallback(vlc_object_t * obj,const char * var,vlc_value_t old,vlc_value_t cur,void * data)148 static int ControlSetCallback (vlc_object_t *obj, const char *var,
149 vlc_value_t old, vlc_value_t cur, void *data)
150 {
151 const vlc_v4l2_ctrl_t *ctrl = data;
152 int ret;
153
154 switch (ctrl->type)
155 {
156 case V4L2_CTRL_TYPE_INTEGER:
157 case V4L2_CTRL_TYPE_MENU:
158 case V4L2_CTRL_TYPE_BITMASK:
159 case V4L2_CTRL_TYPE_INTEGER_MENU:
160 ret = ControlSet (ctrl, cur.i_int);
161 break;
162 case V4L2_CTRL_TYPE_BOOLEAN:
163 ret = ControlSet (ctrl, cur.b_bool);
164 break;
165 case V4L2_CTRL_TYPE_BUTTON:
166 ret = ControlSet (ctrl, 0);
167 break;
168 case V4L2_CTRL_TYPE_INTEGER64:
169 ret = ControlSet64 (ctrl, cur.i_int);
170 break;
171 case V4L2_CTRL_TYPE_STRING:
172 ret = ControlSetStr (ctrl, cur.psz_string);
173 break;
174 default:
175 vlc_assert_unreachable ();
176 }
177
178 if (ret)
179 {
180 msg_Err (obj, "cannot set control %s: %s", var, vlc_strerror_c(errno));
181 return VLC_EGENERIC;
182 }
183 (void) old;
184 return VLC_SUCCESS;
185 }
186
ControlsReset(vlc_object_t * obj,vlc_v4l2_ctrl_t * list)187 static void ControlsReset (vlc_object_t *obj, vlc_v4l2_ctrl_t *list)
188 {
189 while (list != NULL)
190 {
191 switch (list->type)
192 {
193 case V4L2_CTRL_TYPE_INTEGER:
194 case V4L2_CTRL_TYPE_MENU:
195 case V4L2_CTRL_TYPE_INTEGER_MENU:
196 var_SetInteger (obj, list->name, list->default_value);
197 break;
198 case V4L2_CTRL_TYPE_BOOLEAN:
199 var_SetBool (obj, list->name, list->default_value);
200 break;
201 default:;
202 }
203 list = list->next;
204 }
205 }
206
ControlsResetCallback(vlc_object_t * obj,const char * var,vlc_value_t old,vlc_value_t cur,void * data)207 static int ControlsResetCallback (vlc_object_t *obj, const char *var,
208 vlc_value_t old, vlc_value_t cur, void *data)
209 {
210 ControlsReset (obj, data);
211 (void) var; (void) old; (void) cur;
212 return VLC_SUCCESS;
213 }
214
ControlsSetFromString(vlc_object_t * obj,const vlc_v4l2_ctrl_t * list)215 static void ControlsSetFromString (vlc_object_t *obj,
216 const vlc_v4l2_ctrl_t *list)
217 {
218 char *buf = var_InheritString (obj, CFG_PREFIX"set-ctrls");
219 if (buf == NULL)
220 return;
221
222 char *p = buf;
223 if (*p == '{')
224 p++;
225
226 char *end = strchr (p, '}');
227 if (end != NULL)
228 *end = '\0';
229 next:
230 while (p != NULL && *p)
231 {
232 const char *name, *value;
233
234 p += strspn (p, ", ");
235 name = p;
236 end = strchr (p, ',');
237 if (end != NULL)
238 *(end++) = '\0';
239 p = end; /* next name/value pair */
240
241 end = strchr (name, '=');
242 if (end == NULL)
243 {
244 /* TODO? support button controls that way? */
245 msg_Err (obj, "syntax error in \"%s\": missing '='", name);
246 continue;
247 }
248 *(end++) = '\0';
249 value = end;
250
251 for (const vlc_v4l2_ctrl_t *c = list; c != NULL; c = c->next)
252 if (!strcasecmp (name, c->name))
253 switch (c->type)
254 {
255 case V4L2_CTRL_TYPE_INTEGER:
256 case V4L2_CTRL_TYPE_BOOLEAN:
257 case V4L2_CTRL_TYPE_MENU:
258 case V4L2_CTRL_TYPE_INTEGER_MENU:
259 {
260 long val = strtol (value, &end, 0);
261 if (*end)
262 {
263 msg_Err (obj, "syntax error in \"%s\": "
264 " not an integer", value);
265 goto next;
266 }
267 ControlSet (c, val);
268 break;
269 }
270
271 case V4L2_CTRL_TYPE_INTEGER64:
272 {
273 long long val = strtoll (value, &end, 0);
274 if (*end)
275 {
276 msg_Err (obj, "syntax error in \"%s\": "
277 " not an integer", value);
278 goto next;
279 }
280 ControlSet64 (c, val);
281 break;
282 }
283
284 case V4L2_CTRL_TYPE_STRING:
285 ControlSetStr (c, value);
286 break;
287
288 case V4L2_CTRL_TYPE_BITMASK:
289 {
290 unsigned long val = strtoul (value, &end, 0);
291 if (*end)
292 {
293 msg_Err (obj, "syntax error in \"%s\": "
294 " not an integer", value);
295 goto next;
296 }
297 ControlSet (c, val);
298 break;
299 }
300
301 default:
302 msg_Err (obj, "setting \"%s\" not supported", name);
303 goto next;
304 }
305
306 msg_Err (obj, "control \"%s\" not available", name);
307 }
308 free (buf);
309 }
310
cidcmp(const void * a,const void * b)311 static int cidcmp (const void *a, const void *b)
312 {
313 const uint32_t *id = a;
314 const vlc_v4l2_ctrl_name_t *name = b;
315
316 return (int32_t)(*id - name->cid);
317 }
318
319 /**
320 * Creates a VLC-V4L2 control structure:
321 * In particular, determines a name suitable for a VLC object variable.
322 * \param query V4L2 control query structure [IN]
323 * \return NULL on error
324 */
ControlCreate(int fd,const struct v4l2_queryctrl * query)325 static vlc_v4l2_ctrl_t *ControlCreate (int fd,
326 const struct v4l2_queryctrl *query)
327 {
328 vlc_v4l2_ctrl_t *ctrl = malloc (sizeof (*ctrl));
329 if (unlikely(ctrl == NULL))
330 return NULL;
331
332 ctrl->fd = fd;
333 ctrl->id = query->id;
334 ctrl->type = query->type;
335
336 /* Search for a well-known control */
337 const vlc_v4l2_ctrl_name_t *known;
338 known = bsearch (&query->id, controls, sizeof (controls) / sizeof (*known),
339 sizeof (*known), cidcmp);
340 if (known != NULL)
341 strcpy (ctrl->name, known->name);
342 else
343 /* Fallback to automatically-generated control name */
344 {
345 size_t i;
346 for (i = 0; query->name[i]; i++)
347 {
348 unsigned char c = query->name[i];
349 if (c == ' ' || c == ',')
350 c = '_';
351 if (c < 128)
352 c = tolower (c);
353 ctrl->name[i] = c;
354 }
355 ctrl->name[i] = '\0';
356 }
357
358 ctrl->default_value = query->default_value;
359 return ctrl;
360 }
361
362
363 #define CTRL_FLAGS_IGNORE \
364 (V4L2_CTRL_FLAG_DISABLED /* not implemented at all */ \
365 |V4L2_CTRL_FLAG_READ_ONLY /* value is constant */ \
366 |V4L2_CTRL_FLAG_VOLATILE /* value is (variable but) read-only */)
367
ControlAddInteger(vlc_object_t * obj,int fd,const struct v4l2_queryctrl * query)368 static vlc_v4l2_ctrl_t *ControlAddInteger (vlc_object_t *obj, int fd,
369 const struct v4l2_queryctrl *query)
370 {
371 msg_Dbg (obj, " integer %s (%08"PRIX32")", query->name, query->id);
372 if (query->flags & CTRL_FLAGS_IGNORE)
373 return NULL;
374
375 vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
376 if (unlikely(c == NULL))
377 return NULL;
378
379 if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND))
380 {
381 free (c);
382 return NULL;
383 }
384
385 vlc_value_t val;
386 struct v4l2_control ctrl = { .id = query->id };
387
388 if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
389 {
390 msg_Dbg (obj, " current: %3"PRId32", default: %3"PRId32,
391 ctrl.value, query->default_value);
392 val.i_int = ctrl.value;
393 var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
394 }
395 var_Change (obj, c->name, VLC_VAR_SETMINMAX,
396 &(vlc_value_t){ .i_int = query->minimum },
397 &(vlc_value_t){ .i_int = query->maximum } );
398 if (query->step != 1)
399 {
400 val.i_int = query->step;
401 var_Change (obj, c->name, VLC_VAR_SETSTEP, &val, NULL);
402 }
403 return c;
404 }
405
ControlAddBoolean(vlc_object_t * obj,int fd,const struct v4l2_queryctrl * query)406 static vlc_v4l2_ctrl_t *ControlAddBoolean (vlc_object_t *obj, int fd,
407 const struct v4l2_queryctrl *query)
408 {
409 msg_Dbg (obj, " boolean %s (%08"PRIX32")", query->name, query->id);
410 if (query->flags & CTRL_FLAGS_IGNORE)
411 return NULL;
412
413 vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
414 if (unlikely(c == NULL))
415 return NULL;
416
417 if (var_Create (obj, c->name, VLC_VAR_BOOL | VLC_VAR_ISCOMMAND))
418 {
419 free (c);
420 return NULL;
421 }
422
423 vlc_value_t val;
424 struct v4l2_control ctrl = { .id = query->id };
425
426 if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
427 {
428 msg_Dbg (obj, " current: %s, default: %s",
429 ctrl.value ? " true" : "false",
430 query->default_value ? " true" : "false");
431 val.b_bool = ctrl.value;
432 var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
433 }
434 return c;
435 }
436
ControlAddMenu(vlc_object_t * obj,int fd,const struct v4l2_queryctrl * query)437 static vlc_v4l2_ctrl_t *ControlAddMenu (vlc_object_t *obj, int fd,
438 const struct v4l2_queryctrl *query)
439 {
440 msg_Dbg (obj, " menu %s (%08"PRIX32")", query->name, query->id);
441 if (query->flags & CTRL_FLAGS_IGNORE)
442 return NULL;
443
444 vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
445 if (unlikely(c == NULL))
446 return NULL;
447
448 if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND))
449 {
450 free (c);
451 return NULL;
452 }
453
454 vlc_value_t val;
455 struct v4l2_control ctrl = { .id = query->id };
456
457 if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
458 {
459 msg_Dbg (obj, " current: %"PRId32", default: %"PRId32,
460 ctrl.value, query->default_value);
461 val.i_int = ctrl.value;
462 var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
463 }
464 var_Change (obj, c->name, VLC_VAR_SETMINMAX,
465 &(vlc_value_t){ .i_int = query->minimum },
466 &(vlc_value_t){ .i_int = query->maximum } );
467
468 /* Import menu choices */
469 for (uint_fast32_t idx = query->minimum;
470 idx <= (uint_fast32_t)query->maximum;
471 idx++)
472 {
473 struct v4l2_querymenu menu = { .id = query->id, .index = idx };
474
475 if (v4l2_ioctl (fd, VIDIOC_QUERYMENU, &menu) < 0)
476 continue;
477 msg_Dbg (obj, " choice %"PRIu32") %s", menu.index, menu.name);
478
479 vlc_value_t text;
480 val.i_int = menu.index;
481 text.psz_string = (char *)menu.name;
482 var_Change (obj, c->name, VLC_VAR_ADDCHOICE, &val, &text);
483 }
484 return c;
485 }
486
ControlAddButton(vlc_object_t * obj,int fd,const struct v4l2_queryctrl * query)487 static vlc_v4l2_ctrl_t *ControlAddButton (vlc_object_t *obj, int fd,
488 const struct v4l2_queryctrl *query)
489 {
490 msg_Dbg (obj, " button %s (%08"PRIX32")", query->name, query->id);
491 if (query->flags & CTRL_FLAGS_IGNORE)
492 return NULL;
493
494 vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
495 if (unlikely(c == NULL))
496 return NULL;
497
498 if (var_Create (obj, c->name, VLC_VAR_VOID | VLC_VAR_ISCOMMAND))
499 {
500 free (c);
501 return NULL;
502 }
503 return c;
504 }
505
ControlAddInteger64(vlc_object_t * obj,int fd,const struct v4l2_queryctrl * query)506 static vlc_v4l2_ctrl_t *ControlAddInteger64 (vlc_object_t *obj, int fd,
507 const struct v4l2_queryctrl *query)
508 {
509 msg_Dbg (obj, " 64-bits %s (%08"PRIX32")", query->name, query->id);
510 if (query->flags & CTRL_FLAGS_IGNORE)
511 return NULL;
512
513 vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
514 if (unlikely(c == NULL))
515 return NULL;
516
517 if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND))
518 {
519 free (c);
520 return NULL;
521 }
522
523 struct v4l2_ext_control ext_ctrl = { .id = c->id, .size = 0, };
524 struct v4l2_ext_controls ext_ctrls = {
525 .ctrl_class = V4L2_CTRL_ID2CLASS(c->id),
526 .count = 1,
527 .error_idx = 0,
528 .controls = &ext_ctrl,
529 };
530
531 if (v4l2_ioctl (c->fd, VIDIOC_G_EXT_CTRLS, &ext_ctrls) >= 0)
532 {
533 vlc_value_t val = { .i_int = ext_ctrl.value64 };
534
535 msg_Dbg (obj, " current: %"PRId64, val.i_int);
536 var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
537 }
538
539 return c;
540 }
541
ControlAddClass(vlc_object_t * obj,int fd,const struct v4l2_queryctrl * query)542 static vlc_v4l2_ctrl_t *ControlAddClass (vlc_object_t *obj, int fd,
543 const struct v4l2_queryctrl *query)
544 {
545 msg_Dbg (obj, "control class %s:", query->name);
546 (void) fd;
547 return NULL;
548 }
549
ControlAddString(vlc_object_t * obj,int fd,const struct v4l2_queryctrl * query)550 static vlc_v4l2_ctrl_t *ControlAddString (vlc_object_t *obj, int fd,
551 const struct v4l2_queryctrl *query)
552 {
553 msg_Dbg (obj, " string %s (%08"PRIX32")", query->name, query->id);
554 if ((query->flags & CTRL_FLAGS_IGNORE) || query->maximum > 65535)
555 return NULL;
556
557 vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
558 if (unlikely(c == NULL))
559 return NULL;
560
561 if (var_Create (obj, c->name, VLC_VAR_STRING | VLC_VAR_ISCOMMAND))
562 {
563 free (c);
564 return NULL;
565 }
566
567 /* Get current value */
568 char *buf = malloc (query->maximum + 1);
569 if (likely(buf != NULL))
570 {
571 struct v4l2_ext_control ext_ctrl = {
572 .id = c->id,
573 .size = query->maximum + 1,
574 };
575 ext_ctrl.string = buf;
576 struct v4l2_ext_controls ext_ctrls = {
577 .ctrl_class = V4L2_CTRL_ID2CLASS(c->id),
578 .count = 1,
579 .error_idx = 0,
580 .controls = &ext_ctrl,
581 };
582
583 if (v4l2_ioctl (c->fd, VIDIOC_G_EXT_CTRLS, &ext_ctrls) >= 0)
584 {
585 vlc_value_t val = { .psz_string = buf };
586
587 msg_Dbg (obj, " current: \"%s\"", buf);
588 var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
589 }
590 free (buf);
591 }
592
593 return c;
594 }
595
ControlAddBitMask(vlc_object_t * obj,int fd,const struct v4l2_queryctrl * query)596 static vlc_v4l2_ctrl_t *ControlAddBitMask (vlc_object_t *obj, int fd,
597 const struct v4l2_queryctrl *query)
598 {
599 msg_Dbg (obj, " bit mask %s (%08"PRIX32")", query->name, query->id);
600 if (query->flags & CTRL_FLAGS_IGNORE)
601 return NULL;
602
603 vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
604 if (unlikely(c == NULL))
605 return NULL;
606
607 if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND))
608 {
609 free (c);
610 return NULL;
611 }
612
613 vlc_value_t val;
614 struct v4l2_control ctrl = { .id = query->id };
615
616 if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
617 {
618 msg_Dbg (obj, " current: 0x%08"PRIX32", default: 0x%08"PRIX32,
619 ctrl.value, query->default_value);
620 val.i_int = ctrl.value;
621 var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
622 }
623 var_Change (obj, c->name, VLC_VAR_SETMINMAX,
624 &(vlc_value_t){ .i_int = 0 },
625 &(vlc_value_t){ .i_int = (uint32_t)query->maximum } );
626 return c;
627 }
628
ControlAddIntMenu(vlc_object_t * obj,int fd,const struct v4l2_queryctrl * query)629 static vlc_v4l2_ctrl_t *ControlAddIntMenu (vlc_object_t *obj, int fd,
630 const struct v4l2_queryctrl *query)
631 {
632 msg_Dbg (obj, " int menu %s (%08"PRIX32")", query->name, query->id);
633 if (query->flags & CTRL_FLAGS_IGNORE)
634 return NULL;
635
636 vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
637 if (unlikely(c == NULL))
638 return NULL;
639
640 if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND))
641 {
642 free (c);
643 return NULL;
644 }
645
646 vlc_value_t val;
647 struct v4l2_control ctrl = { .id = query->id };
648
649 if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
650 {
651 msg_Dbg (obj, " current: %"PRId32", default: %"PRId32,
652 ctrl.value, query->default_value);
653 val.i_int = ctrl.value;
654 var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
655 }
656 var_Change (obj, c->name, VLC_VAR_SETMINMAX,
657 &(vlc_value_t){ .i_int = query->minimum },
658 &(vlc_value_t){ .i_int = query->maximum } );
659
660 /* Import menu choices */
661 for (uint_fast32_t idx = query->minimum;
662 idx <= (uint_fast32_t)query->maximum;
663 idx++)
664 {
665 struct v4l2_querymenu menu = { .id = query->id, .index = idx };
666 char name[sizeof ("-9223372036854775808")];
667
668 if (v4l2_ioctl (fd, VIDIOC_QUERYMENU, &menu) < 0)
669 continue;
670 msg_Dbg (obj, " choice %"PRIu32") %"PRId64, menu.index,
671 (uint64_t)menu.value);
672
673 vlc_value_t text;
674 val.i_int = menu.index;
675 sprintf (name, "%"PRId64, (int64_t)menu.value);
676 text.psz_string = name;
677 var_Change (obj, c->name, VLC_VAR_ADDCHOICE, &val, &text);
678 }
679 return c;
680 }
681
ControlAddUnknown(vlc_object_t * obj,int fd,const struct v4l2_queryctrl * query)682 static vlc_v4l2_ctrl_t *ControlAddUnknown (vlc_object_t *obj, int fd,
683 const struct v4l2_queryctrl *query)
684 {
685 msg_Dbg (obj, " unknown %s (%08"PRIX32")", query->name, query->id);
686 msg_Warn (obj, " unknown control type %u", (unsigned)query->type);
687 (void) fd;
688 return NULL;
689 }
690
691 typedef vlc_v4l2_ctrl_t *(*ctrl_type_cb) (vlc_object_t *, int,
692 const struct v4l2_queryctrl *);
693
694 /**
695 * Lists all user-class v4l2 controls, sets them to the user specified
696 * value and create the relevant variables to enable run-time changes.
697 */
ControlsInit(vlc_object_t * obj,int fd)698 vlc_v4l2_ctrl_t *ControlsInit (vlc_object_t *obj, int fd)
699 {
700 /* A list of controls that can be modified at run-time is stored in the
701 * "controls" variable. The V4L2 controls dialog can be built from this. */
702 var_Create (obj, "controls", VLC_VAR_INTEGER);
703
704 static const ctrl_type_cb handlers[] =
705 {
706 [V4L2_CTRL_TYPE_INTEGER] = ControlAddInteger,
707 [V4L2_CTRL_TYPE_BOOLEAN] = ControlAddBoolean,
708 [V4L2_CTRL_TYPE_MENU] = ControlAddMenu,
709 [V4L2_CTRL_TYPE_BUTTON] = ControlAddButton,
710 [V4L2_CTRL_TYPE_INTEGER64] = ControlAddInteger64,
711 [V4L2_CTRL_TYPE_CTRL_CLASS] = ControlAddClass,
712 [V4L2_CTRL_TYPE_STRING] = ControlAddString,
713 [V4L2_CTRL_TYPE_BITMASK] = ControlAddBitMask,
714 [V4L2_CTRL_TYPE_INTEGER_MENU] = ControlAddIntMenu,
715 };
716
717 vlc_v4l2_ctrl_t *list = NULL;
718 struct v4l2_queryctrl query;
719
720 query.id = V4L2_CTRL_FLAG_NEXT_CTRL;
721 while (v4l2_ioctl (fd, VIDIOC_QUERYCTRL, &query) >= 0)
722 {
723 ctrl_type_cb handler = NULL;
724 if (query.type < (sizeof (handlers) / sizeof (handlers[0])))
725 handler = handlers[query.type];
726 if (handler == NULL)
727 handler = ControlAddUnknown;
728
729 vlc_v4l2_ctrl_t *c = handler (obj, fd, &query);
730 if (c != NULL)
731 {
732 vlc_value_t val, text;
733
734 var_AddCallback (obj, c->name, ControlSetCallback, c);
735 text.psz_string = (char *)query.name;
736 var_Change (obj, c->name, VLC_VAR_SETTEXT, &text, NULL);
737 val.i_int = query.id;
738 text.psz_string = (char *)c->name;
739 var_Change (obj, "controls", VLC_VAR_ADDCHOICE, &val, &text);
740
741 c->next = list;
742 list = c;
743 }
744 query.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
745 }
746
747 /* Set well-known controls from VLC configuration */
748 for (vlc_v4l2_ctrl_t *ctrl = list; ctrl != NULL; ctrl = ctrl->next)
749 {
750 if (!CTRL_CID_KNOWN (ctrl->id))
751 continue;
752
753 char varname[sizeof (CFG_PREFIX) + sizeof (ctrl->name) - 1];
754 sprintf (varname, CFG_PREFIX"%s", ctrl->name);
755
756 int64_t val = var_InheritInteger (obj, varname);
757 if (val == -1)
758 continue; /* the VLC default value: "do not modify" */
759 ControlSet (ctrl, val); /* NOTE: all known are integers or booleans */
760 }
761
762 /* Set any control from the VLC configuration control string */
763 ControlsSetFromString (obj, list);
764
765 /* Add a control to reset all controls to their default values */
766 {
767 vlc_value_t val, text;
768
769 var_Create (obj, "reset", VLC_VAR_VOID | VLC_VAR_ISCOMMAND);
770 val.psz_string = _("Reset defaults");
771 var_Change (obj, "reset", VLC_VAR_SETTEXT, &val, NULL);
772 val.i_int = -1;
773
774 text.psz_string = (char *)"reset";
775 var_Change (obj, "controls", VLC_VAR_ADDCHOICE, &val, &text);
776 var_AddCallback (obj, "reset", ControlsResetCallback, list);
777 }
778 if (var_InheritBool (obj, CFG_PREFIX"controls-reset"))
779 ControlsReset (obj, list);
780
781 return list;
782 }
783
ControlsDeinit(vlc_object_t * obj,vlc_v4l2_ctrl_t * list)784 void ControlsDeinit (vlc_object_t *obj, vlc_v4l2_ctrl_t *list)
785 {
786 var_DelCallback (obj, "reset", ControlsResetCallback, list);
787 var_Destroy (obj, "reset");
788
789 while (list != NULL)
790 {
791 vlc_v4l2_ctrl_t *next = list->next;
792
793 var_DelCallback (obj, list->name, ControlSetCallback, list);
794 var_Destroy (obj, list->name);
795 free (list);
796 list = next;
797 }
798
799 var_Destroy (obj, "controls");
800 }
801