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