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