MythTV  master
cardutil.cpp
Go to the documentation of this file.
1 // Standard UNIX C headers
2 #include <fcntl.h>
3 #include <unistd.h>
4 
5 #include <algorithm>
6 
7 #if defined(USING_V4L2) || defined(USING_DVB)
8 #include <sys/ioctl.h>
9 #endif
10 
11 // Qt headers
12 #include <QMap>
13 #include <QDir>
14 #include <QRegularExpression>
15 #include <QUrlQuery>
16 
17 // MythTV headers
18 #include "libmythbase/mythconfig.h"
20 #include "libmythbase/mythdb.h"
23 #include "libmythbase/mythmiscutil.h" // for ping()
24 
25 #include "cardutil.h"
26 #include "diseqcsettings.h"
27 #include "inputinfo.h"
28 #include "recorders/dvbchannel.h"
29 #include "sourceutil.h"
30 #include "videosource.h"
31 
32 #ifdef USING_DVB
33 #include "recorders/dvbtypes.h"
34 #endif
35 
36 #ifdef USING_V4L2
37 #include "v4l2util.h"
38 #endif
39 
40 #ifdef USING_HDHOMERUN
41 #include HDHOMERUN_HEADERFILE
42 #endif
43 
44 #ifdef USING_VBOX
45 #include "recorders/vboxutils.h"
46 #endif
47 
48 #ifdef USING_SATIP
49 #include "recorders/satiputils.h"
50 #endif
51 
52 #ifdef USING_ASI
53 #include <sys/types.h>
54 #include <sys/stat.h>
55 #include <dveo/asi.h>
56 #include <dveo/master.h>
57 #endif
58 
59 #define LOC QString("CardUtil: ")
60 
62 {
63  QStringList inputTypes {};
64 
65 #ifdef USING_DVB
66  inputTypes += "'DVB'";
67 #endif // USING_DVB
68 
69 #ifdef USING_V4L2
70  inputTypes += "'V4L'";
71  inputTypes += "'MPEG'";
72 #endif // USING_V4L2
73 
74 #ifdef USING_IPTV
75  inputTypes += "'FREEBOX'";
76 #endif // USING_IPTV
77 
78 #ifdef USING_VBOX
79  inputTypes += "'VBOX'";
80 #endif // USING_VBOX
81 
82 #ifdef USING_HDHOMERUN
83  inputTypes += "'HDHOMERUN'";
84 #endif // USING_HDHOMERUN
85 
86 #ifdef USING_SATIP
87  inputTypes += "'SATIP'";
88 #endif // USING_SATIP
89 
90 #ifdef USING_ASI
91  inputTypes += "'ASI'";
92 #endif
93 
94 #ifdef USING_CETON
95  inputTypes += "'CETON'";
96 #endif // USING_CETON
97 
98 #if !defined( USING_MINGW ) && !defined( _MSC_VER )
99  inputTypes += "'EXTERNAL'";
100 #endif
101 
102  if (inputTypes.isEmpty())
103  return "('DUMMY')";
104 
105  return QString("(%1)").arg(inputTypes.join(','));
106 }
107 
108 bool CardUtil::IsCableCardPresent([[maybe_unused]] uint inputid,
109  const QString &inputType)
110 {
111  if (inputType == "HDHOMERUN")
112  {
113 #ifdef USING_HDHOMERUN
114  hdhomerun_tuner_status_t status {};
115  QString device = GetVideoDevice(inputid);
116  hdhomerun_device_t *hdhr =
117  hdhomerun_device_create_from_str(device.toLatin1(), nullptr);
118  if (!hdhr)
119  return false;
120 
121  int oob = hdhomerun_device_get_oob_status(hdhr, nullptr, &status);
122 
123  // if no OOB tuner, oob will be < 1. If no CC present, OOB
124  // status will be "none."
125  if (oob > 0 && (strncmp(status.channel, "none", 4) != 0))
126  {
127  LOG(VB_GENERAL, LOG_INFO, "Cardutil: HDHomeRun Cablecard Present.");
128  hdhomerun_device_destroy(hdhr);
129  return true;
130  }
131 
132  hdhomerun_device_destroy(hdhr);
133 
134 #endif
135  return false;
136  }
137  if (inputType == "CETON")
138  {
139 #ifdef USING_CETON
140  QString device = GetVideoDevice(inputid);
141 
142  QStringList parts = device.split("-");
143  if (parts.size() != 2)
144  {
145  LOG(VB_GENERAL, LOG_ERR,
146  QString("CardUtil: Ceton invalid device id %1").arg(device));
147  return false;
148  }
149 
150  const QString& ip_address = parts.at(0);
151 
152  QStringList tuner_parts = parts.at(1).split(".");
153  if (tuner_parts.size() != 2)
154  {
155  LOG(VB_GENERAL, LOG_ERR, LOC +
156  QString("CardUtil: Ceton invalid device id %1").arg(device));
157  return false;
158  }
159 
160  uint tuner = tuner_parts.at(1).toUInt();
161 
162  QUrlQuery params;
163  params.addQueryItem("i", QString::number(tuner));
164  params.addQueryItem("s", "cas");
165  params.addQueryItem("v", "CardStatus");
166 
167  QUrl url;
168  url.setScheme("http");
169  url.setHost(ip_address);
170  url.setPath("/get_var.json");
171  url.setQuery(params);
172 
173  auto *request = new QNetworkRequest();
174  request->setAttribute(QNetworkRequest::CacheLoadControlAttribute,
175  QNetworkRequest::AlwaysNetwork);
176  request->setUrl(url);
177 
178  QByteArray data;
180 
181  if (!manager->download(request, &data))
182  {
183  LOG(VB_GENERAL, LOG_ERR,
184  QString("CardUtil: Ceton http request failed %1").arg(device));
185  return false;
186  }
187 
188  QString response = QString(data);
189 
190  static const QRegularExpression regex { "^\\{ \"?result\"?: \"(.*)\" \\}$"};
191  auto match = regex.match(response);
192  if (!match.hasMatch())
193  {
194  LOG(VB_GENERAL, LOG_ERR,
195  QString("CardUtil: Ceton unexpected http response: %1").arg(response));
196  return false;
197  }
198 
199  QString result = match.captured(1);
200 
201  if (result == "Inserted")
202  {
203  LOG(VB_GENERAL, LOG_DEBUG, "Cardutil: Ceton CableCARD present.");
204  return true;
205  }
206 
207  LOG(VB_GENERAL, LOG_DEBUG, "Cardutil: Ceton CableCARD not present.");
208  return false;
209 #else
210  return false;
211 #endif
212  }
213  return false;
214 }
215 
216 bool CardUtil::HasTuner(const QString &rawtype,
217  [[maybe_unused]] const QString & device)
218 {
219  if (rawtype == "DVB" || rawtype == "HDHOMERUN" ||
220  rawtype == "FREEBOX" || rawtype == "CETON" ||
221  rawtype == "VBOX" || rawtype == "SATIP")
222  return true;
223 
224 #ifdef USING_V4L2
225  if (rawtype == "V4L2ENC")
226  {
227  V4L2util v4l2(device);
228  return !v4l2 ? false : v4l2.HasTuner();
229  }
230 #endif
231 
232  if (rawtype == "EXTERNAL")
233  {
234  // TODO: query EXTERNAL for capability
235  return true;
236  }
237 
238  return false;
239 }
240 
241 bool CardUtil::IsTunerShared(uint inputidA, uint inputidB)
242 {
243  LOG(VB_GENERAL, LOG_DEBUG, QString("IsTunerShared(%1,%2)")
244  .arg(inputidA).arg(inputidB));
245 
246  MSqlQuery query(MSqlQuery::InitCon());
247  query.prepare("SELECT videodevice, hostname, cardtype "
248  "FROM capturecard "
249  "WHERE ( (cardid = :INPUTID_A) OR "
250  " (cardid = :INPUTID_B) )");
251  query.bindValue(":INPUTID_A", inputidA);
252  query.bindValue(":INPUTID_B", inputidB);
253 
254  if (!query.exec())
255  {
256  MythDB::DBError("CardUtil::is_tuner_shared()", query);
257  return false;
258  }
259 
260  if (!query.next())
261  return false;
262 
263  const QString vdevice = query.value(0).toString();
264  const QString hostname = query.value(1).toString();
265  const QString inputtype = query.value(2).toString();
266 
267  if (!IsTunerSharingCapable(inputtype.toUpper()))
268  return false;
269 
270  if (!query.next())
271  return false;
272 
273  bool ret = ((vdevice == query.value(0).toString()) &&
274  (hostname == query.value(1).toString()) &&
275  (inputtype == query.value(2).toString()));
276 
277  LOG(VB_RECORD, LOG_DEBUG, QString("IsTunerShared(%1,%2) -> %3")
278  .arg(inputidA).arg(inputidB).arg(ret));
279 
280  return ret;
281 }
282 
288 bool CardUtil::IsInputTypePresent(const QString &rawtype, QString hostname)
289 {
290  if (hostname.isEmpty())
292 
293  MSqlQuery query(MSqlQuery::InitCon());
294  QString qstr =
295  "SELECT count(cardtype) "
296  "FROM capturecard "
297  "WHERE capturecard.hostname = :HOSTNAME ";
298 
299  if (!rawtype.isEmpty())
300  qstr += " AND capturecard.cardtype = :INPUTTYPE";
301 
302  query.prepare(qstr);
303 
304  if (!rawtype.isEmpty())
305  query.bindValue(":INPUTTYPE", rawtype.toUpper());
306 
307  query.bindValue(":HOSTNAME", hostname);
308 
309  if (!query.exec())
310  {
311  MythDB::DBError("CardUtil::IsInputTypePresent()", query);
312  return false;
313  }
314 
315  uint count = 0;
316  if (query.next())
317  count = query.value(0).toUInt();
318 
319  return count > 0;
320 }
321 
323 {
324  InputTypes inputtypes;
325 
326  MSqlQuery query(MSqlQuery::InitCon());
327  query.prepare("SELECT DISTINCT cardtype, videodevice "
328  "FROM capturecard");
329 
330  if (!query.exec())
331  {
332  MythDB::DBError("CardUtil::GetInputTypes()", query);
333  }
334  else
335  {
336  QString cardtype;
337 
338  while (query.next())
339  {
340  cardtype = query.value(0).toString();
341  if (cardtype != "V4L2ENC")
342  {
343  inputtypes[cardtype] = "";
344  }
345 #ifdef USING_V4L2
346  else
347  {
348  V4L2util v4l2(query.value(1).toString());
349  if (v4l2.IsOpen())
350  {
351  QString driver_name = "V4L2:" + v4l2.DriverName();
352  inputtypes[driver_name] = v4l2.CardName();
353  }
354  }
355 #endif
356  }
357  }
358 
359  return inputtypes;
360 }
361 
369 QStringList CardUtil::GetInputTypeNames(uint sourceid)
370 {
371  MSqlQuery query(MSqlQuery::InitCon());
372  query.prepare("SELECT cardtype "
373  "FROM capturecard "
374  "WHERE capturecard.sourceid = :SOURCEID "
375  "GROUP BY cardtype");
376  query.bindValue(":SOURCEID", sourceid);
377 
378  QStringList list;
379  if (!query.exec())
380  {
381  MythDB::DBError("CardUtil::GetInputTypes", query);
382  return list;
383  }
384  while (query.next())
385  list.push_back(query.value(0).toString());
386  return list;
387 }
388 
389 
390 
391 
397 QStringList CardUtil::GetVideoDevices(const QString &rawtype, QString hostname)
398 {
399  QStringList list;
400 
401  if (hostname.isEmpty())
403 
404  MSqlQuery query(MSqlQuery::InitCon());
405  QString qstr =
406  "SELECT videodevice "
407  "FROM capturecard "
408  "WHERE hostname = :HOSTNAME";
409 
410  if (!rawtype.isEmpty())
411  qstr += " AND cardtype = :INPUTTYPE";
412 
413  query.prepare(qstr);
414 
415  if (!rawtype.isEmpty())
416  query.bindValue(":INPUTTYPE", rawtype.toUpper());
417 
418  query.bindValue(":HOSTNAME", hostname);
419 
420  if (!query.exec())
421  {
422  MythDB::DBError("CardUtil::GetVideoDevices", query);
423  return list;
424  }
425 
426  QMap<QString,bool> dup;
427  while (query.next())
428  {
429  QString videodevice = query.value(0).toString();
430  if (dup[videodevice])
431  continue;
432 
433  list.push_back(videodevice);
434  dup[videodevice] = true;
435  }
436 
437  return list;
438 }
439 
440 QMap <QString,QStringList> CardUtil::s_videoDeviceCache;
441 
443 {
444  s_videoDeviceCache.clear();
445 }
446 
447 
448 QStringList CardUtil::ProbeVideoDevices(const QString &rawtype)
449 {
450  if (s_videoDeviceCache.contains(rawtype))
451  return s_videoDeviceCache[rawtype];
452 
453  QStringList devs;
454 
455  if (rawtype.toUpper() == "DVB")
456  {
457  QDir dir("/dev/dvb", "adapter*", QDir::Name, QDir::Dirs);
458  QFileInfoList entries = dir.entryInfoList();
459  for (const auto & it : std::as_const(entries))
460  {
461  QDir subdir(it.filePath(), "frontend*", QDir::Name, QDir::Files | QDir::System);
462  const QFileInfoList subil = subdir.entryInfoList();
463  if (subil.isEmpty())
464  continue;
465 
466  for (const auto & subit : std::as_const(subil))
467  devs.push_back(subit.filePath());
468  }
469  }
470  else if (rawtype.toUpper() == "ASI")
471  {
472  QDir dir("/dev/", "asirx*", QDir::Name, QDir::System);
473  QFileInfoList entries = dir.entryInfoList();
474  for (const auto & it : std::as_const(entries))
475  {
476  if (GetASIDeviceNumber(it.filePath()) >= 0)
477  {
478  devs.push_back(it.filePath());
479  continue;
480  }
481  break;
482  }
483  }
484 #ifdef USING_HDHOMERUN
485  else if (rawtype.toUpper() == "HDHOMERUN")
486  {
487 #if HDHOMERUN_VERSION >= 20221010
488  struct hdhomerun_debug_t *dbg = hdhomerun_debug_create();
489  struct hdhomerun_discover_t *ds = hdhomerun_discover_create(dbg);
490 
491  // Find devices
492  uint32_t type { HDHOMERUN_DEVICE_TYPE_TUNER };
493  uint32_t flags { HDHOMERUN_DISCOVER_FLAGS_IPV4_GENERAL |
494  HDHOMERUN_DISCOVER_FLAGS_IPV6_GENERAL };
495  int result =
496  hdhomerun_discover2_find_devices_broadcast(ds, flags, &type, 1);
497 
498  if (result == -1)
499  {
500  LOG(VB_GENERAL, LOG_ERR, "Error finding HDHomerun devices");
501  }
502  else if (result == 0)
503  {
504  LOG(VB_GENERAL, LOG_INFO, "No HDHomerun devices found.");
505  }
506  else
507  {
508  // For each device found
509  struct hdhomerun_discover2_device_t *device = hdhomerun_discover2_iter_device_first(ds);
510  while (device)
511  {
512  uint8_t tuners = hdhomerun_discover2_device_get_tuner_count(device);
513  uint32_t device_id = hdhomerun_discover2_device_get_device_id(device);
514  QString id = QString("%1").arg(device_id, 0, 16, QChar('0')).toUpper();
515  auto *dev1 = hdhomerun_device_create_from_str(id.toLatin1(), nullptr);
516  QString model = hdhomerun_device_get_model_str(dev1);
517 
518  QHostAddress ip;
519  struct sockaddr_storage saddr {};
520  struct hdhomerun_discover2_device_if_t *device_if { nullptr };
521 
522  // Enumerate all addresses
523  device_if = hdhomerun_discover2_iter_device_if_first(device);
524  while (device_if)
525  {
526  hdhomerun_discover2_device_if_get_ip_addr(device_if, &saddr);
527  ip = QHostAddress((struct sockaddr *)&saddr);
528  LOG(VB_GENERAL, LOG_DEBUG,
529  QString("HDHomerun %1 has IP %2").arg(id, ip.toString()));
530  device_if = hdhomerun_discover2_iter_device_if_next(device_if);
531  }
532 
533  // HDHomerun API recommends using first entry
534  device_if = hdhomerun_discover2_iter_device_if_first(device);
535  if (nullptr == device_if)
536  {
537  LOG(VB_GENERAL, LOG_WARNING,
538  QString("HDHomerun %1 has no IP addresses").arg(id));
539  continue;
540  }
541  hdhomerun_discover2_device_if_get_ip_addr(device_if, &saddr);
542  ip = QHostAddress((struct sockaddr *)&saddr);
543 
544  // Create device name
545  QString hdhrdev = QString("%1 %2 %3").arg(id, ip.toString(), model);
546  devs.push_back(hdhrdev);
547  LOG(VB_GENERAL, LOG_INFO,
548  QString("HDHomerun %1: IP %2, model %3, %4 tuners")
549  .arg(id, ip.toString(), model).arg(tuners));
550 
551  device = hdhomerun_discover2_iter_device_next(device);
552  }
553  hdhomerun_discover_destroy(ds);
554  hdhomerun_debug_destroy(dbg);
555  }
556 #else // HDHOMERUN_VERSION >= 20221010
557  uint32_t target_ip = 0;
558  uint32_t device_type = HDHOMERUN_DEVICE_TYPE_TUNER;
559  uint32_t device_id = HDHOMERUN_DEVICE_ID_WILDCARD;
560  const int max_count = 50;
561  std::array<hdhomerun_discover_device_t,max_count> result_list {};
562 
563  int result = hdhomerun_discover_find_devices_custom_v2(
564  target_ip, device_type, device_id, result_list.data(), result_list.size());
565 
566  if (result == -1)
567  {
568  LOG(VB_GENERAL, LOG_ERR, "Error finding HDHomerun devices");
569  }
570 
571  if (result >= max_count)
572  {
573  LOG(VB_GENERAL, LOG_WARNING,
574  "Warning: may be > 50 HDHomerun devices");
575  }
576 
577  // Return "deviceid ipaddress" pairs
578  for (int i = 0; i < result; i++)
579  {
580  QString id = QString("%1").arg(result_list[i].device_id, 0, 16);
581  QString ip = QString("%1.%2.%3.%4")
582  .arg((result_list[i].ip_addr>>24) & 0xFF)
583  .arg((result_list[i].ip_addr>>16) & 0xFF)
584  .arg((result_list[i].ip_addr>> 8) & 0xFF)
585  .arg((result_list[i].ip_addr>> 0) & 0xFF);
586 
587  QString model = "";
588  hdhomerun_device_t *device = hdhomerun_device_create(
589  result_list[i].device_id, 0, 0, nullptr);
590  if (device)
591  {
592  model = hdhomerun_device_get_model_str(device);
593  hdhomerun_device_destroy(device);
594  }
595 
596  QString hdhrdev = id.toUpper() + " " + ip + " " + model;
597  devs.push_back(hdhrdev);
598  }
599 #endif // HDHOMERUN_VERSION >= 20221010
600  }
601 #endif // USING_HDHOMERUN
602 #ifdef USING_SATIP
603  else if (rawtype.toUpper() == "SATIP")
604  {
605  devs = SatIP::probeDevices();
606  }
607 #endif // USING_SATIP
608 #ifdef USING_VBOX
609  else if (rawtype.toUpper() == "VBOX")
610  {
611  devs = VBox::probeDevices();
612  }
613 #endif // USING_VBOX
614 #ifdef USING_CETON
615  else if (rawtype.toUpper() == "CETON")
616  {
617  // TODO implement CETON probing.
618  LOG(VB_GENERAL, LOG_INFO, "CardUtil::ProbeVideoDevices: "
619  "TODO Probe Ceton devices");
620  }
621 #endif // USING_CETON
622  else
623  {
624  LOG(VB_GENERAL, LOG_ERR, QString("Raw Type: '%1' is not supported")
625  .arg(rawtype));
626  }
627 
628  s_videoDeviceCache.insert(rawtype,devs);
629  return devs;
630 }
631 
632 // Get the list of delivery systems from the card
633 QStringList CardUtil::ProbeDeliverySystems([[maybe_unused]] const QString &device)
634 {
635  QStringList delsyslist;
636 
637 #ifdef USING_DVB
638  int fd_frontend = OpenVideoDevice(device);
639  if (fd_frontend < 0)
640  {
641  return delsyslist;
642  }
643 
644  struct dtv_property prop = {};
645  struct dtv_properties cmd = {};
646 
647  prop.cmd = DTV_API_VERSION;
648  cmd.num = 1;
649  cmd.props = &prop;
650  if (ioctl(fd_frontend, FE_GET_PROPERTY, &cmd) == 0)
651  {
652  LOG(VB_GENERAL, LOG_DEBUG,
653  QString("CardUtil(%1): ").arg(device) +
654  QString("dvb api version %1.%2").arg((prop.u.data>>8)&0xff).arg((prop.u.data)&0xff));
655  }
656  else
657  {
658  LOG(VB_GENERAL, LOG_ERR,
659  QString("CardUtil(%1) FE_GET_PROPERTY ioctl failed").arg(device) + ENO);
660  close(fd_frontend);
661  return delsyslist;
662  }
663 
664  delsyslist = ProbeDeliverySystems(fd_frontend);
665 
666  QString msg = "Delivery systems:";
667  for (const auto & item : std::as_const(delsyslist))
668  {
669  msg += " ";
670  msg += item;
671  }
672  LOG(VB_GENERAL, LOG_INFO, QString("CardUtil(%1): ").arg(device) + msg);
673 
674  close(fd_frontend);
675 #endif // USING_DVB
676 
677  return delsyslist;
678 }
679 
680 // Get the list of all supported delivery systems from the card
681 QStringList CardUtil::ProbeDeliverySystems([[maybe_unused]] int fd_frontend)
682 {
683  QStringList delsyslist;
684 
685 #ifdef USING_DVB
686  struct dtv_property prop = {};
687  struct dtv_properties cmd = {};
688 
689  prop.cmd = DTV_ENUM_DELSYS;
690  cmd.num = 1;
691  cmd.props = &prop;
692  if (ioctl(fd_frontend, FE_GET_PROPERTY, &cmd) == 0)
693  {
694  for (unsigned int i = 0; i < prop.u.buffer.len; i++)
695  {
696  delsyslist.push_back(DTVModulationSystem::toString(prop.u.buffer.data[i]));
697  }
698  }
699  else
700  {
701  LOG(VB_GENERAL, LOG_ERR, LOC + "FE_GET_PROPERTY ioctl failed " + ENO);
702  }
703 #endif // USING_DVB
704 
705  return delsyslist;
706 }
707 
708 QString CardUtil::ProbeDefaultDeliverySystem([[maybe_unused]] const QString &device)
709 {
710  DTVModulationSystem delsys;
711 
712 #ifdef USING_DVB
713  int fd = OpenVideoDevice(device);
714  if (fd >= 0)
715  {
716  delsys = ProbeBestDeliverySystem(fd);
717  close(fd);
718  }
719 #endif // USING_DVB
720 
721  return delsys.toString();
722 }
723 
724 QString CardUtil::ProbeDVBType(const QString &device)
725 {
726  QString ret = "ERROR_UNKNOWN";
727 
728  if (device.isEmpty())
729  return ret;
730 
731 #ifdef USING_DVB
732  DTVTunerType type = ProbeTunerType(device);
733  ret = (type.toString() != "UNKNOWN") ? type.toString().toUpper() : ret;
734 
735  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("(%1) tuner type:%2 %3")
736  .arg(device).arg(type).arg(ret));
737 #endif // USING_DVB
738 
739  return ret;
740 }
741 
745 QString CardUtil::ProbeDVBFrontendName([[maybe_unused]] const QString &device)
746 {
747  QString ret = "ERROR_UNKNOWN";
748 
749 #ifdef USING_DVB
750  QString dvbdev = CardUtil::GetDeviceName(DVB_DEV_FRONTEND, device);
751  QByteArray dev = dvbdev.toLatin1();
752  int fd_frontend = open(dev.constData(), O_RDWR | O_NONBLOCK);
753  if (fd_frontend < 0)
754  return "ERROR_OPEN";
755 
756  struct dvb_frontend_info info {};
757  int err = ioctl(fd_frontend, FE_GET_INFO, &info);
758  if (err < 0)
759  {
760  close(fd_frontend);
761  return "ERROR_PROBE";
762  }
763 
764  ret = info.name;
765 
766  close(fd_frontend);
767 #endif // USING_DVB
768 
769  return ret;
770 }
771 
789 bool CardUtil::HasDVBCRCBug(const QString &device)
790 {
791  QString name = ProbeDVBFrontendName(device);
792  return ((name == "VLSI VES1x93 DVB-S") || // munges PMT
793  (name == "ST STV0299 DVB-S")); // munges PAT
794 }
795 
796 std::chrono::milliseconds CardUtil::GetMinSignalMonitoringDelay(const QString &device)
797 {
798  QString name = ProbeDVBFrontendName(device);
799  if (name.indexOf("DVB-S") >= 0)
800  return 300ms;
801  if (name == "DiBcom 3000P/M-C DVB-T")
802  return 100ms;
803  return 25ms;
804 }
805 
807 {
808  DTVTunerType tunertype;
809 
810  switch (delsys)
811  {
813  tunertype = DTVTunerType::kTunerTypeDVBS1;
814  break;
816  tunertype = DTVTunerType::kTunerTypeDVBS2;
817  break;
820  tunertype = DTVTunerType::kTunerTypeDVBC;
821  break;
823  tunertype = DTVTunerType::kTunerTypeDVBT;
824  break;
826  tunertype = DTVTunerType::kTunerTypeDVBT2;
827  break;
829  tunertype = DTVTunerType::kTunerTypeDVBT;
830  break;
833  tunertype = DTVTunerType::kTunerTypeATSC;
834  break;
837  break;
838  default:
839  LOG(VB_GENERAL, LOG_ERR, LOC +
840  QString("TODO Add to switch case delivery system:%2 %3")
841  .arg(delsys).arg(delsys.toString()));
842  break;
843  }
844 
845  return tunertype;
846 }
847 
848 // Get the currently configured tuner type from the database
850 {
851  DTVModulationSystem delsys = GetDeliverySystem(inputid);
852  DTVTunerType tunertype = ConvertToTunerType(delsys);
853  return tunertype;
854 }
855 
856 // Get the currently configured tuner type from the device
858 {
859  DTVModulationSystem delsys = ProbeCurrentDeliverySystem(fd_frontend);
860  DTVTunerType tunertype = ConvertToTunerType(delsys);
861  return tunertype;
862 }
863 
864 // Get the currently configured tuner type from the device
866 {
868  DTVTunerType tunertype = ConvertToTunerType(delsys);
869  return tunertype;
870 }
871 
872 // Get the tuner type from the multiplex
874 {
875  DTVTunerType tuner_type;
876 
877  MSqlQuery query(MSqlQuery::InitCon());
878  query.prepare(
879  "SELECT mod_sys "
880  "FROM dtv_multiplex "
881  "WHERE dtv_multiplex.mplexid = :MPLEXID");
882  query.bindValue(":MPLEXID", mplexid);
883 
884  if (!query.exec())
885  {
886  MythDB::DBError("CardUtil::GetTunerTypeFromMultiplex", query);
887  return tuner_type;
888  }
889 
890  if (!query.next())
891  {
892  LOG(VB_GENERAL, LOG_ERR, LOC +
893  QString("Could not find entry in dtv_multiplex for mplexid %1")
894  .arg(mplexid));
895 
896  return tuner_type;
897  }
898 
899  DTVModulationSystem mod_sys;
900  mod_sys.Parse(query.value(0).toString());
901  tuner_type = CardUtil::ConvertToTunerType(mod_sys);
902 
903  return tuner_type;
904 }
905 
906 // Get the currently configured delivery system from the database
908 {
909  QString ds = GetDeliverySystemFromDB(inputid);
910  DTVModulationSystem delsys;
911  delsys.Parse(ds);
912  return delsys;
913 }
914 
915 // Get the currently configured delivery system from the device
916 DTVModulationSystem CardUtil::ProbeCurrentDeliverySystem([[maybe_unused]] const QString &device)
917 {
918  DTVModulationSystem delsys;
919 
920  if (device.isEmpty())
921  {
922  return delsys;
923  }
924 
925 #ifdef USING_DVB
926  int fd_frontend = OpenVideoDevice(device);
927  if (fd_frontend < 0)
928  {
929  LOG(VB_GENERAL, LOG_ERR, LOC +
930  QString("open failed (%1)")
931  .arg(device) + ENO);
932  return delsys;
933  }
934 
935  delsys = ProbeCurrentDeliverySystem(fd_frontend);
936 
937  LOG(VB_GENERAL, LOG_DEBUG, QString("CardUtil(%1): delsys:%2 %3")
938  .arg(device).arg(delsys).arg(delsys.toString()));
939 
940  close(fd_frontend);
941 #endif
942 
943  return delsys;
944 }
945 
946 // Get the currently configured delivery system from the device
947 DTVModulationSystem CardUtil::ProbeCurrentDeliverySystem([[maybe_unused]] int fd_frontend)
948 {
949  DTVModulationSystem delsys;
950 
951 #ifdef USING_DVB
952  struct dtv_property prop = {};
953  struct dtv_properties cmd = {};
954 
955  prop.cmd = DTV_DELIVERY_SYSTEM;
956  // prop.u.data = delsys;
957  cmd.num = 1;
958  cmd.props = &prop;
959 
960  int ret = ioctl(fd_frontend, FE_GET_PROPERTY, &cmd);
961  if (ret < 0)
962  {
963  LOG(VB_GENERAL, LOG_ERR, LOC +
964  QString("FE_GET_PROPERTY ioctl failed (fd_frontend:%1)")
965  .arg(fd_frontend) + ENO);
966  return delsys;
967  }
968 
969  delsys = prop.u.data;
970 
971 #endif // USING_DVB
972 
973  return delsys;
974 }
975 
976 // Get the delivery system from database table capturecard.
977 // If there is nothing in the database then get the currently
978 // configured delivery system, check for DVB-T/T2 and DVB-S/S2
979 // and update the database.
980 // Configure the tuner.
981 // Return the tuner type corresponding with the modulation system.
983 {
984  QString type = GetRawInputType(inputid);
985  if ("DVB" != type)
986  return type;
987 
988  DTVTunerType tunertype;
989  int fd_frontend = OpenVideoDevice(inputid);
990  if (fd_frontend < 0)
991  return "ERROR_OPEN";
992 
993  DTVModulationSystem delsys = GetDeliverySystem(inputid);
995  {
996  delsys = ProbeBestDeliverySystem(fd_frontend);
998  {
999  // Update database
1000  set_on_input("inputname", inputid, delsys.toString()); // Update DB capturecard
1001  LOG(VB_GENERAL, LOG_INFO,
1002  QString("CardUtil[%1]: ").arg(inputid) +
1003  QString("Update capturecard delivery system: %1").arg(delsys.toString()));
1004  }
1005  else
1006  {
1007  LOG(VB_GENERAL, LOG_ERR,
1008  QString("CardUtil[%1]: Error probing best delivery system").arg(inputid));
1009  return "ERROR_UNKNOWN";
1010  }
1011  }
1012  SetDeliverySystem(inputid, delsys, fd_frontend);
1013  tunertype = ConvertToTunerType(delsys);
1014  close(fd_frontend);
1015 
1016  QString subtype = "ERROR_UNKNOWN";
1017  if (DTVTunerType::kTunerTypeUnknown != tunertype)
1018  {
1019  subtype = tunertype.toString();
1020  }
1021 
1022  LOG(VB_GENERAL, LOG_DEBUG,
1023  QString("CardUtil[%1]: subtype:%2").arg(inputid).arg(subtype));
1024 
1025  return subtype;
1026 }
1027 
1029 bool CardUtil::IsDVBInputType(const QString &inputType)
1030 {
1031  QString t = inputType.toUpper();
1032  return (t == "DVB") || (t == "QPSK") || (t == "QAM") || (t == "OFDM") ||
1033  (t == "ATSC") || (t == "DVB_S2") || (t == "DVB_T2");
1034 }
1035 
1036 // Get the current delivery system from the card
1037 // Get all supported delivery systems from the card
1038 // If the current delivery system is DVB-T and DVB-T2 is supported then select DVB-T2
1039 // If the current delivery system is DVB-S and DVB-S2 is supported then select DVB-S2
1040 //
1042 {
1043  DTVModulationSystem delsys;
1044 
1045 #ifdef USING_DVB
1046  // Get the current delivery system from the card
1047  delsys = ProbeCurrentDeliverySystem(fd);
1048  LOG(VB_GENERAL, LOG_INFO, LOC +
1049  QString("Current delivery system: %1").arg(delsys.toString()));
1050 
1051  // Get all supported delivery systems from the card
1052  QString msg = "Supported delivery systems:";
1053  QStringList delsyslist = ProbeDeliverySystems(fd);
1054  msg.append(delsyslist.join(" "));
1055  LOG(VB_GENERAL, LOG_DEBUG, LOC + msg);
1056 
1057  // If the current delivery system is DVB-T and DVB-T2 is supported then select DVB-T2
1059  {
1061  if (delsyslist.contains(newdelsys.toString()))
1062  {
1063  LOG(VB_GENERAL, LOG_INFO, LOC +
1064  QString("Changing delivery system from %1 to %2")
1065  .arg(delsys.toString(), newdelsys.toString()));
1066  delsys = newdelsys;
1067  }
1068  }
1069 
1070  // If the current delivery system is DVB-S and DVB-S2 is supported then select DVB-S2
1072  {
1074  if (delsyslist.contains(newdelsys.toString()))
1075  {
1076  LOG(VB_GENERAL, LOG_INFO, LOC +
1077  QString("Changing delivery system from %1 to %2")
1078  .arg(delsys.toString(), newdelsys.toString()));
1079  delsys = newdelsys;
1080  }
1081  }
1082 #endif
1083 
1084  return delsys;
1085 }
1086 
1087 // Get the delivery system from the database
1088 // If not found then get the best delivery system from the card
1089 //
1091  [[maybe_unused]] int fd)
1092 {
1093  DTVModulationSystem delsys;
1094 #ifdef USING_DVB
1095 
1096  // If there is a valid modulation system in the database
1097  // then we return that and do nothing more.
1098  delsys = GetDeliverySystem(inputid);
1100  {
1101  return delsys;
1102  }
1103 
1104  // Nothing in the database, get default and use that
1105  delsys = ProbeBestDeliverySystem(fd);
1106  LOG(VB_GENERAL, LOG_INFO,
1107  QString("CardUtil[%1]: ").arg(inputid) +
1108  QString("No capturecard delivery system in database, using: %1").arg(delsys.toString()));
1109 
1110 #endif
1111 
1112  return delsys;
1113 }
1114 
1115 // Configure the tuner to use the delivery system from database
1116 // If not found then set to the best delivery system from the card
1117 //
1118 int CardUtil::SetDefaultDeliverySystem([[maybe_unused]] uint inputid,
1119  [[maybe_unused]] int fd)
1120 {
1121  int ret = -1;
1122 
1123 #ifdef USING_DVB
1124  DTVModulationSystem delsys = GetOrProbeDeliverySystem(inputid, fd);
1126  {
1127  ret = SetDeliverySystem(inputid, delsys, fd);
1128  }
1129 #endif
1130 
1131  return ret;
1132 }
1133 
1134 // Set delivery system from database
1135 //
1136 int CardUtil::SetDeliverySystem([[maybe_unused]] uint inputid)
1137 {
1138  int ret = -1;
1139 
1140 #ifdef USING_DVB
1141  DTVModulationSystem delsys = GetDeliverySystem(inputid);
1143  {
1144  ret = SetDeliverySystem(inputid, delsys);
1145  }
1146 #endif // USING_DVB
1147 
1148  return ret;
1149 }
1150 
1151 int CardUtil::SetDeliverySystem([[maybe_unused]] uint inputid,
1152  [[maybe_unused]] DTVModulationSystem delsys)
1153 {
1154  int ret = -1;
1155 
1156 #ifdef USING_DVB
1157  QString device = GetVideoDevice(inputid);
1158 
1159  if (device.isEmpty())
1160  {
1161  LOG(VB_GENERAL, LOG_DEBUG,
1162  QString("CardUtil[%1]: ").arg(inputid) +
1163  QString("inputid:%1 ").arg(inputid) +
1164  QString("delsys:%1").arg(delsys.toString()));
1165  return ret;
1166  }
1167 
1168  int fd_frontend = OpenVideoDevice(device);
1169  if (fd_frontend < 0)
1170  {
1171  LOG(VB_GENERAL, LOG_ERR,
1172  QString("CardUtil[%1]: ").arg(inputid) +
1173  QString("open failed (%1)").arg(device) + ENO);
1174  return ret;
1175  }
1176  ret = SetDeliverySystem(inputid, delsys, fd_frontend);
1177 
1178  close(fd_frontend);
1179 #endif // USING_DVB
1180 
1181  return ret;
1182 }
1183 
1184 // Get delivery system from the database and write it to the card
1185 int CardUtil::SetDeliverySystem([[maybe_unused]] uint inputid,
1186  [[maybe_unused]] int fd)
1187 {
1188  int ret = -1;
1189 
1190 #ifdef USING_DVB
1191  DTVModulationSystem delsys = GetDeliverySystem(inputid);
1193  {
1194  ret = SetDeliverySystem(inputid, delsys, fd);
1195  }
1196 #endif // USING_DVB
1197 
1198  return ret;
1199 }
1200 
1201 // Write the delivery system to the card
1202 int CardUtil::SetDeliverySystem([[maybe_unused]] uint inputid,
1203  [[maybe_unused]] DTVModulationSystem delsys,
1204  [[maybe_unused]] int fd)
1205 {
1206  int ret = -1;
1207 
1208 #ifdef USING_DVB
1209  LOG(VB_GENERAL, LOG_INFO,
1210  QString("CardUtil[%1]: ").arg(inputid) +
1211  QString("Set delivery system: %1").arg(delsys.toString()));
1212 
1213  struct dtv_property prop = {};
1214  struct dtv_properties cmd = {};
1215 
1216  prop.cmd = DTV_DELIVERY_SYSTEM;
1217  prop.u.data = delsys;
1218  cmd.num = 1;
1219  cmd.props = &prop;
1220 
1221  ret = ioctl(fd, FE_SET_PROPERTY, &cmd);
1222  if (ret < 0)
1223  {
1224  LOG(VB_GENERAL, LOG_ERR, LOC +
1225  QString("[%1] FE_SET_PROPERTY ioctl failed")
1226  .arg(inputid) + ENO);
1227  return ret;
1228  }
1229 #endif // USING_DVB
1230 
1231  return ret;
1232 }
1233 
1235 {
1236  QString device = GetVideoDevice(inputid);
1237  return OpenVideoDevice(device);
1238 }
1239 
1240 int CardUtil::OpenVideoDevice(const QString &device)
1241 {
1242  if (device.isEmpty())
1243  return -1;
1244 
1245  QString dvbdev = CardUtil::GetDeviceName(DVB_DEV_FRONTEND, device);
1246  QByteArray dev = dvbdev.toLatin1();
1247  int fd_frontend = open(dev.constData(), O_RDWR | O_NONBLOCK);
1248  if (fd_frontend < 0)
1249  {
1250  LOG(VB_GENERAL, LOG_ERR, LOC +
1251  QString("Can't open DVB frontend (%1) for %2.")
1252  .arg(dvbdev, device) + ENO);
1253  }
1254  return fd_frontend;
1255 }
1256 
1257 QString get_on_input(const QString &to_get, uint inputid)
1258 {
1259  MSqlQuery query(MSqlQuery::InitCon());
1260  query.prepare(
1261  QString("SELECT %1 ").arg(to_get) +
1262  "FROM capturecard "
1263  "WHERE capturecard.cardid = :INPUTID");
1264  query.bindValue(":INPUTID", inputid);
1265 
1266  if (!query.exec())
1267  MythDB::DBError("CardUtil::get_on_input", query);
1268  else if (query.next())
1269  return query.value(0).toString();
1270 
1271  return {};
1272 }
1273 
1274 bool set_on_input(const QString &to_set, uint inputid, const QString &value)
1275 {
1276  QString tmp = get_on_input("capturecard.cardid", inputid);
1277  if (tmp.isEmpty())
1278  return false;
1279 
1280  MSqlQuery query(MSqlQuery::InitCon());
1281  query.prepare(
1282  QString("UPDATE capturecard SET %1 = :VALUE ").arg(to_set) +
1283  "WHERE cardid = :INPUTID");
1284  query.bindValue(":INPUTID", inputid);
1285  query.bindValue(":VALUE", value);
1286 
1287  if (query.exec())
1288  return true;
1289 
1290  MythDB::DBError("CardUtil::set_on_input", query);
1291  return false;
1292 }
1293 
1304 std::vector<uint> CardUtil::GetInputIDs(const QString& videodevice,
1305  const QString& rawtype,
1306  const QString& inputname,
1307  QString hostname)
1308 {
1309  std::vector<uint> list;
1310 
1311  if (hostname.isEmpty())
1313 
1314  MSqlQuery query(MSqlQuery::InitCon());
1315  QString qstr =
1316  "SELECT cardid "
1317  "FROM capturecard "
1318  "WHERE hostname = :HOSTNAME ";
1319  if (!videodevice.isEmpty())
1320  qstr += "AND videodevice = :DEVICE ";
1321  if (!inputname.isEmpty())
1322  qstr += "AND inputname = :INPUTNAME ";
1323  if (!rawtype.isEmpty())
1324  qstr += "AND cardtype = :INPUTTYPE ";
1325  qstr += "ORDER BY cardid";
1326 
1327  query.prepare(qstr);
1328 
1329  query.bindValue(":HOSTNAME", hostname);
1330  if (!videodevice.isEmpty())
1331  query.bindValue(":DEVICE", videodevice);
1332  if (!inputname.isEmpty())
1333  query.bindValue(":INPUTNAME", inputname);
1334  if (!rawtype.isEmpty())
1335  query.bindValue(":INPUTTYPE", rawtype.toUpper());
1336 
1337  if (!query.exec())
1338  MythDB::DBError("CardUtil::GetInputIDs(videodevice...)", query);
1339  else
1340  {
1341  while (query.next())
1342  list.push_back(query.value(0).toUInt());
1343  }
1344 
1345  return list;
1346 }
1347 
1349 {
1350  if (!inputid)
1351  return 0;
1352 
1353  MSqlQuery query(MSqlQuery::InitCon());
1354  QString qstr =
1355  "SELECT COUNT(*) "
1356  "FROM capturecard "
1357  "WHERE parentid = :INPUTID";
1358 
1359  query.prepare(qstr);
1360  query.bindValue(":INPUTID", inputid);
1361 
1362  uint count = 0;
1363 
1364  if (!query.exec())
1365  MythDB::DBError("CardUtil::GetChildInputCount()", query);
1366  else if (query.next())
1367  count = query.value(0).toUInt();
1368 
1369  return count;
1370 }
1371 
1372 std::vector<uint> CardUtil::GetChildInputIDs(uint inputid)
1373 {
1374  std::vector<uint> list;
1375 
1376  if (!inputid)
1377  return list;
1378 
1379  MSqlQuery query(MSqlQuery::InitCon());
1380  QString qstr =
1381  "SELECT cardid "
1382  "FROM capturecard "
1383  "WHERE parentid = :INPUTID "
1384  "ORDER BY cardid";
1385 
1386  query.prepare(qstr);
1387  query.bindValue(":INPUTID", inputid);
1388 
1389  if (!query.exec())
1390  MythDB::DBError("CardUtil::GetChildInputIDs()", query);
1391  else
1392  {
1393  while (query.next())
1394  list.push_back(query.value(0).toUInt());
1395  }
1396 
1397  return list;
1398 }
1399 
1400 static uint clone_capturecard(uint src_inputid, uint orig_dst_inputid)
1401 {
1402  uint dst_inputid = orig_dst_inputid;
1403 
1404  MSqlQuery query(MSqlQuery::InitCon());
1405  if (!dst_inputid)
1406  {
1407  query.prepare(
1408  "DELETE FROM capturecard "
1409  "WHERE videodevice = 'temp_dummy'");
1410 
1411  if (!query.exec())
1412  {
1413  MythDB::DBError("clone_capturecard -- delete temp", query);
1414  return 0;
1415  }
1416 
1417  query.prepare(
1418  "INSERT INTO capturecard "
1419  "SET videodevice = 'temp_dummy'");
1420 
1421  if (!query.exec())
1422  {
1423  MythDB::DBError("clone_capturecard -- insert temp", query);
1424  return 0;
1425  }
1426 
1427  query.prepare(
1428  "SELECT cardid "
1429  "FROM capturecard "
1430  "WHERE videodevice = 'temp_dummy'");
1431 
1432  if (!query.exec())
1433  {
1434  MythDB::DBError("clone_capturecard -- get temp id", query);
1435  return 0;
1436  }
1437 
1438  if (!query.next())
1439  {
1440  LOG(VB_GENERAL, LOG_ERR, "clone_capturecard -- get temp id");
1441  return 0;
1442  }
1443 
1444  dst_inputid = query.value(0).toUInt();
1445  }
1446 
1447  query.prepare(
1448  "SELECT videodevice, audiodevice, vbidevice, "
1449  " cardtype, hostname, signal_timeout, "
1450  " channel_timeout, dvb_wait_for_seqstart, dvb_on_demand, "
1451  " dvb_tuning_delay, dvb_diseqc_type, diseqcid, "
1452  " dvb_eitscan, inputname, sourceid, "
1453  " externalcommand, changer_device, changer_model, "
1454  " tunechan, startchan, displayname, "
1455  " dishnet_eit, recpriority, quicktune, "
1456  " livetvorder, reclimit, "
1457  // See below for special handling of the following.
1458  " schedgroup, schedorder "
1459  "FROM capturecard "
1460  "WHERE cardid = :INPUTID");
1461  query.bindValue(":INPUTID", src_inputid);
1462 
1463  if (!query.exec())
1464  {
1465  MythDB::DBError("clone_capturecard -- get data", query);
1466  return 0;
1467  }
1468  if (!query.next())
1469  {
1470  LOG(VB_GENERAL, LOG_ERR, "clone_cardinput -- get data 2");
1471  return 0;
1472  }
1473 
1474  // Handle schedgroup and schedorder specially. If schedgroup is
1475  // set, schedgroup and schedorder should be false and 0,
1476  // respectively, for all children.
1477  bool schedgroup = query.value(26).toBool();
1478  uint schedorder = query.value(27).toUInt();
1479  if (schedgroup)
1480  {
1481  schedgroup = false;
1482  schedorder = 0;
1483  }
1484 
1485  MSqlQuery query2(MSqlQuery::InitCon());
1486  query2.prepare(
1487  "UPDATE capturecard "
1488  "SET videodevice = :V0, "
1489  " audiodevice = :V1, "
1490  " vbidevice = :V2, "
1491  " cardtype = :V3, "
1492  " hostname = :V4, "
1493  " signal_timeout = :V5, "
1494  " channel_timeout = :V6, "
1495  " dvb_wait_for_seqstart = :V7, "
1496  " dvb_on_demand = :V8, "
1497  " dvb_tuning_delay = :V9, "
1498  " dvb_diseqc_type = :V10, "
1499  " diseqcid = :V11,"
1500  " dvb_eitscan = :V12, "
1501  " inputname = :V13, "
1502  " sourceid = :V14, "
1503  " externalcommand = :V15, "
1504  " changer_device = :V16, "
1505  " changer_model = :V17, "
1506  " tunechan = :V18, "
1507  " startchan = :V19, "
1508  " displayname = :V20, "
1509  " dishnet_eit = :V21, "
1510  " recpriority = :V22, "
1511  " quicktune = :V23, "
1512  " livetvorder = :V24, "
1513  " reclimit = :V25, "
1514  " schedgroup = :SCHEDGROUP, "
1515  " schedorder = :SCHEDORDER, "
1516  " parentid = :PARENTID "
1517  "WHERE cardid = :INPUTID");
1518  for (uint i = 0; i < 26; ++i)
1519  query2.bindValue(QString(":V%1").arg(i), query.value(i).toString());
1520  query2.bindValue(":INPUTID", dst_inputid);
1521  query2.bindValue(":PARENTID", src_inputid);
1522  query2.bindValue(":SCHEDGROUP", schedgroup);
1523  query2.bindValue(":SCHEDORDER", schedorder);
1524 
1525  if (!query2.exec())
1526  {
1527  MythDB::DBError("clone_capturecard -- save data", query2);
1528  if (!orig_dst_inputid)
1529  CardUtil::DeleteInput(dst_inputid);
1530  return 0;
1531  }
1532 
1533  // copy input group linkages
1534  std::vector<uint> src_grps = CardUtil::GetInputGroups(src_inputid);
1535  std::vector<uint> dst_grps = CardUtil::GetInputGroups(dst_inputid);
1536  for (uint dst_grp : dst_grps)
1537  CardUtil::UnlinkInputGroup(dst_inputid, dst_grp);
1538  for (uint src_grp : src_grps)
1539  CardUtil::LinkInputGroup(dst_inputid, src_grp);
1540 
1541  // clone diseqc_config (just points to the same diseqc_tree row)
1542  DiSEqCDevSettings diseqc;
1543  if (diseqc.Load(src_inputid))
1544  diseqc.Store(dst_inputid);
1545 
1546  return dst_inputid;
1547 }
1548 
1549 uint CardUtil::CloneCard(uint src_inputid, uint orig_dst_inputid)
1550 {
1551  QString type = CardUtil::GetRawInputType(src_inputid);
1553  return 0;
1554 
1555  uint dst_inputid = clone_capturecard(src_inputid, orig_dst_inputid);
1556  return dst_inputid;
1557 }
1558 
1559 
1560 bool CardUtil::InputSetMaxRecordings(uint parentid, uint max_recordings)
1561 {
1562  if (max_recordings < 1)
1563  {
1564  LOG(VB_GENERAL, LOG_ERR, LOC +
1565  "InputSetMaxRecording: max must be greater than zero.");
1566  return false;
1567  }
1568 
1569  std::vector<uint> cardids = CardUtil::GetChildInputIDs(parentid);
1570 
1571  // Delete old clone cards as required.
1572  for (size_t i = cardids.size() + 1;
1573  (i > max_recordings) && !cardids.empty(); --i)
1574  {
1575  CardUtil::DeleteInput(cardids.back());
1576  cardids.pop_back();
1577  }
1578 
1579  // Clone this config to existing clone cards.
1580  for (uint id : cardids)
1581  CardUtil::CloneCard(parentid, id);
1582 
1583  // Create new clone cards as required.
1584  for (size_t i = cardids.size() + 1; i < max_recordings; ++i)
1585  {
1586  CardUtil::CloneCard(parentid, 0);
1587  }
1588 
1589  return true;
1590 }
1591 
1592 
1594 {
1595  uint inputid = CloneCard(parentid, 0);
1596 
1597  // Update the reclimit for the parent and all children so the new
1598  // child doesn't get removed the next time mythtv-setup is run.
1599  if (inputid)
1600  {
1601  LOG(VB_GENERAL, LOG_INFO, LOC +
1602  QString("Added child input %1 to parent %2")
1603  .arg(inputid).arg(parentid));
1604  MSqlQuery query(MSqlQuery::InitCon());
1605  query.prepare("UPDATE capturecard "
1606  "SET reclimit = reclimit + 1 "
1607  "WHERE cardid = :PARENTID");
1608  query.bindValue(":PARENTID", parentid);
1609  if (!query.exec())
1610  MythDB::DBError("CardUtil::AddChildInput", query);
1611  }
1612  else
1613  {
1614  LOG(VB_GENERAL, LOG_ERR, LOC +
1615  QString("Failed to add child input to parent %1").arg(parentid));
1616  }
1617 
1618  return inputid;
1619 }
1620 
1622 {
1623  QString fwnode;
1624 
1625  MSqlQuery query(MSqlQuery::InitCon());
1626  query.prepare("SELECT changer_device "
1627  "FROM capturecard WHERE cardid = :INPUTID ");
1628  query.bindValue(":INPUTID", inputid);
1629 
1630  if (query.exec() && query.next())
1631  {
1632  fwnode = query.value(0).toString();
1633  }
1634 
1635  return fwnode;
1636 }
1637 
1639 {
1640  QString fwnode;
1641 
1642  MSqlQuery query(MSqlQuery::InitCon());
1643  query.prepare("SELECT changer_model "
1644  "FROM capturecard WHERE cardid = :INPUTID ");
1645  query.bindValue(":INPUTID", inputid);
1646 
1647  if (query.exec() && query.next())
1648  {
1649  fwnode = query.value(0).toString();
1650  }
1651 
1652  return fwnode;
1653 }
1654 
1655 std::vector<uint> CardUtil::GetInputIDs(uint sourceid)
1656 {
1657  MSqlQuery query(MSqlQuery::InitCon());
1658 
1659  query.prepare(
1660  "SELECT DISTINCT cardid "
1661  "FROM capturecard "
1662  "WHERE sourceid = :SOURCEID");
1663  query.bindValue(":SOURCEID", sourceid);
1664 
1665  std::vector<uint> list;
1666 
1667  if (!query.exec())
1668  {
1669  MythDB::DBError("CardUtil::GetInputIDs(sourceid)", query);
1670  return list;
1671  }
1672 
1673  while (query.next())
1674  list.push_back(query.value(0).toUInt());
1675 
1676  return list;
1677 }
1678 
1679 bool CardUtil::SetStartChannel(uint inputid, const QString &channum)
1680 {
1681  MSqlQuery query(MSqlQuery::InitCon());
1682  query.prepare("UPDATE capturecard "
1683  "SET startchan = :CHANNUM "
1684  "WHERE cardid = :INPUTID");
1685  query.bindValue(":CHANNUM", channum);
1686  query.bindValue(":INPUTID", inputid);
1687 
1688  if (!query.exec())
1689  {
1690  MythDB::DBError("CardUtil::SetStartChannel", query);
1691  return false;
1692  }
1693 
1694  return true;
1695 }
1696 
1697 bool CardUtil::GetInputInfo(InputInfo &input, std::vector<uint> *groupids)
1698 {
1699  if (!input.m_inputId)
1700  return false;
1701 
1702  MSqlQuery query(MSqlQuery::InitCon());
1703  query.prepare("SELECT "
1704  "inputname, sourceid, livetvorder, "
1705  "schedorder, displayname, recpriority, quicktune "
1706  "FROM capturecard "
1707  "WHERE cardid = :INPUTID");
1708  query.bindValue(":INPUTID", input.m_inputId);
1709 
1710  if (!query.exec())
1711  {
1712  MythDB::DBError("CardUtil::GetInputInfo()", query);
1713  return false;
1714  }
1715 
1716  if (!query.next())
1717  return false;
1718 
1719  input.m_name = query.value(0).toString();
1720  input.m_sourceId = query.value(1).toUInt();
1721  input.m_liveTvOrder = query.value(2).toUInt();
1722  input.m_scheduleOrder = query.value(3).toUInt();
1723  input.m_displayName = query.value(4).toString();
1724  input.m_recPriority = query.value(5).toInt();
1725  input.m_quickTune = query.value(6).toBool();
1726 
1727  if (groupids)
1728  *groupids = GetInputGroups(input.m_inputId);
1729 
1730  return true;
1731 }
1732 
1733 // virtTuners to include virtual tuners in the list
1734 QList<InputInfo> CardUtil::GetAllInputInfo(bool virtTuners)
1735 {
1736  QList<InputInfo> infoInputList;
1737 
1738  MSqlQuery query(MSqlQuery::InitCon());
1739  QString queryStr = "SELECT cardid, "
1740  "inputname, sourceid, livetvorder, "
1741  "schedorder, displayname, recpriority, quicktune "
1742  "FROM capturecard";
1743 
1744  if (!virtTuners)
1745  queryStr.append(" WHERE parentid = 0");
1746  query.prepare(queryStr);
1747 
1748  if (!query.exec())
1749  {
1750  MythDB::DBError("CardUtil::GetAllInputInfo()", query);
1751  return infoInputList;
1752  }
1753 
1754  while (query.next())
1755  {
1756  InputInfo input;
1757  input.m_inputId = query.value(0).toUInt();
1758  input.m_name = query.value(1).toString();
1759  input.m_sourceId = query.value(2).toUInt();
1760  input.m_liveTvOrder = query.value(3).toUInt();
1761  input.m_scheduleOrder = query.value(4).toUInt();
1762  input.m_displayName = query.value(5).toString();
1763  input.m_recPriority = query.value(6).toInt();
1764  input.m_quickTune = query.value(7).toBool();
1765 
1766  infoInputList.push_back(input);
1767  }
1768 
1769  return infoInputList;
1770 }
1771 
1773 {
1774  InputInfo info("None", 0, inputid, 0, 0, 0);
1775  GetInputInfo(info);
1776  return info.m_name;
1777 }
1778 
1779 // Get start channel for a capture card
1780 //
1781 // The start channel is:
1782 // - the last channel used for Live TV as found in field startchan
1783 // - if that is not filled in, the first visible channel from
1784 // the video source of the capture card
1785 //
1786 // The start channel is maintained per capture card instance so
1787 // different capture cards connected to the same video source
1788 // and different multirec instances of the same capture card
1789 // can have a different starting channel value.
1790 //
1792 {
1793  QString startchan;
1794 
1795  MSqlQuery query(MSqlQuery::InitCon());
1796  query.prepare("SELECT startchan "
1797  "FROM capturecard "
1798  "WHERE cardid = :INPUTID");
1799  query.bindValue(":INPUTID", inputid);
1800 
1801  if (!query.exec() || !query.isActive())
1802  {
1803  MythDB::DBError("CardUtil::GetStartChannel#1", query);
1804  }
1805  else if (query.next())
1806  {
1807  startchan = query.value(0).toString();
1808  }
1809 
1810  // Check if starting channel is valid; skip invisible channels.
1811  if (!startchan.isEmpty())
1812  {
1813  query.prepare("SELECT channel.chanid "
1814  "FROM capturecard, channel "
1815  "WHERE capturecard.cardid = :INPUTID AND "
1816  " capturecard.sourceid = channel.sourceid AND "
1817  " channel.deleted IS NULL AND "
1818  " channel.visible > 0 AND "
1819  " channel.channum = :CHANNUM");
1820  query.bindValue(":INPUTID", inputid);
1821  query.bindValue(":CHANNUM", startchan);
1822 
1823  if (!query.exec() || !query.isActive())
1824  {
1825  MythDB::DBError("CardUtil::GetStartChannel#2", query);
1826  }
1827  else if (!query.next())
1828  {
1829  LOG(VB_GENERAL, LOG_WARNING,
1830  QString("CardUtil[%1]: ").arg(inputid) +
1831  QString("Channel %1 on inputid %2 is invalid").arg(startchan).arg(inputid));
1832  startchan.clear();
1833  }
1834  }
1835 
1836  // If we do not have a start channel yet then use the first one available.
1837  // Any visible channel will do so there is no "order by" done.
1838  if (startchan.isEmpty())
1839  {
1840  query.prepare("SELECT channel.channum "
1841  "FROM capturecard, channel "
1842  "WHERE capturecard.cardid = :INPUTID AND "
1843  " capturecard.sourceid = channel.sourceid AND "
1844  " channel.deleted IS NULL AND "
1845  " channel.visible > 0 "
1846  "LIMIT 1");
1847  query.bindValue(":INPUTID", inputid);
1848 
1849  if (!query.exec() || !query.isActive())
1850  {
1851  MythDB::DBError("CardUtil::GetStartChannel#3", query);
1852  }
1853  else if (query.next())
1854  {
1855  startchan = query.value(0).toString();
1856  }
1857  }
1858 
1859  if (startchan.isEmpty())
1860  {
1861  LOG(VB_GENERAL, LOG_WARNING,
1862  QString("CardUtil[%1]: ").arg(inputid) +
1863  QString("No start channel found on inputid %1").arg(inputid));
1864  }
1865  else
1866  {
1867  LOG(VB_GENERAL, LOG_DEBUG,
1868  QString("CardUtil[%1]: ").arg(inputid) +
1869  QString("Start channel %1 on inputid %2").arg(startchan).arg(inputid));
1870  }
1871 
1872  return startchan;
1873 }
1874 
1876 {
1877  if (!inputid)
1878  return {};
1879 
1880  MSqlQuery query(MSqlQuery::InitCon());
1881  query.prepare("SELECT displayname "
1882  "FROM capturecard "
1883  "WHERE cardid = :INPUTID");
1884  query.bindValue(":INPUTID", inputid);
1885 
1886  if (!query.exec())
1887  MythDB::DBError("CardUtil::GetDisplayName(uint)", query);
1888  else if (query.next())
1889  {
1890  QString result = query.value(0).toString();
1891  return result;
1892  }
1893 
1894  return {};
1895 }
1896 
1897 bool CardUtil::IsUniqueDisplayName(const QString &name, uint exclude_inputid)
1898 {
1899  if (name.isEmpty())
1900  return false;
1901 
1902  QString matching;
1903  bool two { false };
1904  qsizetype idx = name.indexOf('/');
1905  if (idx >= 0)
1906  {
1907  matching = name.right(name.size() - idx -1);
1908  two = false;
1909  }
1910  else
1911  {
1912  matching = name.right(2);
1913  two = true;
1914  }
1915 
1916  MSqlQuery query(MSqlQuery::InitCon());
1917  query.prepare("SELECT cardid, displayname "
1918  "FROM capturecard "
1919  "WHERE parentid = 0 "
1920  " AND cardid <> :INPUTID ");
1921  query.bindValue(":INPUTID", exclude_inputid);
1922 
1923  if (!query.exec())
1924  {
1925  MythDB::DBError("CardUtil::IsUniqueDisplayName()", query);
1926  return false;
1927  }
1928 
1929  while (query.next())
1930  {
1931  QString dn = query.value(1).toString();
1932  idx = dn.indexOf('/');
1933  if (!two && idx >= 0)
1934  {
1935  if (dn.right(dn.size() - idx - 1) == matching)
1936  return false;
1937  }
1938  else if (dn.right(2) == matching.right(2))
1939  {
1940  return false;
1941  }
1942  }
1943 
1944  return true;
1945 }
1946 
1948 {
1949  MSqlQuery query(MSqlQuery::InitCon());
1950  query.prepare(
1951  "SELECT sourceid "
1952  "FROM capturecard "
1953  "WHERE cardid = :INPUTID");
1954  query.bindValue(":INPUTID", inputid);
1955  if (!query.exec() || !query.isActive())
1956  MythDB::DBError("CardUtil::GetSourceID()", query);
1957  else if (query.next())
1958  return query.value(0).toUInt();
1959 
1960  return 0;
1961 }
1962 
1963 // Is this intentionally leaving out the hostname when updating the
1964 // capturecard table? The hostname value does get set when inserting
1965 // into the capturecard table. (Code written in 2011.)
1967  const uint sourceid,
1968  const QString &inputname,
1969  const QString &externalcommand,
1970  const QString &changer_device,
1971  const QString &changer_model,
1972  const QString &/*hostname*/,
1973  const QString &tunechan,
1974  const QString &startchan,
1975  const QString &displayname,
1976  bool dishnet_eit,
1977  const uint recpriority,
1978  const uint quicktune,
1979  const uint schedorder,
1980  const uint livetvorder)
1981 {
1982  MSqlQuery query(MSqlQuery::InitCon());
1983 
1984  query.prepare(
1985  "UPDATE capturecard "
1986  "SET sourceid = :SOURCEID, "
1987  " inputname = :INPUTNAME, "
1988  " externalcommand = :EXTERNALCOMMAND, "
1989  " changer_device = :CHANGERDEVICE, "
1990  " changer_model = :CHANGERMODEL, "
1991  " tunechan = :TUNECHAN, "
1992  " startchan = :STARTCHAN, "
1993  " displayname = :DISPLAYNAME, "
1994  " dishnet_eit = :DISHNETEIT, "
1995  " recpriority = :RECPRIORITY, "
1996  " quicktune = :QUICKTUNE, "
1997  " schedorder = :SCHEDORDER, "
1998  " livetvorder = :LIVETVORDER "
1999  "WHERE cardid = :INPUTID AND "
2000  " inputname = 'None'");
2001 
2002  query.bindValue(":INPUTID", inputid);
2003  query.bindValue(":SOURCEID", sourceid);
2004  query.bindValue(":INPUTNAME", inputname);
2005  query.bindValue(":EXTERNALCOMMAND", externalcommand);
2006  query.bindValue(":CHANGERDEVICE", changer_device);
2007  query.bindValue(":CHANGERMODEL", changer_model);
2008  query.bindValue(":TUNECHAN", tunechan);
2009  query.bindValue(":STARTCHAN", startchan);
2010  query.bindValueNoNull(":DISPLAYNAME", displayname);
2011  query.bindValue(":DISHNETEIT", dishnet_eit);
2012  query.bindValue(":RECPRIORITY", recpriority);
2013  query.bindValue(":QUICKTUNE", quicktune);
2014  query.bindValue(":SCHEDORDER", schedorder);
2015  query.bindValue(":LIVETVORDER", livetvorder);
2016 
2017  if (!query.exec())
2018  {
2019  MythDB::DBError("CardUtil::CreateCardInput()", query);
2020  return -1;
2021  }
2022 
2023  return inputid;
2024 }
2025 
2026 uint CardUtil::CreateInputGroup(const QString &name)
2027 {
2028  MSqlQuery query(MSqlQuery::InitCon());
2029 
2030  query.prepare("SELECT inputgroupid FROM inputgroup "
2031  "WHERE inputgroupname = :GROUPNAME "
2032  "LIMIT 1");
2033  query.bindValue(":GROUPNAME", name);
2034  if (!query.exec())
2035  {
2036  MythDB::DBError("CardUtil::CreateNewInputGroup 0", query);
2037  return 0;
2038  }
2039 
2040  if (query.next())
2041  return query.value(0).toUInt();
2042 
2043  query.prepare("SELECT MAX(inputgroupid) FROM inputgroup");
2044  if (!query.exec())
2045  {
2046  MythDB::DBError("CardUtil::CreateNewInputGroup 1", query);
2047  return 0;
2048  }
2049 
2050  uint inputgroupid = (query.next()) ? query.value(0).toUInt() + 1 : 1;
2051 
2052  query.prepare(
2053  "INSERT INTO inputgroup "
2054  " (cardinputid, inputgroupid, inputgroupname) "
2055  "VALUES (:INPUTID, :GROUPID, :GROUPNAME ) ");
2056  query.bindValue(":INPUTID", 0);
2057  query.bindValue(":GROUPID", inputgroupid);
2058  query.bindValue(":GROUPNAME", name);
2059  if (!query.exec())
2060  {
2061  MythDB::DBError("CardUtil::CreateNewInputGroup 2", query);
2062  return 0;
2063  }
2064 
2065  return inputgroupid;
2066 }
2067 
2069  const QString &type,
2070  const QString &host,
2071  const QString &device)
2072 {
2073  QString name = host + '|' + device;
2074  if (type == "FREEBOX" || type == "IMPORT" ||
2075  type == "DEMO" || type == "EXTERNAL" ||
2076  type == "HDHOMERUN" )
2077  name += QString("|%1").arg(inputid);
2078  return CreateInputGroup(name);
2079 }
2080 
2082 {
2083  MSqlQuery query(MSqlQuery::InitCon());
2084  query.prepare(
2085  "SELECT inputgroupid "
2086  "FROM inputgroup "
2087  "WHERE cardinputid = :INPUTID "
2088  " AND inputgroupname REGEXP '^[a-z_-]*\\\\|'");
2089  query.bindValue(":INPUTID", inputid);
2090 
2091  if (!query.exec())
2092  {
2093  MythDB::DBError("CardUtil::GetDeviceInputGroup()", query);
2094  return 0;
2095  }
2096 
2097  if (query.next())
2098  {
2099  return query.value(0).toUInt();
2100  }
2101 
2102  return 0;
2103 }
2104 
2105 bool CardUtil::LinkInputGroup(uint inputid, uint inputgroupid)
2106 {
2107  MSqlQuery query(MSqlQuery::InitCon());
2108 
2109  query.prepare(
2110  "SELECT cardinputid, inputgroupid, inputgroupname "
2111  "FROM inputgroup "
2112  "WHERE inputgroupid = :GROUPID "
2113  "ORDER BY inputgroupid, cardinputid, inputgroupname");
2114  query.bindValue(":GROUPID", inputgroupid);
2115 
2116  if (!query.exec())
2117  {
2118  MythDB::DBError("CardUtil::LinkInputGroup() 1", query);
2119  return false;
2120  }
2121 
2122  QString name;
2123  while (query.next()) {
2124  name = query.value(2).toString();
2125  uint cardid = query.value(0).toUInt();
2126  // Already linked
2127  if (cardid == inputid)
2128  return true;
2129  }
2130 
2131  // Invalid group id
2132  if (name.isEmpty())
2133  return false;
2134 
2135  query.prepare(
2136  "INSERT INTO inputgroup "
2137  " (cardinputid, inputgroupid, inputgroupname) "
2138  "VALUES (:INPUTID, :GROUPID, :GROUPNAME ) ");
2139 
2140  query.bindValue(":INPUTID", inputid);
2141  query.bindValue(":GROUPID", inputgroupid);
2142  query.bindValue(":GROUPNAME", name);
2143 
2144  if (!query.exec())
2145  {
2146  MythDB::DBError("CardUtil::LinkInputGroup() 2", query);
2147  return false;
2148  }
2149 
2150  // Now that there is a proper linkage, unlink temporary cardid 0
2151  UnlinkInputGroup(0, inputgroupid);
2152 
2153  return true;
2154 }
2155 
2156 bool CardUtil::UnlinkInputGroup(uint inputid, uint inputgroupid)
2157 {
2158  MSqlQuery query(MSqlQuery::InitCon());
2159 
2160  if (!inputid && !inputgroupid)
2161  {
2162  query.prepare(
2163  "DELETE FROM inputgroup "
2164  "WHERE cardinputid NOT IN "
2165  "( SELECT cardid FROM capturecard )");
2166  }
2167  else
2168  {
2169  query.prepare(
2170  "DELETE FROM inputgroup "
2171  "WHERE cardinputid = :INPUTID AND "
2172  " inputgroupid = :GROUPID ");
2173 
2174  query.bindValue(":INPUTID", inputid);
2175  query.bindValue(":GROUPID", inputgroupid);
2176  }
2177 
2178  if (!query.exec())
2179  {
2180  MythDB::DBError("CardUtil::DeleteInputGroup()", query);
2181  return false;
2182  }
2183 
2184  return true;
2185 }
2186 
2187 std::vector<uint> CardUtil::GetInputGroups(uint inputid)
2188 {
2189  std::vector<uint> list;
2190 
2191  MSqlQuery query(MSqlQuery::InitCon());
2192 
2193  query.prepare(
2194  "SELECT inputgroupid "
2195  "FROM inputgroup "
2196  "WHERE cardinputid = :INPUTID "
2197  "ORDER BY inputgroupid, cardinputid, inputgroupname");
2198 
2199  query.bindValue(":INPUTID", inputid);
2200 
2201  if (!query.exec())
2202  {
2203  MythDB::DBError("CardUtil::GetInputGroups()", query);
2204  return list;
2205  }
2206 
2207  while (query.next())
2208  list.push_back(query.value(0).toUInt());
2209 
2210  return list;
2211 }
2212 
2213 std::vector<uint> CardUtil::GetGroupInputIDs(uint inputgroupid)
2214 {
2215  std::vector<uint> list;
2216 
2217  MSqlQuery query(MSqlQuery::InitCon());
2218 
2219  query.prepare(
2220  "SELECT DISTINCT cardid "
2221  "FROM capturecard, inputgroup "
2222  "WHERE inputgroupid = :GROUPID AND "
2223  " capturecard.cardid = inputgroup.cardinputid "
2224  "ORDER BY cardid");
2225 
2226  query.bindValue(":GROUPID", inputgroupid);
2227 
2228  if (!query.exec())
2229  {
2230  MythDB::DBError("CardUtil::GetGroupInputIDs()", query);
2231  return list;
2232  }
2233 
2234  while (query.next())
2235  list.push_back(query.value(0).toUInt());
2236 
2237  return list;
2238 }
2239 
2240 std::vector<uint> CardUtil::GetConflictingInputs(uint inputid)
2241 {
2242  std::vector<uint> inputids;
2243 
2244  MSqlQuery query(MSqlQuery::InitCon());
2245 
2246  query.prepare(
2247  "SELECT DISTINCT c.cardid "
2248  "FROM ( "
2249  " SELECT inputgroupid "
2250  " FROM inputgroup "
2251  " WHERE cardinputid = :INPUTID1 "
2252  ") g "
2253  "JOIN inputgroup ig ON ig.inputgroupid = g.inputgroupid "
2254  "JOIN capturecard c ON c.cardid = ig.cardinputid "
2255  " AND c.cardid <> :INPUTID2 "
2256  " AND c.sourceid > 0 "
2257  "ORDER BY c.cardid");
2258 
2259  query.bindValue(":INPUTID1", inputid);
2260  query.bindValue(":INPUTID2", inputid);
2261 
2262  if (!query.exec())
2263  {
2264  MythDB::DBError("CardUtil::GetConflictingInputs()", query);
2265  return inputids;
2266  }
2267 
2268  while (query.next())
2269  {
2270  inputids.push_back(query.value(0).toUInt());
2271  }
2272 
2273  if (VERBOSE_LEVEL_CHECK(VB_RECORD, LOG_INFO))
2274  {
2275  QString msg = QString("CardUtil[%1]: GetConflictingInputs(%1) ").arg(inputid);
2276  QStringList ids;
2277  for (auto id : inputids)
2278  {
2279  ids.append(QString::number(id));
2280  }
2281  msg.append(ids.join(','));
2282  LOG(VB_RECORD, LOG_INFO, msg);
2283  }
2284  return inputids;
2285 }
2286 
2288  std::chrono::milliseconds &signal_timeout,
2289  std::chrono::milliseconds &channel_timeout)
2290 {
2291  MSqlQuery query(MSqlQuery::InitCon());
2292  query.prepare(
2293  "SELECT signal_timeout, channel_timeout "
2294  "FROM capturecard "
2295  "WHERE cardid = :INPUTID");
2296  query.bindValue(":INPUTID", inputid);
2297 
2298  if (!query.exec() || !query.isActive())
2299  MythDB::DBError("CardUtil::GetTimeouts()", query);
2300  else if (query.next())
2301  {
2302  signal_timeout = std::max(std::chrono::milliseconds(query.value(0).toInt()), 250ms);
2303  channel_timeout = std::max(std::chrono::milliseconds(query.value(1).toInt()), 500ms);
2304  return true;
2305  }
2306 
2307  return false;
2308 }
2309 
2311 {
2312  DiSEqCDevTree *diseqc_tree = DiSEqCDev::FindTree(inputid);
2313 
2314  bool needsConf = false;
2315  if (diseqc_tree)
2316  needsConf = diseqc_tree->IsInNeedOfConf();
2317 
2318  return needsConf;
2319 }
2320 
2321 uint CardUtil::GetQuickTuning(uint inputid, const QString &input_name)
2322 {
2323  uint quicktune = 0;
2324 
2325  MSqlQuery query(MSqlQuery::InitCon());
2326  query.prepare(
2327  "SELECT quicktune "
2328  "FROM capturecard "
2329  "WHERE cardid = :INPUTID AND "
2330  " inputname = :INPUTNAME");
2331  query.bindValue(":INPUTID", inputid);
2332  query.bindValue(":INPUTNAME", input_name);
2333 
2334  if (!query.exec() || !query.isActive())
2335  MythDB::DBError("CardUtil::GetQuickTuning()", query);
2336  else if (query.next())
2337  quicktune = query.value(0).toUInt();
2338 
2339  return quicktune;
2340 }
2341 
2342 bool CardUtil::hasV4L2([[maybe_unused]] int videofd)
2343 {
2344 #ifdef USING_V4L2
2345  struct v4l2_capability vcap {};
2346 
2347  return ((ioctl(videofd, VIDIOC_QUERYCAP, &vcap) >= 0) &&
2348  ((vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE) != 0U));
2349 #else // if !USING_V4L2
2350  return false;
2351 #endif // !USING_V4L2
2352 }
2353 
2355  int videofd, QString &input, QString &driver, uint32_t &version,
2356  uint32_t &capabilities)
2357 {
2358  input.clear();
2359  driver.clear();
2360  version = 0;
2361  capabilities = 0;
2362 
2363  if (videofd < 0)
2364  return false;
2365 
2366 #ifdef USING_V4L2
2367  struct v4l2_capability capability {};
2368  if (ioctl(videofd, VIDIOC_QUERYCAP, &capability) >= 0)
2369  {
2370  input = QString::fromLatin1((const char*)capability.card);
2371  driver = QString::fromLatin1((const char*)capability.driver);
2372  version = capability.version;
2373  capabilities = capability.capabilities;
2374  }
2375 #endif // USING_V4L2
2376 
2377  static const QRegularExpression kBracketedDigitRE { R"(\[[0-9]\]$)" };
2378  if (!driver.isEmpty())
2379  driver.remove( kBracketedDigitRE );
2380 
2381  return !input.isEmpty();
2382 }
2383 
2384 InputNames CardUtil::ProbeV4LVideoInputs([[maybe_unused]] int videofd, bool &ok)
2385 {
2386  InputNames list;
2387  ok = false;
2388 
2389 #ifdef USING_V4L2
2390  bool usingv4l2 = hasV4L2(videofd);
2391 
2392  struct v4l2_input vin {};
2393  while (usingv4l2 && (ioctl(videofd, VIDIOC_ENUMINPUT, &vin) >= 0))
2394  {
2395  QString input((char *)vin.name);
2396  list[vin.index] = input;
2397  vin.index++;
2398  }
2399  if (vin.index)
2400  {
2401  ok = true;
2402  return list;
2403  }
2404 
2405  // Create an input when none are advertised
2406  if (list.isEmpty())
2407  list[0] = "Television";
2408 
2409  ok = true;
2410 #else // if !USING_V4L2
2411  list[-1] += QObject::tr("ERROR, Compile with V4L support to query inputs");
2412 #endif // !USING_V4L2
2413  return list;
2414 }
2415 
2416 InputNames CardUtil::ProbeV4LAudioInputs([[maybe_unused]] int videofd, bool &ok)
2417 {
2418  InputNames list;
2419  ok = false;
2420 
2421 #ifdef USING_V4L2
2422  bool usingv4l2 = hasV4L2(videofd);
2423 
2424  // V4L v2 query
2425  struct v4l2_audio ain {};
2426  while (usingv4l2 && (ioctl(videofd, VIDIOC_ENUMAUDIO, &ain) >= 0))
2427  {
2428  QString input((char *)ain.name);
2429  list[ain.index] = input;
2430  ain.index++;
2431  }
2432  if (ain.index)
2433  {
2434  ok = true;
2435  return list;
2436  }
2437 
2438  ok = true;
2439 #else // if !USING_V4L2
2440  list[-1] += QObject::tr(
2441  "ERROR, Compile with V4L support to query audio inputs");
2442 #endif // !USING_V4L2
2443  return list;
2444 }
2445 
2447 {
2448  InputNames list;
2449  MSqlQuery query(MSqlQuery::InitCon());
2450  query.prepare(
2451  "SELECT cardid, inputname "
2452  "FROM capturecard "
2453  "WHERE hostname = :HOSTNAME "
2454  " AND videodevice = :DEVICE "
2455  " AND parentid = 0 "
2456  " AND inputname <> 'None'");
2457  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
2458  query.bindValue(":DEVICE", device);
2459 
2460  if (!query.exec() || !query.isActive())
2461  MythDB::DBError("CardUtil::GetConfiguredDVBInputs", query);
2462  else
2463  {
2464  while (query.next())
2465  list[query.value(0).toUInt()] = query.value(1).toString();
2466  }
2467  return list;
2468 }
2469 
2470 // Convert DVBv5 frontend capabilities from bitmask to string
2471 //
2472 // Source linuxtv.org v4l-utils/lib/libdvbv5/dvb-v5.c
2473 //
2474 QStringList CardUtil::CapabilitiesToString([[maybe_unused]] uint64_t capabilities)
2475 {
2476  QStringList caps;
2477 #ifdef USING_DVB
2478 
2479  struct fe_caps_name {
2480  unsigned idx;
2481  const char *name;
2482  };
2483 
2484  std::array<fe_caps_name,31> fe_caps_name {{
2485  { FE_CAN_2G_MODULATION, "CAN_2G_MODULATION" },
2486  { FE_CAN_8VSB, "CAN_8VSB" },
2487  { FE_CAN_16VSB, "CAN_16VSB" },
2488  { FE_CAN_BANDWIDTH_AUTO, "CAN_BANDWIDTH_AUTO" },
2489  { FE_CAN_FEC_1_2, "CAN_FEC_1_2" },
2490  { FE_CAN_FEC_2_3, "CAN_FEC_2_3" },
2491  { FE_CAN_FEC_3_4, "CAN_FEC_3_4" },
2492  { FE_CAN_FEC_4_5, "CAN_FEC_4_5" },
2493  { FE_CAN_FEC_5_6, "CAN_FEC_5_6" },
2494  { FE_CAN_FEC_6_7, "CAN_FEC_6_7" },
2495  { FE_CAN_FEC_7_8, "CAN_FEC_7_8" },
2496  { FE_CAN_FEC_8_9, "CAN_FEC_8_9" },
2497  { FE_CAN_FEC_AUTO, "CAN_FEC_AUTO" },
2498  { FE_CAN_GUARD_INTERVAL_AUTO, "CAN_GUARD_INTERVAL_AUTO" },
2499  { FE_CAN_HIERARCHY_AUTO, "CAN_HIERARCHY_AUTO" },
2500  { FE_CAN_INVERSION_AUTO, "CAN_INVERSION_AUTO" },
2501  { FE_CAN_MULTISTREAM, "CAN_MULTISTREAM" },
2502  { FE_CAN_MUTE_TS, "CAN_MUTE_TS" },
2503  { FE_CAN_QAM_16, "CAN_QAM_16" },
2504  { FE_CAN_QAM_32, "CAN_QAM_32" },
2505  { FE_CAN_QAM_64, "CAN_QAM_64" },
2506  { FE_CAN_QAM_128, "CAN_QAM_128" },
2507  { FE_CAN_QAM_256, "CAN_QAM_256" },
2508  { FE_CAN_QAM_AUTO, "CAN_QAM_AUTO" },
2509  { FE_CAN_QPSK, "CAN_QPSK" },
2510  { FE_CAN_RECOVER, "CAN_RECOVER" },
2511  { FE_CAN_TRANSMISSION_MODE_AUTO, "CAN_TRANSMISSION_MODE_AUTO" },
2512  { FE_CAN_TURBO_FEC, "CAN_TURBO_FEC" },
2513  { FE_HAS_EXTENDED_CAPS, "HAS_EXTENDED_CAPS" },
2514  { FE_IS_STUPID, "IS_STUPID" },
2515  { FE_NEEDS_BENDING, "NEEDS_BENDING" },
2516  }};
2517 
2518  for (const auto & cap : fe_caps_name)
2519  {
2520  if (capabilities & cap.idx)
2521  caps.append(cap.name);
2522  }
2523 #endif // USING_DVB
2524  return caps;
2525 }
2526 
2527 QStringList CardUtil::ProbeVideoInputs(const QString& device, const QString& inputtype)
2528 {
2529  QStringList ret;
2530 
2531  if (IsSingleInputType(inputtype))
2532  ret += "MPEG2TS";
2533  else if ("DVB" == inputtype)
2534  ret += ProbeDVBInputs(device);
2535  else
2536  ret += ProbeV4LVideoInputs(device);
2537 
2538  return ret;
2539 }
2540 
2541 QStringList CardUtil::ProbeAudioInputs(const QString& device, const QString& inputtype)
2542 {
2543  LOG(VB_GENERAL, LOG_DEBUG, QString("ProbeAudioInputs(%1,%2)")
2544  .arg(device, inputtype));
2545  QStringList ret;
2546 
2547  if ("HDPVR" == inputtype ||
2548  "V4L2" == inputtype)
2549  ret += ProbeV4LAudioInputs(device);
2550 
2551  return ret;
2552 }
2553 
2554 QStringList CardUtil::ProbeV4LVideoInputs(const QString& device)
2555 {
2556  bool ok = false;
2557  QStringList ret;
2558  QByteArray dev = device.toLatin1();
2559  int videofd = open(dev.constData(), O_RDWR);
2560  if (videofd < 0)
2561  {
2562  ret += QObject::tr("Could not open '%1' "
2563  "to probe its inputs.").arg(device);
2564  return ret;
2565  }
2566  InputNames list = CardUtil::ProbeV4LVideoInputs(videofd, ok);
2567  close(videofd);
2568 
2569  if (!ok)
2570  {
2571  ret += list[-1];
2572  return ret;
2573  }
2574 
2575  InputNames::iterator it;
2576  for (it = list.begin(); it != list.end(); ++it)
2577  {
2578  if (it.key() >= 0)
2579  ret += *it;
2580  }
2581 
2582  return ret;
2583 }
2584 
2585 QStringList CardUtil::ProbeV4LAudioInputs(const QString& device)
2586 {
2587  LOG(VB_GENERAL, LOG_DEBUG, QString("ProbeV4LAudioInputs(%1)").arg(device));
2588 
2589  bool ok = false;
2590  QStringList ret;
2591  int videofd = open(device.toLatin1().constData(), O_RDWR);
2592  if (videofd < 0)
2593  {
2594  LOG(VB_GENERAL, LOG_ERR, "ProbeAudioInputs() -> couldn't open device");
2595  ret += QObject::tr("Could not open '%1' to probe its inputs.")
2596  .arg(device);
2597  return ret;
2598  }
2599  InputNames list = CardUtil::ProbeV4LAudioInputs(videofd, ok);
2600  close(videofd);
2601 
2602  if (!ok)
2603  {
2604  ret += list[-1];
2605  return ret;
2606  }
2607 
2608  InputNames::iterator it;
2609  for (it = list.begin(); it != list.end(); ++it)
2610  {
2611  if (it.key() >= 0)
2612  ret += *it;
2613  }
2614 
2615  return ret;
2616 }
2617 
2618 QStringList CardUtil::ProbeDVBInputs([[maybe_unused]] const QString& device)
2619 {
2620  QStringList ret;
2621 
2622 #ifdef USING_DVB
2623  InputNames list = GetConfiguredDVBInputs(device);
2624  InputNames::iterator it;
2625  for (it = list.begin(); it != list.end(); ++it)
2626  {
2627  if (it.key())
2628  ret += *it;
2629  }
2630 #else
2631  ret += QObject::tr("ERROR, Compile with DVB support to query inputs");
2632 #endif
2633 
2634  return ret;
2635 }
2636 
2637 QString CardUtil::GetDeviceLabel(const QString &inputtype,
2638  const QString &videodevice)
2639 {
2640  return QString("[ %1 : %2 ]").arg(inputtype, videodevice);
2641 }
2642 
2644 {
2645  MSqlQuery query(MSqlQuery::InitCon());
2646  query.prepare("SELECT cardtype, videodevice "
2647  "FROM capturecard WHERE cardid = :INPUTID ");
2648  query.bindValue(":INPUTID", inputid);
2649 
2650  if (query.exec() && query.next())
2651  {
2652  return GetDeviceLabel(query.value(0).toString(),
2653  query.value(1).toString());
2654  }
2655 
2656  return "[ UNKNOWN ]";
2657 }
2658 
2660  const QString &device,
2661  const QString &inputtype,
2662  QStringList &inputs)
2663 {
2664  inputs.clear();
2665  if (IsSingleInputType(inputtype))
2666  inputs += "MPEG2TS";
2667  else if (inputtype == "DVB")
2668  inputs += "DVBInput";
2669  else
2670  inputs += ProbeV4LVideoInputs(device);
2671 }
2672 
2673 int CardUtil::CreateCaptureCard(const QString &videodevice,
2674  const QString &audiodevice,
2675  const QString &vbidevice,
2676  const QString &inputtype,
2677  const uint audioratelimit,
2678  const QString &hostname,
2679  const uint dvb_swfilter,
2680  const uint dvb_sat_type,
2681  bool dvb_wait_for_seqstart,
2682  bool skipbtaudio,
2683  bool dvb_on_demand,
2684  const uint dvb_diseqc_type,
2685  const uint firewire_speed,
2686  const QString &firewire_model,
2687  const uint firewire_connection,
2688  const std::chrono::milliseconds signal_timeout,
2689  const std::chrono::milliseconds channel_timeout,
2690  const uint dvb_tuning_delay,
2691  const uint contrast,
2692  const uint brightness,
2693  const uint colour,
2694  const uint hue,
2695  const uint diseqcid,
2696  bool dvb_eitscan)
2697 {
2698  MSqlQuery query(MSqlQuery::InitCon());
2699 
2700  query.prepare(
2701  "INSERT INTO capturecard "
2702  "(videodevice, audiodevice, vbidevice, cardtype, "
2703  "audioratelimit, hostname, dvb_swfilter, dvb_sat_type, "
2704  "dvb_wait_for_seqstart, skipbtaudio, dvb_on_demand, dvb_diseqc_type, "
2705  "firewire_speed, firewire_model, firewire_connection, signal_timeout, "
2706  "channel_timeout, dvb_tuning_delay, contrast, brightness, colour, "
2707  "hue, diseqcid, dvb_eitscan) "
2708  "VALUES (:VIDEODEVICE, :AUDIODEVICE, :VBIDEVICE, :INPUTTYPE, "
2709  ":AUDIORATELIMIT, :HOSTNAME, :DVBSWFILTER, :DVBSATTYPE, "
2710  ":DVBWAITFORSEQSTART, :SKIPBTAUDIO, :DVBONDEMAND, :DVBDISEQCTYPE, "
2711  ":FIREWIRESPEED, :FIREWIREMODEL, :FIREWIRECONNECTION, :SIGNALTIMEOUT, "
2712  ":CHANNELTIMEOUT, :DVBTUNINGDELAY, :CONTRAST, :BRIGHTNESS, :COLOUR, "
2713  ":HUE, :DISEQCID, :DVBEITSCAN ) ");
2714 
2715  query.bindValue(":VIDEODEVICE", videodevice);
2716  if (audiodevice.length() == 0) // Empty string is set to null
2717  {
2718 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2719  query.bindValue(":AUDIODEVICE", QVariant(QVariant::String));
2720 #else
2721  query.bindValue(":AUDIODEVICE", QVariant(QMetaType(QMetaType::QString)));
2722 #endif
2723  }
2724  else
2725  {
2726  query.bindValue(":AUDIODEVICE", audiodevice);
2727  }
2728  if (vbidevice.length() == 0) // Empty string is set to null
2729  {
2730 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2731  query.bindValue(":VBIDEVICE", QVariant(QVariant::String));
2732 #else
2733  query.bindValue(":VBIDEVICE", QVariant(QMetaType(QMetaType::QString)));
2734 #endif
2735  }
2736  else
2737  {
2738  query.bindValue(":VBIDEVICE", vbidevice);
2739  }
2740  query.bindValue(":INPUTTYPE", inputtype);
2741  if (audioratelimit == 0) // Value 0 is set to null
2742  {
2743 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2744  query.bindValue(":AUDIORATELIMIT", QVariant(QVariant::UInt));
2745 #else
2746  query.bindValue(":AUDIORATELIMIT", QVariant(QMetaType(QMetaType::UInt)));
2747 #endif
2748  }
2749  else
2750  {
2751  query.bindValue(":AUDIORATELIMIT", audioratelimit);
2752  }
2753  query.bindValue(":HOSTNAME", hostname);
2754  query.bindValue(":DVBSWFILTER", dvb_swfilter);
2755  query.bindValue(":DVBSATTYPE", dvb_sat_type);
2756  query.bindValue(":DVBWAITFORSEQSTART", dvb_wait_for_seqstart);
2757  query.bindValue(":SKIPBTAUDIO", skipbtaudio);
2758  query.bindValue(":DVBONDEMAND", dvb_on_demand);
2759  query.bindValue(":DVBDISEQCTYPE", dvb_diseqc_type);
2760  query.bindValue(":FIREWIRESPEED", firewire_speed);
2761  if (firewire_model.length() == 0) // Empty string is set to null
2762  {
2763 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2764  query.bindValue(":FIREWIREMODEL", QVariant(QVariant::String));
2765 #else
2766  query.bindValue(":FIREWIREMODEL", QVariant(QMetaType(QMetaType::QString)));
2767 #endif
2768  }
2769  else
2770  {
2771  query.bindValue(":FIREWIREMODEL", firewire_model);
2772  }
2773  query.bindValue(":FIREWIRECONNECTION", firewire_connection);
2774  query.bindValue(":SIGNALTIMEOUT", static_cast<qint64>(signal_timeout.count()));
2775  query.bindValue(":CHANNELTIMEOUT", static_cast<qint64>(channel_timeout.count()));
2776  query.bindValue(":DVBTUNINGDELAY", dvb_tuning_delay);
2777  query.bindValue(":CONTRAST", contrast);
2778  query.bindValue(":BRIGHTNESS", brightness);
2779  query.bindValue(":COLOUR", colour);
2780  query.bindValue(":HUE", hue);
2781  if (diseqcid == 0) // Value 0 is set to null
2782  {
2783 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2784  query.bindValue(":DISEQCID", QVariant(QVariant::UInt));
2785 #else
2786  query.bindValue(":DISEQCID", QVariant(QMetaType(QMetaType::UInt)));
2787 #endif
2788  }
2789  else
2790  {
2791  query.bindValue(":DISEQCID", diseqcid);
2792  }
2793  query.bindValue(":DVBEITSCAN", dvb_eitscan);
2794 
2795  if (!query.exec())
2796  {
2797  MythDB::DBError("CreateCaptureCard", query);
2798  return -1;
2799  }
2800 
2801  query.prepare("SELECT MAX(cardid) FROM capturecard");
2802 
2803  if (!query.exec())
2804  {
2805  MythDB::DBError("CreateCaptureCard maxinput", query);
2806  return -1;
2807  }
2808 
2809  int inputid = -1; /* must be int not uint because of return type. */
2810 
2811  if (query.next())
2812  {
2813  inputid = query.value(0).toInt();
2814  uint groupid = CardUtil::CreateDeviceInputGroup(inputid, inputtype,
2815  hostname, videodevice);
2816  CardUtil::LinkInputGroup(inputid, groupid);
2817  }
2818 
2819  return inputid;
2820 }
2821 
2823 {
2824  std::vector<uint> childids = GetChildInputIDs(inputid);
2825  for (uint childid : childids)
2826  {
2827  if (!DeleteInput(childid))
2828  {
2829  LOG(VB_GENERAL, LOG_ERR, LOC +
2830  QString("CardUtil: Failed to delete child input %1")
2831  .arg(childid));
2832  return false;
2833  }
2834  }
2835 
2836  MSqlQuery query(MSqlQuery::InitCon());
2837 
2838  DiSEqCDevTree tree;
2839  tree.Load(inputid);
2840 
2841  // Delete the capturecard row for this input
2842  query.prepare("DELETE FROM capturecard WHERE cardid = :INPUTID");
2843  query.bindValue(":INPUTID", inputid);
2844  if (!query.exec())
2845  {
2846  MythDB::DBError("DeleteCard -- delete capturecard", query);
2847  return false;
2848  }
2849 
2850  // Update the reclimit of the parent input
2851  query.prepare("UPDATE capturecard SET reclimit=reclimit-1 "
2852  "WHERE cardid = :INPUTID");
2853  query.bindValue(":INPUTID", inputid);
2854  if (!query.exec())
2855  {
2856  MythDB::DBError("DeleteCard -- update capturecard", query);
2857  return false;
2858  }
2859 
2860  // Delete the inputgroup rows for this input
2861  query.prepare("DELETE FROM inputgroup WHERE cardinputid = :INPUTID");
2862  query.bindValue(":INPUTID", inputid);
2863  if (!query.exec())
2864  {
2865  MythDB::DBError("DeleteCard -- delete inputgroup", query);
2866  return false;
2867  }
2868 
2869  // Delete the diseqc tree if no more inputs reference it.
2870  if (tree.Root())
2871  {
2872  query.prepare("SELECT cardid FROM capturecard "
2873  "WHERE diseqcid = :DISEQCID LIMIT 1");
2874  query.bindValue(":DISEQCID", tree.Root()->GetDeviceID());
2875  if (!query.exec())
2876  {
2877  MythDB::DBError("DeleteCard -- find diseqc tree", query);
2878  }
2879  else if (!query.next())
2880  {
2881  tree.SetRoot(nullptr);
2882  tree.Store(inputid);
2883  }
2884  }
2885 
2886  // Delete any unused input groups
2887  UnlinkInputGroup(0, 0);
2888 
2889  return true;
2890 }
2891 
2893 {
2894  MSqlQuery query(MSqlQuery::InitCon());
2895  return (query.exec("TRUNCATE TABLE inputgroup") &&
2896  query.exec("TRUNCATE TABLE diseqc_config") &&
2897  query.exec("TRUNCATE TABLE diseqc_tree") &&
2898  query.exec("TRUNCATE TABLE capturecard"));
2899 }
2900 
2901 std::vector<uint> CardUtil::GetInputList(void)
2902 {
2903  std::vector<uint> list;
2904 
2905  MSqlQuery query(MSqlQuery::InitCon());
2906  query.prepare(
2907  "SELECT cardid "
2908  "FROM capturecard "
2909  "ORDER BY cardid");
2910 
2911  if (!query.exec())
2912  MythDB::DBError("CardUtil::GetInputList()", query);
2913  else
2914  {
2915  while (query.next())
2916  list.push_back(query.value(0).toUInt());
2917  }
2918 
2919  return list;
2920 }
2921 
2922 std::vector<uint> CardUtil::GetSchedInputList(void)
2923 {
2924  std::vector<uint> list;
2925 
2926  MSqlQuery query(MSqlQuery::InitCon());
2927  query.prepare(
2928  "SELECT DISTINCT cardid "
2929  "FROM capturecard "
2930  "WHERE schedorder <> 0 "
2931  "ORDER BY schedorder, cardid");
2932 
2933  if (!query.exec())
2934  MythDB::DBError("CardUtil::GetSchedInputList()", query);
2935  else
2936  {
2937  while (query.next())
2938  list.push_back(query.value(0).toUInt());
2939  }
2940 
2941  return list;
2942 }
2943 
2944 std::vector<uint> CardUtil::GetLiveTVInputList(void)
2945 {
2946  std::vector<uint> list;
2947 
2948  MSqlQuery query(MSqlQuery::InitCon());
2949  query.prepare(
2950  "SELECT DISTINCT cardid "
2951  "FROM capturecard "
2952  "WHERE livetvorder <> 0 "
2953  "ORDER BY livetvorder, cardid");
2954 
2955  if (!query.exec())
2956  MythDB::DBError("CardUtil::GetLiveTVInputList()", query);
2957  else
2958  {
2959  while (query.next())
2960  list.push_back(query.value(0).toUInt());
2961  }
2962 
2963  return list;
2964 }
2965 
2966 QString CardUtil::GetDeviceName(dvb_dev_type_t type, const QString &device)
2967 {
2968  QString devname = QString(device);
2969 #if 0
2970  LOG(VB_RECORD, LOG_DEBUG, LOC + QString("DVB Device (%1)").arg(devname));
2971 #endif
2972 
2973  if (DVB_DEV_FRONTEND == type)
2974  {
2975  return devname;
2976  }
2977 
2978  if (DVB_DEV_DVR == type)
2979  {
2980  QString tmp = devname;
2981  tmp = tmp.replace(tmp.indexOf("frontend"), 8, "dvr");
2982  if (QFile::exists(tmp))
2983  {
2984  LOG(VB_RECORD, LOG_DEBUG, LOC +
2985  QString("Adapter Frontend dvr number matches (%1)").arg(tmp));
2986  return tmp;
2987  }
2988 
2989  // Use dvr0, allows multi-standard frontends which only have one dvr
2990  tmp = devname;
2991  tmp = tmp.replace(tmp.indexOf("frontend"), 9, "dvr0");
2992  if (QFile::exists(tmp))
2993  {
2994  LOG(VB_RECORD, LOG_DEBUG, LOC +
2995  QString("Adapter Frontend dvr number not matching, using dvr0 instead (%1)").arg(tmp));
2996  return tmp;
2997  }
2998 
2999  LOG(VB_RECORD, LOG_DEBUG, LOC +
3000  QString("Adapter Frontend no dvr device found for (%1)").arg(devname));
3001  return "";
3002  }
3003 
3004  if (DVB_DEV_DEMUX == type)
3005  {
3006  QString tmp = devname;
3007  tmp = tmp.replace(tmp.indexOf("frontend"), 8, "demux");
3008  if (QFile::exists(tmp))
3009  {
3010  LOG(VB_RECORD, LOG_DEBUG, LOC +
3011  QString("Adapter Frontend demux number matches (%1)").arg(tmp));
3012  return tmp;
3013  }
3014 
3015  // Use demux0, allows multi-standard frontends, which only have one demux
3016  tmp = devname;
3017  tmp = tmp.replace(tmp.indexOf("frontend"), 9, "demux0");
3018  if (QFile::exists(tmp))
3019  {
3020  LOG(VB_RECORD, LOG_DEBUG, LOC +
3021  QString("Adapter Frontend demux number not matching, using demux0 instead (%1)").arg(tmp));
3022  return tmp;
3023  }
3024 
3025  LOG(VB_RECORD, LOG_DEBUG, LOC +
3026  QString("Adapter Frontend no demux device found for (%1)").arg(devname));
3027  return "";
3028  }
3029 
3030  if (DVB_DEV_CA == type)
3031  {
3032  QString tmp = devname;
3033  tmp = tmp.replace(tmp.indexOf("frontend"), 8, "ca");
3034  if (QFile::exists(tmp))
3035  {
3036  LOG(VB_RECORD, LOG_DEBUG, LOC +
3037  QString("Adapter Frontend ca number matches (%1)").arg(tmp));
3038  return tmp;
3039  }
3040 
3041  // Use ca0, allows multi-standard frontends, which only have one ca
3042  tmp = devname;
3043  tmp = tmp.replace(tmp.indexOf("frontend"), 9, "ca0");
3044  if (QFile::exists(tmp))
3045  {
3046  LOG(VB_RECORD, LOG_DEBUG, LOC +
3047  QString("Adapter Frontend ca number not matching, using ca0 instead (%1)").arg(tmp));
3048  return tmp;
3049  }
3050 
3051  LOG(VB_RECORD, LOG_DEBUG, LOC +
3052  QString("Adapter Frontend no ca device found for (%1)").arg(devname));
3053  return "";
3054  }
3055 
3056  if (DVB_DEV_AUDIO == type)
3057  {
3058  return devname.replace(devname.indexOf("frontend"), 8, "audio");
3059  }
3060 
3061  if (DVB_DEV_VIDEO == type)
3062  {
3063  return devname.replace(devname.indexOf("frontend"), 8, "video");
3064  }
3065 
3066  return "";
3067 }
3068 
3069 
3079 bool CardUtil::HDHRdoesDVB([[maybe_unused]] const QString &device)
3080 {
3081 #ifdef USING_HDHOMERUN
3082  hdhomerun_device_t *hdhr =
3083  hdhomerun_device_create_from_str(device.toLatin1(), nullptr);
3084  if (!hdhr)
3085  return false;
3086 
3087  const char *model = hdhomerun_device_get_model_str(hdhr);
3088  if (model && strstr(model, "dvb"))
3089  {
3090  hdhomerun_device_destroy(hdhr);
3091  return true;
3092  }
3093 
3094  hdhomerun_device_destroy(hdhr);
3095 
3096 #endif
3097 
3098  return false;
3099 }
3100 
3105 bool CardUtil::HDHRdoesDVBC([[maybe_unused]] const QString &device)
3106 {
3107 #ifdef USING_HDHOMERUN
3108  hdhomerun_device_t *hdhr =
3109  hdhomerun_device_create_from_str(device.toLatin1(), nullptr);
3110  if (!hdhr)
3111  return false;
3112 
3113  const char *model = hdhomerun_device_get_model_str(hdhr);
3114  if (model && strstr(model, "dvbc"))
3115  {
3116  hdhomerun_device_destroy(hdhr);
3117  return true;
3118  }
3119 
3120  hdhomerun_device_destroy(hdhr);
3121 
3122 #endif
3123 
3124  return false;
3125 }
3126 
3131 QString CardUtil::GetHDHRdesc([[maybe_unused]] const QString &device)
3132 {
3133  QString connectErr = QObject::tr("Unable to connect to device.");
3134 
3135 #ifdef USING_HDHOMERUN
3136  [[maybe_unused]] bool deviceIsIP = false;
3137 
3138  if (device.contains('.')) // Simplistic check, but also allows DNS names
3139  deviceIsIP = true;
3140  else
3141  {
3142  bool validID = false;
3143 
3144  uint32_t dev = device.toUInt(&validID, 16);
3145  if (!validID || !hdhomerun_discover_validate_device_id(dev))
3146  return QObject::tr("Invalid Device ID");
3147  }
3148 
3149  LOG(VB_GENERAL, LOG_INFO, "CardUtil::GetHDHRdescription(" + device +
3150  ") - trying to locate device");
3151 
3152  hdhomerun_device_t *hdhr =
3153  hdhomerun_device_create_from_str(device.toLatin1(), nullptr);
3154  if (!hdhr)
3155  return QObject::tr("Invalid Device ID or address.");
3156 
3157  const char *model = hdhomerun_device_get_model_str(hdhr);
3158  if (!model)
3159  {
3160  hdhomerun_device_destroy(hdhr);
3161  return connectErr;
3162  }
3163 
3164 
3165  QString description = model;
3166  char *sVersion = nullptr;
3167  uint32_t iVersion = 0;
3168 
3169  if (hdhomerun_device_get_version(hdhr, &sVersion, &iVersion))
3170  description += QObject::tr(", firmware: %2").arg(sVersion);
3171 
3172  hdhomerun_device_destroy(hdhr);
3173 
3174  return description;
3175 #else
3176  return connectErr;
3177 #endif
3178 }
3179 
3184 QString CardUtil::GetVBoxdesc([[maybe_unused]] const QString &id,
3185  [[maybe_unused]] const QString &ip,
3186  [[maybe_unused]] const QString &tunerNo,
3187  [[maybe_unused]] const QString &tunerType)
3188 {
3189  QString connectErr = QObject::tr("Unable to connect to device.");
3190 
3191 #ifdef USING_VBOX
3192  VBox *vbox = new VBox(ip);
3193 
3194  if (!vbox->checkConnection())
3195  {
3196  delete vbox;
3197  return connectErr;
3198  }
3199 
3200  QString version;
3201 
3202  if (!vbox->checkVersion(version))
3203  {
3204  QString apiVersionErr = QObject::tr("The VBox software version is too old (%1), we require %2")
3206  delete vbox;
3207  return apiVersionErr;
3208 
3209  }
3210 
3211  delete vbox;
3212 
3213  return QString("V@Box TV Gateway - ID: %1, IP: %2, Tuner: %3-%4")
3214  .arg(id, ip, tunerNo, tunerType);
3215 
3216 #else
3217  return connectErr;
3218 #endif
3219 }
3220 
3221 #ifdef USING_ASI
3222 static QString sys_dev(uint device_num, const QString& dev)
3223 {
3224  return QString("/sys/class/asi/asirx%1/%2").arg(device_num).arg(dev);
3225 }
3226 
3227 static QString read_sys(const QString& sys_dev)
3228 {
3229  QFile f(sys_dev);
3230  f.open(QIODevice::ReadOnly);
3231  QByteArray sdba = f.readAll();
3232  f.close();
3233  return sdba;
3234 }
3235 
3236 static bool write_sys(const QString& sys_dev, const QString& str)
3237 {
3238  QFile f(sys_dev);
3239  f.open(QIODevice::WriteOnly);
3240  QByteArray ba = str.toLocal8Bit();
3241  qint64 offset = 0;
3242  for (uint tries = 0; (offset < ba.size()) && tries < 5; tries++)
3243  {
3244  qint64 written = f.write(ba.data()+offset, ba.size()-offset);
3245  if (written < 0)
3246  return false;
3247  offset += written;
3248  }
3249  return true;
3250 }
3251 #endif
3252 
3253 int CardUtil::GetASIDeviceNumber([[maybe_unused]] const QString &device, QString *error)
3254 {
3255 #ifdef USING_ASI
3256  // basic confirmation
3257  struct stat statbuf {};
3258  if (stat(device.toLocal8Bit().constData(), &statbuf) < 0)
3259  {
3260  if (error)
3261  *error = QString("Unable to stat '%1'").arg(device) + ENO;
3262  return -1;
3263  }
3264 
3265  if (!S_ISCHR(statbuf.st_mode))
3266  {
3267  if (error)
3268  *error = QString("'%1' is not a character device").arg(device);
3269  return -1;
3270  }
3271 
3272  if (!(statbuf.st_rdev & 0x0080))
3273  {
3274  if (error)
3275  *error = QString("'%1' not a DVEO ASI receiver").arg(device);
3276  return -1;
3277  }
3278 
3279  int device_num = statbuf.st_rdev & 0x007f;
3280 
3281  // extra confirmation
3282  QString sys_dev_contents = read_sys(sys_dev(device_num, "dev"));
3283  QStringList sys_dev_clist = sys_dev_contents.split(":");
3284  if (2 != sys_dev_clist.size())
3285  {
3286  if (error)
3287  {
3288  *error = QString("Unable to read '%1'")
3289  .arg(sys_dev(device_num, "dev"));
3290  }
3291  return -1;
3292  }
3293  if (sys_dev_clist[0].toUInt() != (statbuf.st_rdev>>8))
3294  {
3295  if (error)
3296  *error = QString("'%1' not a DVEO ASI device").arg(device);
3297  return -1;
3298  }
3299 
3300  return device_num;
3301 #else
3302  if (error)
3303  *error = "Not compiled with ASI support.";
3304  return -1;
3305 #endif
3306 }
3307 
3308 uint CardUtil::GetASIBufferSize([[maybe_unused]] uint device_num, QString *error)
3309 {
3310 #ifdef USING_ASI
3311  // get the buffer size
3312  QString sys_bufsize_contents = read_sys(sys_dev(device_num, "bufsize"));
3313  bool ok = false;
3314  uint buf_size = sys_bufsize_contents.toUInt(&ok);
3315  if (!ok)
3316  {
3317  if (error)
3318  {
3319  *error = QString("Failed to read buffer size from '%1'")
3320  .arg(sys_dev(device_num, "bufsize"));
3321  }
3322  return 0;
3323  }
3324  return buf_size;
3325 #else
3326  if (error)
3327  *error = "Not compiled with ASI support.";
3328  return 0;
3329 #endif
3330 }
3331 
3332 uint CardUtil::GetASINumBuffers([[maybe_unused]] uint device_num, QString *error)
3333 {
3334 #ifdef USING_ASI
3335  // get the buffer size
3336  QString sys_numbuffers_contents = read_sys(sys_dev(device_num, "buffers"));
3337  bool ok = false;
3338  uint num_buffers = sys_numbuffers_contents.toUInt(&ok);
3339  if (!ok)
3340  {
3341  if (error)
3342  {
3343  *error = QString("Failed to read num buffers from '%1'")
3344  .arg(sys_dev(device_num, "buffers"));
3345  }
3346  return 0;
3347  }
3348  return num_buffers;
3349 #else
3350  if (error)
3351  *error = "Not compiled with ASI support.";
3352  return 0;
3353 #endif
3354 }
3355 
3356 int CardUtil::GetASIMode([[maybe_unused]] uint device_num, QString *error)
3357 {
3358 #ifdef USING_ASI
3359  QString sys_bufsize_contents = read_sys(sys_dev(device_num, "mode"));
3360  bool ok = false;
3361  uint mode = sys_bufsize_contents.toUInt(&ok);
3362  if (!ok)
3363  {
3364  if (error)
3365  {
3366  *error = QString("Failed to read mode from '%1'")
3367  .arg(sys_dev(device_num, "mode"));
3368  }
3369  return -1;
3370  }
3371  return mode;
3372 #else
3373  if (error)
3374  *error = "Not compiled with ASI support.";
3375  return -1;
3376 #endif
3377 }
3378 
3379 bool CardUtil::SetASIMode([[maybe_unused]] uint device_num,
3380  [[maybe_unused]] uint mode,
3381  QString *error)
3382 {
3383 #ifdef USING_ASI
3384  QString sys_bufsize_contents = read_sys(sys_dev(device_num, "mode"));
3385  bool ok = false;
3386  uint old_mode = sys_bufsize_contents.toUInt(&ok);
3387  if (ok && old_mode == mode)
3388  return true;
3389  ok = write_sys(sys_dev(device_num, "mode"), QString("%1\n").arg(mode));
3390  if (!ok && error)
3391  {
3392  *error = QString("Failed to set mode to %1 using '%2'")
3393  .arg(mode).arg(sys_dev(device_num, "mode"));
3394  }
3395  return ok;
3396 #else
3397  if (error)
3398  *error = "Not compiled with ASI support.";
3399  return false;
3400 #endif
3401 }
3402 
3403 #ifdef USING_VBOX
3404 
3408 bool CardUtil::IsVBoxPresent(uint inputid)
3409 {
3410  // should only be called if inputtype == VBOX
3411  if (!inputid )
3412  {
3413  LOG(VB_GENERAL, LOG_ERR, QString("VBOX inputid (%1) not valid, redo mythtv-setup")
3414  .arg(inputid));
3415  return false;
3416  }
3417 
3418  // get sourceid and startchan from table capturecard for inputid
3419  uint chanid = 0;
3420  chanid = ChannelUtil::GetChannelValueInt("chanid",GetSourceID(inputid),GetStartChannel(inputid));
3421  if (!chanid)
3422  {
3423  // no chanid, presume bad setup
3424  LOG(VB_GENERAL, LOG_ERR, QString("VBOX chanid (%1) not found for inputid (%2), redo mythtv-setup")
3425  .arg(chanid).arg(inputid));
3426  return false;
3427  }
3428 
3429  // get timeouts for inputid
3430  std::chrono::milliseconds signal_timeout = 0ms;
3431  std::chrono::milliseconds tuning_timeout = 0ms;
3432  if (!GetTimeouts(inputid,signal_timeout,tuning_timeout))
3433  {
3434  LOG(VB_GENERAL, LOG_ERR, QString("Failed to get timeouts for inputid (%1)")
3435  .arg(inputid));
3436  return false;
3437  }
3438 
3439  // now get url from iptv_channel table
3440  QUrl url;
3441  MSqlQuery query(MSqlQuery::InitCon());
3442  query.prepare("SELECT url "
3443  "FROM iptv_channel "
3444  "WHERE chanid = :CHANID");
3445  query.bindValue(":CHANID", chanid);
3446 
3447  if (!query.exec())
3448  MythDB::DBError("CardUtil::IsVBoxPresent url", query);
3449  else if (query.next())
3450  url = query.value(0).toString();
3451 
3452  // now get just the IP address from the url
3453  QString ip = url.host();
3454  LOG(VB_GENERAL, LOG_INFO, QString("VBOX IP found (%1) for inputid (%2)")
3455  .arg(ip).arg(inputid));
3456 
3457  if (!ping(ip,signal_timeout))
3458  {
3459  LOG(VB_GENERAL, LOG_ERR, QString("VBOX at IP (%1) failed to respond to network ping for inputid (%2) timeout (%3)")
3460  .arg(ip).arg(inputid).arg(signal_timeout.count()));
3461  return false;
3462  }
3463 
3464  return true;
3465 }
3466 #endif
3467 
3468 #ifdef USING_SATIP
3469 
3473 bool CardUtil::IsSatIPPresent(uint inputid)
3474 {
3475  // Should only be called if inputtype == SATIP
3476  if (!inputid )
3477  {
3478  LOG(VB_GENERAL, LOG_ERR, QString("SatIP inputid (%1) not valid, redo mythtv-setup")
3479  .arg(inputid));
3480  return false;
3481  }
3482 
3483  // Get sourceid and startchan from table capturecard for inputid
3484  uint chanid = 0;
3485  chanid = ChannelUtil::GetChannelValueInt("chanid", GetSourceID(inputid), GetStartChannel(inputid));
3486  if (!chanid)
3487  {
3488  // no chanid, presume bad setup
3489  LOG(VB_GENERAL, LOG_ERR, QString("SatIP chanid (%1) not found for inputid (%2), redo mythtv-setup")
3490  .arg(chanid).arg(inputid));
3491  return false;
3492  }
3493 
3494  // Get timeouts for inputid
3495  std::chrono::milliseconds signal_timeout = 0ms;
3496  std::chrono::milliseconds tuning_timeout = 0ms;
3497  if (!GetTimeouts(inputid,signal_timeout,tuning_timeout))
3498  {
3499  LOG(VB_GENERAL, LOG_ERR, QString("Failed to get timeouts for inputid (%1)")
3500  .arg(inputid));
3501  return false;
3502  }
3503 
3504  // Ping the SatIP box to see if it can be reached on the network
3505  QString device = CardUtil::GetVideoDevice(inputid);
3506  QStringList devinfo = device.split(":");
3507  if (devinfo.value(0).toUpper() == "UUID")
3508  {
3509  QString deviceId = QString("uuid:%1").arg(devinfo.value(1));
3510  QString ip = SatIP::findDeviceIP(deviceId);
3511  LOG(VB_GENERAL, LOG_INFO, QString("SatIP[%1] IP address %2 device %3")
3512  .arg(inputid).arg(ip, device));
3513 
3514  if (!ping(ip, signal_timeout))
3515  {
3516  LOG(VB_GENERAL, LOG_ERR, QString("SatIP[%1] at IP %2 failed to respond to network ping (timeout %3)")
3517  .arg(inputid).arg(ip).arg(signal_timeout.count()));
3518  return false;
3519  }
3520  return true;
3521  }
3522  return false;
3523 }
3524 #endif
CardUtil::HDHRdoesDVBC
static bool HDHRdoesDVBC(const QString &device)
If the device is valid, check if the model does DVB-C.
Definition: cardutil.cpp:3105
VBox::checkVersion
bool checkVersion(QString &version)
Definition: vboxutils.cpp:215
DTVModulationSystem::kModulationSystem_DVBC_ANNEX_B
@ kModulationSystem_DVBC_ANNEX_B
Definition: dtvconfparserhelpers.h:658
manager
static MythSystemLegacyManager * manager
Definition: mythsystemunix.cpp:53
MSqlQuery::isActive
bool isActive(void) const
Definition: mythdbcon.h:215
CardUtil::s_videoDeviceCache
static QMap< QString, QStringList > s_videoDeviceCache
Definition: cardutil.h:471
CardUtil::GetScanableInputTypes
static QString GetScanableInputTypes(void)
Definition: cardutil.cpp:61
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
CardUtil::IsTunerShared
static bool IsTunerShared(uint inputidA, uint inputidB)
Definition: cardutil.cpp:241
O_NONBLOCK
#define O_NONBLOCK
Definition: compat.h:328
CardUtil::GetInputIDs
static std::vector< uint > GetInputIDs(const QString &videodevice=QString(), const QString &rawtype=QString(), const QString &inputname=QString(), QString hostname=QString())
Returns all inputids of inputs that uses the specified videodevice if specified, and optionally rawty...
Definition: cardutil.cpp:1304
DVB_DEV_AUDIO
@ DVB_DEV_AUDIO
Definition: cardutil.h:34
CardUtil::GetVBoxdesc
static QString GetVBoxdesc(const QString &id, const QString &ip, const QString &tunerNo, const QString &tunerType)
Get a nicely formatted string describing the device.
Definition: cardutil.cpp:3184
CardUtil::IsInNeedOfExternalInputConf
static bool IsInNeedOfExternalInputConf(uint inputid)
Definition: cardutil.cpp:2310
DiSEqCDevTree::IsInNeedOfConf
bool IsInNeedOfConf(void) const
Definition: diseqc.cpp:834
MSqlQuery::bindValueNoNull
void bindValueNoNull(const QString &placeholder, const QVariant &val)
Add a single binding, taking care not to set a NULL value.
Definition: mythdbcon.cpp:902
CardUtil::CreateCardInput
static int CreateCardInput(uint inputid, uint sourceid, const QString &inputname, const QString &externalcommand, const QString &changer_device, const QString &changer_model, const QString &hostname, const QString &tunechan, const QString &startchan, const QString &displayname, bool dishnet_eit, uint recpriority, uint quicktune, uint schedorder, uint livetvorder)
Definition: cardutil.cpp:1966
ENO
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:74
CardUtil::GetDisplayName
static QString GetDisplayName(uint inputid)
Definition: cardutil.cpp:1875
DTVTunerType::kTunerTypeDVBC
static const int kTunerTypeDVBC
Definition: dtvconfparserhelpers.h:95
VBox::checkConnection
bool checkConnection(void)
Definition: vboxutils.cpp:209
CardUtil::GetDeviceLabel
static QString GetDeviceLabel(const QString &inputtype, const QString &videodevice)
Definition: cardutil.cpp:2637
mythdb.h
CardUtil::GetOrProbeDeliverySystem
static DTVModulationSystem GetOrProbeDeliverySystem(uint inputid, int fd)
Definition: cardutil.cpp:1090
CardUtil::GetVideoDevices
static QStringList GetVideoDevices(const QString &rawtype, QString hostname=QString())
Returns the videodevices of the matching inputs, duplicates removed.
Definition: cardutil.cpp:397
false
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:89
CardUtil::ClearVideoDeviceCache
static void ClearVideoDeviceCache()
Definition: cardutil.cpp:442
CardUtil::GetInputName
static QString GetInputName(uint inputid)
Definition: cardutil.cpp:1772
DTVModulationSystem::kModulationSystem_DVBT
@ kModulationSystem_DVBT
Definition: dtvconfparserhelpers.h:659
CardUtil::ProbeV4LAudioInputs
static InputNames ProbeV4LAudioInputs(int videofd, bool &ok)
CardUtil::GetASIDeviceNumber
static int GetASIDeviceNumber(const QString &device, QString *error=nullptr)
Definition: cardutil.cpp:3253
V4L2util::DriverName
QString DriverName(void) const
Definition: v4l2util.h:48
CardUtil::DeleteAllInputs
static bool DeleteAllInputs(void)
Definition: cardutil.cpp:2892
DTVModulationSystem::kModulationSystem_DVBS2
@ kModulationSystem_DVBS2
Definition: dtvconfparserhelpers.h:662
DTVTunerType::kTunerTypeDVBS1
static const int kTunerTypeDVBS1
Definition: dtvconfparserhelpers.h:93
CardUtil::SetStartChannel
static bool SetStartChannel(uint inputid, const QString &channum)
Definition: cardutil.cpp:1679
CardUtil::ProbeV4LVideoInputs
static InputNames ProbeV4LVideoInputs(int videofd, bool &ok)
CardUtil::ProbeVideoInputs
static QStringList ProbeVideoInputs(const QString &device, const QString &inputtype=QString())
Definition: cardutil.cpp:2527
CardUtil::ProbeBestDeliverySystem
static DTVModulationSystem ProbeBestDeliverySystem(int fd)
Definition: cardutil.cpp:1041
VERBOSE_LEVEL_CHECK
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
Definition: mythlogging.h:29
xbmcvfs.exists
bool exists(str path)
Definition: xbmcvfs.py:51
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:204
CardUtil::HasDVBCRCBug
static bool HasDVBCRCBug(const QString &device)
Returns true if and only if the device munges PAT/PMT tables, and then doesn't fix the CRC.
Definition: cardutil.cpp:789
V4L2util::HasTuner
bool HasTuner(void) const
Definition: v4l2util.cpp:689
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:618
set_on_input
bool set_on_input(const QString &to_set, uint inputid, const QString &value)
Definition: cardutil.cpp:1274
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythDownloadManager
Definition: mythdownloadmanager.h:48
DiSEqCDevSettings::Load
bool Load(uint card_input_id)
Loads configuration chain from DB for specified card input id.
Definition: diseqc.cpp:128
read_sys
static QString read_sys(const QString &sys_dev)
Definition: cardutil.cpp:3227
CardUtil::ProbeTunerType
static DTVTunerType ProbeTunerType(int fd_frontend)
Definition: cardutil.cpp:857
CardUtil::CloneCard
static uint CloneCard(uint src_inputid, uint dst_inputid)
Definition: cardutil.cpp:1549
CardUtil::IsUniqueDisplayName
static bool IsUniqueDisplayName(const QString &name, uint exclude_inputid)
Definition: cardutil.cpp:1897
dvbchannel.h
SatIP::findDeviceIP
static QString findDeviceIP(const QString &deviceuuid)
Definition: satiputils.cpp:154
CardUtil::GetFirewireChangerModel
static QString GetFirewireChangerModel(uint inputid)
Definition: cardutil.cpp:1638
close
#define close
Definition: compat.h:43
tmp
static guint32 * tmp
Definition: goom_core.cpp:26
CardUtil::ProbeSubTypeName
static QString ProbeSubTypeName(uint inputid)
Definition: cardutil.cpp:982
CardUtil::ProbeDefaultDeliverySystem
static QString ProbeDefaultDeliverySystem(const QString &device)
Definition: cardutil.cpp:708
DTVTunerType
Definition: dtvconfparserhelpers.h:76
CardUtil::GetASINumBuffers
static uint GetASINumBuffers(uint device_num, QString *error=nullptr)
Definition: cardutil.cpp:3332
CardUtil::ProbeDVBType
static QString ProbeDVBType(const QString &device)
Definition: cardutil.cpp:724
satiputils.h
CardUtil::ProbeVideoDevices
static QStringList ProbeVideoDevices(const QString &rawtype)
Definition: cardutil.cpp:448
DiSEqCDevSettings
DVB-S device settings class.
Definition: diseqc.h:36
CardUtil::SetDefaultDeliverySystem
static int SetDefaultDeliverySystem(uint inputid, int fd)
Definition: cardutil.cpp:1118
CardUtil::ProbeDVBFrontendName
static QString ProbeDVBFrontendName(const QString &device)
Returns the input type from the video device.
Definition: cardutil.cpp:745
CardUtil::GetASIBufferSize
static uint GetASIBufferSize(uint device_num, QString *error=nullptr)
Definition: cardutil.cpp:3308
CardUtil::SetDeliverySystem
static int SetDeliverySystem(uint inputid)
VBOX_MIN_API_VERSION
static constexpr const char * VBOX_MIN_API_VERSION
Definition: vboxutils.h:14
CardUtil::GetInputGroups
static std::vector< uint > GetInputGroups(uint inputid)
Definition: cardutil.cpp:2187
sourceutil.h
CardUtil::UnlinkInputGroup
static bool UnlinkInputGroup(uint inputid, uint inputgroupid)
Definition: cardutil.cpp:2156
InputInfo::m_recPriority
int m_recPriority
Definition: inputinfo.h:53
DTVModulationSystem::kModulationSystem_UNDEFINED
@ kModulationSystem_UNDEFINED
Definition: dtvconfparserhelpers.h:656
dvbtypes.h
mythlogging.h
CardUtil::GetInputList
static std::vector< uint > GetInputList(void)
Definition: cardutil.cpp:2901
SatIP::probeDevices
static QStringList probeDevices(void)
Definition: satiputils.cpp:24
InputInfo::m_liveTvOrder
uint m_liveTvOrder
order for live TV use
Definition: inputinfo.h:55
DTVModulationSystem::kModulationSystem_ATSC
@ kModulationSystem_ATSC
Definition: dtvconfparserhelpers.h:667
CardUtil::GetV4LInfo
static bool GetV4LInfo(int videofd, QString &input, QString &driver, uint32_t &version, uint32_t &capabilities)
Definition: cardutil.cpp:2354
v4l2util.h
CardUtil::IsSingleInputType
static bool IsSingleInputType(const QString &rawtype)
Definition: cardutil.h:212
CardUtil::GetMinSignalMonitoringDelay
static std::chrono::milliseconds GetMinSignalMonitoringDelay(const QString &device)
Definition: cardutil.cpp:796
CardUtil::CreateDeviceInputGroup
static uint CreateDeviceInputGroup(uint inputid, const QString &type, const QString &host, const QString &device)
Definition: cardutil.cpp:2068
CardUtil::ProbeDVBInputs
static QStringList ProbeDVBInputs(const QString &device)
Definition: cardutil.cpp:2618
DTVTunerType::kTunerTypeUnknown
static const int kTunerTypeUnknown
Definition: dtvconfparserhelpers.h:103
hardwareprofile.i18n.t
t
Definition: i18n.py:36
CardUtil::GetChildInputIDs
static std::vector< uint > GetChildInputIDs(uint inputid)
Definition: cardutil.cpp:1372
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:226
CardUtil::hasV4L2
static bool hasV4L2(int videofd)
Definition: cardutil.cpp:2342
DTVTunerType::toString
QString toString() const
Definition: dtvconfparserhelpers.h:154
vboxutils.h
DiSEqCDevTree::Load
bool Load(const QString &device)
Loads the device tree from the database.
Definition: diseqc.cpp:311
CardUtil::GetDeliverySystemFromDB
static QString GetDeliverySystemFromDB(uint inputid)
Definition: cardutil.h:300
CardUtil::HasTuner
static bool HasTuner(const QString &rawtype, const QString &device)
Definition: cardutil.cpp:216
DiSEqCDevTree::SetRoot
void SetRoot(DiSEqCDevDevice *root)
Changes the root node of the tree.
Definition: diseqc.cpp:632
CardUtil::GetDeviceInputGroup
static uint GetDeviceInputGroup(uint inputid)
Definition: cardutil.cpp:2081
CardUtil::IsDVBInputType
static bool IsDVBInputType(const QString &inputType)
Returns true iff the input_type is one of the DVB types.
Definition: cardutil.cpp:1029
CardUtil::GetTunerType
static DTVTunerType GetTunerType(uint inputid)
Definition: cardutil.cpp:849
CardUtil::GetTimeouts
static bool GetTimeouts(uint inputid, std::chrono::milliseconds &signal_timeout, std::chrono::milliseconds &channel_timeout)
Definition: cardutil.cpp:2287
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:57
DiSEqCDev::FindTree
static DiSEqCDevTree * FindTree(uint cardid)
Retrieve device tree.
Definition: diseqc.cpp:237
DiSEqCDevTree::Root
DiSEqCDevDevice * Root(void)
Retrieves the root node in the tree.
Definition: diseqc.h:93
CardUtil::ProbeDeliverySystems
static QStringList ProbeDeliverySystems(const QString &device)
CardUtil::ProbeCurrentDeliverySystem
static DTVModulationSystem ProbeCurrentDeliverySystem(const QString &device)
DVB_DEV_DEMUX
@ DVB_DEV_DEMUX
Definition: cardutil.h:32
CardUtil::GetFirewireChangerNode
static QString GetFirewireChangerNode(uint inputid)
Definition: cardutil.cpp:1621
ChannelUtil::GetChannelValueInt
static int GetChannelValueInt(const QString &channel_field, uint sourceid, const QString &channum)
Definition: channelutil.cpp:979
CardUtil::InputSetMaxRecordings
static bool InputSetMaxRecordings(uint parentid, uint max_recordings)
Definition: cardutil.cpp:1560
hardwareprofile.smolt.error
def error(message)
Definition: smolt.py:409
InputInfo::m_inputId
uint m_inputId
unique key in DB for this input
Definition: inputinfo.h:49
DTVTunerType::kTunerTypeDVBS2
static const int kTunerTypeDVBS2
Definition: dtvconfparserhelpers.h:94
InputInfo::m_quickTune
bool m_quickTune
Definition: inputinfo.h:56
sys_dev
static QString sys_dev(uint device_num, const QString &dev)
Definition: cardutil.cpp:3222
DTVModulationSystem::toString
QString toString() const
Definition: dtvconfparserhelpers.h:719
CardUtil::GetQuickTuning
static uint GetQuickTuning(uint inputid, const QString &input_name)
Definition: cardutil.cpp:2321
LOC
#define LOC
Definition: cardutil.cpp:59
CardUtil::GetDeviceName
static QString GetDeviceName(dvb_dev_type_t type, const QString &device)
Definition: cardutil.cpp:2966
CardUtil::GetConfiguredDVBInputs
static InputNames GetConfiguredDVBInputs(const QString &device)
Definition: cardutil.cpp:2446
CardUtil::GetRawInputType
static QString GetRawInputType(uint inputid)
Definition: cardutil.h:292
DTVModulationSystem::kModulationSystem_DVBS
@ kModulationSystem_DVBS
Definition: dtvconfparserhelpers.h:661
InputInfo::m_scheduleOrder
uint m_scheduleOrder
Definition: inputinfo.h:54
CardUtil::IsInputTypePresent
static bool IsInputTypePresent(const QString &rawtype, QString hostname=QString())
Returns true if the input type is present and connected to an input.
Definition: cardutil.cpp:288
CardUtil::OpenVideoDevice
static int OpenVideoDevice(int inputid)
Definition: cardutil.cpp:1234
InputInfo::m_sourceId
uint m_sourceId
associated channel listings source
Definition: inputinfo.h:48
DVB_DEV_CA
@ DVB_DEV_CA
Definition: cardutil.h:33
CardUtil::GetTunerTypeFromMultiplex
static DTVTunerType GetTunerTypeFromMultiplex(uint mplexid)
Definition: cardutil.cpp:873
clone_capturecard
static uint clone_capturecard(uint src_inputid, uint orig_dst_inputid)
Definition: cardutil.cpp:1400
V4L2util::IsOpen
bool IsOpen(void) const
Definition: v4l2util.h:29
CardUtil::IsCableCardPresent
static bool IsCableCardPresent(uint inputid, const QString &inputType)
Definition: cardutil.cpp:108
ping
bool ping(const QString &host, std::chrono::milliseconds timeout)
Can we ping host within timeout seconds?
Definition: mythmiscutil.cpp:199
DiSEqCDevSettings::Store
bool Store(uint card_input_id) const
Stores configuration chain to DB for specified card input id.
Definition: diseqc.cpp:162
mythmiscutil.h
V4L2util
Definition: v4l2util.h:16
CardUtil::InputTypes
QMap< QString, QString > InputTypes
Definition: cardutil.h:46
mythcorecontext.h
DTVModulationSystem
Definition: dtvconfparserhelpers.h:644
cardutil.h
CardUtil::CreateCaptureCard
static int CreateCaptureCard(const QString &videodevice, const QString &audiodevice, const QString &vbidevice, const QString &inputtype, uint audioratelimit, const QString &hostname, uint dvb_swfilter, uint dvb_sat_type, bool dvb_wait_for_seqstart, bool skipbtaudio, bool dvb_on_demand, uint dvb_diseqc_type, uint firewire_speed, const QString &firewire_model, uint firewire_connection, std::chrono::milliseconds signal_timeout, std::chrono::milliseconds channel_timeout, uint dvb_tuning_delay, uint contrast, uint brightness, uint colour, uint hue, uint diseqcid, bool dvb_eitscan)
Definition: cardutil.cpp:2673
dvb_dev_type_t
dvb_dev_type_t
Definition: cardutil.h:28
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
VBox
Definition: vboxutils.h:16
CardUtil::GetInputTypes
static InputTypes GetInputTypes(void)
Definition: cardutil.cpp:322
CardUtil::AddChildInput
static uint AddChildInput(uint parentid)
Definition: cardutil.cpp:1593
CardUtil::GetChildInputCount
static uint GetChildInputCount(uint inputid)
Definition: cardutil.cpp:1348
CardUtil::HDHRdoesDVB
static bool HDHRdoesDVB(const QString &device)
If the device is valid, check if the model does DVB.
Definition: cardutil.cpp:3079
CardUtil::GetDeliverySystem
static DTVModulationSystem GetDeliverySystem(uint inputid)
Definition: cardutil.cpp:907
CardUtil::ProbeAudioInputs
static QStringList ProbeAudioInputs(const QString &device, const QString &inputtype=QString())
Definition: cardutil.cpp:2541
DTVModulationSystem::kModulationSystem_DVBT2
@ kModulationSystem_DVBT2
Definition: dtvconfparserhelpers.h:672
DTVModulationSystem::kModulationSystem_DVBC_ANNEX_A
@ kModulationSystem_DVBC_ANNEX_A
Definition: dtvconfparserhelpers.h:657
CardUtil::GetHDHRdesc
static QString GetHDHRdesc(const QString &device)
Get a nicely formatted string describing the device.
Definition: cardutil.cpp:3131
CardUtil::GetSourceID
static uint GetSourceID(uint inputid)
Definition: cardutil.cpp:1947
inputinfo.h
CardUtil::GetSchedInputList
static std::vector< uint > GetSchedInputList(void)
Definition: cardutil.cpp:2922
InputInfo
Definition: inputinfo.h:14
VBox::probeDevices
static QStringList probeDevices(void)
Definition: vboxutils.cpp:29
CardUtil::IsTunerSharingCapable
static bool IsTunerSharingCapable(const QString &rawtype)
Definition: cardutil.h:177
DiSEqCDevTree
DVB-S device tree class. Represents a tree of DVB-S devices.
Definition: diseqc.h:74
get_on_input
QString get_on_input(const QString &to_get, uint inputid)
Definition: cardutil.cpp:1257
CardUtil::GetInputTypeNames
static QStringList GetInputTypeNames(uint sourceid)
Get a list of card input types for a source id.
Definition: cardutil.cpp:369
CardUtil::ConvertToTunerType
static DTVTunerType ConvertToTunerType(DTVModulationSystem delsys)
Definition: cardutil.cpp:806
DTVTunerType::kTunerTypeATSC
static const int kTunerTypeATSC
Definition: dtvconfparserhelpers.h:98
CardUtil::GetInputInfo
static bool GetInputInfo(InputInfo &input, std::vector< uint > *groupids=nullptr)
Definition: cardutil.cpp:1697
InputNames
QMap< int, QString > InputNames
Definition: cardutil.h:22
MythCoreContext::GetHostName
QString GetHostName(void)
Definition: mythcorecontext.cpp:844
DiSEqCDevTree::Store
bool Store(uint cardid, const QString &device="")
Stores the device tree to the database.
Definition: diseqc.cpp:422
azlyrics.info
dictionary info
Definition: azlyrics.py:7
musicbrainzngs.caa.hostname
string hostname
Definition: caa.py:17
DiSEqCDevDevice::GetDeviceID
uint GetDeviceID(void) const
Definition: diseqc.h:170
DTVTunerType::kTunerTypeDVBT
static const int kTunerTypeDVBT
Definition: dtvconfparserhelpers.h:96
CardUtil::GetLiveTVInputList
static std::vector< uint > GetLiveTVInputList(void)
Definition: cardutil.cpp:2944
CardUtil::CapabilitiesToString
static QStringList CapabilitiesToString(uint64_t capabilities)
Definition: cardutil.cpp:2474
CardUtil::GetAllInputInfo
static QList< InputInfo > GetAllInputInfo(bool virtTuners)
Definition: cardutil.cpp:1734
mythdownloadmanager.h
CardUtil::GetDeviceInputNames
static void GetDeviceInputNames(const QString &device, const QString &inputtype, QStringList &inputs)
Definition: cardutil.cpp:2659
CardUtil::GetConflictingInputs
static std::vector< uint > GetConflictingInputs(uint inputid)
Definition: cardutil.cpp:2240
V4L2util::CardName
QString CardName(void) const
Definition: v4l2util.h:49
DTVTunerType::kTunerTypeDVBT2
static const int kTunerTypeDVBT2
Definition: dtvconfparserhelpers.h:97
DVB_DEV_VIDEO
@ DVB_DEV_VIDEO
Definition: cardutil.h:35
CardUtil::GetGroupInputIDs
static std::vector< uint > GetGroupInputIDs(uint inputgroupid)
Definition: cardutil.cpp:2213
nv_python_libs.bbciplayer.bbciplayer_api.version
string version
Definition: bbciplayer_api.py:77
DVB_DEV_DVR
@ DVB_DEV_DVR
Definition: cardutil.h:31
InputInfo::m_displayName
QString m_displayName
Definition: inputinfo.h:52
CardUtil::CreateInputGroup
static uint CreateInputGroup(const QString &name)
Definition: cardutil.cpp:2026
CardUtil::LinkInputGroup
static bool LinkInputGroup(uint inputid, uint inputgroupid)
Definition: cardutil.cpp:2105
DTVModulationSystem::kModulationSystem_DTMB
@ kModulationSystem_DTMB
Definition: dtvconfparserhelpers.h:669
videosource.h
CardUtil::GetStartChannel
static QString GetStartChannel(uint inputid)
Definition: cardutil.cpp:1791
diseqcsettings.h
CardUtil::SetASIMode
static bool SetASIMode(uint device_num, uint mode, QString *error=nullptr)
Definition: cardutil.cpp:3379
CardUtil::GetVideoDevice
static QString GetVideoDevice(uint inputid)
Definition: cardutil.h:294
uint
unsigned int uint
Definition: freesurround.h:24
InputInfo::m_name
QString m_name
input name
Definition: inputinfo.h:47
GetMythDownloadManager
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
Definition: mythdownloadmanager.cpp:146
DTVModulationSystem::kModulationSystem_DVBC_ANNEX_C
@ kModulationSystem_DVBC_ANNEX_C
Definition: dtvconfparserhelpers.h:674
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:837
write_sys
static bool write_sys(const QString &sys_dev, const QString &str)
Definition: cardutil.cpp:3236
DTVModulationSystem::Parse
bool Parse(const QString &_value)
Definition: dtvconfparserhelpers.h:716
CardUtil::GetASIMode
static int GetASIMode(uint device_num, QString *error=nullptr)
Definition: cardutil.cpp:3356
DVB_DEV_FRONTEND
@ DVB_DEV_FRONTEND
Definition: cardutil.h:30
CardUtil::DeleteInput
static bool DeleteInput(uint inputid)
Definition: cardutil.cpp:2822