MythTV master
v4l2util.cpp
Go to the documentation of this file.
1#include "v4l2util.h"
3
4#include <sys/ioctl.h>
5#include <sys/types.h>
6#include <sys/stat.h>
7#include <algorithm>
8#include <climits>
9#include <fcntl.h>
10#include <unistd.h>
11
12#include <array>
13
14#include <QRegularExpression>
15
16#define LOC QString("V4L2(%1): ").arg(m_deviceName)
17
18V4L2util::V4L2util(const QString& dev_name)
19{
20 Open(dev_name);
21}
22
23V4L2util::V4L2util(const QString& dev_name, const QString& vbi_dev_name)
24 : m_fd(0)
25{
26 Open(dev_name, vbi_dev_name);
27}
28
30{
31 Close();
32}
33
34bool V4L2util::Open(const QString& dev_name, const QString& vbi_dev_name)
35{
36 if (m_fd >= 0 && dev_name == m_deviceName)
37 return true;
38
39 Close();
40
41 m_fd = open(dev_name.toLatin1().constData(), O_RDWR);
42 if (m_fd < 0)
43 {
44 LOG(VB_CHANNEL, LOG_INFO, LOC +
45 QString("Could not open '%1': ").arg(dev_name) + ENO);
46 return false;
47 }
48 m_deviceName = dev_name;
49
50 struct v4l2_query_ext_ctrl qc {};
51 qc.id = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND;
52 m_haveQueryExtCtrl = (ioctl(m_fd, VIDIOC_QUERY_EXT_CTRL, &qc) == 0);
53
54 m_cardName.clear();
55 m_driverName.clear();
56 m_version = 0;
58
59 struct v4l2_capability capability {};
60 if (ioctl(m_fd, VIDIOC_QUERYCAP, &capability) >= 0)
61 {
62 m_cardName = QString::fromLatin1((const char*)capability.card);
63 m_driverName = QString::fromLatin1((const char*)capability.driver);
64 m_version = capability.version;
65 m_capabilities = capability.capabilities;
66 }
67 else
68 {
69 Close();
70 return false;
71 }
72
73 static const QRegularExpression kDigitsRE { R"(\[[0-9]\]$)" };
74 if (!m_driverName.isEmpty())
75 m_driverName.remove( kDigitsRE );
76
77 OpenVBI(vbi_dev_name);
78
79 LOG(VB_CHANNEL, LOG_INFO, LOC + "Opened");
80 return true;
81}
82
84{
85 if (m_fd >= 0)
86 {
87 close(m_fd);
88 LOG(VB_CHANNEL, LOG_INFO, LOC + "Closed");
89 }
90 m_fd = -1;
91 m_options.clear();
92}
93
94bool V4L2util::HasStreaming(void) const
95{
96 if (m_capabilities ^ V4L2_CAP_STREAMING)
97 return false;
98
99 struct v4l2_requestbuffers reqbuf {};
100
101 if (-1 == ioctl (m_fd, VIDIOC_REQBUFS, &reqbuf))
102 {
103 if (errno == EINVAL)
104 {
105 LOG(VB_CHANNEL, LOG_INFO, LOC +
106 "Video capturing or mmap-streaming is not supported");
107 }
108 else
109 {
110 LOG(VB_CHANNEL, LOG_WARNING, LOC + "VIDIOC_REQBUFS" + ENO);
111 }
112 return false;
113 }
114
115 return true;
116}
117
119{
120 return (m_capabilities & V4L2_CAP_SLICED_VBI_CAPTURE) != 0U;
121}
122
123void V4L2util::bitmask_toString(QString& result, uint32_t flags,
124 uint32_t mask, const QString& desc)
125{
126 if (flags& mask)
127 {
128 if (!result.isEmpty())
129 result += '|';
130 result += desc;
131 }
132}
133
134QString V4L2util::ctrlflags_toString(uint32_t flags)
135{
136 QString result;
137
138 bitmask_toString(result, flags, V4L2_CTRL_FLAG_DISABLED,
139 "disabled");
140 bitmask_toString(result, flags, V4L2_CTRL_FLAG_GRABBED,
141 "grabbed");
142 bitmask_toString(result, flags, V4L2_CTRL_FLAG_READ_ONLY,
143 "read-only");
144 bitmask_toString(result, flags, V4L2_CTRL_FLAG_UPDATE,
145 "update");
146 bitmask_toString(result, flags, V4L2_CTRL_FLAG_INACTIVE,
147 "inactive");
148 bitmask_toString(result, flags, V4L2_CTRL_FLAG_SLIDER,
149 "slider");
150 bitmask_toString(result, flags, V4L2_CTRL_FLAG_WRITE_ONLY,
151 "write-only");
152 bitmask_toString(result, flags, V4L2_CTRL_FLAG_VOLATILE,
153 "volatile");
154 bitmask_toString(result, flags, V4L2_CTRL_FLAG_HAS_PAYLOAD,
155 "has-payload");
156 bitmask_toString(result, flags, V4L2_CTRL_FLAG_EXECUTE_ON_WRITE,
157 "execute-on-write");
158
159 return result;
160}
161
163{
164 switch (type)
165 {
166 case V4L2_CTRL_TYPE_INTEGER:
167 return "int";
168 case V4L2_CTRL_TYPE_INTEGER64:
169 return "int64";
170 case V4L2_CTRL_TYPE_STRING:
171 return "str";
172 case V4L2_CTRL_TYPE_BOOLEAN:
173 return "bool";
174 case V4L2_CTRL_TYPE_MENU:
175 return "menu";
176 case V4L2_CTRL_TYPE_INTEGER_MENU:
177 return "intmenu";
178 case V4L2_CTRL_TYPE_BUTTON:
179 return "button";
180 case V4L2_CTRL_TYPE_BITMASK:
181 return "bitmask";
182 case V4L2_CTRL_TYPE_U8:
183 return "u8";
184 case V4L2_CTRL_TYPE_U16:
185 return "u16";
186 case V4L2_CTRL_TYPE_U32:
187 return "u32";
188 default:
189 return "unknown";
190 }
191}
192
193void V4L2util::log_qctrl(struct v4l2_queryctrl& queryctrl,
194 DriverOption& drv_opt, QString& msg)
195{
196 struct v4l2_querymenu qmenu {};
197 QString nameStr((char *)queryctrl.name);
198
199 qmenu.id = queryctrl.id;
200
201 // Replace non-printable with _
202 static const QRegularExpression kNonPrintableRE { "[^a-zA-Z\\d\\s]" };
203 nameStr.replace(kNonPrintableRE, "_");
204
205 drv_opt.m_name = nameStr;
206 drv_opt.m_minimum = queryctrl.minimum;
207 drv_opt.m_maximum = queryctrl.maximum;
208 drv_opt.m_step = queryctrl.step;
209 drv_opt.m_defaultValue = queryctrl.default_value;;
210
211 if (nameStr == "Stream Type")
213 else if (nameStr == "Video Encoding")
215 else if (nameStr == "Video Aspect")
217 else if (nameStr == "Video B Frames")
219 else if (nameStr == "Video GOP Size")
221 else if (nameStr == "Video Bitrate Mode")
223 else if (nameStr == "Video Bitrate")
225 else if (nameStr == "Video Peak Bitrate")
227 else if (nameStr == "Audio Encoding")
229 else if (nameStr == "Audio Bitrate Mode")
231 else if (nameStr == "Audio Bitrate")
233 else if (nameStr == "Brightness")
235 else if (nameStr == "Contrast")
237 else if (nameStr == "Saturation")
239 else if (nameStr == "Hue")
241 else if (nameStr == "Sharpness")
243 else if (nameStr == "Volume")
245 else
247
248 switch (queryctrl.type)
249 {
250 case V4L2_CTRL_TYPE_INTEGER:
251 case V4L2_CTRL_TYPE_INTEGER64:
252 case V4L2_CTRL_TYPE_U8:
253 case V4L2_CTRL_TYPE_U16:
254 case V4L2_CTRL_TYPE_U32:
255 msg = QString("%1 : min=%2 max=%3 step=%4 default=%5")
256 .arg(QString("%1 (%2)").arg(nameStr, queryctrl_toString(queryctrl.type)), 31, QChar(' '))
257 .arg(queryctrl.minimum)
258 .arg(queryctrl.maximum)
259 .arg(queryctrl.step)
260 .arg(queryctrl.default_value);
262 break;
263 case V4L2_CTRL_TYPE_STRING:
264 msg = QString("%1 : min=%2 max=%3 step=%4")
265 .arg(QString("%1 (%2)").arg(nameStr, queryctrl_toString(queryctrl.type)), 31, QChar(' '))
266 .arg(queryctrl.minimum)
267 .arg(queryctrl.maximum)
268 .arg(queryctrl.step);
270 break;
271 case V4L2_CTRL_TYPE_BOOLEAN:
272 msg = QString("%1 : default=%2")
273 .arg(QString("%1 (%2)").arg(nameStr, queryctrl_toString(queryctrl.type)), 31, QChar(' '))
274 .arg(queryctrl.default_value);
276 break;
277 case V4L2_CTRL_TYPE_MENU:
278 case V4L2_CTRL_TYPE_INTEGER_MENU:
279 {
280 msg = QString("%1 : min=%3 max=%4 default=%5")
281 .arg(QString("%1 (%2)").arg(nameStr, queryctrl_toString(queryctrl.type)), 31, QChar(' '))
282 .arg(queryctrl.minimum)
283 .arg(queryctrl.maximum)
284 .arg(queryctrl.default_value);
285#if 0
286 struct v4l2_querymenu querymenu = { 0, };
287 memset (&querymenu, 0, sizeof (querymenu));
288 querymenu.id = queryctrl.id;
289
290 for (querymenu.index = queryctrl.minimum;
291 static_cast<int>(querymenu.index) <= queryctrl.maximum;
292 ++querymenu.index)
293 {
294 drv_opt.menu.clear();
295 if (0 == ioctl(m_fd, VIDIOC_QUERYMENU, &querymenu))
296 {
297 msg += QString(" menu>%1").arg((char *)querymenu.name);
298 drv_opt.menu[querymenu.index] =
299 QString((char *)querymenu.name);
300 }
301 }
302#endif
303 drv_opt.m_type = DriverOption::MENU;
304 break;
305 }
306 case V4L2_CTRL_TYPE_BUTTON:
307 msg = QString("%1 :")
308 .arg(QString("%1 (%2)").arg(nameStr, queryctrl_toString(queryctrl.type)), 31, QChar(' '));
310 break;
311 case V4L2_CTRL_TYPE_BITMASK:
312 msg = QString("%1 : max=0x%2 default=0x%3")
313 .arg(QString("%1 (%2)").arg(nameStr, queryctrl_toString(queryctrl.type)), 31, QChar(' '))
314 .arg(queryctrl.maximum, 8, 16, QChar(' '))
315 .arg(queryctrl.default_value, 8, 16, QChar(' '));
317 break;
318
319 default:
320 msg = QString("%1 : type=%2")
321 .arg(QString("%1 (%2)").arg(nameStr, queryctrl_toString(queryctrl.type)), 31, QChar(' '))
322 .arg(queryctrl.type);
324 break;
325 }
326
327 if (queryctrl.flags)
328 msg += QString(" flags=%1").arg(ctrlflags_toString(queryctrl.flags));
329
330 if (queryctrl.type == V4L2_CTRL_TYPE_MENU ||
331 queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU)
332 {
333 for (int idx = queryctrl.minimum; idx <= queryctrl.maximum; ++idx)
334 {
335 qmenu.index = idx;
336 if (ioctl(m_fd, VIDIOC_QUERYMENU, &qmenu))
337 continue;
338
339 drv_opt.m_menu[idx] = QString((char *)qmenu.name);
340 if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
341 msg += QString("\t\t%1: %2").arg(idx).arg((char *)qmenu.name);
342 else
343 {
344 msg += QString("\t\t%1: %2 (0x%3)")
345 .arg(idx).arg(qmenu.value)
346 .arg(qmenu.value, 0, 16, QChar('0'));
347 }
348 }
349 }
350
351 LOG(VB_CHANNEL, LOG_INFO, LOC + msg);
352}
353
354bool V4L2util::log_control(struct v4l2_queryctrl& qctrl, DriverOption& drv_opt,
355 QString& msg)
356{
357 struct v4l2_control ctrl {};
358 struct v4l2_ext_control ext_ctrl {};
359 struct v4l2_ext_controls ctrls {};
360
361 if (qctrl.flags& V4L2_CTRL_FLAG_DISABLED)
362 {
363 msg += QString("'%1' Disabled").arg((char *)qctrl.name);
364 return true;
365 }
366
367 if (qctrl.type == V4L2_CTRL_TYPE_CTRL_CLASS)
368 {
369 msg += QString("'%1' V4L2_CTRL_TYPE_CTRL_CLASS").arg((char *)qctrl.name);
370 return true;
371 }
372
373 ext_ctrl.id = qctrl.id;
374 if ((qctrl.flags& V4L2_CTRL_FLAG_WRITE_ONLY) ||
375 qctrl.type == V4L2_CTRL_TYPE_BUTTON)
376 {
377 log_qctrl(qctrl, drv_opt, msg);
378 return true;
379 }
380
381 if (qctrl.type >= V4L2_CTRL_COMPOUND_TYPES)
382 {
383 log_qctrl(qctrl, drv_opt, msg);
384 return true;
385 }
386
387 ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(qctrl.id);
388 ctrls.count = 1;
389 ctrls.controls = &ext_ctrl;
390 if (qctrl.type == V4L2_CTRL_TYPE_INTEGER64 ||
391 qctrl.type == V4L2_CTRL_TYPE_STRING ||
392 (V4L2_CTRL_ID2CLASS(qctrl.id) != V4L2_CTRL_CLASS_USER &&
393 qctrl.id < V4L2_CID_PRIVATE_BASE))
394 {
395 if (qctrl.type == V4L2_CTRL_TYPE_STRING)
396 {
397 ext_ctrl.size = qctrl.maximum + 1;
398 ext_ctrl.string = (char *)malloc(ext_ctrl.size);
399 ext_ctrl.string[0] = 0;
400 }
401 if (ioctl(m_fd, VIDIOC_G_EXT_CTRLS, &ctrls))
402 {
403 LOG(VB_CHANNEL, LOG_WARNING, LOC +
404 QString("Failed to get ext_ctr %1: ")
405 .arg((char *)qctrl.name) + ENO);
406 return false;
407 }
408 }
409 else {
410 ctrl.id = qctrl.id;
411 if (ioctl(m_fd, VIDIOC_G_CTRL, &ctrl))
412 {
413 LOG(VB_CHANNEL, LOG_WARNING, LOC +
414 QString("Failed to get ctrl %1: ")
415 .arg((char *)qctrl.name) + ENO);
416 return false;
417 }
418 ext_ctrl.value = ctrl.value;
419 }
420 log_qctrl(qctrl, drv_opt, msg);
421
422 if (qctrl.type == V4L2_CTRL_TYPE_STRING)
423 free(ext_ctrl.string);
424 return true;
425}
426
427// Some drivers don't set 'default' options, so make some assumptions
429{
431 {
432 DriverOption drv_opt;
434 drv_opt.m_name = "Video Encoding";
435 drv_opt.m_minimum = drv_opt.m_maximum = drv_opt.m_defaultValue =
436 V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
437 drv_opt.m_menu[drv_opt.m_defaultValue] = "MPEG-2 Video";
438 options[drv_opt.m_category] = drv_opt;
439 }
440
442 {
443 DriverOption drv_opt;
444
445 // V4L2_CID_MPEG_AUDIO_ENCODING
447 drv_opt.m_name = "Audio Encoding";
448 drv_opt.m_minimum = drv_opt.m_maximum = drv_opt.m_defaultValue =
449 V4L2_MPEG_AUDIO_ENCODING_LAYER_2;
450 drv_opt.m_menu[drv_opt.m_defaultValue] = "MPEG-1/2 Layer II encoding";
451 options[drv_opt.m_category] = drv_opt;
452
454 drv_opt.m_name = "Audio Bitrate";
455 drv_opt.m_minimum = drv_opt.m_maximum = drv_opt.m_defaultValue =
456 V4L2_MPEG_AUDIO_ENCODING_LAYER_2;
457 drv_opt.m_menu[drv_opt.m_defaultValue] = "MPEG-1/2 Layer II encoding";
458 options[drv_opt.m_category] = drv_opt;
459
460 // V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ
462 drv_opt.m_name = "MPEG Audio sampling frequency";
463 drv_opt.m_minimum = drv_opt.m_maximum = drv_opt.m_defaultValue =
464 V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000;
465 drv_opt.m_menu[drv_opt.m_defaultValue] = "48 kHz";
466 options[drv_opt.m_category] = drv_opt;
467
468 // VIDIOC_S_TUNER
470 drv_opt.m_name = "Tuner Audio Modes";
471 drv_opt.m_minimum = drv_opt.m_maximum = drv_opt.m_defaultValue =
472 V4L2_TUNER_MODE_STEREO;
473 drv_opt.m_menu[drv_opt.m_defaultValue] = "Play stereo audio";
474 options[drv_opt.m_category] = drv_opt;
475 }
476
477 DriverOption::Options::iterator Iopt = options.begin();
478 for ( ; Iopt != options.end(); ++Iopt)
479 {
480 // If the driver provides a menu of options, use it to set limits
481 if (!(*Iopt).m_menu.isEmpty())
482 {
483 int minimum = INT_MAX;
484 int maximum = -1;
485
486 DriverOption::menu_t::iterator Imenu = (*Iopt).m_menu.begin();
487 for ( ; Imenu != (*Iopt).m_menu.end(); ++Imenu)
488 {
489 minimum = std::min(Imenu.key(), minimum);
490 maximum = std::max(Imenu.key(), maximum);
491 }
492 if ((*Iopt).m_minimum != minimum)
493 {
494 LOG(VB_CHANNEL, LOG_INFO, LOC +
495 QString("%1 menu options overrides minimum from %2 to %3")
496 .arg((*Iopt).m_name).arg((*Iopt).m_minimum).arg(minimum));
497 (*Iopt).m_minimum = minimum;
498 }
499 if ((*Iopt).m_maximum != maximum)
500 {
501 LOG(VB_CHANNEL, LOG_INFO, LOC +
502 QString("%1 menu options overrides maximum from %2 to %3")
503 .arg((*Iopt).m_name).arg((*Iopt).m_maximum).arg(maximum));
504 (*Iopt).m_maximum = maximum;
505 }
506 }
507 }
508}
509
511{
512 struct v4l2_fmtdesc vid_fmtdesc {};
513 const std::array<const QString,2> flags {"uncompressed", "compressed"};
514
515 vid_fmtdesc.index = 0;
516 vid_fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
517 while(ioctl(m_fd, VIDIOC_ENUM_FMT, &vid_fmtdesc) == 0)
518 {
519 formats << QString("%1 (%2)").arg((char *)vid_fmtdesc.description,
520 flags[vid_fmtdesc.flags]);
521
522 /* Convert the pixelformat attributes from FourCC into 'human readab
523 fprintf(stdout, " pixelformat :%c%c%c%c\\n",
524 vid_fmtdesc.pixelformat& 0xFF, (vid_fmtdesc.pixelformat >>
525 (vid_fmtdesc.pixelformat >> 16)& 0xFF, (vid_fmtdesc.pixelfo
526 */
527
528 vid_fmtdesc.index++;
529 }
530
531 LOG(VB_CHANNEL, LOG_DEBUG, LOC + QString("GetFormats: %1")
532 .arg(formats.join(",")));
533
534 return true;
535}
536
538{
539 LOG(VB_CHANNEL, LOG_INFO, LOC + "Options");
540
541 if (!m_options.isEmpty())
542 {
544 return true;
545 }
546
547 struct v4l2_queryctrl qctrl {};
548 qctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
549 while (0 == ioctl (m_fd, VIDIOC_QUERYCTRL, &qctrl))
550 {
551 QString msg;
552 DriverOption drv_opt;
553
554 log_control(qctrl, drv_opt, msg);
555 m_options[drv_opt.m_category] = drv_opt;
556
557 qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
558 }
559
562 return true;
563}
564
566{
567 if (m_options.isEmpty())
569
570 if (!m_options.contains(cat))
571 {
572 LOG(VB_CHANNEL, LOG_WARNING, LOC +
573 QString("Driver does not support option."));
574 return -1;
575 }
576
577 DriverOption drv_opt = m_options.value(cat);
578 DriverOption::menu_t::iterator Imenu = drv_opt.m_menu.begin();
579 for ( ; Imenu != drv_opt.m_menu.end(); ++Imenu)
580 {
581 if ((*Imenu) == desc)
582 {
583 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("GetOptionValue '%1' = %2")
584 .arg(desc).arg(Imenu.key()));
585 return Imenu.key();
586 }
587 }
588
589 LOG(VB_CHANNEL, LOG_WARNING, LOC +
590 QString("'%1' not found in driver options menu.").arg(desc));
591 return -1;
592}
593
594bool V4L2util::GetVideoStandard(QString& name) const
595{
596 v4l2_std_id std_id = 0;
597 struct v4l2_standard standard {};
598
599 if (-1 == ioctl (m_fd, VIDIOC_G_STD, &std_id))
600 {
601 /* Note when VIDIOC_ENUMSTD always returns EINVAL this
602 is no video device or it falls under the USB exception,
603 and VIDIOC_G_STD returning EINVAL is no error. */
604 LOG(VB_CHANNEL, LOG_WARNING, LOC +
605 "GetVideoStandard: Failed to detect signal." + ENO);
606 return false;
607 }
608
609 standard.index = 0;
610
611 while (0 == ioctl (m_fd, VIDIOC_ENUMSTD, &standard))
612 {
613 if (standard.id & std_id)
614 {
615 name = (char *)standard.name;
616 return true;
617 }
618
619 ++standard.index;
620 }
621
622 /* EINVAL indicates the end of the enumeration, which cannot be
623 empty unless this device falls under the USB exception. */
624 if (errno == EINVAL || standard.index == 0)
625 {
626 LOG(VB_CHANNEL, LOG_WARNING, LOC +
627 "GetVideoStandard: Failed to find signal." + ENO);
628 }
629
630 return false;
631}
632
634{
635 return -1; // Does not work
636
637 struct v4l2_tuner tuner {};
638
639 if (ioctl(m_fd, VIDIOC_G_TUNER, &tuner, 0) != 0)
640 {
641 LOG(VB_GENERAL, LOG_ERR, "GetSignalStrength() : "
642 "Failed to probe signal (v4l2)" + ENO);
643 return -1;
644 }
645
646 tuner.signal /= 655.35; // Set to 0-100 range
647
648 LOG(VB_RECORD, LOG_INFO, LOC + QString("GetSignalStrength() : "
649 "(%1\%)")
650 .arg(tuner.signal));
651 return tuner.signal;
652}
653
654bool V4L2util::GetResolution(int& width, int& height) const
655{
656 struct v4l2_format vfmt {};
657
658 vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
659 if (ioctl(m_fd, VIDIOC_G_FMT, &vfmt) != 0)
660 {
661 LOG(VB_CHANNEL, LOG_WARNING, LOC +
662 "Failed to determine resolution: " + ENO);
663 width = height = -1;
664 return false;
665 }
666
667 width = vfmt.fmt.pix.width;
668 height = vfmt.fmt.pix.height;
669 LOG(VB_CHANNEL, LOG_INFO, LOC +
670 QString("Resolution: %1x%2").arg(width).arg(height));
671 return true;
672}
673
674uint32_t V4L2util::GetCapabilities(void) const
675{
676 return m_capabilities;
677}
678
679QString V4L2util::GetDeviceName(void) const
680{
681 return m_deviceName;
682}
683
684QString V4L2util::GetDriverName(void) const
685{
686 return m_driverName;
687}
688
689bool V4L2util::HasTuner(void) const
690{
691 return (m_capabilities & V4L2_CAP_TUNER) != 0U;
692}
693
695{
696 return (m_capabilities & V4L2_CAP_AUDIO) != 0U;
697}
698
699bool V4L2util::IsEncoder(void) const
700{
701 struct v4l2_queryctrl qctrl {};
702
703 qctrl.id = V4L2_CTRL_CLASS_MPEG | V4L2_CTRL_FLAG_NEXT_CTRL;
704 return (0 == ioctl (m_fd, VIDIOC_QUERYCTRL, &qctrl) &&
705 V4L2_CTRL_ID2CLASS (qctrl.id) == V4L2_CTRL_CLASS_MPEG);
706}
707
709{
710 // I have not been able to come up with a way of querying the
711 // driver to answer this question.
712
713 return m_driverName != "hdpvr";
714}
715
716int V4L2util::GetExtControl(int request, const QString& ctrl_desc) const
717{
718 struct v4l2_ext_control ctrl {};
719 struct v4l2_ext_controls ctrls {};
720
721 ctrl.id = request;
722
723 ctrls.count = 1;
724 ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
725 ctrls.controls = &ctrl;
726
727 if (ioctl(m_fd, VIDIOC_G_EXT_CTRLS, &ctrls) != 0)
728 {
729 LOG(VB_GENERAL, LOG_ERR, LOC +
730 QString("Failed to retrieve current %1 value.")
731 .arg(ctrl_desc) + ENO);
732 return -1;
733 }
734
735 return ctrls.controls->value;
736}
737
738
739bool V4L2util::SetExtControl(int request, int value, const QString& ctrl_desc,
740 const QString& value_desc)
741{
742 int current_value = GetExtControl(request, ctrl_desc);
743
744 if (current_value < 0)
745 return false;
746 if (current_value == value)
747 {
748 LOG(VB_CHANNEL, LOG_INFO, LOC +
749 QString("%1 value is already %2 (%3).")
750 .arg(ctrl_desc, value_desc, QString::number(value)));
751 return true;
752 }
753
754 struct v4l2_ext_control ctrl {};
755 struct v4l2_ext_controls ctrls {};
756
757 ctrl.id = request;
758 ctrl.value = value;
759
760 ctrls.count = 1;
761 ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
762 ctrls.controls = &ctrl;
763
764 if (ioctl(m_fd, VIDIOC_S_EXT_CTRLS, &ctrls) < 0)
765 {
766 LOG(VB_GENERAL, LOG_ERR, LOC +
767 QString("Failed to set %1 value to %2 (%3).")
768 .arg(ctrl_desc, value_desc, QString::number(value)) + ENO);
769 return false;
770 }
771
772 LOG(VB_CHANNEL, LOG_INFO, LOC +
773 QString("%1 value set to %2 (%3).")
774 .arg(ctrl_desc, value_desc, QString::number(value)));
775
776 return true;
777}
778
779QString V4L2util::StreamTypeDesc(int value)
780{
781 switch (value)
782 {
783 case V4L2_MPEG_STREAM_TYPE_MPEG2_PS:
784 return "MPEG-2 program stream";
785 case V4L2_MPEG_STREAM_TYPE_MPEG2_TS:
786 return "MPEG-2 transport stream";
787 case V4L2_MPEG_STREAM_TYPE_MPEG1_SS:
788 return "MPEG-1 system stream";
789 case V4L2_MPEG_STREAM_TYPE_MPEG2_DVD:
790 return "MPEG-2 DVD-compatible stream";
791 case V4L2_MPEG_STREAM_TYPE_MPEG1_VCD:
792 return "MPEG-1 VCD-compatible stream";
793 case V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD:
794 return "MPEG-2 SVCD-compatible stream";
795 }
796 return "Unknown";
797}
798
800{
801 int type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS;
802
803 if (DriverName().startsWith("saa7164"))
804 {
805 // The saa7164 driver reports that it can do TS, but it doesn't work!
806 type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS;
807 }
808 else
809 {
810 type = GetExtControl(V4L2_CID_MPEG_STREAM_TYPE, "Stream Type");
811 }
812
813 LOG(VB_CHANNEL, LOG_INFO, LOC +
814 QString("MPEG Stream Type is currently set to %1 (%2)")
815 .arg(StreamTypeDesc(type)).arg(type));
816
817 return type;
818}
819
821{
822 if (DriverName().startsWith("saa7164") ||
823 DriverName().startsWith("ivtv"))
824 {
825 // The saa7164 driver reports that it can do TS, but it doesn't work!
826 value = V4L2_MPEG_STREAM_TYPE_MPEG2_PS;
827 }
828
829 return SetExtControl(V4L2_CID_MPEG_STREAM_TYPE, value,
830 "MPEG Stream type", StreamTypeDesc(value));
831}
832
833// Video controls
835{
836 QString desc;
837 switch (value)
838 {
839 case V4L2_MPEG_VIDEO_ASPECT_1x1:
840 desc = "Square";
841 break;
842 case V4L2_MPEG_VIDEO_ASPECT_4x3:
843 desc = "4x3";
844 break;
845 case V4L2_MPEG_VIDEO_ASPECT_16x9:
846 desc = "16x9";
847 break;
848 case V4L2_MPEG_VIDEO_ASPECT_221x100:
849 desc = "221x100";
850 break;
851 default:
852 desc = "Unknown";
853 }
854
855 return SetExtControl(V4L2_CID_MPEG_VIDEO_ASPECT, value,
856 "Video Aspect ratio", desc);
857}
858
860{
861 QString desc;
862 switch (value)
863 {
864 case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR:
865 desc = "VBR";
866 break;
867 case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR:
868 desc = "CBR";
869 break;
870 }
871
872 return SetExtControl(V4L2_CID_MPEG_VIDEO_BITRATE_MODE, value,
873 "Video Bitrate Mode", desc);
874}
875
877{
878 QString desc = QString("%1").arg(value);
879 return SetExtControl(V4L2_CID_MPEG_VIDEO_BITRATE, value,
880 "Video Average Bitrate", desc);
881}
882
884{
885 QString desc = QString("%1").arg(value);
886 return SetExtControl(V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, value,
887 "Video Peak Bitrate", desc);
888}
889
890bool V4L2util::SetResolution(uint32_t width, uint32_t height)
891{
892 struct v4l2_format vfmt {};
893
894 vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
895
896 if (ioctl(m_fd, VIDIOC_G_FMT, &vfmt) < 0)
897 {
898 LOG(VB_GENERAL, LOG_ERR, LOC +
899 "SetResolution() -- Error getting format" + ENO);
900 return false;
901 }
902
903 if ((vfmt.fmt.pix.width == width) && (vfmt.fmt.pix.height == height))
904 {
905 LOG(VB_RECORD, LOG_INFO, LOC + QString("Resolution is already %1x%2")
906 .arg(width).arg(height));
907 return true;
908 }
909
910 vfmt.fmt.pix.width = width;
911 vfmt.fmt.pix.height = height;
912
913 if (ioctl(m_fd, VIDIOC_S_FMT, &vfmt) < 0)
914 {
915 LOG(VB_GENERAL, LOG_ERR, LOC +
916 "SetResolution() -- Error setting format" + ENO);
917 return false;
918 }
919
920 LOG(VB_RECORD, LOG_INFO, LOC + QString("Resolution set to %1x%2")
921 .arg(width).arg(height));
922 return true;
923}
924
925// Audio controls
927{
928 struct v4l2_audio ain {};
929
930 ain.index = value;
931 if (ioctl(m_fd, VIDIOC_ENUMAUDIO, &ain) < 0)
932 {
933 LOG(VB_GENERAL, LOG_WARNING, LOC +
934 QString("Failed to retrieve audio input.") + ENO);
935 return false;
936 }
937
938 ain.index = value;
939 if (ioctl(m_fd, VIDIOC_S_AUDIO, &ain) < 0)
940 {
941 LOG(VB_GENERAL, LOG_WARNING,
942 LOC + QString("Failed to set audio input to %1.").arg(value) + ENO);
943 return false;
944 }
945
946 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Audio input set to %1.")
947 .arg(value));
948 return true;
949}
950
952{
953#if 0
954 if (DriverName().startsWith("ivtv"))
955 value = V4L2_MPEG_AUDIO_ENCODING_LAYER_2;
956#endif
957
958 QString desc;
959 switch (value)
960 {
961 case V4L2_MPEG_AUDIO_ENCODING_LAYER_1:
962 desc = "Layer I";
963 break;
964 case V4L2_MPEG_AUDIO_ENCODING_LAYER_2:
965 desc = "Layer II";
966 break;
967 case V4L2_MPEG_AUDIO_ENCODING_LAYER_3:
968 desc = "Layer III";
969 break;
970 case V4L2_MPEG_AUDIO_ENCODING_AAC:
971 desc = "AAC";
972 break;
973 case V4L2_MPEG_AUDIO_ENCODING_AC3:
974 desc = "AC3";
975 break;
976 default:
977 desc = "Unknown";
978 break;
979 }
980
981#if 0
982 if (DriverName().startsWith("ivtv"))
983 {
984 LOG(VB_CHANNEL, LOG_INFO, LOC +
985 QString("Overriding AudioCodec for %1 to %2")
986 .arg(DriverName()).arg(desc));
987 }
988#endif
989
990 return SetExtControl(V4L2_CID_MPEG_AUDIO_ENCODING, value,
991 "Audio Codec", desc);
992}
993
994
995bool V4L2util::SetVolume(int volume)
996{
997 // Get volume min/max values
998 struct v4l2_queryctrl qctrl {};
999 qctrl.id = V4L2_CID_AUDIO_VOLUME;
1000 if ((ioctl(m_fd, VIDIOC_QUERYCTRL, &qctrl) < 0) ||
1001 (qctrl.flags & V4L2_CTRL_FLAG_DISABLED))
1002 {
1003 LOG(VB_CHANNEL, LOG_WARNING,
1004 LOC + "SetRecordingVolume() -- Audio volume control not supported.");
1005 return false;
1006 }
1007
1008 // calculate volume in card units.
1009 int range = qctrl.maximum - qctrl.minimum;
1010 int value = (int) ((range * volume * 0.01F) + qctrl.minimum);
1011 int ctrl_volume = std::clamp(value, qctrl.minimum, qctrl.maximum);
1012
1013 // Set recording volume
1014 struct v4l2_control ctrl {};
1015 ctrl.id = V4L2_CID_AUDIO_VOLUME;
1016 ctrl.value = ctrl_volume;
1017
1018 if (ioctl(m_fd, VIDIOC_S_CTRL, &ctrl) < 0)
1019 {
1020 LOG(VB_GENERAL, LOG_WARNING, LOC +
1021 "SetRecordingVolume() -- Failed to set recording volume" + ENO);
1022// "If you are using an AverMedia M179 card this is normal."
1023 return false;
1024 }
1025
1026 LOG(VB_RECORD, LOG_INFO, LOC + "SetRecordingVolume() -- volume set.");
1027 return true;
1028}
1029
1031{
1032 struct v4l2_tuner vt {};
1033
1034 if (ioctl(m_fd, VIDIOC_G_TUNER, &vt) < 0)
1035 {
1036 LOG(VB_CHANNEL, LOG_WARNING, LOC +
1037 "SetLanguageMode() -- Failed to retrieve audio mode" + ENO);
1038 return false;
1039 }
1040
1041 vt.audmode = mode;
1042
1043 if (ioctl(m_fd, VIDIOC_S_TUNER, &vt) < 0)
1044 {
1045 LOG(VB_CHANNEL, LOG_WARNING, LOC +
1046 "SetLanguageMode -- Failed to set audio mode" + ENO);
1047 return false;
1048 }
1049
1050 QString desc;
1051 switch (mode)
1052 {
1053 case V4L2_TUNER_MODE_MONO:
1054 desc = "Mono";
1055 break;
1056 case V4L2_TUNER_MODE_STEREO:
1057 desc = "Stereo";
1058 break;
1059#if 0
1060 case V4L2_TUNER_MODE_LANG2:
1061 desc = "Lang2";
1062 break;
1063#endif
1064 case V4L2_TUNER_MODE_SAP:
1065 desc = "SAP";
1066 break;
1067 case V4L2_TUNER_MODE_LANG1:
1068 desc = "LANG1";
1069 break;
1070 case V4L2_TUNER_MODE_LANG1_LANG2:
1071 desc = "LANG1&Lang2";
1072 break;
1073 default:
1074 desc = "Unknown";
1075 break;
1076 }
1077
1078 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Language Mode set to %1 (%2)")
1079 .arg(desc).arg(mode));
1080 return true;
1081}
1082
1084{
1085 QString desc;
1086
1087#if 0
1088 if (DriverName().startsWith("ivtv"))
1089 value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000;
1090#endif
1091
1092 switch (value)
1093 {
1094 case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100:
1095 desc = "44.1kHz";
1096 break;
1097 case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000:
1098 desc = "48kHz";
1099 break;
1100 case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000:
1101 desc = "32kHz";
1102 break;
1103 default:
1104 desc = "Unknown";
1105 }
1106
1107#if 0
1108 if (DriverName().startsWith("ivtv"))
1109 {
1110 LOG(VB_CHANNEL, LOG_INFO, LOC +
1111 QString("Overriding sampling frequence for %1 to %2")
1112 .arg(DriverName()).arg(desc));
1113 }
1114#endif
1115
1116 return SetExtControl(V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, value,
1117 "Audio Sample Rate", desc);
1118}
1119
1121{
1122 QString desc;
1123 switch (value)
1124 {
1125 case V4L2_MPEG_AUDIO_L2_BITRATE_32K:
1126 desc = "32K";
1127 break;
1128 case V4L2_MPEG_AUDIO_L2_BITRATE_48K:
1129 desc = "48K";
1130 break;
1131 case V4L2_MPEG_AUDIO_L2_BITRATE_56K:
1132 desc = "56K";
1133 break;
1134 case V4L2_MPEG_AUDIO_L2_BITRATE_64K:
1135 desc = "64K";
1136 break;
1137 case V4L2_MPEG_AUDIO_L2_BITRATE_80K:
1138 desc = "80K";
1139 break;
1140 case V4L2_MPEG_AUDIO_L2_BITRATE_96K:
1141 desc = "96K";
1142 break;
1143 case V4L2_MPEG_AUDIO_L2_BITRATE_112K:
1144 desc = "112K";
1145 break;
1146 case V4L2_MPEG_AUDIO_L2_BITRATE_128K:
1147 desc = "128K";
1148 break;
1149 case V4L2_MPEG_AUDIO_L2_BITRATE_160K:
1150 desc = "160K";
1151 break;
1152 case V4L2_MPEG_AUDIO_L2_BITRATE_192K:
1153 desc = "192K";
1154 break;
1155 case V4L2_MPEG_AUDIO_L2_BITRATE_224K:
1156 desc = "224K";
1157 break;
1158 case V4L2_MPEG_AUDIO_L2_BITRATE_256K:
1159 desc = "256K";
1160 break;
1161 case V4L2_MPEG_AUDIO_L2_BITRATE_320K:
1162 desc = "320K";
1163 break;
1164 case V4L2_MPEG_AUDIO_L2_BITRATE_384K:
1165 desc = "384K";
1166 break;
1167 default:
1168 desc = "Unknown";
1169 }
1170
1171 return SetExtControl(V4L2_CID_MPEG_AUDIO_L2_BITRATE, value,
1172 "Audio L2 Bitrate", desc);
1173}
1174
1175// Actions
1176bool V4L2util::SetEncoderState(int mode, const QString& desc)
1177{
1178 struct v4l2_encoder_cmd command {};
1179
1180 command.cmd = mode;
1181 if (ioctl(m_fd, VIDIOC_ENCODER_CMD, &command) != 0 && errno != ENOTTY)
1182 {
1183 // Some drivers do not support this ioctl at all. It is marked as
1184 // "experimental" in the V4L2 API spec. These drivers return EINVAL
1185 // in older kernels and ENOTTY in 3.1+
1186 LOG(VB_CHANNEL, LOG_WARNING, LOC +
1187 QString("SetEncoderState(%1) -- failed").arg(desc) + ENO);
1188 return false;
1189 }
1190 LOG(VB_CHANNEL, LOG_INFO, LOC +
1191 QString("SetEncoderState(%1) -- success").arg(desc));
1192 return true;
1193}
1194
1196{
1197 return SetEncoderState(V4L2_ENC_CMD_START, "Start");
1198}
1199
1201{
1202 return SetEncoderState(V4L2_ENC_CMD_STOP, "Stop");
1203}
1204
1206{
1207 return SetEncoderState(V4L2_ENC_CMD_PAUSE, "Pause");
1208}
1209
1211{
1212 return SetEncoderState(V4L2_ENC_CMD_RESUME, "Resume");
1213}
1214
1215bool V4L2util::OpenVBI(const QString& /*vbi_dev_name*/)
1216{
1217 return false;
1218}
1219
1221{
1222 struct v4l2_format vbifmt {};
1223
1224 vbifmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
1225 vbifmt.fmt.sliced.service_set |= (VBIMode::PAL_TT == vbimode) ?
1226 V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
1227
1228 int fd = m_vbiFd < 0 ? m_fd : m_vbiFd;
1229
1230 if (ioctl(fd, VIDIOC_S_FMT, &vbifmt) < 0)
1231 {
1232 LOG(VB_CHANNEL, LOG_WARNING, LOC + "ConfigureVBI() -- " +
1233 "Failed to enable VBI embedding (/dev/videoX)" + ENO);
1234 return false;
1235 }
1236
1237 if (ioctl(fd, VIDIOC_G_FMT, &vbifmt) >= 0)
1238 {
1239 LOG(VB_RECORD, LOG_INFO,
1240 LOC + QString("VBI service: %1, io size: %2")
1241 .arg(vbifmt.fmt.sliced.service_set)
1242 .arg(vbifmt.fmt.sliced.io_size));
1243
1244 // V4L2_MPEG_STREAM_VBI_FMT_NONE = 0, /* No VBI in the MPEG stream */
1245 // V4L2_MPEG_STREAM_VBI_FMT_IVTV = 1, /* VBI in private packets, IVTV form
1246 return SetExtControl(V4L2_CID_MPEG_STREAM_VBI_FMT,
1247 V4L2_MPEG_STREAM_VBI_FMT_IVTV,
1248 "MPEG Stream VBI format",
1249 "VBI in private packets, IVTV form");
1250
1251 }
1252
1253 return false;
1254}
bool GetFormats(QStringList &formats)
Definition: v4l2util.cpp:510
bool GetVideoStandard(QString &name) const
Definition: v4l2util.cpp:594
QString m_deviceName
Definition: v4l2util.h:104
bool HasTuner(void) const
Definition: v4l2util.cpp:689
bool ResumeEncoding(void)
Definition: v4l2util.cpp:1210
static bool OpenVBI(const QString &vbi_dev_name)
Definition: v4l2util.cpp:1215
bool Open(const QString &dev_name, const QString &vbi_dev_name="")
Definition: v4l2util.cpp:34
bool SetVideoBitrate(int value)
Definition: v4l2util.cpp:876
DriverOption::Options m_options
Definition: v4l2util.h:103
bool StopEncoding(void)
Definition: v4l2util.cpp:1200
QString GetDriverName(void) const
Definition: v4l2util.cpp:684
bool GetResolution(int &width, int &height) const
Definition: v4l2util.cpp:654
bool log_control(struct v4l2_queryctrl &qctrl, DriverOption &drv_opt, QString &msg)
Definition: v4l2util.cpp:354
bool IsEncoder(void) const
Definition: v4l2util.cpp:699
~V4L2util(void)
Definition: v4l2util.cpp:29
bool StartEncoding(void)
Definition: v4l2util.cpp:1195
void log_qctrl(struct v4l2_queryctrl &queryctrl, DriverOption &drv_opt, QString &msg)
Definition: v4l2util.cpp:193
V4L2util(void)=default
int m_version
Definition: v4l2util.h:107
bool PauseEncoding(void)
Definition: v4l2util.cpp:1205
bool SetLanguageMode(int mode)
Definition: v4l2util.cpp:1030
bool SetAudioSamplingRate(int value)
Definition: v4l2util.cpp:1083
int GetOptionValue(DriverOption::category_t cat, const QString &desc)
Definition: v4l2util.cpp:565
bool SetExtControl(int request, int value, const QString &ctrl_desc, const QString &value_desc)
Definition: v4l2util.cpp:739
static QString ctrlflags_toString(uint32_t flags)
Definition: v4l2util.cpp:134
bool SetSlicedVBI(VBIMode::vbimode_t vbimode)
Definition: v4l2util.cpp:1220
bool SetEncoderState(int mode, const QString &desc)
Definition: v4l2util.cpp:1176
bool SetVideoAspect(int value)
Definition: v4l2util.cpp:834
bool UserAdjustableResolution(void) const
Definition: v4l2util.cpp:708
QString DriverName(void) const
Definition: v4l2util.h:50
QString GetDeviceName(void) const
Definition: v4l2util.cpp:679
static QString queryctrl_toString(int type)
Definition: v4l2util.cpp:162
bool HasSlicedVBI(void) const
Definition: v4l2util.cpp:118
bool SetVolume(int volume)
Definition: v4l2util.cpp:995
bool HasAudioSupport(void) const
Definition: v4l2util.cpp:694
bool SetStreamType(int value)
Definition: v4l2util.cpp:820
uint32_t m_capabilities
Definition: v4l2util.h:108
int m_fd
Definition: v4l2util.h:101
bool HasStreaming(void) const
Definition: v4l2util.cpp:94
static void bitmask_toString(QString &result, uint32_t flags, uint32_t mask, const QString &desc)
Definition: v4l2util.cpp:123
void SetDefaultOptions(DriverOption::Options &options)
Definition: v4l2util.cpp:428
int GetStreamType(void) const
Definition: v4l2util.cpp:799
bool GetOptions(DriverOption::Options &options)
Definition: v4l2util.cpp:537
QString m_cardName
Definition: v4l2util.h:106
uint32_t GetCapabilities(void) const
Definition: v4l2util.cpp:674
int GetExtControl(int request, const QString &ctrl_desc="") const
Definition: v4l2util.cpp:716
bool SetAudioInput(int value)
Definition: v4l2util.cpp:926
bool SetResolution(uint32_t width, uint32_t height)
Definition: v4l2util.cpp:890
static QString StreamTypeDesc(int value)
Definition: v4l2util.cpp:779
bool SetVideoBitrateMode(int value)
Definition: v4l2util.cpp:859
QString m_driverName
Definition: v4l2util.h:105
bool SetVideoBitratePeak(int value)
Definition: v4l2util.cpp:883
int m_vbiFd
Definition: v4l2util.h:102
void Close(void)
Definition: v4l2util.cpp:83
bool SetAudioBitrateL2(int value)
Definition: v4l2util.cpp:1120
int GetSignalStrength(void) const
Definition: v4l2util.cpp:633
bool m_haveQueryExtCtrl
Definition: v4l2util.h:109
bool SetAudioCodec(int value)
Definition: v4l2util.cpp:951
vbimode_t
Definition: tv.h:11
@ PAL_TT
Definition: tv.h:13
#define close
Definition: compat.h:39
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:74
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:206
uint32_t m_step
Definition: driveroption.h:33
menu_t m_menu
Definition: driveroption.h:35
QMap< category_t, DriverOption > Options
Definition: driveroption.h:22
int32_t m_minimum
Definition: driveroption.h:29
int32_t m_maximum
Definition: driveroption.h:30
QString m_name
Definition: driveroption.h:27
int32_t m_defaultValue
Definition: driveroption.h:31
category_t m_category
Definition: driveroption.h:28
type_t m_type
Definition: driveroption.h:36
#define LOC
Definition: v4l2util.cpp:16
const std::array< const std::string, 8 > formats
Definition: vbilut.cpp:189
vbimode
Definition: vbilut.h:22