MythTV  master
v4l2util.cpp
Go to the documentation of this file.
1 #include "v4l2util.h"
2 #include "mythlogging.h"
3 
4 #include <sys/ioctl.h>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <fcntl.h>
8 #include <unistd.h>
9 
10 #include <QRegularExpression>
11 
12 #define v4l2_ioctl(_FD_, _REQUEST_, _DATA_) ioctl(_FD_, _REQUEST_, _DATA_)
13 #define LOC QString("V4L2(%1): ").arg(m_deviceName)
14 
15 V4L2util::V4L2util(const QString& dev_name)
16 {
17  Open(dev_name);
18 }
19 
20 V4L2util::V4L2util(const QString& dev_name, const QString& vbi_dev_name)
21  : m_fd(0)
22 {
23  Open(dev_name, vbi_dev_name);
24 }
25 
27 {
28  Close();
29 }
30 
31 bool V4L2util::Open(const QString& dev_name, const QString& vbi_dev_name)
32 {
33  if (m_fd >= 0 && dev_name == m_deviceName)
34  return true;
35 
36  Close();
37 
38  m_fd = open(dev_name.toLatin1().constData(), O_RDWR);
39  if (m_fd < 0)
40  {
41  LOG(VB_GENERAL, LOG_ERR, LOC +
42  QString("Could not open '%1': ").arg(dev_name) + ENO);
43  return false;
44  }
45  m_deviceName = dev_name;
46 
47  struct v4l2_query_ext_ctrl qc {};
48  qc.id = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND;
49  m_haveQueryExtCtrl = (v4l2_ioctl(m_fd, VIDIOC_QUERY_EXT_CTRL, &qc) == 0);
50 
51  m_cardName.clear();
52  m_driverName.clear();
53  m_version = 0;
54  m_capabilities = 0;
55 
56  struct v4l2_capability capability {};
57  if (ioctl(m_fd, VIDIOC_QUERYCAP, &capability) >= 0)
58  {
59  m_cardName = QString::fromLatin1((const char*)capability.card);
60  m_driverName = QString::fromLatin1((const char*)capability.driver);
61  m_version = capability.version;
62  m_capabilities = capability.capabilities;
63  }
64  else
65  {
66  Close();
67  return false;
68  }
69 
70  if (!m_driverName.isEmpty())
71  m_driverName.remove( QRegExp("\\[[0-9]\\]$") );
72 
73  OpenVBI(vbi_dev_name);
74 
75  LOG(VB_CHANNEL, LOG_INFO, LOC + "Opened");
76  return true;
77 }
78 
79 void V4L2util::Close()
80 {
81  if (m_fd >= 0)
82  {
83  close(m_fd);
84  LOG(VB_CHANNEL, LOG_INFO, LOC + "Closed");
85  }
86  m_fd = -1;
87  m_options.clear();
88 }
89 
90 bool V4L2util::HasStreaming(void) const
91 {
92  if (m_capabilities ^ V4L2_CAP_STREAMING)
93  return false;
94 
95  struct v4l2_requestbuffers reqbuf {};
96 
97  if (-1 == ioctl (m_fd, VIDIOC_REQBUFS, &reqbuf))
98  {
99  if (errno == EINVAL)
100  {
101  LOG(VB_CHANNEL, LOG_INFO, LOC +
102  "Video capturing or mmap-streaming is not supported");
103  }
104  else
105  {
106  LOG(VB_CHANNEL, LOG_WARNING, LOC + "VIDIOC_REQBUFS" + ENO);
107  }
108  return false;
109  }
110 
111  return true;
112 }
113 
114 bool V4L2util::HasSlicedVBI(void) const
115 {
116  return (m_capabilities & V4L2_CAP_SLICED_VBI_CAPTURE) != 0U;
117 }
118 
119 void V4L2util::bitmask_toString(QString& result, uint32_t flags,
120  uint32_t mask, const QString& desc)
121 {
122  if (flags& mask)
123  {
124  if (!result.isEmpty())
125  result += '|';
126  result += desc;
127  }
128 }
129 
130 QString V4L2util::ctrlflags_toString(uint32_t flags)
131 {
132  QString result;
133 
134  bitmask_toString(result, flags, V4L2_CTRL_FLAG_DISABLED,
135  "disabled");
136  bitmask_toString(result, flags, V4L2_CTRL_FLAG_GRABBED,
137  "grabbed");
138  bitmask_toString(result, flags, V4L2_CTRL_FLAG_READ_ONLY,
139  "read-only");
140  bitmask_toString(result, flags, V4L2_CTRL_FLAG_UPDATE,
141  "update");
142  bitmask_toString(result, flags, V4L2_CTRL_FLAG_INACTIVE,
143  "inactive");
144  bitmask_toString(result, flags, V4L2_CTRL_FLAG_SLIDER,
145  "slider");
146  bitmask_toString(result, flags, V4L2_CTRL_FLAG_WRITE_ONLY,
147  "write-only");
148  bitmask_toString(result, flags, V4L2_CTRL_FLAG_VOLATILE,
149  "volatile");
150  bitmask_toString(result, flags, V4L2_CTRL_FLAG_HAS_PAYLOAD,
151  "has-payload");
152  bitmask_toString(result, flags, V4L2_CTRL_FLAG_EXECUTE_ON_WRITE,
153  "execute-on-write");
154 
155  return result;
156 }
157 
159 {
160  switch (type)
161  {
162  case V4L2_CTRL_TYPE_INTEGER:
163  return "int";
164  case V4L2_CTRL_TYPE_INTEGER64:
165  return "int64";
166  case V4L2_CTRL_TYPE_STRING:
167  return "str";
168  case V4L2_CTRL_TYPE_BOOLEAN:
169  return "bool";
170  case V4L2_CTRL_TYPE_MENU:
171  return "menu";
172  case V4L2_CTRL_TYPE_INTEGER_MENU:
173  return "intmenu";
174  case V4L2_CTRL_TYPE_BUTTON:
175  return "button";
176  case V4L2_CTRL_TYPE_BITMASK:
177  return "bitmask";
178  case V4L2_CTRL_TYPE_U8:
179  return "u8";
180  case V4L2_CTRL_TYPE_U16:
181  return "u16";
182  case V4L2_CTRL_TYPE_U32:
183  return "u32";
184  default:
185  return "unknown";
186  }
187 }
188 
189 void V4L2util::log_qctrl(struct v4l2_queryctrl& queryctrl,
190  DriverOption& drv_opt, QString& msg)
191 {
192  struct v4l2_querymenu qmenu {};
193  QString nameStr((char *)queryctrl.name);
194 
195  qmenu.id = queryctrl.id;
196 
197  // Replace non-printable with _
198  nameStr.replace(QRegularExpression("[^a-zA-Z\\d\\s]"), "_");
199 
200  drv_opt.m_name = nameStr;
201  drv_opt.m_minimum = queryctrl.minimum;
202  drv_opt.m_maximum = queryctrl.maximum;
203  drv_opt.m_step = queryctrl.step;
204  drv_opt.m_default_value = queryctrl.default_value;;
205 
206  if (nameStr == "Stream Type")
208  else if (nameStr == "Video Encoding")
210  else if (nameStr == "Video Aspect")
212  else if (nameStr == "Video B Frames")
214  else if (nameStr == "Video GOP Size")
216  else if (nameStr == "Video Bitrate Mode")
218  else if (nameStr == "Video Bitrate")
220  else if (nameStr == "Video Peak Bitrate")
222  else if (nameStr == "Audio Encoding")
224  else if (nameStr == "Audio Bitrate Mode")
226  else if (nameStr == "Audio Bitrate")
228  else if (nameStr == "Brightness")
230  else if (nameStr == "Contrast")
232  else if (nameStr == "Saturation")
234  else if (nameStr == "Hue")
235  drv_opt.m_category = DriverOption::HUE;
236  else if (nameStr == "Sharpness")
238  else if (nameStr == "Volume")
240  else
242 
243  switch (queryctrl.type)
244  {
245  case V4L2_CTRL_TYPE_INTEGER:
246  case V4L2_CTRL_TYPE_INTEGER64:
247  case V4L2_CTRL_TYPE_U8:
248  case V4L2_CTRL_TYPE_U16:
249  case V4L2_CTRL_TYPE_U32:
250  msg = QString("%1 : min=%2 max=%3 step=%4 default=%5")
251  .arg(QString("%1 (%2)").arg(nameStr)
252  .arg(queryctrl_toString(queryctrl.type)), 31, QChar(' '))
253  .arg(queryctrl.minimum)
254  .arg(queryctrl.maximum)
255  .arg(queryctrl.step)
256  .arg(queryctrl.default_value);
257  drv_opt.m_type = DriverOption::INTEGER;
258  break;
259  case V4L2_CTRL_TYPE_STRING:
260  msg = QString("%1 : min=%2 max=%3 step=%4")
261  .arg(QString("%1 (%2)").arg(nameStr)
262  .arg(queryctrl_toString(queryctrl.type)), 31, QChar(' '))
263  .arg(queryctrl.minimum)
264  .arg(queryctrl.maximum)
265  .arg(queryctrl.step);
266  drv_opt.m_type = DriverOption::STRING;
267  break;
268  case V4L2_CTRL_TYPE_BOOLEAN:
269  msg = QString("%1 : default=%2")
270  .arg(QString("%1 (%2)").arg(nameStr)
271  .arg(queryctrl_toString(queryctrl.type)), 31, QChar(' '))
272  .arg(queryctrl.default_value);
273  drv_opt.m_type = DriverOption::BOOLEAN;
274  break;
275  case V4L2_CTRL_TYPE_MENU:
276  case V4L2_CTRL_TYPE_INTEGER_MENU:
277  {
278  msg = QString("%1 : min=%3 max=%4 default=%5")
279  .arg(QString("%1 (%2)").arg(nameStr)
280  .arg(queryctrl_toString(queryctrl.type)), 31, QChar(' '))
281  .arg(queryctrl.minimum)
282  .arg(queryctrl.maximum)
283  .arg(queryctrl.default_value);
284 #if 0
285  struct v4l2_querymenu querymenu = { 0, };
286  memset (&querymenu, 0, sizeof (querymenu));
287  querymenu.id = queryctrl.id;
288 
289  for (querymenu.index = queryctrl.minimum;
290  static_cast<int>(querymenu.index) <= queryctrl.maximum;
291  ++querymenu.index)
292  {
293  drv_opt.menu.clear();
294  if (0 == ioctl(m_fd, VIDIOC_QUERYMENU, &querymenu))
295  {
296  msg += QString(" menu>%1").arg((char *)querymenu.name);
297  drv_opt.menu[querymenu.index] =
298  QString((char *)querymenu.name);
299  }
300  }
301 #endif
302  drv_opt.m_type = DriverOption::MENU;
303  break;
304  }
305  case V4L2_CTRL_TYPE_BUTTON:
306  msg = QString("%1 :")
307  .arg(QString("%1 (%2)").arg(nameStr)
308  .arg(queryctrl_toString(queryctrl.type)), 31, QChar(' '));
309  drv_opt.m_type = DriverOption::BUTTON;
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)
314  .arg(queryctrl_toString(queryctrl.type)), 31, QChar(' '))
315  .arg(queryctrl.maximum, 8, 16, QChar(' '))
316  .arg(queryctrl.default_value, 8, 16, QChar(' '));
317  drv_opt.m_type = DriverOption::BITMASK;
318  break;
319 
320  default:
321  msg = QString("%1 : type=%2")
322  .arg(QString("%1 (%2)").arg(nameStr)
323  .arg(queryctrl_toString(queryctrl.type)), 31, QChar(' '))
324  .arg(queryctrl.type);
326  break;
327  }
328 
329  if (queryctrl.flags)
330  msg += QString(" flags=%1").arg(ctrlflags_toString(queryctrl.flags));
331 
332  if (queryctrl.type == V4L2_CTRL_TYPE_MENU ||
333  queryctrl.type == V4L2_CTRL_TYPE_INTEGER_MENU)
334  {
335  for (int idx = queryctrl.minimum; idx <= queryctrl.maximum; ++idx)
336  {
337  qmenu.index = idx;
338  if (v4l2_ioctl(m_fd, VIDIOC_QUERYMENU, &qmenu))
339  continue;
340 
341  drv_opt.m_menu[idx] = QString((char *)qmenu.name);
342  if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
343  msg += QString("\t\t%1: %2").arg(idx).arg((char *)qmenu.name);
344  else
345  msg += QString("\t\t%1: %2 (0x%3)")
346  .arg(idx).arg(qmenu.value)
347  .arg(qmenu.value, 0, 16, QChar('0'));
348  }
349  }
350 
351  LOG(VB_CHANNEL, LOG_INFO, LOC + msg);
352 }
353 
354 bool 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 (v4l2_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 (v4l2_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 {
430  if (!options.contains(DriverOption::VIDEO_ENCODING))
431  {
432  DriverOption drv_opt;
434  drv_opt.m_name = "Video Encoding";
435  drv_opt.m_minimum = drv_opt.m_maximum = drv_opt.m_default_value =
436  V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
437  drv_opt.m_menu[drv_opt.m_default_value] = "MPEG-2 Video";
438  options[drv_opt.m_category] = drv_opt;
439  }
440 
441  if (!options.contains(DriverOption::AUDIO_ENCODING))
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_default_value =
449  V4L2_MPEG_AUDIO_ENCODING_LAYER_2;
450  drv_opt.m_menu[drv_opt.m_default_value] = "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_default_value =
456  V4L2_MPEG_AUDIO_ENCODING_LAYER_2;
457  drv_opt.m_menu[drv_opt.m_default_value] = "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_default_value =
464  V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000;
465  drv_opt.m_menu[drv_opt.m_default_value] = "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_default_value =
472  V4L2_TUNER_MODE_STEREO;
473  drv_opt.m_menu[drv_opt.m_default_value] = "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  if (Imenu.key() < minimum) minimum = Imenu.key();
490  if (Imenu.key() > maximum) maximum = Imenu.key();
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 
510 bool V4L2util::GetFormats(QStringList& formats)
511 {
512  struct v4l2_fmtdesc vid_fmtdesc {};
513  const char *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  .arg((char *)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  {
543  options = m_options;
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 
561  options = m_options;
562  return true;
563 }
564 
565 int V4L2util::GetOptionValue(DriverOption::category_t cat, const QString& desc)
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 
594 bool V4L2util::GetVideoStandard(QString& name) const
595 {
596  v4l2_std_id std_id;
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 
633 int V4L2util::GetSignalStrength(void) const
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 
654 bool 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 
674 bool V4L2util::HasTuner(void) const
675 {
676  return (m_capabilities & V4L2_CAP_TUNER) != 0U;
677 }
678 
679 bool V4L2util::HasAudioSupport(void) const
680 {
681  return (m_capabilities & V4L2_CAP_AUDIO) != 0U;
682 }
683 
684 bool V4L2util::IsEncoder(void) const
685 {
686  struct v4l2_queryctrl qctrl {};
687 
688  qctrl.id = V4L2_CTRL_CLASS_MPEG | V4L2_CTRL_FLAG_NEXT_CTRL;
689  return (0 == ioctl (m_fd, VIDIOC_QUERYCTRL, &qctrl) &&
690  V4L2_CTRL_ID2CLASS (qctrl.id) == V4L2_CTRL_CLASS_MPEG);
691 }
692 
693 bool V4L2util::UserAdjustableResolution(void) const
694 {
695  // I have not been able to come up with a way of querying the
696  // driver to answer this question.
697 
698  return m_driverName != "hdpvr";
699 }
700 
701 int V4L2util::GetExtControl(int request, const QString& ctrl_desc) const
702 {
703  struct v4l2_ext_control ctrl {};
704  struct v4l2_ext_controls ctrls {};
705 
706  ctrl.id = request;
707 
708  ctrls.count = 1;
709  ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
710  ctrls.controls = &ctrl;
711 
712  if (ioctl(m_fd, VIDIOC_G_EXT_CTRLS, &ctrls) != 0)
713  {
714  LOG(VB_GENERAL, LOG_ERR, LOC +
715  QString("Failed to retrieve current %1 value.")
716  .arg(ctrl_desc) + ENO);
717  return -1;
718  }
719 
720  return ctrls.controls->value;
721 }
722 
723 
724 bool V4L2util::SetExtControl(int request, int value, const QString& ctrl_desc,
725  const QString& value_desc)
726 {
727  int current_value = GetExtControl(request, ctrl_desc);
728 
729  if (current_value < 0)
730  return false;
731  if (current_value == value)
732  {
733  LOG(VB_CHANNEL, LOG_INFO, LOC +
734  QString("%1 value is already %2 (%3).").arg(ctrl_desc)
735  .arg(value_desc).arg(value));
736  return true;
737  }
738 
739  struct v4l2_ext_control ctrl {};
740  struct v4l2_ext_controls ctrls {};
741 
742  ctrl.id = request;
743  ctrl.value = value;
744 
745  ctrls.count = 1;
746  ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
747  ctrls.controls = &ctrl;
748 
749  if (ioctl(m_fd, VIDIOC_S_EXT_CTRLS, &ctrls) < 0)
750  {
751  LOG(VB_GENERAL, LOG_ERR, LOC +
752  QString("Failed to set %1 value to %2 (%3).")
753  .arg(ctrl_desc).arg(value_desc).arg(value) + ENO);
754  return false;
755  }
756 
757  LOG(VB_CHANNEL, LOG_INFO, LOC +
758  QString("%1 value set to %2 (%3).").arg(ctrl_desc)
759  .arg(value_desc).arg(value));
760 
761  return true;
762 }
763 
764 QString V4L2util::StreamTypeDesc(int value)
765 {
766  switch (value)
767  {
768  case V4L2_MPEG_STREAM_TYPE_MPEG2_PS:
769  return "MPEG-2 program stream";
770  case V4L2_MPEG_STREAM_TYPE_MPEG2_TS:
771  return "MPEG-2 transport stream";
772  case V4L2_MPEG_STREAM_TYPE_MPEG1_SS:
773  return "MPEG-1 system stream";
774  case V4L2_MPEG_STREAM_TYPE_MPEG2_DVD:
775  return "MPEG-2 DVD-compatible stream";
776  case V4L2_MPEG_STREAM_TYPE_MPEG1_VCD:
777  return "MPEG-1 VCD-compatible stream";
778  case V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD:
779  return "MPEG-2 SVCD-compatible stream";
780  }
781  return "Unknown";
782 }
783 
784 int V4L2util::GetStreamType(void) const
785 {
786  int type;
787 
788  if (DriverName().startsWith("saa7164"))
789  {
790  // The saa7164 driver reports that it can do TS, but it doesn't work!
791  type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS;
792  }
793  else
794  type = GetExtControl(V4L2_CID_MPEG_STREAM_TYPE, "Stream Type");
795 
796  LOG(VB_CHANNEL, LOG_INFO, LOC +
797  QString("MPEG Stream Type is currently set to %1 (%2)")
798  .arg(StreamTypeDesc(type)).arg(type));
799 
800  return type;
801 }
802 
803 bool V4L2util::SetStreamType(int value)
804 {
805  QString desc;
806 
807  if (DriverName().startsWith("saa7164") ||
808  DriverName().startsWith("ivtv"))
809  {
810  // The saa7164 driver reports that it can do TS, but it doesn't work!
811  value = V4L2_MPEG_STREAM_TYPE_MPEG2_PS;
812  }
813 
814  return SetExtControl(V4L2_CID_MPEG_STREAM_TYPE, value,
815  "MPEG Stream type", StreamTypeDesc(value));
816 }
817 
818 // Video controls
819 bool V4L2util::SetVideoAspect(int value)
820 {
821  QString desc;
822  switch (value)
823  {
824  case V4L2_MPEG_VIDEO_ASPECT_1x1:
825  desc = "Square";
826  break;
827  case V4L2_MPEG_VIDEO_ASPECT_4x3:
828  desc = "4x3";
829  break;
830  case V4L2_MPEG_VIDEO_ASPECT_16x9:
831  desc = "16x9";
832  break;
833  case V4L2_MPEG_VIDEO_ASPECT_221x100:
834  desc = "221x100";
835  break;
836  default:
837  desc = "Unknown";
838  }
839 
840  return SetExtControl(V4L2_CID_MPEG_VIDEO_ASPECT, value,
841  "Video Aspect ratio", desc);
842 }
843 
844 bool V4L2util::SetVideoBitrateMode(int value)
845 {
846  QString desc;
847  switch (value)
848  {
849  case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR:
850  desc = "VBR";
851  break;
852  case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR:
853  desc = "CBR";
854  break;
855  }
856 
857  return SetExtControl(V4L2_CID_MPEG_VIDEO_BITRATE_MODE, value,
858  "Video Bitrate Mode", desc);
859 }
860 
861 bool V4L2util::SetVideoBitrate(int value)
862 {
863  QString desc = QString("%1").arg(value);
864  return SetExtControl(V4L2_CID_MPEG_VIDEO_BITRATE, value,
865  "Video Average Bitrate", desc);
866 }
867 
868 bool V4L2util::SetVideoBitratePeak(int value)
869 {
870  QString desc = QString("%1").arg(value);
871  return SetExtControl(V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, value,
872  "Video Peak Bitrate", desc);
873 }
874 
875 bool V4L2util::SetResolution(uint32_t width, uint32_t height)
876 {
877  struct v4l2_format vfmt {};
878 
879  vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
880 
881  if (ioctl(m_fd, VIDIOC_G_FMT, &vfmt) < 0)
882  {
883  LOG(VB_GENERAL, LOG_ERR, LOC +
884  "SetResolution() -- Error getting format" + ENO);
885  return false;
886  }
887 
888  if ((vfmt.fmt.pix.width == width) && (vfmt.fmt.pix.height == height))
889  {
890  LOG(VB_RECORD, LOG_INFO, LOC + QString("Resolution is already %1x%2")
891  .arg(width).arg(height));
892  return true;
893  }
894 
895  vfmt.fmt.pix.width = width;
896  vfmt.fmt.pix.height = height;
897 
898  if (ioctl(m_fd, VIDIOC_S_FMT, &vfmt) < 0)
899  {
900  LOG(VB_GENERAL, LOG_ERR, LOC +
901  "SetResolution() -- Error setting format" + ENO);
902  return false;
903  }
904 
905  LOG(VB_RECORD, LOG_INFO, LOC + QString("Resolution set to %1x%2")
906  .arg(width).arg(height));
907  return true;
908 }
909 
910 // Audio controls
911 bool V4L2util::SetAudioInput(int value)
912 {
913  struct v4l2_audio ain {};
914 
915  ain.index = value;
916  if (ioctl(m_fd, VIDIOC_ENUMAUDIO, &ain) < 0)
917  {
918  LOG(VB_GENERAL, LOG_WARNING, LOC +
919  QString("Failed to retrieve audio input.") + ENO);
920  return false;
921  }
922 
923  ain.index = value;
924  if (ioctl(m_fd, VIDIOC_S_AUDIO, &ain) < 0)
925  {
926  LOG(VB_GENERAL, LOG_WARNING,
927  LOC + QString("Failed to set audio input to %1.").arg(value) + ENO);
928  return false;
929  }
930 
931  LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Audio input set to %1.")
932  .arg(value));
933  return true;
934 }
935 
936 bool V4L2util::SetAudioCodec(int value)
937 {
938 #if 0
939  if (DriverName().startsWith("ivtv"))
940  value = V4L2_MPEG_AUDIO_ENCODING_LAYER_2;
941 #endif
942 
943  QString desc;
944  switch (value)
945  {
946  case V4L2_MPEG_AUDIO_ENCODING_LAYER_1:
947  desc = "Layer I";
948  break;
949  case V4L2_MPEG_AUDIO_ENCODING_LAYER_2:
950  desc = "Layer II";
951  break;
952  case V4L2_MPEG_AUDIO_ENCODING_LAYER_3:
953  desc = "Layer III";
954  break;
955  case V4L2_MPEG_AUDIO_ENCODING_AAC:
956  desc = "AAC";
957  break;
958  case V4L2_MPEG_AUDIO_ENCODING_AC3:
959  desc = "AC3";
960  break;
961  default:
962  desc = "Unknown";
963  break;
964  }
965 
966 #if 0
967  if (DriverName().startsWith("ivtv"))
968  {
969  LOG(VB_CHANNEL, LOG_INFO, LOC +
970  QString("Overriding AudioCodec for %1 to %2")
971  .arg(DriverName()).arg(desc));
972  }
973 #endif
974 
975  return SetExtControl(V4L2_CID_MPEG_AUDIO_ENCODING, value,
976  "Audio Codec", desc);
977 }
978 
979 
980 bool V4L2util::SetVolume(int volume)
981 {
982  // Get volume min/max values
983  struct v4l2_queryctrl qctrl {};
984  qctrl.id = V4L2_CID_AUDIO_VOLUME;
985  if ((ioctl(m_fd, VIDIOC_QUERYCTRL, &qctrl) < 0) ||
986  (qctrl.flags & V4L2_CTRL_FLAG_DISABLED))
987  {
988  LOG(VB_CHANNEL, LOG_WARNING,
989  LOC + "SetRecordingVolume() -- Audio volume control not supported.");
990  return false;
991  }
992 
993  // calculate volume in card units.
994  int range = qctrl.maximum - qctrl.minimum;
995  int value = (int) ((range * volume * 0.01F) + qctrl.minimum);
996  int ctrl_volume = std::min(qctrl.maximum, std::max(qctrl.minimum, value));
997 
998  // Set recording volume
999  struct v4l2_control ctrl {};
1000  ctrl.id = V4L2_CID_AUDIO_VOLUME;
1001  ctrl.value = ctrl_volume;
1002 
1003  if (ioctl(m_fd, VIDIOC_S_CTRL, &ctrl) < 0)
1004  {
1005  LOG(VB_GENERAL, LOG_WARNING, LOC +
1006  "SetRecordingVolume() -- Failed to set recording volume" + ENO);
1007 // "If you are using an AverMedia M179 card this is normal."
1008  return false;
1009  }
1010 
1011  LOG(VB_RECORD, LOG_INFO, LOC + "SetRecordingVolume() -- volume set.");
1012  return true;
1013 }
1014 
1015 bool V4L2util::SetLanguageMode(int mode)
1016 {
1017  struct v4l2_tuner vt {};
1018 
1019  if (ioctl(m_fd, VIDIOC_G_TUNER, &vt) < 0)
1020  {
1021  LOG(VB_CHANNEL, LOG_WARNING, LOC +
1022  "SetLanguageMode() -- Failed to retrieve audio mode" + ENO);
1023  return false;
1024  }
1025 
1026  vt.audmode = mode;
1027 
1028  if (ioctl(m_fd, VIDIOC_S_TUNER, &vt) < 0)
1029  {
1030  LOG(VB_CHANNEL, LOG_WARNING, LOC +
1031  "SetLanguageMode -- Failed to set audio mode" + ENO);
1032  return false;
1033  }
1034 
1035  QString desc;
1036  switch (mode)
1037  {
1038  case V4L2_TUNER_MODE_MONO:
1039  desc = "Mono";
1040  break;
1041  case V4L2_TUNER_MODE_STEREO:
1042  desc = "Stereo";
1043  break;
1044 #if 0
1045  case V4L2_TUNER_MODE_LANG2:
1046  desc = "Lang2";
1047  break;
1048 #endif
1049  case V4L2_TUNER_MODE_SAP:
1050  desc = "SAP";
1051  break;
1052  case V4L2_TUNER_MODE_LANG1:
1053  desc = "LANG1";
1054  break;
1055  case V4L2_TUNER_MODE_LANG1_LANG2:
1056  desc = "LANG1&Lang2";
1057  break;
1058  default:
1059  desc = "Unknown";
1060  break;
1061  }
1062 
1063  LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Language Mode set to %1 (%2)")
1064  .arg(desc).arg(mode));
1065  return true;
1066 }
1067 
1068 bool V4L2util::SetAudioSamplingRate(int value)
1069 {
1070  QString desc;
1071 
1072 #if 0
1073  if (DriverName().startsWith("ivtv"))
1074  value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000;
1075 #endif
1076 
1077  switch (value)
1078  {
1079  case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100:
1080  desc = "44.1kHz";
1081  break;
1082  case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000:
1083  desc = "48kHz";
1084  break;
1085  case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000:
1086  desc = "32kHz";
1087  break;
1088  default:
1089  desc = "Unknown";
1090  }
1091 
1092 #if 0
1093  if (DriverName().startsWith("ivtv"))
1094  {
1095  LOG(VB_CHANNEL, LOG_INFO, LOC +
1096  QString("Overriding sampling frequence for %1 to %2")
1097  .arg(DriverName()).arg(desc));
1098  }
1099 #endif
1100 
1101  return SetExtControl(V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, value,
1102  "Audio Sample Rate", desc);
1103 }
1104 
1105 bool V4L2util::SetAudioBitrateL2(int value)
1106 {
1107  QString desc;
1108  switch (value)
1109  {
1110  case V4L2_MPEG_AUDIO_L2_BITRATE_32K:
1111  desc = "32K";
1112  break;
1113  case V4L2_MPEG_AUDIO_L2_BITRATE_48K:
1114  desc = "48K";
1115  break;
1116  case V4L2_MPEG_AUDIO_L2_BITRATE_56K:
1117  desc = "56K";
1118  break;
1119  case V4L2_MPEG_AUDIO_L2_BITRATE_64K:
1120  desc = "64K";
1121  break;
1122  case V4L2_MPEG_AUDIO_L2_BITRATE_80K:
1123  desc = "80K";
1124  break;
1125  case V4L2_MPEG_AUDIO_L2_BITRATE_96K:
1126  desc = "96K";
1127  break;
1128  case V4L2_MPEG_AUDIO_L2_BITRATE_112K:
1129  desc = "112K";
1130  break;
1131  case V4L2_MPEG_AUDIO_L2_BITRATE_128K:
1132  desc = "128K";
1133  break;
1134  case V4L2_MPEG_AUDIO_L2_BITRATE_160K:
1135  desc = "160K";
1136  break;
1137  case V4L2_MPEG_AUDIO_L2_BITRATE_192K:
1138  desc = "192K";
1139  break;
1140  case V4L2_MPEG_AUDIO_L2_BITRATE_224K:
1141  desc = "224K";
1142  break;
1143  case V4L2_MPEG_AUDIO_L2_BITRATE_256K:
1144  desc = "256K";
1145  break;
1146  case V4L2_MPEG_AUDIO_L2_BITRATE_320K:
1147  desc = "320K";
1148  break;
1149  case V4L2_MPEG_AUDIO_L2_BITRATE_384K:
1150  desc = "384K";
1151  break;
1152  default:
1153  desc = "Unknown";
1154  }
1155 
1156  return SetExtControl(V4L2_CID_MPEG_AUDIO_L2_BITRATE, value,
1157  "Audio L2 Bitrate", desc);
1158 }
1159 
1160 // Actions
1161 bool V4L2util::SetEncoderState(int mode, const QString& desc)
1162 {
1163  struct v4l2_encoder_cmd command {};
1164 
1165  command.cmd = mode;
1166  if (ioctl(m_fd, VIDIOC_ENCODER_CMD, &command) != 0 && errno != ENOTTY)
1167  {
1168  // Some drivers do not support this ioctl at all. It is marked as
1169  // "experimental" in the V4L2 API spec. These drivers return EINVAL
1170  // in older kernels and ENOTTY in 3.1+
1171  LOG(VB_CHANNEL, LOG_WARNING, LOC +
1172  QString("SetEncoderState(%1) -- failed").arg(desc) + ENO);
1173  return false;
1174  }
1175  LOG(VB_CHANNEL, LOG_INFO, LOC +
1176  QString("SetEncoderState(%1) -- success").arg(desc));
1177  return true;
1178 }
1179 
1180 bool V4L2util::StartEncoding(void)
1181 {
1182  return SetEncoderState(V4L2_ENC_CMD_START, "Start");
1183 }
1184 
1185 bool V4L2util::StopEncoding(void)
1186 {
1187  return SetEncoderState(V4L2_ENC_CMD_STOP, "Stop");
1188 }
1189 
1190 bool V4L2util::PauseEncoding(void)
1191 {
1192  return SetEncoderState(V4L2_ENC_CMD_PAUSE, "Pause");
1193 }
1194 
1195 bool V4L2util::ResumeEncoding(void)
1196 {
1197  return SetEncoderState(V4L2_ENC_CMD_RESUME, "Resume");
1198 }
1199 
1200 bool V4L2util::OpenVBI(const QString& /*vbi_dev_name*/)
1201 {
1202  return false;
1203 }
1204 
1206 {
1207  struct v4l2_format vbifmt {};
1208 
1209  vbifmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
1210  vbifmt.fmt.sliced.service_set |= (VBIMode::PAL_TT == vbimode) ?
1211  V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
1212 
1213  int fd = m_vbiFd < 0 ? m_fd : m_vbiFd;
1214 
1215  if (ioctl(fd, VIDIOC_S_FMT, &vbifmt) < 0)
1216  {
1217  LOG(VB_CHANNEL, LOG_WARNING, LOC + "ConfigureVBI() -- " +
1218  "Failed to enable VBI embedding (/dev/videoX)" + ENO);
1219  return false;
1220  }
1221 
1222  if (ioctl(fd, VIDIOC_G_FMT, &vbifmt) >= 0)
1223  {
1224  LOG(VB_RECORD, LOG_INFO,
1225  LOC + QString("VBI service: %1, io size: %2")
1226  .arg(vbifmt.fmt.sliced.service_set)
1227  .arg(vbifmt.fmt.sliced.io_size));
1228 
1229  // V4L2_MPEG_STREAM_VBI_FMT_NONE = 0, /* No VBI in the MPEG stream */
1230  // V4L2_MPEG_STREAM_VBI_FMT_IVTV = 1, /* VBI in private packets, IVTV form
1231  return SetExtControl(V4L2_CID_MPEG_STREAM_VBI_FMT,
1232  V4L2_MPEG_STREAM_VBI_FMT_IVTV,
1233  "MPEG Stream VBI format",
1234  "VBI in private packets, IVTV form");
1235 
1236  }
1237 
1238  return false;
1239 }
#define v4l2_ioctl(_FD_, _REQUEST_, _DATA_)
Definition: v4l2util.cpp:12
void log_qctrl(struct v4l2_queryctrl &queryctrl, DriverOption &drv_opt, QString &msg)
static QString StreamTypeDesc(int value)
QString m_name
Definition: driveroption.h:25
QMap< category_t, DriverOption > Options
Definition: driveroption.h:20
bool Open(const QString &dev_name, const QString &vbi_dev_name="")
Definition: v4l2util.cpp:31
bool UserAdjustableResolution(void) const
bool SetVolume(int volume)
static QString ctrlflags_toString(uint32_t flags)
bool SetLanguageMode(int mode)
bool SetStreamType(int value)
bool GetResolution(int &width, int &height) const
bool SetAudioBitrateL2(int value)
vbimode
Definition: vbilut.h:20
DriverOption::Options m_options
Definition: v4l2util.h:98
QString m_driverName
Definition: v4l2util.h:100
int32_t m_minimum
Definition: driveroption.h:27
bool SetAudioInput(int value)
bool StopEncoding(void)
bool m_haveQueryExtCtrl
Definition: v4l2util.h:104
int m_fd
Definition: v4l2util.h:96
V4L2util(void)=default
int32_t m_maximum
Definition: driveroption.h:28
category_t m_category
Definition: driveroption.h:26
uint32_t m_step
Definition: driveroption.h:31
~V4L2util(void)
Definition: v4l2util.cpp:26
bool SetSlicedVBI(const VBIMode::vbimode_t &vbimode)
const char * formats[8]
Definition: vbilut.cpp:190
bool SetVideoAspect(int value)
menu_t m_menu
Definition: driveroption.h:33
bool SetAudioSamplingRate(int value)
int GetStreamType(void) const
int GetSignalStrength(void) const
#define close
Definition: compat.h:16
bool SetEncoderState(int mode, const QString &desc)
void Close(void)
bool SetResolution(uint32_t width, uint32_t height)
int GetExtControl(int request, const QString &ctrl_desc="") const
bool GetVideoStandard(QString &name) const
vbimode_t
Definition: tv.h:9
bool PauseEncoding(void)
void SetDefaultOptions(DriverOption::Options &options)
bool OpenVBI(const QString &vbi_dev_name)
bool StartEncoding(void)
const char * name
Definition: ParseText.cpp:328
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:99
bool SetVideoBitratePeak(int value)
int m_version
Definition: v4l2util.h:102
uint32_t m_capabilities
Definition: v4l2util.h:103
bool SetVideoBitrate(int value)
bool HasStreaming(void) const
bool GetFormats(QStringList &formats)
int m_vbiFd
Definition: v4l2util.h:97
int32_t m_default_value
Definition: driveroption.h:29
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
static QString queryctrl_toString(int type)
bool HasAudioSupport(void) const
bool HasTuner(void) const
bool SetVideoBitrateMode(int value)
bool GetOptions(DriverOption::Options &options)
bool log_control(struct v4l2_queryctrl &qctrl, DriverOption &drv_opt, QString &msg)
bool SetExtControl(int request, int value, const QString &ctrl_desc, const QString &value_desc)
QString m_cardName
Definition: v4l2util.h:101
#define LOC
Definition: v4l2util.cpp:13
QString m_deviceName
Definition: v4l2util.h:99
#define V4L2_CID_PRIVATE_BASE
Definition: ivtv_myth.h:62
bool IsEncoder(void) const
bool ResumeEncoding(void)
bool SetAudioCodec(int value)
type_t m_type
Definition: driveroption.h:34
bool HasSlicedVBI(void) const
QString DriverName(void) const
Definition: v4l2util.h:45
int GetOptionValue(DriverOption::category_t cat, const QString &desc)
static void bitmask_toString(QString &result, uint32_t flags, uint32_t mask, const QString &desc)