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 {
342 msg += QString("\t\t%1: %2").arg(idx).arg((char *)qmenu.name);
343 }
344 else
345 {
346 msg += QString("\t\t%1: %2 (0x%3)")
347 .arg(idx).arg(qmenu.value)
348 .arg(qmenu.value, 0, 16, QChar('0'));
349 }
350 }
351 }
352
353 LOG(VB_CHANNEL, LOG_INFO, LOC + msg);
354}
355
356bool V4L2util::log_control(struct v4l2_queryctrl& qctrl, DriverOption& drv_opt,
357 QString& msg)
358{
359 struct v4l2_control ctrl {};
360 struct v4l2_ext_control ext_ctrl {};
361 struct v4l2_ext_controls ctrls {};
362
363 if (qctrl.flags& V4L2_CTRL_FLAG_DISABLED)
364 {
365 msg += QString("'%1' Disabled").arg((char *)qctrl.name);
366 return true;
367 }
368
369 if (qctrl.type == V4L2_CTRL_TYPE_CTRL_CLASS)
370 {
371 msg += QString("'%1' V4L2_CTRL_TYPE_CTRL_CLASS").arg((char *)qctrl.name);
372 return true;
373 }
374
375 ext_ctrl.id = qctrl.id;
376 if ((qctrl.flags& V4L2_CTRL_FLAG_WRITE_ONLY) ||
377 qctrl.type == V4L2_CTRL_TYPE_BUTTON)
378 {
379 log_qctrl(qctrl, drv_opt, msg);
380 return true;
381 }
382
383 if (qctrl.type >= V4L2_CTRL_COMPOUND_TYPES)
384 {
385 log_qctrl(qctrl, drv_opt, msg);
386 return true;
387 }
388
389 ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(qctrl.id);
390 ctrls.count = 1;
391 ctrls.controls = &ext_ctrl;
392 if (qctrl.type == V4L2_CTRL_TYPE_INTEGER64 ||
393 qctrl.type == V4L2_CTRL_TYPE_STRING ||
394 (V4L2_CTRL_ID2CLASS(qctrl.id) != V4L2_CTRL_CLASS_USER &&
395 qctrl.id < V4L2_CID_PRIVATE_BASE))
396 {
397 if (qctrl.type == V4L2_CTRL_TYPE_STRING)
398 {
399 ext_ctrl.size = qctrl.maximum + 1;
400 // C library structure. NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
401 ext_ctrl.string = (char *)malloc(ext_ctrl.size);
402 ext_ctrl.string[0] = 0;
403 }
404 if (ioctl(m_fd, VIDIOC_G_EXT_CTRLS, &ctrls))
405 {
406 LOG(VB_CHANNEL, LOG_WARNING, LOC +
407 QString("Failed to get ext_ctr %1: ")
408 .arg((char *)qctrl.name) + ENO);
409 return false;
410 }
411 }
412 else {
413 ctrl.id = qctrl.id;
414 if (ioctl(m_fd, VIDIOC_G_CTRL, &ctrl))
415 {
416 LOG(VB_CHANNEL, LOG_WARNING, LOC +
417 QString("Failed to get ctrl %1: ")
418 .arg((char *)qctrl.name) + ENO);
419 return false;
420 }
421 ext_ctrl.value = ctrl.value;
422 }
423 log_qctrl(qctrl, drv_opt, msg);
424
425 if (qctrl.type == V4L2_CTRL_TYPE_STRING)
426 free(ext_ctrl.string); // NOLINT(cppcoreguidelines-no-malloc)
427 return true;
428}
429
430// Some drivers don't set 'default' options, so make some assumptions
432{
434 {
435 DriverOption drv_opt;
437 drv_opt.m_name = "Video Encoding";
438 drv_opt.m_minimum = drv_opt.m_maximum = drv_opt.m_defaultValue =
439 V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
440 drv_opt.m_menu[drv_opt.m_defaultValue] = "MPEG-2 Video";
441 options[drv_opt.m_category] = drv_opt;
442 }
443
445 {
446 DriverOption drv_opt;
447
448 // V4L2_CID_MPEG_AUDIO_ENCODING
450 drv_opt.m_name = "Audio Encoding";
451 drv_opt.m_minimum = drv_opt.m_maximum = drv_opt.m_defaultValue =
452 V4L2_MPEG_AUDIO_ENCODING_LAYER_2;
453 drv_opt.m_menu[drv_opt.m_defaultValue] = "MPEG-1/2 Layer II encoding";
454 options[drv_opt.m_category] = drv_opt;
455
457 drv_opt.m_name = "Audio Bitrate";
458 drv_opt.m_minimum = drv_opt.m_maximum = drv_opt.m_defaultValue =
459 V4L2_MPEG_AUDIO_ENCODING_LAYER_2;
460 drv_opt.m_menu[drv_opt.m_defaultValue] = "MPEG-1/2 Layer II encoding";
461 options[drv_opt.m_category] = drv_opt;
462
463 // V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ
465 drv_opt.m_name = "MPEG Audio sampling frequency";
466 drv_opt.m_minimum = drv_opt.m_maximum = drv_opt.m_defaultValue =
467 V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000;
468 drv_opt.m_menu[drv_opt.m_defaultValue] = "48 kHz";
469 options[drv_opt.m_category] = drv_opt;
470
471 // VIDIOC_S_TUNER
473 drv_opt.m_name = "Tuner Audio Modes";
474 drv_opt.m_minimum = drv_opt.m_maximum = drv_opt.m_defaultValue =
475 V4L2_TUNER_MODE_STEREO;
476 drv_opt.m_menu[drv_opt.m_defaultValue] = "Play stereo audio";
477 options[drv_opt.m_category] = drv_opt;
478 }
479
480 DriverOption::Options::iterator Iopt = options.begin();
481 for ( ; Iopt != options.end(); ++Iopt)
482 {
483 // If the driver provides a menu of options, use it to set limits
484 if (!(*Iopt).m_menu.isEmpty())
485 {
486 int minimum = INT_MAX;
487 int maximum = -1;
488
489 DriverOption::menu_t::iterator Imenu = (*Iopt).m_menu.begin();
490 for ( ; Imenu != (*Iopt).m_menu.end(); ++Imenu)
491 {
492 minimum = std::min(Imenu.key(), minimum);
493 maximum = std::max(Imenu.key(), maximum);
494 }
495 if ((*Iopt).m_minimum != minimum)
496 {
497 LOG(VB_CHANNEL, LOG_INFO, LOC +
498 QString("%1 menu options overrides minimum from %2 to %3")
499 .arg((*Iopt).m_name).arg((*Iopt).m_minimum).arg(minimum));
500 (*Iopt).m_minimum = minimum;
501 }
502 if ((*Iopt).m_maximum != maximum)
503 {
504 LOG(VB_CHANNEL, LOG_INFO, LOC +
505 QString("%1 menu options overrides maximum from %2 to %3")
506 .arg((*Iopt).m_name).arg((*Iopt).m_maximum).arg(maximum));
507 (*Iopt).m_maximum = maximum;
508 }
509 }
510 }
511}
512
514{
515 struct v4l2_fmtdesc vid_fmtdesc {};
516 const std::array<const QString,2> flags {"uncompressed", "compressed"};
517
518 vid_fmtdesc.index = 0;
519 vid_fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
520 while(ioctl(m_fd, VIDIOC_ENUM_FMT, &vid_fmtdesc) == 0)
521 {
522 formats << QString("%1 (%2)").arg((char *)vid_fmtdesc.description,
523 flags[vid_fmtdesc.flags]);
524
525 /* Convert the pixelformat attributes from FourCC into 'human readab
526 fprintf(stdout, " pixelformat :%c%c%c%c\\n",
527 vid_fmtdesc.pixelformat& 0xFF, (vid_fmtdesc.pixelformat >>
528 (vid_fmtdesc.pixelformat >> 16)& 0xFF, (vid_fmtdesc.pixelfo
529 */
530
531 vid_fmtdesc.index++;
532 }
533
534 LOG(VB_CHANNEL, LOG_DEBUG, LOC + QString("GetFormats: %1")
535 .arg(formats.join(",")));
536
537 return true;
538}
539
541{
542 LOG(VB_CHANNEL, LOG_INFO, LOC + "Options");
543
544 if (!m_options.isEmpty())
545 {
547 return true;
548 }
549
550 struct v4l2_queryctrl qctrl {};
551 qctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
552 while (0 == ioctl (m_fd, VIDIOC_QUERYCTRL, &qctrl))
553 {
554 QString msg;
555 DriverOption drv_opt;
556
557 log_control(qctrl, drv_opt, msg);
558 m_options[drv_opt.m_category] = drv_opt;
559
560 qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
561 }
562
565 return true;
566}
567
569{
570 if (m_options.isEmpty())
572
573 if (!m_options.contains(cat))
574 {
575 LOG(VB_CHANNEL, LOG_WARNING, LOC +
576 QString("Driver does not support option."));
577 return -1;
578 }
579
580 DriverOption drv_opt = m_options.value(cat);
581 DriverOption::menu_t::iterator Imenu = drv_opt.m_menu.begin();
582 for ( ; Imenu != drv_opt.m_menu.end(); ++Imenu)
583 {
584 if ((*Imenu) == desc)
585 {
586 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("GetOptionValue '%1' = %2")
587 .arg(desc).arg(Imenu.key()));
588 return Imenu.key();
589 }
590 }
591
592 LOG(VB_CHANNEL, LOG_WARNING, LOC +
593 QString("'%1' not found in driver options menu.").arg(desc));
594 return -1;
595}
596
597bool V4L2util::GetVideoStandard(QString& name) const
598{
599 v4l2_std_id std_id = 0;
600 struct v4l2_standard standard {};
601
602 if (-1 == ioctl (m_fd, VIDIOC_G_STD, &std_id))
603 {
604 /* Note when VIDIOC_ENUMSTD always returns EINVAL this
605 is no video device or it falls under the USB exception,
606 and VIDIOC_G_STD returning EINVAL is no error. */
607 LOG(VB_CHANNEL, LOG_WARNING, LOC +
608 "GetVideoStandard: Failed to detect signal." + ENO);
609 return false;
610 }
611
612 standard.index = 0;
613
614 while (0 == ioctl (m_fd, VIDIOC_ENUMSTD, &standard))
615 {
616 if (standard.id & std_id)
617 {
618 name = (char *)standard.name;
619 return true;
620 }
621
622 ++standard.index;
623 }
624
625 /* EINVAL indicates the end of the enumeration, which cannot be
626 empty unless this device falls under the USB exception. */
627 if (errno == EINVAL || standard.index == 0)
628 {
629 LOG(VB_CHANNEL, LOG_WARNING, LOC +
630 "GetVideoStandard: Failed to find signal." + ENO);
631 }
632
633 return false;
634}
635
637{
638 return -1; // Does not work
639
640 struct v4l2_tuner tuner {};
641
642 if (ioctl(m_fd, VIDIOC_G_TUNER, &tuner, 0) != 0)
643 {
644 LOG(VB_GENERAL, LOG_ERR, "GetSignalStrength() : "
645 "Failed to probe signal (v4l2)" + ENO);
646 return -1;
647 }
648
649 tuner.signal /= 655.35; // Set to 0-100 range
650
651 LOG(VB_RECORD, LOG_INFO, LOC + QString("GetSignalStrength() : "
652 "(%1\%)")
653 .arg(tuner.signal));
654 return tuner.signal;
655}
656
657bool V4L2util::GetResolution(int& width, int& height) const
658{
659 struct v4l2_format vfmt {};
660
661 vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
662 if (ioctl(m_fd, VIDIOC_G_FMT, &vfmt) != 0)
663 {
664 LOG(VB_CHANNEL, LOG_WARNING, LOC +
665 "Failed to determine resolution: " + ENO);
666 width = height = -1;
667 return false;
668 }
669
670 width = vfmt.fmt.pix.width;
671 height = vfmt.fmt.pix.height;
672 LOG(VB_CHANNEL, LOG_INFO, LOC +
673 QString("Resolution: %1x%2").arg(width).arg(height));
674 return true;
675}
676
677uint32_t V4L2util::GetCapabilities(void) const
678{
679 return m_capabilities;
680}
681
682QString V4L2util::GetDeviceName(void) const
683{
684 return m_deviceName;
685}
686
687QString V4L2util::GetDriverName(void) const
688{
689 return m_driverName;
690}
691
692bool V4L2util::HasTuner(void) const
693{
694 return (m_capabilities & V4L2_CAP_TUNER) != 0U;
695}
696
698{
699 return (m_capabilities & V4L2_CAP_AUDIO) != 0U;
700}
701
702bool V4L2util::IsEncoder(void) const
703{
704 struct v4l2_queryctrl qctrl {};
705
706 qctrl.id = V4L2_CTRL_CLASS_MPEG | V4L2_CTRL_FLAG_NEXT_CTRL;
707 return (0 == ioctl (m_fd, VIDIOC_QUERYCTRL, &qctrl) &&
708 V4L2_CTRL_ID2CLASS (qctrl.id) == V4L2_CTRL_CLASS_MPEG);
709}
710
712{
713 // I have not been able to come up with a way of querying the
714 // driver to answer this question.
715
716 return m_driverName != "hdpvr";
717}
718
719int V4L2util::GetExtControl(int request, const QString& ctrl_desc) const
720{
721 struct v4l2_ext_control ctrl {};
722 struct v4l2_ext_controls ctrls {};
723
724 ctrl.id = request;
725
726 ctrls.count = 1;
727 ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
728 ctrls.controls = &ctrl;
729
730 if (ioctl(m_fd, VIDIOC_G_EXT_CTRLS, &ctrls) != 0)
731 {
732 LOG(VB_GENERAL, LOG_ERR, LOC +
733 QString("Failed to retrieve current %1 value.")
734 .arg(ctrl_desc) + ENO);
735 return -1;
736 }
737
738 return ctrls.controls->value;
739}
740
741
742bool V4L2util::SetExtControl(int request, int value, const QString& ctrl_desc,
743 const QString& value_desc)
744{
745 int current_value = GetExtControl(request, ctrl_desc);
746
747 if (current_value < 0)
748 return false;
749 if (current_value == value)
750 {
751 LOG(VB_CHANNEL, LOG_INFO, LOC +
752 QString("%1 value is already %2 (%3).")
753 .arg(ctrl_desc, value_desc, QString::number(value)));
754 return true;
755 }
756
757 struct v4l2_ext_control ctrl {};
758 struct v4l2_ext_controls ctrls {};
759
760 ctrl.id = request;
761 ctrl.value = value;
762
763 ctrls.count = 1;
764 ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
765 ctrls.controls = &ctrl;
766
767 if (ioctl(m_fd, VIDIOC_S_EXT_CTRLS, &ctrls) < 0)
768 {
769 LOG(VB_GENERAL, LOG_ERR, LOC +
770 QString("Failed to set %1 value to %2 (%3).")
771 .arg(ctrl_desc, value_desc, QString::number(value)) + ENO);
772 return false;
773 }
774
775 LOG(VB_CHANNEL, LOG_INFO, LOC +
776 QString("%1 value set to %2 (%3).")
777 .arg(ctrl_desc, value_desc, QString::number(value)));
778
779 return true;
780}
781
782QString V4L2util::StreamTypeDesc(int value)
783{
784 switch (value)
785 {
786 case V4L2_MPEG_STREAM_TYPE_MPEG2_PS:
787 return "MPEG-2 program stream";
788 case V4L2_MPEG_STREAM_TYPE_MPEG2_TS:
789 return "MPEG-2 transport stream";
790 case V4L2_MPEG_STREAM_TYPE_MPEG1_SS:
791 return "MPEG-1 system stream";
792 case V4L2_MPEG_STREAM_TYPE_MPEG2_DVD:
793 return "MPEG-2 DVD-compatible stream";
794 case V4L2_MPEG_STREAM_TYPE_MPEG1_VCD:
795 return "MPEG-1 VCD-compatible stream";
796 case V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD:
797 return "MPEG-2 SVCD-compatible stream";
798 }
799 return "Unknown";
800}
801
803{
804 int type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS;
805
806 if (DriverName().startsWith("saa7164"))
807 {
808 // The saa7164 driver reports that it can do TS, but it doesn't work!
809 type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS;
810 }
811 else
812 {
813 type = GetExtControl(V4L2_CID_MPEG_STREAM_TYPE, "Stream Type");
814 }
815
816 LOG(VB_CHANNEL, LOG_INFO, LOC +
817 QString("MPEG Stream Type is currently set to %1 (%2)")
818 .arg(StreamTypeDesc(type)).arg(type));
819
820 return type;
821}
822
824{
825 if (DriverName().startsWith("saa7164") ||
826 DriverName().startsWith("ivtv"))
827 {
828 // The saa7164 driver reports that it can do TS, but it doesn't work!
829 value = V4L2_MPEG_STREAM_TYPE_MPEG2_PS;
830 }
831
832 return SetExtControl(V4L2_CID_MPEG_STREAM_TYPE, value,
833 "MPEG Stream type", StreamTypeDesc(value));
834}
835
836// Video controls
838{
839 QString desc;
840 switch (value)
841 {
842 case V4L2_MPEG_VIDEO_ASPECT_1x1:
843 desc = "Square";
844 break;
845 case V4L2_MPEG_VIDEO_ASPECT_4x3:
846 desc = "4x3";
847 break;
848 case V4L2_MPEG_VIDEO_ASPECT_16x9:
849 desc = "16x9";
850 break;
851 case V4L2_MPEG_VIDEO_ASPECT_221x100:
852 desc = "221x100";
853 break;
854 default:
855 desc = "Unknown";
856 }
857
858 return SetExtControl(V4L2_CID_MPEG_VIDEO_ASPECT, value,
859 "Video Aspect ratio", desc);
860}
861
863{
864 QString desc;
865 switch (value)
866 {
867 case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR:
868 desc = "VBR";
869 break;
870 case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR:
871 desc = "CBR";
872 break;
873 }
874
875 return SetExtControl(V4L2_CID_MPEG_VIDEO_BITRATE_MODE, value,
876 "Video Bitrate Mode", desc);
877}
878
880{
881 QString desc = QString("%1").arg(value);
882 return SetExtControl(V4L2_CID_MPEG_VIDEO_BITRATE, value,
883 "Video Average Bitrate", desc);
884}
885
887{
888 QString desc = QString("%1").arg(value);
889 return SetExtControl(V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, value,
890 "Video Peak Bitrate", desc);
891}
892
893bool V4L2util::SetResolution(uint32_t width, uint32_t height)
894{
895 struct v4l2_format vfmt {};
896
897 vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
898
899 if (ioctl(m_fd, VIDIOC_G_FMT, &vfmt) < 0)
900 {
901 LOG(VB_GENERAL, LOG_ERR, LOC +
902 "SetResolution() -- Error getting format" + ENO);
903 return false;
904 }
905
906 if ((vfmt.fmt.pix.width == width) && (vfmt.fmt.pix.height == height))
907 {
908 LOG(VB_RECORD, LOG_INFO, LOC + QString("Resolution is already %1x%2")
909 .arg(width).arg(height));
910 return true;
911 }
912
913 vfmt.fmt.pix.width = width;
914 vfmt.fmt.pix.height = height;
915
916 if (ioctl(m_fd, VIDIOC_S_FMT, &vfmt) < 0)
917 {
918 LOG(VB_GENERAL, LOG_ERR, LOC +
919 "SetResolution() -- Error setting format" + ENO);
920 return false;
921 }
922
923 LOG(VB_RECORD, LOG_INFO, LOC + QString("Resolution set to %1x%2")
924 .arg(width).arg(height));
925 return true;
926}
927
928// Audio controls
930{
931 struct v4l2_audio ain {};
932
933 ain.index = value;
934 if (ioctl(m_fd, VIDIOC_ENUMAUDIO, &ain) < 0)
935 {
936 LOG(VB_GENERAL, LOG_WARNING, LOC +
937 QString("Failed to retrieve audio input.") + ENO);
938 return false;
939 }
940
941 ain.index = value;
942 if (ioctl(m_fd, VIDIOC_S_AUDIO, &ain) < 0)
943 {
944 LOG(VB_GENERAL, LOG_WARNING,
945 LOC + QString("Failed to set audio input to %1.").arg(value) + ENO);
946 return false;
947 }
948
949 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Audio input set to %1.")
950 .arg(value));
951 return true;
952}
953
955{
956#if 0
957 if (DriverName().startsWith("ivtv"))
958 value = V4L2_MPEG_AUDIO_ENCODING_LAYER_2;
959#endif
960
961 QString desc;
962 switch (value)
963 {
964 case V4L2_MPEG_AUDIO_ENCODING_LAYER_1:
965 desc = "Layer I";
966 break;
967 case V4L2_MPEG_AUDIO_ENCODING_LAYER_2:
968 desc = "Layer II";
969 break;
970 case V4L2_MPEG_AUDIO_ENCODING_LAYER_3:
971 desc = "Layer III";
972 break;
973 case V4L2_MPEG_AUDIO_ENCODING_AAC:
974 desc = "AAC";
975 break;
976 case V4L2_MPEG_AUDIO_ENCODING_AC3:
977 desc = "AC3";
978 break;
979 default:
980 desc = "Unknown";
981 break;
982 }
983
984#if 0
985 if (DriverName().startsWith("ivtv"))
986 {
987 LOG(VB_CHANNEL, LOG_INFO, LOC +
988 QString("Overriding AudioCodec for %1 to %2")
989 .arg(DriverName()).arg(desc));
990 }
991#endif
992
993 return SetExtControl(V4L2_CID_MPEG_AUDIO_ENCODING, value,
994 "Audio Codec", desc);
995}
996
997
998bool V4L2util::SetVolume(int volume)
999{
1000 // Get volume min/max values
1001 struct v4l2_queryctrl qctrl {};
1002 qctrl.id = V4L2_CID_AUDIO_VOLUME;
1003 if ((ioctl(m_fd, VIDIOC_QUERYCTRL, &qctrl) < 0) ||
1004 (qctrl.flags & V4L2_CTRL_FLAG_DISABLED))
1005 {
1006 LOG(VB_CHANNEL, LOG_WARNING,
1007 LOC + "SetRecordingVolume() -- Audio volume control not supported.");
1008 return false;
1009 }
1010
1011 // calculate volume in card units.
1012 int range = qctrl.maximum - qctrl.minimum;
1013 int value = (int) ((range * volume * 0.01F) + qctrl.minimum);
1014 int ctrl_volume = std::clamp(value, qctrl.minimum, qctrl.maximum);
1015
1016 // Set recording volume
1017 struct v4l2_control ctrl {};
1018 ctrl.id = V4L2_CID_AUDIO_VOLUME;
1019 ctrl.value = ctrl_volume;
1020
1021 if (ioctl(m_fd, VIDIOC_S_CTRL, &ctrl) < 0)
1022 {
1023 LOG(VB_GENERAL, LOG_WARNING, LOC +
1024 "SetRecordingVolume() -- Failed to set recording volume" + ENO);
1025// "If you are using an AverMedia M179 card this is normal."
1026 return false;
1027 }
1028
1029 LOG(VB_RECORD, LOG_INFO, LOC + "SetRecordingVolume() -- volume set.");
1030 return true;
1031}
1032
1034{
1035 struct v4l2_tuner vt {};
1036
1037 if (ioctl(m_fd, VIDIOC_G_TUNER, &vt) < 0)
1038 {
1039 LOG(VB_CHANNEL, LOG_WARNING, LOC +
1040 "SetLanguageMode() -- Failed to retrieve audio mode" + ENO);
1041 return false;
1042 }
1043
1044 vt.audmode = mode;
1045
1046 if (ioctl(m_fd, VIDIOC_S_TUNER, &vt) < 0)
1047 {
1048 LOG(VB_CHANNEL, LOG_WARNING, LOC +
1049 "SetLanguageMode -- Failed to set audio mode" + ENO);
1050 return false;
1051 }
1052
1053 QString desc;
1054 switch (mode)
1055 {
1056 case V4L2_TUNER_MODE_MONO:
1057 desc = "Mono";
1058 break;
1059 case V4L2_TUNER_MODE_STEREO:
1060 desc = "Stereo";
1061 break;
1062#if 0
1063 case V4L2_TUNER_MODE_LANG2:
1064 desc = "Lang2";
1065 break;
1066#endif
1067 case V4L2_TUNER_MODE_SAP:
1068 desc = "SAP";
1069 break;
1070 case V4L2_TUNER_MODE_LANG1:
1071 desc = "LANG1";
1072 break;
1073 case V4L2_TUNER_MODE_LANG1_LANG2:
1074 desc = "LANG1&Lang2";
1075 break;
1076 default:
1077 desc = "Unknown";
1078 break;
1079 }
1080
1081 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Language Mode set to %1 (%2)")
1082 .arg(desc).arg(mode));
1083 return true;
1084}
1085
1087{
1088 QString desc;
1089
1090#if 0
1091 if (DriverName().startsWith("ivtv"))
1092 value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000;
1093#endif
1094
1095 switch (value)
1096 {
1097 case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100:
1098 desc = "44.1kHz";
1099 break;
1100 case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000:
1101 desc = "48kHz";
1102 break;
1103 case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000:
1104 desc = "32kHz";
1105 break;
1106 default:
1107 desc = "Unknown";
1108 }
1109
1110#if 0
1111 if (DriverName().startsWith("ivtv"))
1112 {
1113 LOG(VB_CHANNEL, LOG_INFO, LOC +
1114 QString("Overriding sampling frequence for %1 to %2")
1115 .arg(DriverName()).arg(desc));
1116 }
1117#endif
1118
1119 return SetExtControl(V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, value,
1120 "Audio Sample Rate", desc);
1121}
1122
1124{
1125 QString desc;
1126 switch (value)
1127 {
1128 case V4L2_MPEG_AUDIO_L2_BITRATE_32K:
1129 desc = "32K";
1130 break;
1131 case V4L2_MPEG_AUDIO_L2_BITRATE_48K:
1132 desc = "48K";
1133 break;
1134 case V4L2_MPEG_AUDIO_L2_BITRATE_56K:
1135 desc = "56K";
1136 break;
1137 case V4L2_MPEG_AUDIO_L2_BITRATE_64K:
1138 desc = "64K";
1139 break;
1140 case V4L2_MPEG_AUDIO_L2_BITRATE_80K:
1141 desc = "80K";
1142 break;
1143 case V4L2_MPEG_AUDIO_L2_BITRATE_96K:
1144 desc = "96K";
1145 break;
1146 case V4L2_MPEG_AUDIO_L2_BITRATE_112K:
1147 desc = "112K";
1148 break;
1149 case V4L2_MPEG_AUDIO_L2_BITRATE_128K:
1150 desc = "128K";
1151 break;
1152 case V4L2_MPEG_AUDIO_L2_BITRATE_160K:
1153 desc = "160K";
1154 break;
1155 case V4L2_MPEG_AUDIO_L2_BITRATE_192K:
1156 desc = "192K";
1157 break;
1158 case V4L2_MPEG_AUDIO_L2_BITRATE_224K:
1159 desc = "224K";
1160 break;
1161 case V4L2_MPEG_AUDIO_L2_BITRATE_256K:
1162 desc = "256K";
1163 break;
1164 case V4L2_MPEG_AUDIO_L2_BITRATE_320K:
1165 desc = "320K";
1166 break;
1167 case V4L2_MPEG_AUDIO_L2_BITRATE_384K:
1168 desc = "384K";
1169 break;
1170 default:
1171 desc = "Unknown";
1172 }
1173
1174 return SetExtControl(V4L2_CID_MPEG_AUDIO_L2_BITRATE, value,
1175 "Audio L2 Bitrate", desc);
1176}
1177
1178// Actions
1179bool V4L2util::SetEncoderState(int mode, const QString& desc)
1180{
1181 struct v4l2_encoder_cmd command {};
1182
1183 command.cmd = mode;
1184 if (ioctl(m_fd, VIDIOC_ENCODER_CMD, &command) != 0 && errno != ENOTTY)
1185 {
1186 // Some drivers do not support this ioctl at all. It is marked as
1187 // "experimental" in the V4L2 API spec. These drivers return EINVAL
1188 // in older kernels and ENOTTY in 3.1+
1189 LOG(VB_CHANNEL, LOG_WARNING, LOC +
1190 QString("SetEncoderState(%1) -- failed").arg(desc) + ENO);
1191 return false;
1192 }
1193 LOG(VB_CHANNEL, LOG_INFO, LOC +
1194 QString("SetEncoderState(%1) -- success").arg(desc));
1195 return true;
1196}
1197
1199{
1200 return SetEncoderState(V4L2_ENC_CMD_START, "Start");
1201}
1202
1204{
1205 return SetEncoderState(V4L2_ENC_CMD_STOP, "Stop");
1206}
1207
1209{
1210 return SetEncoderState(V4L2_ENC_CMD_PAUSE, "Pause");
1211}
1212
1214{
1215 return SetEncoderState(V4L2_ENC_CMD_RESUME, "Resume");
1216}
1217
1218bool V4L2util::OpenVBI(const QString& /*vbi_dev_name*/)
1219{
1220 return false;
1221}
1222
1224{
1225 struct v4l2_format vbifmt {};
1226
1227 vbifmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
1228 vbifmt.fmt.sliced.service_set |= (VBIMode::PAL_TT == vbimode) ?
1229 V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
1230
1231 int fd = m_vbiFd < 0 ? m_fd : m_vbiFd;
1232
1233 if (ioctl(fd, VIDIOC_S_FMT, &vbifmt) < 0)
1234 {
1235 LOG(VB_CHANNEL, LOG_WARNING, LOC + "ConfigureVBI() -- " +
1236 "Failed to enable VBI embedding (/dev/videoX)" + ENO);
1237 return false;
1238 }
1239
1240 if (ioctl(fd, VIDIOC_G_FMT, &vbifmt) >= 0)
1241 {
1242 LOG(VB_RECORD, LOG_INFO,
1243 LOC + QString("VBI service: %1, io size: %2")
1244 .arg(vbifmt.fmt.sliced.service_set)
1245 .arg(vbifmt.fmt.sliced.io_size));
1246
1247 // V4L2_MPEG_STREAM_VBI_FMT_NONE = 0, /* No VBI in the MPEG stream */
1248 // V4L2_MPEG_STREAM_VBI_FMT_IVTV = 1, /* VBI in private packets, IVTV form
1249 return SetExtControl(V4L2_CID_MPEG_STREAM_VBI_FMT,
1250 V4L2_MPEG_STREAM_VBI_FMT_IVTV,
1251 "MPEG Stream VBI format",
1252 "VBI in private packets, IVTV form");
1253
1254 }
1255
1256 return false;
1257}
static const std::array< const std::string, 8 > formats
bool GetFormats(QStringList &formats)
Definition: v4l2util.cpp:513
bool GetVideoStandard(QString &name) const
Definition: v4l2util.cpp:597
QString m_deviceName
Definition: v4l2util.h:104
bool HasTuner(void) const
Definition: v4l2util.cpp:692
bool ResumeEncoding(void)
Definition: v4l2util.cpp:1213
static bool OpenVBI(const QString &vbi_dev_name)
Definition: v4l2util.cpp:1218
bool Open(const QString &dev_name, const QString &vbi_dev_name="")
Definition: v4l2util.cpp:34
bool SetVideoBitrate(int value)
Definition: v4l2util.cpp:879
DriverOption::Options m_options
Definition: v4l2util.h:103
bool StopEncoding(void)
Definition: v4l2util.cpp:1203
QString GetDriverName(void) const
Definition: v4l2util.cpp:687
bool GetResolution(int &width, int &height) const
Definition: v4l2util.cpp:657
bool log_control(struct v4l2_queryctrl &qctrl, DriverOption &drv_opt, QString &msg)
Definition: v4l2util.cpp:356
bool IsEncoder(void) const
Definition: v4l2util.cpp:702
~V4L2util(void)
Definition: v4l2util.cpp:29
bool StartEncoding(void)
Definition: v4l2util.cpp:1198
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:1208
bool SetLanguageMode(int mode)
Definition: v4l2util.cpp:1033
bool SetAudioSamplingRate(int value)
Definition: v4l2util.cpp:1086
int GetOptionValue(DriverOption::category_t cat, const QString &desc)
Definition: v4l2util.cpp:568
bool SetExtControl(int request, int value, const QString &ctrl_desc, const QString &value_desc)
Definition: v4l2util.cpp:742
static QString ctrlflags_toString(uint32_t flags)
Definition: v4l2util.cpp:134
bool SetSlicedVBI(VBIMode::vbimode_t vbimode)
Definition: v4l2util.cpp:1223
bool SetEncoderState(int mode, const QString &desc)
Definition: v4l2util.cpp:1179
bool SetVideoAspect(int value)
Definition: v4l2util.cpp:837
bool UserAdjustableResolution(void) const
Definition: v4l2util.cpp:711
QString DriverName(void) const
Definition: v4l2util.h:50
QString GetDeviceName(void) const
Definition: v4l2util.cpp:682
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:998
bool HasAudioSupport(void) const
Definition: v4l2util.cpp:697
bool SetStreamType(int value)
Definition: v4l2util.cpp:823
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:431
int GetStreamType(void) const
Definition: v4l2util.cpp:802
bool GetOptions(DriverOption::Options &options)
Definition: v4l2util.cpp:540
QString m_cardName
Definition: v4l2util.h:106
uint32_t GetCapabilities(void) const
Definition: v4l2util.cpp:677
int GetExtControl(int request, const QString &ctrl_desc="") const
Definition: v4l2util.cpp:719
bool SetAudioInput(int value)
Definition: v4l2util.cpp:929
bool SetResolution(uint32_t width, uint32_t height)
Definition: v4l2util.cpp:893
static QString StreamTypeDesc(int value)
Definition: v4l2util.cpp:782
bool SetVideoBitrateMode(int value)
Definition: v4l2util.cpp:862
QString m_driverName
Definition: v4l2util.h:105
bool SetVideoBitratePeak(int value)
Definition: v4l2util.cpp:886
int m_vbiFd
Definition: v4l2util.h:102
void Close(void)
Definition: v4l2util.cpp:83
bool SetAudioBitrateL2(int value)
Definition: v4l2util.cpp:1123
int GetSignalStrength(void) const
Definition: v4l2util.cpp:636
bool m_haveQueryExtCtrl
Definition: v4l2util.h:109
bool SetAudioCodec(int value)
Definition: v4l2util.cpp:954
vbimode_t
Definition: tv.h:11
@ PAL_TT
Definition: tv.h:13
#define close
Definition: compat.h:28
#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:204
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
vbimode
Definition: vbilut.h:7