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