MythTV  master
v4lchannel.cpp
Go to the documentation of this file.
1 // Std C headers
2 #include <cstdio>
3 #include <cstdlib>
4 #include <cerrno>
5 
6 // POSIX headers
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <sys/ioctl.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <sys/wait.h>
13 
14 // C++ headers
15 #include <algorithm>
16 #include <iostream>
17 
18 #include <linux/videodev2.h>
19 
20 // MythTV headers
22 #include "libmythbase/mythdb.h"
24 
25 #include "cardutil.h"
26 #include "channelutil.h"
27 #include "frequencies.h"
28 #include "tv_rec.h"
29 #include "v4lchannel.h"
30 
31 #define DEBUG_ATTRIB 1
32 
33 #define LOC QString("V4LChannel[%1](%2): ") \
34  .arg(m_inputId).arg(GetDevice())
35 
36 static int format_to_mode(const QString &fmt);
37 static QString mode_to_format(int mode);
38 
40 {
42 }
43 
44 bool V4LChannel::Init(QString &startchannel, bool setchan)
45 {
46  if (setchan)
47  {
48  SetFormat(gCoreContext->GetSetting("TVFormat"));
50  }
51  return ChannelBase::Init(startchannel, setchan);
52 }
53 
54 bool V4LChannel::Open(void)
55 {
56 #if FAKE_VIDEO
57  return true;
58 #endif
59  if (m_videoFd >= 0)
60  return true;
61 
62  QByteArray ascii_device = m_device.toLatin1();
63  m_videoFd = open(ascii_device.constData(), O_RDWR);
64  if (m_videoFd < 0)
65  {
66  LOG(VB_GENERAL, LOG_ERR, LOC + "Can't open video device." + ENO);
67  return false;
68  }
69 
70  uint32_t version = 0;
71  uint32_t capabilities = 0;
73  version, capabilities))
74  {
75  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to query capabilities." + ENO);
76  Close();
77  return false;
78  }
79 
80  m_hasStreamIO = ((capabilities & V4L2_CAP_STREAMING) != 0U);
81  m_hasStdIO = ((capabilities & V4L2_CAP_READWRITE) != 0U);
82  m_hasAsyncIO = ((capabilities & V4L2_CAP_ASYNCIO) != 0U);
83  m_hasTuner = ((capabilities & V4L2_CAP_TUNER) != 0U);
84  m_hasSlicedVbi = ((capabilities & V4L2_CAP_SLICED_VBI_CAPTURE) != 0U);
85 
86  if (m_driverName == "bttv" || m_driverName == "cx8800" || m_driverName == "cx88_blackbird"
87  || m_driverName == "saa7164")
88  m_hasStreamIO = false; // driver workaround, see #9825, #10519 and #12336
89 
90  LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Device name '%1' driver '%2'.")
91  .arg(m_deviceName, m_driverName));
92 
93  LOG(VB_CHANNEL, LOG_INFO, LOC +
94  QString("v4l2: stream io: %2 std io: %3 async io: %4 "
95  "tuner %5 sliced vbi %6")
96  .arg(m_hasStreamIO).arg(m_hasStdIO).arg(m_hasAsyncIO)
97  .arg(m_hasTuner).arg(m_hasSlicedVbi));
98 
99  if (!InitializeInputs())
100  {
101  Close();
102  return false;
103  }
104 
105  SetFormat("Default");
106 
107  return true;
108 }
109 
111 {
112  if (m_videoFd >= 0)
113  close(m_videoFd);
114  m_videoFd = -1;
115 }
116 
117 void V4LChannel::SetFd(int fd)
118 {
119  if (fd != m_videoFd)
120  Close();
121  m_videoFd = (fd >= 0) ? fd : -1;
122 }
123 
124 static int format_to_mode(const QString &fmt)
125 {
126  if (fmt == "PAL-BG")
127  return V4L2_STD_PAL_BG;
128  if (fmt == "PAL-D")
129  return V4L2_STD_PAL_D;
130  if (fmt == "PAL-DK")
131  return V4L2_STD_PAL_DK;
132  if (fmt == "PAL-I")
133  return V4L2_STD_PAL_I;
134  if (fmt == "PAL-60")
135  return V4L2_STD_PAL_60;
136  if (fmt == "SECAM")
137  return V4L2_STD_SECAM;
138  if (fmt == "SECAM-D")
139  return V4L2_STD_SECAM_D;
140  if (fmt == "SECAM-DK")
141  return V4L2_STD_SECAM_DK;
142  if (fmt == "PAL-NC")
143  return V4L2_STD_PAL_Nc;
144  if (fmt == "PAL-M")
145  return V4L2_STD_PAL_M;
146  if (fmt == "PAL-N")
147  return V4L2_STD_PAL_N;
148  if (fmt == "NTSC-JP")
149  return V4L2_STD_NTSC_M_JP;
150  // generics...
151  if (fmt.startsWith("NTSC"))
152  return V4L2_STD_NTSC;
153  if (fmt.startsWith("ATSC"))
154  return V4L2_STD_NTSC; // We've dropped V4L ATSC support...
155  if (fmt.startsWith("PAL"))
156  return V4L2_STD_PAL;
157  return V4L2_STD_NTSC;
158 }
159 
160 static QString mode_to_format(int mode)
161 {
162  if (mode == V4L2_STD_NTSC)
163  return "NTSC";
164  if (mode == V4L2_STD_NTSC_M_JP)
165  return "NTSC-JP";
166  if (mode == V4L2_STD_PAL)
167  return "PAL";
168  if (mode == V4L2_STD_PAL_60)
169  return "PAL-60";
170  if (mode == V4L2_STD_PAL_BG)
171  return "PAL-BG";
172  if (mode == V4L2_STD_PAL_D)
173  return "PAL-D";
174  if (mode == V4L2_STD_PAL_DK)
175  return "PAL-DK";
176  if (mode == V4L2_STD_PAL_I)
177  return "PAL-I";
178  if (mode == V4L2_STD_PAL_M)
179  return "PAL-M";
180  if (mode == V4L2_STD_PAL_N)
181  return "PAL-N";
182  if (mode == V4L2_STD_PAL_Nc)
183  return "PAL-NC";
184  if (mode == V4L2_STD_SECAM)
185  return "SECAM";
186  if (mode == V4L2_STD_SECAM_D)
187  return "SECAM-D";
188  // generic..
189  if ((V4L2_STD_NTSC_M == mode) ||
190  (V4L2_STD_NTSC_443 == mode) ||
191  (V4L2_STD_NTSC_M_KR == mode))
192  return "NTSC";
193  if ((V4L2_STD_PAL_B == mode) ||
194  (V4L2_STD_PAL_B1 == mode) ||
195  (V4L2_STD_PAL_G == mode) ||
196  (V4L2_STD_PAL_H == mode) ||
197  (V4L2_STD_PAL_D1 == mode) ||
198  (V4L2_STD_PAL_K == mode))
199  return "PAL";
200  if ((V4L2_STD_SECAM_B == mode) ||
201  (V4L2_STD_SECAM_DK == mode) ||
202  (V4L2_STD_SECAM_G == mode) ||
203  (V4L2_STD_SECAM_H == mode) ||
204  (V4L2_STD_SECAM_K == mode) ||
205  (V4L2_STD_SECAM_K1 == mode) ||
206  (V4L2_STD_SECAM_L == mode) ||
207  (V4L2_STD_SECAM_LC == mode))
208  return "SECAM";
209  if ((V4L2_STD_ATSC == mode) ||
210  (V4L2_STD_ATSC_8_VSB == mode) ||
211  (V4L2_STD_ATSC_16_VSB == mode))
212  {
213  // We've dropped V4L ATSC support, but this still needs to be
214  // returned here so we will change the mode if the device is
215  // in ATSC mode already.
216  return "ATSC";
217  }
218 
219  return "Unknown";
220 }
221 
229 {
230  // Get Inputs from DB
232  return false;
233 
234  // Get global TVFormat setting
235  QString fmt = gCoreContext->GetSetting("TVFormat");
236  LOG(VB_CHANNEL, LOG_INFO, QString("Global TVFormat Setting '%1'").arg(fmt));
237  int videomode_v4l2 = format_to_mode(fmt.toUpper());
238 
239  bool ok = false;
241 
242  // Insert info from hardware
243  uint valid_cnt = 0;
244  for (auto v4l_it = v4l_inputs.cbegin(); v4l_it != v4l_inputs.cend(); ++v4l_it)
245  {
246  if (*v4l_it == m_name)
247  {
248  m_inputNumV4L = v4l_it.key();
249  m_videoModeV4L2 = videomode_v4l2;
250  valid_cnt++;
251  }
252  }
253 
254  // print it
255  LOG(VB_CHANNEL, LOG_INFO, LOC +
256  QString("Input #%1: '%2' schan(%3) tun(%4) v4l2(%6)")
257  .arg(QString::number(m_inputId), m_name, m_startChanNum,
260 
261  return valid_cnt != 0U;
262 }
263 
273 void V4LChannel::SetFormat(const QString &format)
274 {
275  if (!Open())
276  return;
277 
278  int inputNum = m_inputId;
279 
280  QString fmt = format;
281  if ((fmt == "Default") || format.isEmpty())
282  {
283  if (m_inputId)
285  }
286 
287  LOG(VB_CHANNEL, LOG_INFO, LOC + QString("SetFormat(%1) fmt(%2) input(%3)")
288  .arg(format, fmt, QString::number(inputNum)));
289 
290  if (!SetInputAndFormat(inputNum, fmt))
291  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to set format." + ENO);
292 
293 }
294 
295 int V4LChannel::SetDefaultFreqTable(const QString &name)
296 {
298  return m_defaultFreqTable;
299 }
300 
301 void V4LChannel::SetFreqTable(const int index)
302 {
303  m_curList = gChanLists[index].list;
304 }
305 
306 int V4LChannel::SetFreqTable(const QString &tablename)
307 {
308  const QString& name = tablename;
309  bool use_default = (name.toLower() == "default" || name.isEmpty());
310 
311  m_curList.clear();
312  for (size_t i = 0; i < gChanLists.size(); i++)
313  {
314  char *listname = (char *)gChanLists[i].name;
315 
316  if (use_default)
317  {
318  if (i == static_cast<size_t>(m_defaultFreqTable))
319  {
320  SetFreqTable(i);
321  return i;
322  }
323  }
324  else if (name == listname)
325  {
326  SetFreqTable(i);
327  return i;
328  }
329  }
330 
331  LOG(VB_CHANNEL, LOG_ERR,
332  QString("Channel(%1)::SetFreqTable(): Invalid "
333  "frequency table name %2, using %3.").
334  arg(m_device, name, (char *)gChanLists[1].name));
335  SetFreqTable(1);
336  return 1;
337 }
338 
339 int V4LChannel::GetCurrentChannelNum(const QString &channame)
340 {
341  for (size_t i = 0; i < m_curList.size(); i++)
342  {
343  if (channame == m_curList[i].name)
344  return i;
345  }
346 
347  LOG(VB_GENERAL, LOG_ERR, LOC +
348  QString("GetCurrentChannelNum(%1): Failed to find Channel")
349  .arg(channame));
350 
351  return -1;
352 }
353 
354 bool V4LChannel::Tune(const QString &freqid, int finetune)
355 {
356  int i = GetCurrentChannelNum(freqid);
357  LOG(VB_CHANNEL, LOG_INFO,
358  QString("Channel(%1)::Tune(%2): curList[%3].freq(%4)")
359  .arg(m_device, freqid, QString::number(i))
360  .arg((i != -1) ? m_curList[i].freq : -1));
361 
362  if (i == -1)
363  {
364  LOG(VB_GENERAL, LOG_ERR,
365  QString("Channel(%1)::Tune(%2): Error, failed to find channel.")
366  .arg(m_device, freqid));
367  return false;
368  }
369 
370  int frequency = (m_curList[i].freq + finetune) * 1000;
371 
372  return Tune(frequency);
373 }
374 
375 bool V4LChannel::Tune(const DTVMultiplex &tuning)
376 {
377  return Tune(tuning.m_frequency - 1750000); // to visual carrier
378 }
379 
389 bool V4LChannel::Tune(uint64_t frequency)
390 {
391  LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Tune(%1)").arg(frequency));
392 
393  int ioctlval = 0;
394 
395  uint mplexid_restriction = 0;
396  uint chanid_restriction = 0;
397  if (!IsInputAvailable(mplexid_restriction, chanid_restriction))
398  return false;
399 
400  // Video4Linux version 2 tuning
401  bool isTunerCapLow = false;
402  struct v4l2_modulator mod {};
403  mod.index = 0;
404  ioctlval = ioctl(m_videoFd, VIDIOC_G_MODULATOR, &mod);
405  if (ioctlval >= 0)
406  {
407  isTunerCapLow = ((mod.capability & V4L2_TUNER_CAP_LOW) != 0U);
408  LOG(VB_CHANNEL, LOG_INFO,
409  QString(" name: %1").arg((char *)mod.name));
410  LOG(VB_CHANNEL, LOG_INFO, QString("CapLow: %1").arg(isTunerCapLow));
411  }
412 
413  struct v4l2_frequency vf {};
414  vf.tuner = 0; // use first tuner
415  vf.frequency = (isTunerCapLow) ?
416  ((int)(frequency / 62.5)) : (frequency / 62500);
417 
418  vf.type = V4L2_TUNER_ANALOG_TV;
419 
420  ioctlval = ioctl(m_videoFd, VIDIOC_S_FREQUENCY, &vf);
421  if (ioctlval < 0)
422  {
423  LOG(VB_GENERAL, LOG_ERR,
424  QString("Channel(%1)::Tune(): Error %2 "
425  "while setting frequency (v2): %3")
426  .arg(m_device).arg(ioctlval).arg(strerror(errno)));
427  return false;
428  }
429  ioctlval = ioctl(m_videoFd, VIDIOC_G_FREQUENCY, &vf);
430 
431  if (ioctlval >= 0)
432  {
433  LOG(VB_CHANNEL, LOG_INFO,
434  QString("Channel(%1)::Tune(): Frequency is now %2")
435  .arg(m_device).arg(vf.frequency * 62500));
436  }
437 
438  return true;
439 }
440 
447 {
448  struct v4l2_frequency vf {};
449  vf.tuner = 0; // use first tuner
450  vf.type = V4L2_TUNER_ANALOG_TV;
451 
452  // Get the last tuned frequency
453  int ioctlval = ioctl(m_videoFd, VIDIOC_G_FREQUENCY, &vf);
454  if (ioctlval < 0)
455  {
456  LOG(VB_GENERAL, LOG_ERR, LOC + "Retune failed (1)" + ENO);
457  return false;
458  }
459 
460  // Set the last tuned frequency again...
461  ioctlval = ioctl(m_videoFd, VIDIOC_S_FREQUENCY, &vf);
462  if (ioctlval < 0)
463  {
464  LOG(VB_GENERAL, LOG_ERR, LOC + "Retune failed (2)" + ENO);
465  return false;
466  }
467 
468  return true;
469 }
470 
471 QString V4LChannel::GetFormatForChannel(const QString& channum, const QString& inputname)
472 {
473  MSqlQuery query(MSqlQuery::InitCon());
474  query.prepare(
475  "SELECT tvformat "
476  "FROM channel, capturecard "
477  "WHERE deleted IS NULL AND "
478  " channum = :CHANNUM AND "
479  " inputname = :INPUTNAME AND "
480  " capturecard.cardid = :INPUTID AND "
481  " capturecard.sourceid = channel.sourceid");
482  query.bindValue(":CHANNUM", channum);
483  query.bindValue(":INPUTNAME", inputname);
484  query.bindValue(":INPUTID", m_inputId);
485 
486  QString fmt;
487  if (!query.exec() || !query.isActive())
488  MythDB::DBError("GetFormatForChannel:find format", query);
489  else if (query.next())
490  fmt = query.value(0).toString();
491  return fmt;
492 }
493 
494 bool V4LChannel::SetInputAndFormat(int inputNum, const QString& newFmt)
495 {
496  if (!m_inputId || m_inputNumV4L < 0)
497  return false;
498 
499  int inputNumV4L = m_inputNumV4L;
500  bool ok = true;
501 
502  QString msg =
503  QString("SetInputAndFormat(%1, %2) ").arg(inputNum).arg(newFmt);
504 
505  struct v4l2_input input {};
506  int ioctlval = ioctl(m_videoFd, VIDIOC_G_INPUT, &input);
507  bool input_switch = (0 != ioctlval || (uint)inputNumV4L != input.index);
508 
509  const v4l2_std_id new_vid_mode = format_to_mode(newFmt);
510  v4l2_std_id cur_vid_mode = 0;
511  ioctlval = ioctl(m_videoFd, VIDIOC_G_STD, &cur_vid_mode);
512  bool mode_switch = (0 != ioctlval || new_vid_mode != cur_vid_mode);
513  bool needs_switch = input_switch || mode_switch;
514 
515  LOG(VB_GENERAL, LOG_INFO, LOC + msg + "(v4l v2) " +
516  QString("input_switch: %1 mode_switch: %2")
517  .arg(input_switch).arg(mode_switch));
518 
519  // ConvertX (wis-go7007) requires streaming to be disabled
520  // before an input switch, do this if CAP_STREAMING is set.
521  bool streamingDisabled = false;
522  int streamType = V4L2_BUF_TYPE_VIDEO_CAPTURE;
523  if (needs_switch && m_hasStreamIO)
524  {
525  ioctlval = ioctl(m_videoFd, VIDIOC_STREAMOFF, &streamType);
526  if (ioctlval < 0)
527  {
528  LOG(VB_GENERAL, LOG_ERR, LOC + msg +
529  "\n\t\t\twhile disabling streaming (v4l v2)" + ENO);
530  ok = false;
531  }
532  else
533  {
534  streamingDisabled = true;
535  }
536  }
537 
538  if (input_switch)
539  {
540  // Send the input switch ioctl.
541  ioctlval = ioctl(m_videoFd, VIDIOC_S_INPUT, &inputNumV4L);
542  if (ioctlval < 0)
543  {
544  LOG(VB_GENERAL, LOG_ERR, LOC + msg +
545  "\n\t\t\twhile setting input (v4l v2)" + ENO);
546 
547  ok = false;
548  }
549  }
550 
551  if (mode_switch)
552  {
553  ioctlval = ioctl(m_videoFd, VIDIOC_S_STD, &new_vid_mode);
554  if (ioctlval < 0)
555  {
556  LOG(VB_GENERAL, LOG_ERR, LOC + msg +
557  "\n\t\t\twhile setting format (v4l v2)" + ENO);
558 
559  ok = false;
560  }
561  }
562 
563  if (streamingDisabled)
564  {
565  ioctlval = ioctl(m_videoFd, VIDIOC_STREAMON, &streamType);
566  if (ioctlval < 0)
567  {
568  LOG(VB_GENERAL, LOG_ERR, LOC + msg +
569  "\n\t\t\twhile reenabling streaming (v4l v2)" + ENO);
570 
571  ok = false;
572  }
573  }
574 
575  return ok;
576 }
577 
578 static int get_v4l2_attribute(const QString &db_col_name)
579 {
580  if ("brightness" == db_col_name)
581  return V4L2_CID_BRIGHTNESS;
582  if ("contrast" == db_col_name)
583  return V4L2_CID_CONTRAST;
584  if ("colour" == db_col_name)
585  return V4L2_CID_SATURATION;
586  if ("hue" == db_col_name)
587  return V4L2_CID_HUE;
588  return -1;
589 }
590 
591 bool V4LChannel::InitPictureAttribute(const QString &db_col_name)
592 {
593  if (!m_pParent)
594  return false;
595 
596  int v4l2_attrib = get_v4l2_attribute(db_col_name);
597  if (v4l2_attrib == -1)
598  return false;
599 
600  int cfield = ChannelUtil::GetChannelValueInt(
601  db_col_name, GetSourceID(), m_curChannelName);
602  int sfield = CardUtil::GetValueInt(
603  db_col_name, m_inputId);
604 
605  if ((cfield == -1) || (sfield == -1))
606  return false;
607 
608  QString loc = LOC +
609  QString("InitPictureAttribute(%1): ").arg(db_col_name, 10);
610 
611  struct v4l2_control ctrl {};
612  struct v4l2_queryctrl qctrl {};
613 
614  ctrl.id = qctrl.id = v4l2_attrib;
615  if (ioctl(m_videoFd, VIDIOC_QUERYCTRL, &qctrl) < 0)
616  {
617  LOG(VB_GENERAL, LOG_ERR, loc + "failed to query controls." + ENO);
618  return false;
619  }
620 
621  float new_range = qctrl.maximum - qctrl.minimum;
622  float old_range = 65535 - 0;
623  float scl_range = new_range / old_range;
624  float dfl = (qctrl.default_value - qctrl.minimum) / new_range;
625  int norm_dfl = (0x10000 + (int)(dfl * old_range) - 32768) & 0xFFFF;
626 
627  if (m_pictAttrDefault.find(db_col_name) == m_pictAttrDefault.end())
628  {
629  if (m_deviceName == "pcHDTV HD3000 HDTV")
630  {
631  m_pictAttrDefault["brightness"] = 9830;
632  m_pictAttrDefault["contrast"] = 39322;
633  m_pictAttrDefault["colour"] = 45875;
634  m_pictAttrDefault["hue"] = 0;
635  }
636  else
637  {
638  m_pictAttrDefault[db_col_name] = norm_dfl;
639  }
640  }
641 
642  int dfield = m_pictAttrDefault[db_col_name];
643  int field = (cfield + sfield + dfield) & 0xFFFF;
644  int value0 = (int) ((scl_range * field) + qctrl.minimum);
645  int value1 = std::min(value0, qctrl.maximum);
646  ctrl.value = std::max(value1, qctrl.minimum);
647 
648 #if DEBUG_ATTRIB
649  LOG(VB_CHANNEL, LOG_DEBUG, loc + QString(" %1\n\t\t\t"
650  "[%2,%3] dflt(%4, %5, %6)")
651  .arg(value0).arg(qctrl.minimum, 5).arg(qctrl.maximum, 5)
652  .arg(qctrl.default_value, 5).arg(dfl, 4, 'f', 2)
653  .arg(norm_dfl));
654 #endif
655 
656  if (ioctl(m_videoFd, VIDIOC_S_CTRL, &ctrl) < 0)
657  {
658  LOG(VB_GENERAL, LOG_ERR, loc + "failed to set controls" + ENO);
659  return false;
660  }
661 
662  return true;
663 }
664 
666 {
667  return (InitPictureAttribute("brightness") &&
668  InitPictureAttribute("contrast") &&
669  InitPictureAttribute("colour") &&
670  InitPictureAttribute("hue"));
671 }
672 
674 {
675  QString db_col_name = toDBString(attr);
676  if (db_col_name.isEmpty())
677  return -1;
678 
679  int cfield = ChannelUtil::GetChannelValueInt(
680  db_col_name, GetSourceID(), m_curChannelName);
681  int sfield = CardUtil::GetValueInt(
682  db_col_name, m_inputId);
683  int dfield = 0;
684 
685  if (m_pictAttrDefault.find(db_col_name) != m_pictAttrDefault.end())
686  dfield = m_pictAttrDefault[db_col_name];
687 
688  int val = (cfield + sfield + dfield) & 0xFFFF;
689 
690 #if DEBUG_ATTRIB
691  LOG(VB_CHANNEL, LOG_DEBUG,
692  QString("GetPictureAttribute(%1) -> cdb %2 rdb %3 d %4 -> %5")
693  .arg(db_col_name).arg(cfield).arg(sfield)
694  .arg(dfield).arg(val));
695 #endif
696 
697  return val;
698 }
699 
700 static int get_v4l2_attribute_value(int videofd, int v4l2_attrib)
701 {
702  struct v4l2_control ctrl {};
703  struct v4l2_queryctrl qctrl {};
704 
705  ctrl.id = qctrl.id = v4l2_attrib;
706  if (ioctl(videofd, VIDIOC_QUERYCTRL, &qctrl) < 0)
707  {
708  LOG(VB_GENERAL, LOG_ERR,
709  "get_v4l2_attribute_value: failed to query controls (1)" + ENO);
710  return -1;
711  }
712 
713  if (ioctl(videofd, VIDIOC_G_CTRL, &ctrl) < 0)
714  {
715  LOG(VB_GENERAL, LOG_ERR,
716  "get_v4l2_attribute_value: failed to get controls (2)" + ENO);
717  return -1;
718  }
719 
720  float mult = 65535.0 / (qctrl.maximum - qctrl.minimum);
721  return std::clamp((int)(mult * (ctrl.value - qctrl.minimum)), 0, 65525);
722 }
723 
724 static int set_v4l2_attribute_value(int videofd, int v4l2_attrib, int newvalue)
725 {
726  struct v4l2_control ctrl {};
727  struct v4l2_queryctrl qctrl {};
728 
729  ctrl.id = qctrl.id = v4l2_attrib;
730  if (ioctl(videofd, VIDIOC_QUERYCTRL, &qctrl) < 0)
731  {
732  LOG(VB_GENERAL, LOG_ERR,
733  "set_v4l2_attribute_value: failed to query control" + ENO);
734  return -1;
735  }
736 
737  float mult = (qctrl.maximum - qctrl.minimum) / 65535.0;
738  ctrl.value = (int)((mult * newvalue) + qctrl.minimum);
739  ctrl.value = std::min(ctrl.value, qctrl.maximum);
740  ctrl.value = std::max(ctrl.value, qctrl.minimum);
741 
742  if (ioctl(videofd, VIDIOC_S_CTRL, &ctrl) < 0)
743  {
744  LOG(VB_GENERAL, LOG_ERR,
745  "set_v4l2_attribute_value: failed to set control" + ENO);
746  return -1;
747  }
748 
749  return 0;
750 }
751 
753  PictureAdjustType type, PictureAttribute attr, bool up)
754 {
755  if (!m_pParent)
756  return -1;
757 
758  QString db_col_name = toDBString(attr);
759  if (db_col_name.isEmpty())
760  return -1;
761 
762  int v4l2_attrib = get_v4l2_attribute(db_col_name);
763  if (v4l2_attrib == -1)
764  return -1;
765 
766  // get the old attribute value from the hardware, this is
767  // just a sanity check on whether this attribute exists
768  if (get_v4l2_attribute_value(m_videoFd, v4l2_attrib) < 0)
769  return -1;
770 
771  int old_value = GetPictureAttribute(attr);
772  int new_value = old_value + ((up) ? 655 : -655);
773 
774  // make sure we are within bounds (wrap around for hue)
775  if (V4L2_CID_HUE == v4l2_attrib)
776  new_value &= 0xffff;
777  new_value = std::clamp(new_value, 0, 65535);
778 
779 #if DEBUG_ATTRIB
780  LOG(VB_CHANNEL, LOG_DEBUG,
781  QString("ChangePictureAttribute(%1,%2,%3) cur %4 -> new %5")
782  .arg(type).arg(db_col_name).arg(up)
783  .arg(old_value).arg(new_value));
784 #endif
785 
786  // actually set the new attribute value on the hardware
787  if (set_v4l2_attribute_value(m_videoFd, v4l2_attrib, new_value) < 0)
788  return -1;
789 
790  // tell the DB about the new attribute value
792  {
793  int adj_value = ChannelUtil::GetChannelValueInt(
794  db_col_name, GetSourceID(), m_curChannelName);
795 
796  int tmp = new_value - old_value + adj_value;
797  tmp = (tmp < 0) ? tmp + 0x10000 : tmp;
798  tmp = (tmp > 0xffff) ? tmp - 0x10000 : tmp;
799  ChannelUtil::SetChannelValue(db_col_name, QString::number(tmp),
801  }
802  else if (kAdjustingPicture_Recording == type)
803  {
804  int adj_value = CardUtil::GetValueInt(
805  db_col_name, m_inputId);
806 
807  int tmp = new_value - old_value + adj_value;
808  tmp = (tmp < 0) ? tmp + 0x10000 : tmp;
809  tmp = (tmp > 0xffff) ? tmp - 0x10000 : tmp;
810  CardUtil::SetValue(db_col_name, m_inputId, tmp);
811  }
812 
813  return new_value;
814 }
V4LChannel::m_hasSlicedVbi
bool m_hasSlicedVbi
Definition: v4lchannel.h:105
DTVMultiplex::m_frequency
uint64_t m_frequency
Definition: dtvmultiplex.h:94
V4LChannel::~V4LChannel
~V4LChannel(void) override
Definition: v4lchannel.cpp:39
MSqlQuery::isActive
bool isActive(void) const
Definition: mythdbcon.h:215
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:812
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:127
DTVMultiplex
Definition: dtvmultiplex.h:24
V4LChannel::Init
bool Init(QString &startchannel, bool setchan) override
Definition: v4lchannel.cpp:44
ChannelBase::Init
virtual bool Init(QString &startchannel, bool setchan)
Definition: channelbase.cpp:66
ENO
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:74
V4LChannel::InitializeInputs
bool InitializeInputs(void)
Definition: v4lchannel.cpp:228
get_v4l2_attribute
static int get_v4l2_attribute(const QString &db_col_name)
Definition: v4lchannel.cpp:578
PictureAttribute
PictureAttribute
Definition: videoouttypes.h:103
V4LChannel::Open
bool Open(void) override
Opens the channel changing hardware for use.
Definition: v4lchannel.cpp:54
mythdb.h
ChannelBase::m_name
QString m_name
Definition: channelbase.h:139
mode_to_format
static QString mode_to_format(int mode)
Definition: v4lchannel.cpp:160
V4LChannel::SetFormat
void SetFormat(const QString &format) override
Initializes tuner and modulator variables.
Definition: v4lchannel.cpp:273
CardUtil::ProbeV4LVideoInputs
static InputNames ProbeV4LVideoInputs(int videofd, bool &ok)
freq
static const std::array< const uint32_t, 4 > freq
Definition: element.cpp:45
frequencies.h
ChannelUtil::SetChannelValue
static bool SetChannelValue(const QString &field_name, const QString &value, uint sourceid, const QString &channum)
Definition: channelutil.cpp:1163
V4LChannel::GetCurrentChannelNum
int GetCurrentChannelNum(const QString &channame)
Definition: v4lchannel.cpp:339
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:204
V4LChannel::Close
void Close(void) override
Closes the channel changing hardware to use.
Definition: v4lchannel.cpp:110
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:618
ChannelBase::m_inputId
uint m_inputId
Definition: channelbase.h:137
format_to_mode
static int format_to_mode(const QString &fmt)
Definition: v4lchannel.cpp:124
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
ChannelBase::m_startChanNum
QString m_startChanNum
Definition: channelbase.h:140
toDBString
QString toDBString(PrimariesMode Mode)
Definition: videoouttypes.h:156
V4LChannel::m_pictAttrDefault
QMap< QString, int > m_pictAttrDefault
Definition: v4lchannel.h:97
V4LChannel::m_curList
CHANLIST_vec m_curList
Definition: v4lchannel.h:99
V4LChannel::m_inputNumV4L
int m_inputNumV4L
Definition: v4lchannel.h:108
ChannelBase::m_tuneToChannel
QString m_tuneToChannel
Definition: channelbase.h:142
V4LChannel::m_device
QString m_device
Definition: v4lchannel.h:92
close
#define close
Definition: compat.h:43
tmp
static guint32 * tmp
Definition: goom_core.cpp:26
ChannelBase::IsInputAvailable
virtual bool IsInputAvailable(uint &mplexid_restriction, uint &chanid_restriction) const
Switches to another input on hardware, and sets the channel is setstarting is true.
Definition: channelbase.cpp:231
LOC
#define LOC
Definition: v4lchannel.cpp:33
CardUtil::SetValue
static bool SetValue(const QString &col, uint inputid, int val)
Definition: cardutil.h:310
V4LChannel::m_hasTuner
bool m_hasTuner
Definition: v4lchannel.h:104
V4LChannel::m_driverName
QString m_driverName
Definition: v4lchannel.h:96
mythlogging.h
V4LChannel::ChangePictureAttribute
int ChangePictureAttribute(PictureAdjustType type, PictureAttribute attr, bool up) override
Definition: v4lchannel.cpp:752
CardUtil::GetV4LInfo
static bool GetV4LInfo(int videofd, QString &input, QString &driver, uint32_t &version, uint32_t &capabilities)
Definition: cardutil.cpp:2354
ChannelBase::m_curChannelName
QString m_curChannelName
Definition: channelbase.h:135
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
v4lchannel.h
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:226
get_v4l2_attribute_value
static int get_v4l2_attribute_value(int videofd, int v4l2_attrib)
Definition: v4lchannel.cpp:700
kAdjustingPicture_Channel
@ kAdjustingPicture_Channel
Definition: tv.h:127
V4LChannel::m_hasStdIO
bool m_hasStdIO
Definition: v4lchannel.h:102
V4LChannel::m_defaultFreqTable
int m_defaultFreqTable
Definition: v4lchannel.h:107
CardUtil::GetValueInt
static int GetValueInt(const QString &col, uint inputid)
Definition: cardutil.h:308
V4LChannel::InitPictureAttributes
bool InitPictureAttributes(void) override
Definition: v4lchannel.cpp:665
V4LChannel::Retune
bool Retune(void) override
Retunes to last tuned frequency.
Definition: v4lchannel.cpp:446
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:57
clamp
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:204
set_v4l2_attribute_value
static int set_v4l2_attribute_value(int videofd, int v4l2_attrib, int newvalue)
Definition: v4lchannel.cpp:724
V4LChannel::SetFd
void SetFd(int fd) override
Sets file descriptor.
Definition: v4lchannel.cpp:117
V4LChannel::GetPictureAttribute
int GetPictureAttribute(PictureAttribute attr) const override
Definition: v4lchannel.cpp:673
kAdjustingPicture_Recording
@ kAdjustingPicture_Recording
Definition: tv.h:128
ChannelUtil::GetChannelValueInt
static int GetChannelValueInt(const QString &channel_field, uint sourceid, const QString &channum)
Definition: channelutil.cpp:979
channelutil.h
V4LChannel::InitPictureAttribute
bool InitPictureAttribute(const QString &db_col_name)
Definition: v4lchannel.cpp:591
gChanLists
const CHANLISTS_vec gChanLists
Definition: frequencies.cpp:2215
V4LChannel::SetDefaultFreqTable
int SetDefaultFreqTable(const QString &name)
Definition: v4lchannel.cpp:295
mythcorecontext.h
cardutil.h
V4LChannel::Tune
bool Tune(const DTVMultiplex &tuning) override
This performs the actual frequency tuning and in some cases input switching.
Definition: v4lchannel.cpp:375
V4LChannel::m_videoFd
int m_videoFd
Definition: v4lchannel.h:94
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
V4LChannel::SetFreqTable
void SetFreqTable(int index)
Definition: v4lchannel.cpp:301
tv_rec.h
InputNames
QMap< int, QString > InputNames
Definition: cardutil.h:22
ChannelBase::GetSourceID
virtual uint GetSourceID(void) const
Definition: channelbase.h:71
V4LChannel::m_deviceName
QString m_deviceName
Definition: v4lchannel.h:95
ChannelBase::InitializeInput
virtual bool InitializeInput(void)
Fills in input map from DB.
Definition: channelbase.cpp:554
V4LChannel::SetInputAndFormat
bool SetInputAndFormat(int inputNum, const QString &newFmt)
Definition: v4lchannel.cpp:494
V4LChannel::m_videoModeV4L2
int m_videoModeV4L2
Definition: v4lchannel.h:109
V4LChannel::GetFormatForChannel
QString GetFormatForChannel(const QString &channum, const QString &inputname)
Definition: v4lchannel.cpp:471
nv_python_libs.bbciplayer.bbciplayer_api.version
string version
Definition: bbciplayer_api.py:77
V4LChannel::m_hasAsyncIO
bool m_hasAsyncIO
Definition: v4lchannel.h:103
ChannelBase::m_pParent
TVRec * m_pParent
Definition: channelbase.h:134
V4LChannel::m_hasStreamIO
bool m_hasStreamIO
Definition: v4lchannel.h:101
uint
unsigned int uint
Definition: freesurround.h:24
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:904
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:837
PictureAdjustType
PictureAdjustType
Definition: tv.h:123