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