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