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.h"
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 (inputType == "HDHOMERUN")
121  {
122 #ifdef USING_HDHOMERUN
123  hdhomerun_device_t *hdhr;
124  hdhomerun_tuner_status_t status;
125  QString device = GetVideoDevice(inputid);
126  hdhr = hdhomerun_device_create_from_str(device.toLatin1(), NULL);
127  if (!hdhr)
128  return false;
129 
130  int oob = -1;
131  oob = hdhomerun_device_get_oob_status(hdhr, NULL, &status);
132 
133  // if no OOB tuner, oob will be < 1. If no CC present, OOB
134  // status will be "none."
135  if (oob > 0 && (strncmp(status.channel, "none", 4) != 0))
136  {
137  LOG(VB_GENERAL, LOG_INFO, "Cardutil: HDHomeRun Cablecard Present.");
138  hdhomerun_device_destroy(hdhr);
139  return true;
140  }
141 
142  hdhomerun_device_destroy(hdhr);
143 
144 #endif
145  return false;
146  }
147  else if (inputType == "CETON")
148  {
149 #ifdef USING_CETON
150  QString device = GetVideoDevice(inputid);
151 
152  QStringList parts = device.split("-");
153  if (parts.size() != 2)
154  {
155  LOG(VB_GENERAL, LOG_ERR,
156  QString("CardUtil: Ceton invalid device id %1").arg(device));
157  return false;
158  }
159 
160  QString ip_address = parts.at(0);
161 
162  QStringList tuner_parts = parts.at(1).split(".");
163  if (tuner_parts.size() != 2)
164  {
165  LOG(VB_GENERAL, LOG_ERR, LOC +
166  QString("CardUtil: Ceton invalid device id %1").arg(device));
167  return false;
168  }
169 
170  uint tuner = tuner_parts.at(1).toUInt();
171 
172 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
173  QUrl params;
174 #else
175  QUrlQuery params;
176 #endif
177  params.addQueryItem("i", QString::number(tuner));
178  params.addQueryItem("s", "cas");
179  params.addQueryItem("v", "CardStatus");
180 
181  QUrl url;
182  url.setScheme("http");
183  url.setHost(ip_address);
184  url.setPath("/get_var.json");
185 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
186  url.setEncodedQuery(params.encodedQuery());
187 #else
188  url.setQuery(params);
189 #endif
190 
191  QNetworkRequest *request = new QNetworkRequest();
192  request->setAttribute(QNetworkRequest::CacheLoadControlAttribute,
193  QNetworkRequest::AlwaysNetwork);
194  request->setUrl(url);
195 
196  QByteArray data;
198 
199  if (!manager->download(request, &data))
200  {
201  LOG(VB_GENERAL, LOG_ERR,
202  QString("CardUtil: Ceton http request failed %1").arg(device));
203  return false;
204  }
205 
206  QString response = QString(data);
207 
208  QRegExp regex("^\\{ \"?result\"?: \"(.*)\" \\}$");
209  if (regex.indexIn(response) == -1)
210  {
211  LOG(VB_GENERAL, LOG_ERR,
212  QString("CardUtil: Ceton unexpected http response: %1").arg(response));
213  return false;
214  }
215 
216  QString result = regex.cap(1);
217 
218  if (result == "Inserted")
219  {
220  LOG(VB_GENERAL, LOG_DEBUG, "Cardutil: Ceton CableCARD present.");
221  return true;
222  }
223 
224  LOG(VB_GENERAL, LOG_DEBUG, "Cardutil: Ceton CableCARD not present.");
225  return false;
226 #else
227  return false;
228 #endif
229  }
230  else
231  return false;
232 }
233 
234 bool CardUtil::HasTuner(const QString &rawtype, const QString & device)
235 {
236  if (rawtype == "DVB" || rawtype == "HDHOMERUN" ||
237  rawtype == "FREEBOX" || rawtype == "CETON" || rawtype == "VBOX")
238  return true;
239 
240 #ifdef USING_V4L2
241  if (rawtype == "V4L2ENC")
242  {
243  V4L2util v4l2(device);
244  return !v4l2 ? false : v4l2.HasTuner();
245  }
246 #endif
247 
248  if (rawtype == "EXTERNAL")
249  {
250  // TODO: query EXTERNAL for capability
251  return true;
252  }
253 
254  return false;
255 }
256 
257 bool CardUtil::IsTunerShared(uint inputidA, uint inputidB)
258 {
259  LOG(VB_GENERAL, LOG_DEBUG, QString("IsTunerShared(%1,%2)")
260  .arg(inputidA).arg(inputidB));
261 
262  MSqlQuery query(MSqlQuery::InitCon());
263  query.prepare("SELECT videodevice, hostname, cardtype "
264  "FROM capturecard "
265  "WHERE ( (cardid = :INPUTID_A) OR "
266  " (cardid = :INPUTID_B) )");
267  query.bindValue(":INPUTID_A", inputidA);
268  query.bindValue(":INPUTID_B", inputidB);
269 
270  if (!query.exec())
271  {
272  MythDB::DBError("CardUtil::is_tuner_shared", query);
273  return false;
274  }
275 
276  if (!query.next())
277  return false;
278 
279  const QString vdevice = query.value(0).toString();
280  const QString hostname = query.value(1).toString();
281  const QString inputtype = query.value(2).toString();
282 
283  if (!IsTunerSharingCapable(inputtype.toUpper()))
284  return false;
285 
286  if (!query.next())
287  return false;
288 
289  bool ret = ((vdevice == query.value(0).toString()) &&
290  (hostname == query.value(1).toString()) &&
291  (inputtype == query.value(2).toString()));
292 
293  LOG(VB_RECORD, LOG_DEBUG, QString("IsTunerShared(%1,%2) -> %3")
294  .arg(inputidA).arg(inputidB).arg(ret));
295 
296  return ret;
297 }
298 
304 bool CardUtil::IsInputTypePresent(const QString &rawtype, QString hostname)
305 {
306  if (hostname.isEmpty())
307  hostname = gCoreContext->GetHostName();
308 
309  MSqlQuery query(MSqlQuery::InitCon());
310  QString qstr =
311  "SELECT count(cardtype) "
312  "FROM capturecard "
313  "WHERE capturecard.hostname = :HOSTNAME ";
314 
315  if (!rawtype.isEmpty())
316  qstr += " AND capturecard.cardtype = :INPUTTYPE";
317 
318  query.prepare(qstr);
319 
320  if (!rawtype.isEmpty())
321  query.bindValue(":INPUTTYPE", rawtype.toUpper());
322 
323  query.bindValue(":HOSTNAME", hostname);
324 
325  if (!query.exec())
326  {
327  MythDB::DBError("CardUtil::IsInputTypePresent", query);
328  return false;
329  }
330 
331  uint count = 0;
332  if (query.next())
333  count = query.value(0).toUInt();
334 
335  return count > 0;
336 }
337 
339 {
340  InputTypes inputtypes;
341 
342  MSqlQuery query(MSqlQuery::InitCon());
343  query.prepare("SELECT DISTINCT cardtype, videodevice "
344  "FROM capturecard");
345 
346  if (!query.exec())
347  {
348  MythDB::DBError("CardUtil::GetInputTypes()", query);
349  }
350  else
351  {
352  QString cardtype;
353 
354  while (query.next())
355  {
356  cardtype = query.value(0).toString();
357  if (cardtype != "V4L2ENC")
358  inputtypes[cardtype] = "";
359 #ifdef USING_V4L2
360  else
361  {
362  V4L2util v4l2(query.value(1).toString());
363  if (v4l2.IsOpen())
364  {
365  QString driver_name = "V4L2:" + v4l2.DriverName();
366  inputtypes[driver_name] = v4l2.CardName();
367  }
368  }
369 #endif
370  }
371  }
372 
373  return inputtypes;
374 }
375 
383 QStringList CardUtil::GetInputTypeNames(uint sourceid)
384 {
385  MSqlQuery query(MSqlQuery::InitCon());
386  query.prepare("SELECT cardtype "
387  "FROM capturecard "
388  "WHERE capturecard.sourceid = :SOURCEID "
389  "GROUP BY cardtype");
390  query.bindValue(":SOURCEID", sourceid);
391 
392  QStringList list;
393  if (!query.exec())
394  {
395  MythDB::DBError("CardUtil::GetInputTypes", query);
396  return list;
397  }
398  while (query.next())
399  list.push_back(query.value(0).toString());
400  return list;
401 }
402 
403 
404 
405 
411 QStringList CardUtil::GetVideoDevices(const QString &rawtype, QString hostname)
412 {
413  QStringList list;
414 
415  if (hostname.isEmpty())
416  hostname = gCoreContext->GetHostName();
417 
418  MSqlQuery query(MSqlQuery::InitCon());
419  QString qstr =
420  "SELECT videodevice "
421  "FROM capturecard "
422  "WHERE hostname = :HOSTNAME";
423 
424  if (!rawtype.isEmpty())
425  qstr += " AND cardtype = :INPUTTYPE";
426 
427  query.prepare(qstr);
428 
429  if (!rawtype.isEmpty())
430  query.bindValue(":INPUTTYPE", rawtype.toUpper());
431 
432  query.bindValue(":HOSTNAME", hostname);
433 
434  if (!query.exec())
435  {
436  MythDB::DBError("CardUtil::GetVideoDevices", query);
437  return list;
438  }
439 
440  QMap<QString,bool> dup;
441  while (query.next())
442  {
443  QString videodevice = query.value(0).toString();
444  if (dup[videodevice])
445  continue;
446 
447  list.push_back(videodevice);
448  dup[videodevice] = true;
449  }
450 
451  return list;
452 }
453 
454 QMap <QString,QStringList> CardUtil::videoDeviceCache;
455 
457 {
458  videoDeviceCache.clear();
459 }
460 
461 
462 QStringList CardUtil::ProbeVideoDevices(const QString &rawtype)
463 {
464  if (videoDeviceCache.contains(rawtype))
465  return videoDeviceCache[rawtype];
466 
467  QStringList devs;
468 
469  if (rawtype.toUpper() == "DVB")
470  {
471  QDir dir("/dev/dvb", "adapter*", QDir::Name, QDir::Dirs);
472  const QFileInfoList il = dir.entryInfoList();
473 
474  QFileInfoList::const_iterator it = il.begin();
475 
476  for (; it != il.end(); ++it)
477  {
478  QDir subdir(it->filePath(), "frontend*", QDir::Name, QDir::Files | QDir::System);
479  const QFileInfoList subil = subdir.entryInfoList();
480  if (subil.isEmpty())
481  continue;
482 
483  QFileInfoList::const_iterator subit = subil.begin();
484  for (; subit != subil.end(); ++subit)
485  devs.push_back(subit->filePath());
486  }
487  }
488  else if (rawtype.toUpper() == "ASI")
489  {
490  QDir dir("/dev/", "asirx*", QDir::Name, QDir::System);
491  const QFileInfoList il = dir.entryInfoList();
492 
493  QFileInfoList::const_iterator it = il.begin();
494  for (; it != il.end(); ++it)
495  {
496  if (GetASIDeviceNumber(it->filePath()) >= 0)
497  {
498  devs.push_back(it->filePath());
499  continue;
500  }
501  break;
502  }
503  }
504 #ifdef USING_HDHOMERUN
505  else if (rawtype.toUpper() == "HDHOMERUN")
506  {
507  uint32_t target_ip = 0;
508  uint32_t device_type = HDHOMERUN_DEVICE_TYPE_TUNER;
509  uint32_t device_id = HDHOMERUN_DEVICE_ID_WILDCARD;
510  const int max_count = 50;
511  hdhomerun_discover_device_t result_list[max_count];
512 
513  int result = hdhomerun_discover_find_devices_custom(
514  target_ip, device_type, device_id, result_list, max_count);
515 
516  if (result == -1)
517  {
518  LOG(VB_GENERAL, LOG_ERR, "Error finding HDHomerun devices");
519  }
520 
521  if (result >= max_count)
522  {
523  LOG(VB_GENERAL, LOG_WARNING,
524  "Warning: may be > 50 HDHomerun devices");
525  }
526 
527  // Return "deviceid ipaddress" pairs
528  for (int i = 0; i < result; i++)
529  {
530  QString id = QString("%1").arg(result_list[i].device_id, 0, 16);
531  QString ip = QString("%1.%2.%3.%4")
532  .arg((result_list[i].ip_addr>>24) & 0xFF)
533  .arg((result_list[i].ip_addr>>16) & 0xFF)
534  .arg((result_list[i].ip_addr>> 8) & 0xFF)
535  .arg((result_list[i].ip_addr>> 0) & 0xFF);
536 
537  for (int tuner = 0; tuner < result_list[i].tuner_count; tuner++)
538  {
539  QString hdhrdev = id.toUpper() + " " + ip + " " +
540  QString("%1").arg(tuner);
541  devs.push_back(hdhrdev);
542  }
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  videoDeviceCache.insert(rawtype,devs);
567  return devs;
568 }
569 
570 QString CardUtil::ProbeDVBType(const QString &device)
571 {
572  QString ret = "ERROR_UNKNOWN";
573 
574  if (device.isEmpty())
575  return ret;
576 
577 #ifdef USING_DVB
578  QString dvbdev = CardUtil::GetDeviceName(DVB_DEV_FRONTEND, device);
579  QByteArray dev = dvbdev.toLatin1();
580 
581  int fd_frontend = open(dev.constData(), O_RDWR | O_NONBLOCK);
582  if (fd_frontend < 0)
583  {
584  LOG(VB_GENERAL, LOG_ERR, QString("Can't open DVB frontend (%1) for %2." + ENO)
585  .arg(dvbdev).arg(device));
586  return ret;
587  }
588 
589  struct dvb_frontend_info info;
590  memset(&info, 0, sizeof(info));
591  int err = ioctl(fd_frontend, FE_GET_INFO, &info);
592  if (err < 0)
593  {
594  close(fd_frontend);
595  LOG(VB_GENERAL, LOG_ERR, QString("FE_GET_INFO ioctl failed (%1)")
596  .arg(dvbdev) + ENO);
597  return ret;
598  }
599 
600  DTVTunerType type(info.type);
601 #if HAVE_FE_CAN_2G_MODULATION
602  if (info.caps & FE_CAN_2G_MODULATION)
603  {
604  if (type == DTVTunerType::kTunerTypeDVBS1)
606  else if (type == DTVTunerType::kTunerTypeDVBT)
608  }
609 #endif // HAVE_FE_CAN_2G_MODULATION
610 
611 #if DVB_API_VERSION >=5
612  unsigned int i;
613  struct dtv_property prop;
614  struct dtv_properties cmd;
615 
616  memset(&prop, 0, sizeof(prop));
617  prop.cmd = DTV_ENUM_DELSYS;
618  cmd.num = 1;
619  cmd.props = &prop;
620 
621  if (ioctl(fd_frontend, FE_GET_PROPERTY, &cmd) == 0)
622  {
623  for (i = 0; i < prop.u.buffer.len; i++)
624  {
625  switch (prop.u.buffer.data[i])
626  {
627  // TODO: not supported. you can have DVBC and DVBT on the same card
628  // The following are backwards compatible so its ok
629  case SYS_DVBS2:
631  break;
632  case SYS_DVBT2:
634  break;
635  default:
636  break;
637  }
638  }
639  }
640 #endif
641  close(fd_frontend);
642 
643  ret = (type.toString() != "UNKNOWN") ? type.toString().toUpper() : ret;
644 #endif // USING_DVB
645 
646  return ret;
647 }
648 
652 QString CardUtil::ProbeDVBFrontendName(const QString &device)
653 {
654  QString ret = "ERROR_UNKNOWN";
655  (void) device;
656 
657 #ifdef USING_DVB
658  QString dvbdev = CardUtil::GetDeviceName(DVB_DEV_FRONTEND, device);
659  QByteArray dev = dvbdev.toLatin1();
660  int fd_frontend = open(dev.constData(), O_RDWR | O_NONBLOCK);
661  if (fd_frontend < 0)
662  return "ERROR_OPEN";
663 
664  struct dvb_frontend_info info;
665  memset(&info, 0, sizeof(info));
666  int err = ioctl(fd_frontend, FE_GET_INFO, &info);
667  if (err < 0)
668  {
669  close(fd_frontend);
670  return "ERROR_PROBE";
671  }
672 
673  ret = info.name;
674 
675  close(fd_frontend);
676 #endif // USING_DVB
677 
678  return ret;
679 }
680 
698 bool CardUtil::HasDVBCRCBug(const QString &device)
699 {
700  QString name = ProbeDVBFrontendName(device);
701  return ((name == "VLSI VES1x93 DVB-S") || // munges PMT
702  (name == "ST STV0299 DVB-S")); // munges PAT
703 }
704 
706 {
707  QString name = ProbeDVBFrontendName(device);
708  if (name.indexOf("DVB-S") >= 0)
709  return 300;
710  if (name == "DiBcom 3000P/M-C DVB-T")
711  return 100;
712  return 25;
713 }
714 
716 {
717  QString type = GetRawInputType(inputid);
718  if ("DVB" != type)
719  return type;
720 
721  QString device = GetVideoDevice(inputid);
722 
723  if (device.isEmpty())
724  return "ERROR_OPEN";
725 
726  return ProbeDVBType(device);
727 }
728 
730 bool CardUtil::IsDVBInputType(const QString &inputType)
731 {
732  QString t = inputType.toUpper();
733  return (t == "DVB") || (t == "QAM") || (t == "QPSK") ||
734  (t == "OFDM") || (t == "ATSC") || (t == "DVB_S2");
735 }
736 
737 QString get_on_input(const QString &to_get, uint inputid)
738 {
739  MSqlQuery query(MSqlQuery::InitCon());
740  query.prepare(
741  QString("SELECT %1 ").arg(to_get) +
742  "FROM capturecard "
743  "WHERE capturecard.cardid = :INPUTID");
744  query.bindValue(":INPUTID", inputid);
745 
746  if (!query.exec())
747  MythDB::DBError("CardUtil::get_on_source", query);
748  else if (query.next())
749  return query.value(0).toString();
750 
751  return QString::null;
752 }
753 
754 bool set_on_input(const QString &to_set, uint inputid, const QString &value)
755 {
756  QString tmp = get_on_input("capturecard.cardid", inputid);
757  if (tmp.isEmpty())
758  return false;
759 
760  MSqlQuery query(MSqlQuery::InitCon());
761  query.prepare(
762  QString("UPDATE capturecard SET %1 = :VALUE ").arg(to_set) +
763  "WHERE cardid = :INPUTID");
764  query.bindValue(":INPUTID", inputid);
765  query.bindValue(":VALUE", value);
766 
767  if (query.exec())
768  return true;
769 
770  MythDB::DBError("CardUtil::set_on_input", query);
771  return false;
772 }
773 
784 vector<uint> CardUtil::GetInputIDs(QString videodevice,
785  QString rawtype,
786  QString inputname,
787  QString hostname)
788 {
789  vector<uint> list;
790 
791  if (hostname.isEmpty())
792  hostname = gCoreContext->GetHostName();
793 
794  MSqlQuery query(MSqlQuery::InitCon());
795  QString qstr =
796  "SELECT cardid "
797  "FROM capturecard "
798  "WHERE hostname = :HOSTNAME ";
799  if (!videodevice.isEmpty())
800  qstr += "AND videodevice = :DEVICE ";
801  if (!inputname.isEmpty())
802  qstr += "AND inputname = :INPUTNAME ";
803  if (!rawtype.isEmpty())
804  qstr += "AND cardtype = :INPUTTYPE ";
805  qstr += "ORDER BY cardid";
806 
807  query.prepare(qstr);
808 
809  query.bindValue(":HOSTNAME", hostname);
810  if (!videodevice.isEmpty())
811  query.bindValue(":DEVICE", videodevice);
812  if (!inputname.isEmpty())
813  query.bindValue(":INPUTNAME", inputname);
814  if (!rawtype.isEmpty())
815  query.bindValue(":INPUTTYPE", rawtype.toUpper());
816 
817  if (!query.exec())
818  MythDB::DBError("CardUtil::GetInputIDs(videodevice...)", query);
819  else
820  {
821  while (query.next())
822  list.push_back(query.value(0).toUInt());
823  }
824 
825  return list;
826 }
827 
829 {
830  if (!inputid)
831  return 0;
832 
833  MSqlQuery query(MSqlQuery::InitCon());
834  QString qstr =
835  "SELECT COUNT(*) "
836  "FROM capturecard "
837  "WHERE parentid = :INPUTID";
838 
839  query.prepare(qstr);
840  query.bindValue(":INPUTID", inputid);
841 
842  uint count = 0;
843 
844  if (!query.exec())
845  MythDB::DBError("CardUtil::GetChildInputCount()", query);
846  else if (query.next())
847  count = query.value(0).toUInt();
848 
849  return count;
850 }
851 
852 vector<uint> CardUtil::GetChildInputIDs(uint inputid)
853 {
854  vector<uint> list;
855 
856  if (!inputid)
857  return list;
858 
859  MSqlQuery query(MSqlQuery::InitCon());
860  QString qstr =
861  "SELECT cardid "
862  "FROM capturecard "
863  "WHERE parentid = :INPUTID "
864  "ORDER BY cardid";
865 
866  query.prepare(qstr);
867  query.bindValue(":INPUTID", inputid);
868 
869  if (!query.exec())
870  MythDB::DBError("CardUtil::GetChildInputIDs()", query);
871  else
872  {
873  while (query.next())
874  list.push_back(query.value(0).toUInt());
875  }
876 
877  return list;
878 }
879 
880 static uint clone_capturecard(uint src_inputid, uint orig_dst_inputid)
881 {
882  uint dst_inputid = orig_dst_inputid;
883 
884  MSqlQuery query(MSqlQuery::InitCon());
885  if (!dst_inputid)
886  {
887  query.prepare(
888  "DELETE FROM capturecard "
889  "WHERE videodevice = 'temp_dummy'");
890 
891  if (!query.exec())
892  {
893  MythDB::DBError("clone_capturecard -- delete temp", query);
894  return 0;
895  }
896 
897  query.prepare(
898  "INSERT INTO capturecard "
899  "SET videodevice = 'temp_dummy'");
900 
901  if (!query.exec())
902  {
903  MythDB::DBError("clone_capturecard -- insert temp", query);
904  return 0;
905  }
906 
907  query.prepare(
908  "SELECT cardid "
909  "FROM capturecard "
910  "WHERE videodevice = 'temp_dummy'");
911 
912  if (!query.exec())
913  {
914  MythDB::DBError("clone_capturecard -- get temp id", query);
915  return 0;
916  }
917 
918  if (!query.next())
919  {
920  LOG(VB_GENERAL, LOG_ERR, "clone_capturecard -- get temp id");
921  return 0;
922  }
923 
924  dst_inputid = query.value(0).toUInt();
925  }
926 
927  query.prepare(
928  "SELECT videodevice, audiodevice, vbidevice, "
929  " cardtype, hostname, signal_timeout, "
930  " channel_timeout, dvb_wait_for_seqstart, dvb_on_demand, "
931  " dvb_tuning_delay, dvb_diseqc_type, diseqcid, "
932  " dvb_eitscan, inputname, sourceid, "
933  " externalcommand, changer_device, changer_model, "
934  " tunechan, startchan, displayname, "
935  " dishnet_eit, recpriority, quicktune, "
936  " livetvorder, reclimit, "
937  // See below for special handling of the following.
938  " schedgroup, schedorder "
939  "FROM capturecard "
940  "WHERE cardid = :INPUTID");
941  query.bindValue(":INPUTID", src_inputid);
942 
943  if (!query.exec())
944  {
945  MythDB::DBError("clone_capturecard -- get data", query);
946  return 0;
947  }
948  if (!query.next())
949  {
950  LOG(VB_GENERAL, LOG_ERR, "clone_cardinput -- get data 2");
951  return 0;
952  }
953 
954  // Hangel schedgroup and schedorder specially. If schedgroup is
955  // set, schedgroup and schedorder should be false and 0,
956  // respectively, for all children.
957  bool schedgroup = query.value(26).toBool();
958  uint schedorder = query.value(27).toUInt();
959  if (schedgroup)
960  {
961  schedgroup = false;
962  schedorder = 0;
963  }
964 
965  MSqlQuery query2(MSqlQuery::InitCon());
966  query2.prepare(
967  "UPDATE capturecard "
968  "SET videodevice = :V0, "
969  " audiodevice = :V1, "
970  " vbidevice = :V2, "
971  " cardtype = :V3, "
972  " hostname = :V4, "
973  " signal_timeout = :V5, "
974  " channel_timeout = :V6, "
975  " dvb_wait_for_seqstart = :V7, "
976  " dvb_on_demand = :V8, "
977  " dvb_tuning_delay = :V9, "
978  " dvb_diseqc_type = :V10, "
979  " diseqcid = :V11,"
980  " dvb_eitscan = :V12, "
981  " inputname = :V13, "
982  " sourceid = :V14, "
983  " externalcommand = :V15, "
984  " changer_device = :V16, "
985  " changer_model = :V17, "
986  " tunechan = :V18, "
987  " startchan = :V19, "
988  " displayname = :V20, "
989  " dishnet_eit = :V21, "
990  " recpriority = :V22, "
991  " quicktune = :V23, "
992  " livetvorder = :V24, "
993  " reclimit = :V25, "
994  " schedgroup = :SCHEDGROUP, "
995  " schedorder = :SCHEDORDER, "
996  " parentid = :PARENTID "
997  "WHERE cardid = :INPUTID");
998  for (uint i = 0; i < 26; ++i)
999  query2.bindValue(QString(":V%1").arg(i), query.value(i).toString());
1000  query2.bindValue(":INPUTID", dst_inputid);
1001  query2.bindValue(":PARENTID", src_inputid);
1002  query2.bindValue(":SCHEDGROUP", schedgroup);
1003  query2.bindValue(":SCHEDORDER", schedorder);
1004 
1005  if (!query2.exec())
1006  {
1007  MythDB::DBError("clone_capturecard -- save data", query2);
1008  if (!orig_dst_inputid)
1009  CardUtil::DeleteCard(dst_inputid);
1010  return 0;
1011  }
1012 
1013  // copy input group linkages
1014  vector<uint> src_grps = CardUtil::GetInputGroups(src_inputid);
1015  vector<uint> dst_grps = CardUtil::GetInputGroups(dst_inputid);
1016  for (uint j = 0; j < dst_grps.size(); j++)
1017  CardUtil::UnlinkInputGroup(dst_inputid, dst_grps[j]);
1018  for (uint j = 0; j < src_grps.size(); j++)
1019  CardUtil::LinkInputGroup(dst_inputid, src_grps[j]);
1020 
1021  // clone diseqc_config (just points to the same diseqc_tree row)
1022  DiSEqCDevSettings diseqc;
1023  if (diseqc.Load(src_inputid))
1024  diseqc.Store(dst_inputid);
1025 
1026  return dst_inputid;
1027 }
1028 
1029 bool CardUtil::CloneCard(uint src_inputid, uint orig_dst_inputid)
1030 {
1031  QString type = CardUtil::GetRawInputType(src_inputid);
1032  if (!IsTunerSharingCapable(type))
1033  return false;
1034 
1035  uint dst_inputid = clone_capturecard(src_inputid, orig_dst_inputid);
1036  if (!dst_inputid)
1037  return false;
1038 
1039  return true;
1040 }
1041 
1043 {
1044  QString fwnode;
1045 
1046  MSqlQuery query(MSqlQuery::InitCon());
1047  query.prepare("SELECT changer_device "
1048  "FROM capturecard WHERE cardid = :INPUTID ");
1049  query.bindValue(":INPUTID", inputid);
1050 
1051  if (query.exec() && query.next())
1052  {
1053  fwnode = query.value(0).toString();
1054  }
1055 
1056  return fwnode;
1057 }
1058 
1060 {
1061  QString fwnode;
1062 
1063  MSqlQuery query(MSqlQuery::InitCon());
1064  query.prepare("SELECT changer_model "
1065  "FROM capturecard WHERE cardid = :INPUTID ");
1066  query.bindValue(":INPUTID", inputid);
1067 
1068  if (query.exec() && query.next())
1069  {
1070  fwnode = query.value(0).toString();
1071  }
1072 
1073  return fwnode;
1074 }
1075 
1076 vector<uint> CardUtil::GetInputIDs(uint sourceid)
1077 {
1078  MSqlQuery query(MSqlQuery::InitCon());
1079 
1080  query.prepare(
1081  "SELECT DISTINCT cardid "
1082  "FROM capturecard "
1083  "WHERE sourceid = :SOURCEID");
1084  query.bindValue(":SOURCEID", sourceid);
1085 
1086  vector<uint> list;
1087 
1088  if (!query.exec())
1089  {
1090  MythDB::DBError("CardUtil::GetInputIDs()", query);
1091  return list;
1092  }
1093 
1094  while (query.next())
1095  list.push_back(query.value(0).toUInt());
1096 
1097  return list;
1098 }
1099 
1100 bool CardUtil::SetStartChannel(uint inputid, const QString &channum)
1101 {
1102  MSqlQuery query(MSqlQuery::InitCon());
1103  query.prepare("UPDATE capturecard "
1104  "SET startchan = :CHANNUM "
1105  "WHERE cardid = :INPUTID");
1106  query.bindValue(":CHANNUM", channum);
1107  query.bindValue(":INPUTID", inputid);
1108 
1109  if (!query.exec())
1110  {
1111  MythDB::DBError("set_startchan", query);
1112  return false;
1113  }
1114 
1115  return true;
1116 }
1117 
1118 bool CardUtil::GetInputInfo(InputInfo &input, vector<uint> *groupids)
1119 {
1120  if (!input.inputid)
1121  return false;
1122 
1123  MSqlQuery query(MSqlQuery::InitCon());
1124  query.prepare("SELECT "
1125  "inputname, sourceid, livetvorder, "
1126  "schedorder, displayname, recpriority, quicktune "
1127  "FROM capturecard "
1128  "WHERE cardid = :INPUTID");
1129  query.bindValue(":INPUTID", input.inputid);
1130 
1131  if (!query.exec())
1132  {
1133  MythDB::DBError("CardUtil::GetInputInfo()", query);
1134  return false;
1135  }
1136 
1137  if (!query.next())
1138  return false;
1139 
1140  input.name = query.value(0).toString();
1141  input.sourceid = query.value(1).toUInt();
1142  input.livetvorder = query.value(2).toUInt();
1143  input.scheduleOrder = query.value(3).toUInt();
1144  input.displayName = query.value(4).toString();
1145  input.recPriority = query.value(5).toInt();
1146  input.quickTune = query.value(6).toBool();
1147 
1148  if (input.displayName.isEmpty())
1149  input.displayName = QObject::tr("Input %1:%2")
1150  .arg(input.inputid).arg(input.name);
1151 
1152  if (groupids)
1153  *groupids = GetInputGroups(input.inputid);
1154 
1155  return true;
1156 }
1157 
1158 QList<InputInfo> CardUtil::GetAllInputInfo()
1159 {
1160  QList<InputInfo> infoInputList;
1161 
1162  MSqlQuery query(MSqlQuery::InitCon());
1163  query.prepare("SELECT cardid, "
1164  "inputname, sourceid, livetvorder, "
1165  "schedorder, displayname, recpriority, quicktune "
1166  "FROM capturecard");
1167 
1168  if (!query.exec())
1169  {
1170  MythDB::DBError("CardUtil::GetAllInputInfo()", query);
1171  return infoInputList;
1172  }
1173 
1174  while (query.next())
1175  {
1176  InputInfo input;
1177  input.inputid = query.value(0).toUInt();
1178  input.name = query.value(1).toString();
1179  input.sourceid = query.value(2).toUInt();
1180  input.livetvorder = query.value(3).toUInt();
1181  input.scheduleOrder = query.value(4).toUInt();
1182  input.displayName = query.value(5).toString();
1183  input.recPriority = query.value(6).toInt();
1184  input.quickTune = query.value(7).toBool();
1185 
1186  infoInputList.push_back(input);
1187  }
1188 
1189  return infoInputList;
1190 }
1191 
1193 {
1194  InputInfo info("None", 0, inputid, 0, 0, 0);
1195  GetInputInfo(info);
1196  return info.name;
1197 }
1198 
1200 {
1201  MSqlQuery query(MSqlQuery::InitCon());
1202  query.prepare("SELECT startchan "
1203  "FROM capturecard "
1204  "WHERE cardid = :INPUTID");
1205  query.bindValue(":INPUTID", inputid);
1206 
1207  if (!query.exec())
1208  MythDB::DBError("CardUtil::GetStartingChannel(uint)", query);
1209  else if (query.next())
1210  return query.value(0).toString();
1211 
1212  return QString::null;
1213 }
1214 
1216 {
1217  if (!inputid)
1218  return QString::null;
1219 
1220  MSqlQuery query(MSqlQuery::InitCon());
1221  query.prepare("SELECT displayname, cardid, cardtype, inputname "
1222  "FROM capturecard "
1223  "WHERE cardid = :INPUTID");
1224  query.bindValue(":INPUTID", inputid);
1225 
1226  if (!query.exec())
1227  MythDB::DBError("CardUtil::GetDisplayName(uint)", query);
1228  else if (query.next())
1229  {
1230  QString result = query.value(0).toString();
1231  if (result.isEmpty())
1232  result = QString("%1: %2/%3").arg(query.value(1).toInt())
1233  .arg(query.value(2).toString()).arg(query.value(3).toString());
1234  return result;
1235  }
1236 
1237  return QString::null;
1238 }
1239 
1241 {
1242  MSqlQuery query(MSqlQuery::InitCon());
1243  query.prepare(
1244  "SELECT sourceid "
1245  "FROM capturecard "
1246  "WHERE cardid = :INPUTID");
1247  query.bindValue(":INPUTID", inputid);
1248  if (!query.exec() || !query.isActive())
1249  MythDB::DBError("CardUtil::GetSourceID()", query);
1250  else if (query.next())
1251  return query.value(0).toUInt();
1252 
1253  return 0;
1254 }
1255 
1256 // Is this intentionally leaving out the hostname when updating the
1257 // capturecard table? The hostname value does get set when inserting
1258 // into the capturecard table. (Code written in 2011.)
1260  const uint sourceid,
1261  const QString &inputname,
1262  const QString &externalcommand,
1263  const QString &changer_device,
1264  const QString &changer_model,
1265  const QString &/*hostname*/,
1266  const QString &tunechan,
1267  const QString &startchan,
1268  const QString &displayname,
1269  bool dishnet_eit,
1270  const uint recpriority,
1271  const uint quicktune,
1272  const uint schedorder,
1273  const uint livetvorder)
1274 {
1275  MSqlQuery query(MSqlQuery::InitCon());
1276 
1277  query.prepare(
1278  "UPDATE capturecard "
1279  "SET sourceid = :SOURCEID, "
1280  " inputname = :INPUTNAME, "
1281  " externalcommand = :EXTERNALCOMMAND, "
1282  " changer_device = :CHANGERDEVICE, "
1283  " changer_model = :CHANGERMODEL, "
1284  " tunechan = :TUNECHAN, "
1285  " startchan = :STARTCHAN, "
1286  " displayname = :DISPLAYNAME, "
1287  " dishnet_eit = :DISHNETEIT, "
1288  " recpriority = :RECPRIORITY, "
1289  " quicktune = :QUICKTUNE, "
1290  " schedorder = :SCHEDORDER, "
1291  " livetvorder = :LIVETVORDER "
1292  "WHERE cardid = :INPUTID AND "
1293  " inputname = 'None'");
1294 
1295  query.bindValue(":INPUTID", inputid);
1296  query.bindValue(":SOURCEID", sourceid);
1297  query.bindValue(":INPUTNAME", inputname);
1298  query.bindValue(":EXTERNALCOMMAND", externalcommand);
1299  query.bindValue(":CHANGERDEVICE", changer_device);
1300  query.bindValue(":CHANGERMODEL", changer_model);
1301  query.bindValue(":TUNECHAN", tunechan);
1302  query.bindValue(":STARTCHAN", startchan);
1303  query.bindValue(":DISPLAYNAME", displayname.isNull() ? "" : displayname);
1304  query.bindValue(":DISHNETEIT", dishnet_eit);
1305  query.bindValue(":RECPRIORITY", recpriority);
1306  query.bindValue(":QUICKTUNE", quicktune);
1307  query.bindValue(":SCHEDORDER", schedorder);
1308  query.bindValue(":LIVETVORDER", livetvorder);
1309 
1310  if (!query.exec())
1311  {
1312  MythDB::DBError("CreateCardInput", query);
1313  return -1;
1314  }
1315 
1316  return inputid;
1317 }
1318 
1320 {
1321  MSqlQuery query(MSqlQuery::InitCon());
1322  query.prepare(
1323  "UPDATE capturecard "
1324  "SET sourceid = 0, "
1325  " inputname = 'None', "
1326  " externalcommand = '', "
1327  " changer_device = '', "
1328  " changer_model = '', "
1329  " tunechan = '', "
1330  " startchan = '', "
1331  " displayname = '', "
1332  " dishnet_eit = 0, "
1333  " recpriority = 0, "
1334  " quicktune = 0, "
1335  " schedorder = 1, "
1336  " livetvorder = 1 "
1337  "WHERE cardid = :INPUTID");
1338  query.bindValue(":INPUTID", inputid);
1339 
1340  if (!query.exec())
1341  {
1342  MythDB::DBError("DeleteInput", query);
1343  return false;
1344  }
1345 
1346  query.prepare("DELETE FROM inputgroup "
1347  "WHERE cardinputid = :INPUTID AND "
1348  " inputgroupname LIKE 'user:%'");
1349  query.bindValue(":INPUTID", inputid);
1350 
1351  if (!query.exec())
1352  {
1353  MythDB::DBError("DeleteInput2", query);
1354  return false;
1355  }
1356 
1357  return true;
1358 }
1359 
1361 {
1362  MSqlQuery query(MSqlQuery::InitCon());
1363 
1364  query.prepare("SELECT inputgroupid FROM inputgroup "
1365  "WHERE inputgroupname = :GROUPNAME "
1366  "LIMIT 1");
1367  query.bindValue(":GROUPNAME", name);
1368  if (!query.exec())
1369  {
1370  MythDB::DBError("CreateNewInputGroup 0", query);
1371  return 0;
1372  }
1373 
1374  if (query.next())
1375  return query.value(0).toUInt();
1376 
1377  query.prepare("SELECT MAX(inputgroupid) FROM inputgroup");
1378  if (!query.exec())
1379  {
1380  MythDB::DBError("CreateNewInputGroup 1", query);
1381  return 0;
1382  }
1383 
1384  uint inputgroupid = (query.next()) ? query.value(0).toUInt() + 1 : 1;
1385 
1386  query.prepare(
1387  "INSERT INTO inputgroup "
1388  " (cardinputid, inputgroupid, inputgroupname) "
1389  "VALUES (:INPUTID, :GROUPID, :GROUPNAME ) ");
1390  query.bindValue(":INPUTID", 0);
1391  query.bindValue(":GROUPID", inputgroupid);
1392  query.bindValue(":GROUPNAME", name);
1393  if (!query.exec())
1394  {
1395  MythDB::DBError("CreateNewInputGroup 2", query);
1396  return 0;
1397  }
1398 
1399  return inputgroupid;
1400 }
1401 
1403  const QString &type,
1404  const QString &host,
1405  const QString &device)
1406 {
1407  QString name = host + '|' + device;
1408  if (type == "FREEBOX" || type == "IMPORT" ||
1409  type == "DEMO" || type == "EXTERNAL")
1410  name += QString("|%1").arg(inputid);
1411  return CreateInputGroup(name);
1412 }
1413 
1415 {
1416  MSqlQuery query(MSqlQuery::InitCon());
1417  query.prepare(
1418  "SELECT inputgroupid "
1419  "FROM inputgroup "
1420  "WHERE cardinputid = :INPUTID "
1421  " AND inputgroupname REGEXP '^[a-z_-]*\\\\|'");
1422  query.bindValue(":INPUTID", inputid);
1423 
1424  if (!query.exec())
1425  {
1426  MythDB::DBError("CardUtil::GetDeviceInputGroup()", query);
1427  return false;
1428  }
1429 
1430  if (query.next())
1431  {
1432  return query.value(0).toUInt();
1433  }
1434 
1435  return 0;
1436 }
1437 
1438 bool CardUtil::LinkInputGroup(uint inputid, uint inputgroupid)
1439 {
1440  MSqlQuery query(MSqlQuery::InitCon());
1441 
1442  query.prepare(
1443  "SELECT cardinputid, inputgroupid, inputgroupname "
1444  "FROM inputgroup "
1445  "WHERE inputgroupid = :GROUPID "
1446  "ORDER BY inputgroupid, cardinputid, inputgroupname");
1447  query.bindValue(":GROUPID", inputgroupid);
1448 
1449  if (!query.exec())
1450  {
1451  MythDB::DBError("CardUtil::CreateInputGroup() 1", query);
1452  return false;
1453  }
1454 
1455  if (!query.next())
1456  return false;
1457 
1458  const QString name = query.value(2).toString();
1459 
1460  query.prepare(
1461  "INSERT INTO inputgroup "
1462  " (cardinputid, inputgroupid, inputgroupname) "
1463  "VALUES (:INPUTID, :GROUPID, :GROUPNAME ) ");
1464 
1465  query.bindValue(":INPUTID", inputid);
1466  query.bindValue(":GROUPID", inputgroupid);
1467  query.bindValue(":GROUPNAME", name);
1468 
1469  if (!query.exec())
1470  {
1471  MythDB::DBError("CardUtil::CreateInputGroup() 2", query);
1472  return false;
1473  }
1474 
1475  return true;
1476 }
1477 
1478 bool CardUtil::UnlinkInputGroup(uint inputid, uint inputgroupid)
1479 {
1480  MSqlQuery query(MSqlQuery::InitCon());
1481 
1482  if (!inputid && !inputgroupid)
1483  {
1484  query.prepare(
1485  "DELETE FROM inputgroup "
1486  "WHERE cardinputid NOT IN "
1487  "( SELECT cardid FROM capturecard )");
1488  }
1489  else
1490  {
1491  query.prepare(
1492  "DELETE FROM inputgroup "
1493  "WHERE cardinputid = :INPUTID AND "
1494  " inputgroupid = :GROUPID ");
1495 
1496  query.bindValue(":INPUTID", inputid);
1497  query.bindValue(":GROUPID", inputgroupid);
1498  }
1499 
1500  if (!query.exec())
1501  {
1502  MythDB::DBError("CardUtil::DeleteInputGroup()", query);
1503  return false;
1504  }
1505 
1506  return true;
1507 }
1508 
1509 vector<uint> CardUtil::GetInputGroups(uint inputid)
1510 {
1511  vector<uint> list;
1512 
1513  MSqlQuery query(MSqlQuery::InitCon());
1514 
1515  query.prepare(
1516  "SELECT inputgroupid "
1517  "FROM inputgroup "
1518  "WHERE cardinputid = :INPUTID "
1519  "ORDER BY inputgroupid, cardinputid, inputgroupname");
1520 
1521  query.bindValue(":INPUTID", inputid);
1522 
1523  if (!query.exec())
1524  {
1525  MythDB::DBError("CardUtil::GetInputGroups()", query);
1526  return list;
1527  }
1528 
1529  while (query.next())
1530  list.push_back(query.value(0).toUInt());
1531 
1532  return list;
1533 }
1534 
1535 vector<uint> CardUtil::GetGroupInputIDs(uint inputgroupid)
1536 {
1537  vector<uint> list;
1538 
1539  MSqlQuery query(MSqlQuery::InitCon());
1540 
1541  query.prepare(
1542  "SELECT DISTINCT cardid "
1543  "FROM capturecard, inputgroup "
1544  "WHERE inputgroupid = :GROUPID AND "
1545  " capturecard.cardid = inputgroup.cardinputid "
1546  "ORDER BY cardid");
1547 
1548  query.bindValue(":GROUPID", inputgroupid);
1549 
1550  if (!query.exec())
1551  {
1552  MythDB::DBError("CardUtil::GetGroupInputIDs()", query);
1553  return list;
1554  }
1555 
1556  while (query.next())
1557  list.push_back(query.value(0).toUInt());
1558 
1559  return list;
1560 }
1561 
1563 {
1564  LOG(VB_RECORD, LOG_INFO,
1565  LOC + QString("GetConflictingInputs() input %1").arg(inputid));
1566 
1567  vector<uint> inputids;
1568 
1569  MSqlQuery query(MSqlQuery::InitCon());
1570 
1571  query.prepare(
1572  "SELECT DISTINCT c.cardid "
1573  "FROM ( "
1574  " SELECT inputgroupid "
1575  " FROM inputgroup "
1576  " WHERE cardinputid = :INPUTID1 "
1577  ") g "
1578  "JOIN inputgroup ig ON ig.inputgroupid = g.inputgroupid "
1579  "JOIN capturecard c ON c.cardid = ig.cardinputid "
1580  " AND c.cardid <> :INPUTID2 "
1581  "ORDER BY c.cardid");
1582 
1583  query.bindValue(":INPUTID1", inputid);
1584  query.bindValue(":INPUTID2", inputid);
1585 
1586  if (!query.exec())
1587  {
1588  MythDB::DBError("CardUtil::GetConflictingInputs()", query);
1589  return inputids;
1590  }
1591 
1592  while (query.next())
1593  {
1594  inputids.push_back(query.value(0).toUInt());
1595  LOG(VB_RECORD, LOG_INFO,
1596  LOC + QString("GetConflictingInputs() got input %1").arg(inputids.back()));
1597  }
1598 
1599  return inputids;
1600 }
1601 
1603  uint &signal_timeout, uint &channel_timeout)
1604 {
1605  MSqlQuery query(MSqlQuery::InitCon());
1606  query.prepare(
1607  "SELECT signal_timeout, channel_timeout "
1608  "FROM capturecard "
1609  "WHERE cardid = :INPUTID");
1610  query.bindValue(":INPUTID", inputid);
1611 
1612  if (!query.exec() || !query.isActive())
1613  MythDB::DBError("CardUtil::GetTimeouts()", query);
1614  else if (query.next())
1615  {
1616  signal_timeout = (uint) max(query.value(0).toInt(), 250);
1617  channel_timeout = (uint) max(query.value(1).toInt(), 500);
1618  return true;
1619  }
1620 
1621  return false;
1622 }
1623 
1625 {
1626  DiSEqCDev dev;
1627  DiSEqCDevTree *diseqc_tree = dev.FindTree(inputid);
1628 
1629  bool needsConf = false;
1630  if (diseqc_tree)
1631  needsConf = diseqc_tree->IsInNeedOfConf();
1632 
1633  return needsConf;
1634 }
1635 
1636 uint CardUtil::GetQuickTuning(uint inputid, const QString &input_name)
1637 {
1638  uint quicktune = 0;
1639 
1640  MSqlQuery query(MSqlQuery::InitCon());
1641  query.prepare(
1642  "SELECT quicktune "
1643  "FROM capturecard "
1644  "WHERE cardid = :INPUTID AND "
1645  " inputname = :INPUTNAME");
1646  query.bindValue(":INPUTID", inputid);
1647  query.bindValue(":INPUTNAME", input_name);
1648 
1649  if (!query.exec() || !query.isActive())
1650  MythDB::DBError("CardUtil::GetQuickTuning()", query);
1651  else if (query.next())
1652  quicktune = query.value(0).toUInt();
1653 
1654  return quicktune;
1655 }
1656 
1657 bool CardUtil::hasV4L2(int videofd)
1658 {
1659  (void) videofd;
1660 #ifdef USING_V4L2
1661  struct v4l2_capability vcap;
1662  memset(&vcap, 0, sizeof(vcap));
1663 
1664  return ((ioctl(videofd, VIDIOC_QUERYCAP, &vcap) >= 0) &&
1665  (vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE));
1666 #else // if !USING_V4L2
1667  return false;
1668 #endif // !USING_V4L2
1669 }
1670 
1672  int videofd, QString &input, QString &driver, uint32_t &version,
1673  uint32_t &capabilities)
1674 {
1675  input = driver = QString::null;
1676  version = 0;
1677  capabilities = 0;
1678 
1679  if (videofd < 0)
1680  return false;
1681 
1682 #ifdef USING_V4L2
1683  // First try V4L2 query
1684  struct v4l2_capability capability;
1685  memset(&capability, 0, sizeof(struct v4l2_capability));
1686  if (ioctl(videofd, VIDIOC_QUERYCAP, &capability) >= 0)
1687  {
1688  input = QString::fromLatin1((const char*)capability.card);
1689  driver = QString::fromLatin1((const char*)capability.driver);
1690  version = capability.version;
1691  capabilities = capability.capabilities;
1692  }
1693 #ifdef USING_V4L1
1694  else // Fallback to V4L1 query
1695  {
1696  struct video_capability capability;
1697  if (ioctl(videofd, VIDIOCGCAP, &capability) >= 0)
1698  input = QString::fromLatin1((const char*)capability.name);
1699  }
1700 #endif // USING_V4L1
1701 #endif // USING_V4L2
1702 
1703  if (!driver.isEmpty())
1704  driver.remove( QRegExp("\\[[0-9]\\]$") );
1705 
1706  return !input.isEmpty();
1707 }
1708 
1710 {
1711  (void) videofd;
1712 
1713  InputNames list;
1714  ok = false;
1715 
1716 #ifdef USING_V4L2
1717  bool usingv4l2 = hasV4L2(videofd);
1718 
1719  // V4L v2 query
1720  struct v4l2_input vin;
1721  memset(&vin, 0, sizeof(vin));
1722  while (usingv4l2 && (ioctl(videofd, VIDIOC_ENUMINPUT, &vin) >= 0))
1723  {
1724  QString input((char *)vin.name);
1725  list[vin.index] = input;
1726  vin.index++;
1727  }
1728  if (vin.index)
1729  {
1730  ok = true;
1731  return list;
1732  }
1733 
1734 #ifdef USING_V4L1
1735  // V4L v1 query
1736  struct video_capability vidcap;
1737  memset(&vidcap, 0, sizeof(vidcap));
1738  if (ioctl(videofd, VIDIOCGCAP, &vidcap) != 0)
1739  {
1740  QString msg = QObject::tr("Could not query inputs.");
1741  LOG(VB_GENERAL, LOG_ERR, "ProbeV4LVideoInputs(): Error, " + msg + ENO);
1742  list[-1] = msg;
1743  vidcap.channels = 0;
1744  }
1745 
1746  for (int i = 0; i < vidcap.channels; i++)
1747  {
1748  struct video_channel test;
1749  memset(&test, 0, sizeof(test));
1750  test.channel = i;
1751 
1752  if (ioctl(videofd, VIDIOCGCHAN, &test) != 0)
1753  {
1754  LOG(VB_GENERAL, LOG_ERR, "ProbeV4LVideoInputs(): Error, " +
1755  QString("Could determine name of input #%1"
1756  "\n\t\t\tNot adding it to the list.")
1757  .arg(test.channel) + ENO);
1758  continue;
1759  }
1760 
1761  list[i] = test.name;
1762  }
1763 #endif // USING_V4L1
1764 
1765  // Create an input when none are advertised
1766  if (list.isEmpty())
1767  list[0] = "Television";
1768 
1769  ok = true;
1770 #else // if !USING_V4L2
1771  list[-1] += QObject::tr("ERROR, Compile with V4L support to query inputs");
1772 #endif // !USING_V4L2
1773  return list;
1774 }
1775 
1777 {
1778  (void) videofd;
1779 
1780  InputNames list;
1781  ok = false;
1782 
1783 #ifdef USING_V4L2
1784  bool usingv4l2 = hasV4L2(videofd);
1785 
1786  // V4L v2 query
1787  struct v4l2_audio ain;
1788  memset(&ain, 0, sizeof(ain));
1789  while (usingv4l2 && (ioctl(videofd, VIDIOC_ENUMAUDIO, &ain) >= 0))
1790  {
1791  QString input((char *)ain.name);
1792  list[ain.index] = input;
1793  ain.index++;
1794  }
1795  if (ain.index)
1796  {
1797  ok = true;
1798  return list;
1799  }
1800 
1801  ok = true;
1802 #else // if !USING_V4L2
1803  list[-1] += QObject::tr(
1804  "ERROR, Compile with V4L support to query audio inputs");
1805 #endif // !USING_V4L2
1806  return list;
1807 }
1808 
1810 {
1811  InputNames list;
1812  MSqlQuery query(MSqlQuery::InitCon());
1813  query.prepare(
1814  "SELECT cardid, inputname "
1815  "FROM capturecard "
1816  "WHERE hostname = :HOSTNAME "
1817  " AND videodevice = :DEVICE "
1818  " AND parentid = 0 "
1819  " AND inputname <> 'None'");
1820  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
1821  query.bindValue(":DEVICE", device);
1822 
1823  if (!query.exec() || !query.isActive())
1824  MythDB::DBError("CardUtil::GetConfiguredDVBInputs", query);
1825  else
1826  {
1827  while (query.next())
1828  list[query.value(0).toUInt()] = query.value(1).toString();
1829  }
1830  return list;
1831 }
1832 
1833 QStringList CardUtil::ProbeVideoInputs(QString device, QString inputtype)
1834 {
1835  QStringList ret;
1836 
1837  if (IsSingleInputType(inputtype))
1838  ret += "MPEG2TS";
1839  else if ("DVB" == inputtype)
1840  ret += ProbeDVBInputs(device);
1841  else
1842  ret += ProbeV4LVideoInputs(device);
1843 
1844  return ret;
1845 }
1846 
1847 QStringList CardUtil::ProbeAudioInputs(QString device, QString inputtype)
1848 {
1849  LOG(VB_GENERAL, LOG_DEBUG, QString("ProbeAudioInputs(%1,%2)")
1850  .arg(device).arg(inputtype));
1851  QStringList ret;
1852 
1853  if ("HDPVR" == inputtype ||
1854  "V4L2" == inputtype)
1855  ret += ProbeV4LAudioInputs(device);
1856 
1857  return ret;
1858 }
1859 
1860 QStringList CardUtil::ProbeV4LVideoInputs(QString device)
1861 {
1862  bool ok;
1863  QStringList ret;
1864  QByteArray dev = device.toLatin1();
1865  int videofd = open(dev.constData(), O_RDWR);
1866  if (videofd < 0)
1867  {
1868  ret += QObject::tr("Could not open '%1' "
1869  "to probe its inputs.").arg(device);
1870  return ret;
1871  }
1872  InputNames list = CardUtil::ProbeV4LVideoInputs(videofd, ok);
1873  close(videofd);
1874 
1875  if (!ok)
1876  {
1877  ret += list[-1];
1878  return ret;
1879  }
1880 
1881  InputNames::iterator it;
1882  for (it = list.begin(); it != list.end(); ++it)
1883  {
1884  if (it.key() >= 0)
1885  ret += *it;
1886  }
1887 
1888  return ret;
1889 }
1890 
1891 QStringList CardUtil::ProbeV4LAudioInputs(QString device)
1892 {
1893  LOG(VB_GENERAL, LOG_DEBUG, QString("ProbeV4LAudioInputs(%1)").arg(device));
1894 
1895  bool ok;
1896  QStringList ret;
1897  int videofd = open(device.toLatin1().constData(), O_RDWR);
1898  if (videofd < 0)
1899  {
1900  LOG(VB_GENERAL, LOG_ERR, "ProbeAudioInputs() -> couldn't open device");
1901  ret += QObject::tr("Could not open '%1' to probe its inputs.")
1902  .arg(device);
1903  return ret;
1904  }
1905  InputNames list = CardUtil::ProbeV4LAudioInputs(videofd, ok);
1906  close(videofd);
1907 
1908  if (!ok)
1909  {
1910  ret += list[-1];
1911  return ret;
1912  }
1913 
1914  InputNames::iterator it;
1915  for (it = list.begin(); it != list.end(); ++it)
1916  {
1917  if (it.key() >= 0)
1918  ret += *it;
1919  }
1920 
1921  return ret;
1922 }
1923 
1924 QStringList CardUtil::ProbeDVBInputs(QString device)
1925 {
1926  QStringList ret;
1927 
1928 #ifdef USING_DVB
1929  InputNames list = GetConfiguredDVBInputs(device);
1930  InputNames::iterator it;
1931  for (it = list.begin(); it != list.end(); ++it)
1932  {
1933  if (it.key())
1934  ret += *it;
1935  }
1936 #else
1937  (void) device;
1938  ret += QObject::tr("ERROR, Compile with DVB support to query inputs");
1939 #endif
1940 
1941  return ret;
1942 }
1943 
1944 QString CardUtil::GetDeviceLabel(const QString &inputtype,
1945  const QString &videodevice)
1946 {
1947  return QString("[ %1 : %2 ]").arg(inputtype).arg(videodevice);
1948 }
1949 
1951 {
1952  QString devlabel;
1953  MSqlQuery query(MSqlQuery::InitCon());
1954  query.prepare("SELECT cardtype, videodevice "
1955  "FROM capturecard WHERE cardid = :INPUTID ");
1956  query.bindValue(":INPUTID", inputid);
1957 
1958  if (query.exec() && query.next())
1959  {
1960  return GetDeviceLabel(query.value(0).toString(),
1961  query.value(1).toString());
1962  }
1963 
1964  return "[ UNKNOWN ]";
1965 }
1966 
1968  const QString &device,
1969  const QString &inputtype,
1970  QStringList &inputs)
1971 {
1972  inputs.clear();
1973  if (IsSingleInputType(inputtype))
1974  inputs += "MPEG2TS";
1975  else if (inputtype == "DVB")
1976  inputs += "DVBInput";
1977  else
1978  inputs += ProbeV4LVideoInputs(device);
1979 }
1980 
1981 int CardUtil::CreateCaptureCard(const QString &videodevice,
1982  const QString &audiodevice,
1983  const QString &vbidevice,
1984  const QString &inputtype,
1985  const uint audioratelimit,
1986  const QString &hostname,
1987  const uint dvb_swfilter,
1988  const uint dvb_sat_type,
1989  bool dvb_wait_for_seqstart,
1990  bool skipbtaudio,
1991  bool dvb_on_demand,
1992  const uint dvb_diseqc_type,
1993  const uint firewire_speed,
1994  const QString &firewire_model,
1995  const uint firewire_connection,
1996  const uint signal_timeout,
1997  const uint channel_timeout,
1998  const uint dvb_tuning_delay,
1999  const uint contrast,
2000  const uint brightness,
2001  const uint colour,
2002  const uint hue,
2003  const uint diseqcid,
2004  bool dvb_eitscan)
2005 {
2006  MSqlQuery query(MSqlQuery::InitCon());
2007 
2008  query.prepare(
2009  "INSERT INTO capturecard "
2010  "(videodevice, audiodevice, vbidevice, cardtype, "
2011  "audioratelimit, hostname, dvb_swfilter, dvb_sat_type, "
2012  "dvb_wait_for_seqstart, skipbtaudio, dvb_on_demand, dvb_diseqc_type, "
2013  "firewire_speed, firewire_model, firewire_connection, signal_timeout, "
2014  "channel_timeout, dvb_tuning_delay, contrast, brightness, colour, "
2015  "hue, diseqcid, dvb_eitscan) "
2016  "VALUES (:VIDEODEVICE, :AUDIODEVICE, :VBIDEVICE, :INPUTTYPE, "
2017  ":AUDIORATELIMIT, :HOSTNAME, :DVBSWFILTER, :DVBSATTYPE, "
2018  ":DVBWAITFORSEQSTART, :SKIPBTAUDIO, :DVBONDEMAND, :DVBDISEQCTYPE, "
2019  ":FIREWIRESPEED, :FIREWIREMODEL, :FIREWIRECONNECTION, :SIGNALTIMEOUT, "
2020  ":CHANNELTIMEOUT, :DVBTUNINGDELAY, :CONTRAST, :BRIGHTNESS, :COLOUR, "
2021  ":HUE, :DISEQCID, :DVBEITSCAN ) ");
2022 
2023  query.bindValue(":VIDEODEVICE", videodevice);
2024  query.bindValue(":AUDIODEVICE", audiodevice);
2025  query.bindValue(":VBIDEVICE", vbidevice);
2026  query.bindValue(":INPUTTYPE", inputtype);
2027  query.bindValue(":AUDIORATELIMIT", audioratelimit);
2028  query.bindValue(":HOSTNAME", hostname);
2029  query.bindValue(":DVBSWFILTER", dvb_swfilter);
2030  query.bindValue(":DVBSATTYPE", dvb_sat_type);
2031  query.bindValue(":DVBWAITFORSEQSTART", dvb_wait_for_seqstart);
2032  query.bindValue(":SKIPBTAUDIO", skipbtaudio);
2033  query.bindValue(":DVBONDEMAND", dvb_on_demand);
2034  query.bindValue(":DVBDISEQCTYPE", dvb_diseqc_type);
2035  query.bindValue(":FIREWIRESPEED", firewire_speed);
2036  query.bindValue(":FIREWIREMODEL", firewire_model);
2037  query.bindValue(":FIREWIRECONNECTION", firewire_connection);
2038  query.bindValue(":SIGNALTIMEOUT", signal_timeout);
2039  query.bindValue(":CHANNELTIMEOUT", channel_timeout);
2040  query.bindValue(":DVBTUNINGDELAY", dvb_tuning_delay);
2041  query.bindValue(":CONTRAST", contrast);
2042  query.bindValue(":BRIGHTNESS", brightness);
2043  query.bindValue(":COLOUR", colour);
2044  query.bindValue(":HUE", hue);
2045  query.bindValue(":DISEQCID", diseqcid);
2046  query.bindValue(":DVBEITSCAN", dvb_eitscan);
2047 
2048  if (!query.exec())
2049  {
2050  MythDB::DBError("CreateCaptureCard", query);
2051  return -1;
2052  }
2053 
2054  query.prepare("SELECT MAX(cardid) FROM capturecard");
2055 
2056  if (!query.exec())
2057  {
2058  MythDB::DBError("CreateCaptureCard maxinput", query);
2059  return -1;
2060  }
2061 
2062  int inputid = -1; /* must be int not uint because of return type. */
2063 
2064  if (query.next())
2065  {
2066  inputid = query.value(0).toInt();
2067  uint groupid = CardUtil::CreateDeviceInputGroup(inputid, inputtype,
2068  hostname, videodevice);
2069  CardUtil::LinkInputGroup(inputid, groupid);
2070  }
2071 
2072  return inputid;
2073 }
2074 
2076 {
2077  MSqlQuery query(MSqlQuery::InitCon());
2078  bool ok = true;
2079 
2080  if (!inputid)
2081  return true;
2082 
2083  DiSEqCDevTree tree;
2084  tree.Load(inputid);
2085 
2086  // delete any clones
2087  QString rawtype = GetRawInputType(inputid);
2088  QString videodevice = GetVideoDevice(inputid);
2089  if (IsTunerSharingCapable(rawtype) && !videodevice.isEmpty())
2090  {
2091  query.prepare(
2092  "SELECT cardid "
2093  "FROM capturecard "
2094  "WHERE parentid = :INPUTID");
2095  query.bindValue(":INPUTID", inputid);
2096 
2097  if (!query.exec())
2098  {
2099  MythDB::DBError("DeleteCard -- find clone inputs", query);
2100  return false;
2101  }
2102 
2103  while (query.next())
2104  ok &= DeleteCard(query.value(0).toUInt());
2105 
2106  if (!ok)
2107  return false;
2108  }
2109 
2110  ok &= CardUtil::DeleteInput(inputid);
2111 
2112  if (!ok)
2113  return false;
2114 
2115  // actually delete the capturecard row for this input
2116  query.prepare("DELETE FROM capturecard WHERE cardid = :INPUTID");
2117  query.bindValue(":INPUTID", inputid);
2118 
2119  if (!query.exec())
2120  {
2121  MythDB::DBError("DeleteCard -- delete row", query);
2122  ok = false;
2123  }
2124 
2125  if (ok)
2126  {
2127  // Delete the diseqc tree if no more inputs reference it.
2128  if (tree.Root())
2129  {
2130  query.prepare("SELECT cardid FROM capturecard "
2131  "WHERE diseqcid = :DISEQCID LIMIT 1");
2132  query.bindValue(":DISEQCID", tree.Root()->GetDeviceID());
2133  if (!query.exec())
2134  {
2135  MythDB::DBError("DeleteCard -- find diseqc tree", query);
2136  }
2137  else if (!query.next())
2138  {
2139  tree.SetRoot(NULL);
2140  tree.Store(inputid);
2141  }
2142  }
2143 
2144  // delete any unused input groups
2145  UnlinkInputGroup(0,0);
2146  }
2147 
2148  return ok;
2149 }
2150 
2152 {
2153  MSqlQuery query(MSqlQuery::InitCon());
2154  return (query.exec("TRUNCATE TABLE inputgroup") &&
2155  query.exec("TRUNCATE TABLE diseqc_config") &&
2156  query.exec("TRUNCATE TABLE diseqc_tree") &&
2157  query.exec("TRUNCATE TABLE capturecard") &&
2158  query.exec("TRUNCATE TABLE iptv_channel"));
2159 }
2160 
2161 vector<uint> CardUtil::GetInputList(void)
2162 {
2163  vector<uint> list;
2164 
2165  MSqlQuery query(MSqlQuery::InitCon());
2166  query.prepare(
2167  "SELECT cardid "
2168  "FROM capturecard "
2169  "ORDER BY cardid");
2170 
2171  if (!query.exec())
2172  MythDB::DBError("CardUtil::GetInputList()", query);
2173  else
2174  {
2175  while (query.next())
2176  list.push_back(query.value(0).toUInt());
2177  }
2178 
2179  return list;
2180 }
2181 
2183 {
2184  vector<uint> list;
2185 
2186  MSqlQuery query(MSqlQuery::InitCon());
2187  query.prepare(
2188  "SELECT DISTINCT cardid "
2189  "FROM capturecard "
2190  "WHERE schedorder <> 0 "
2191  "ORDER BY schedorder, cardid");
2192 
2193  if (!query.exec())
2194  MythDB::DBError("CardUtil::GetSchedInputList()", query);
2195  else
2196  {
2197  while (query.next())
2198  list.push_back(query.value(0).toUInt());
2199  }
2200 
2201  return list;
2202 }
2203 
2205 {
2206  vector<uint> list;
2207 
2208  MSqlQuery query(MSqlQuery::InitCon());
2209  query.prepare(
2210  "SELECT DISTINCT cardid "
2211  "FROM capturecard "
2212  "WHERE livetvorder <> 0 "
2213  "ORDER BY livetvorder, cardid");
2214 
2215  if (!query.exec())
2216  MythDB::DBError("CardUtil::GetLiveTVInputList()", query);
2217  else
2218  {
2219  while (query.next())
2220  list.push_back(query.value(0).toUInt());
2221  }
2222 
2223  return list;
2224 }
2225 
2226 QString CardUtil::GetDeviceName(dvb_dev_type_t type, const QString &device)
2227 {
2228  QString devname = QString(device);
2229 
2230  if (DVB_DEV_FRONTEND == type)
2231  return devname;
2232  else if (DVB_DEV_DVR == type)
2233  return devname.replace(devname.indexOf("frontend"), 8, "dvr");
2234  else if (DVB_DEV_DEMUX == type)
2235  return devname.replace(devname.indexOf("frontend"), 8, "demux");
2236  else if (DVB_DEV_CA == type)
2237  return devname.replace(devname.indexOf("frontend"), 8, "ca");
2238  else if (DVB_DEV_AUDIO == type)
2239  return devname.replace(devname.indexOf("frontend"), 8, "audio");
2240  else if (DVB_DEV_VIDEO == type)
2241  return devname.replace(devname.indexOf("frontend"), 8, "video");
2242 
2243  return "";
2244 }
2245 
2255 bool CardUtil::HDHRdoesDVB(const QString &device)
2256 {
2257  (void) device;
2258 
2259 #ifdef USING_HDHOMERUN
2260  hdhomerun_device_t *hdhr;
2261  hdhr = hdhomerun_device_create_from_str(device.toLatin1(), NULL);
2262  if (!hdhr)
2263  return false;
2264 
2265  const char *model = hdhomerun_device_get_model_str(hdhr);
2266  if (model && strstr(model, "dvb"))
2267  {
2268  hdhomerun_device_destroy(hdhr);
2269  return true;
2270  }
2271 
2272  hdhomerun_device_destroy(hdhr);
2273 
2274 #endif
2275 
2276  return false;
2277 }
2278 
2283 QString CardUtil::GetHDHRdesc(const QString &device)
2284 {
2285  QString connectErr = QObject::tr("Unable to connect to device.");
2286 
2287 #ifdef USING_HDHOMERUN
2288  bool deviceIsIP = false;
2289  uint32_t dev;
2290 
2291  if (device.contains('.')) // Simplistic check, but also allows DNS names
2292  deviceIsIP = true;
2293  else
2294  {
2295  bool validID;
2296 
2297  dev = device.toUInt(&validID, 16);
2298  if (!validID || !hdhomerun_discover_validate_device_id(dev))
2299  return QObject::tr("Invalid Device ID");
2300  }
2301  (void) deviceIsIP;
2302 
2303  LOG(VB_GENERAL, LOG_INFO, "CardUtil::GetHDHRdescription(" + device +
2304  ") - trying to locate device");
2305 
2306  hdhomerun_device_t *hdhr;
2307  hdhr = hdhomerun_device_create_from_str(device.toLatin1(), NULL);
2308  if (!hdhr)
2309  return QObject::tr("Invalid Device ID or address.");
2310 
2311  const char *model = hdhomerun_device_get_model_str(hdhr);
2312  if (!model)
2313  {
2314  hdhomerun_device_destroy(hdhr);
2315  return connectErr;
2316  }
2317 
2318 
2319  QString description = model;
2320  char *sVersion;
2321  uint32_t iVersion;
2322 
2323  if (hdhomerun_device_get_version(hdhr, &sVersion, &iVersion))
2324  description += QObject::tr(", firmware: %2").arg(sVersion);
2325 
2326  hdhomerun_device_destroy(hdhr);
2327 
2328  return description;
2329 #else
2330 
2331  (void) device;
2332  return connectErr;
2333 #endif
2334 }
2335 
2340 QString CardUtil::GetVBoxdesc(const QString &id, const QString &ip,
2341  const QString &tunerNo, const QString &tunerType)
2342 {
2343  QString connectErr = QObject::tr("Unable to connect to device.");
2344 
2345 #ifdef USING_VBOX
2346  VBox *vbox = new VBox(ip);
2347 
2348  if (!vbox->checkConnection())
2349  {
2350  delete vbox;
2351  return connectErr;
2352  }
2353 
2354  QString version;
2355 
2356  if (!vbox->checkVersion(version))
2357  {
2358  QString apiVersionErr = QObject::tr("The VBox software version is too old (%1), we require %2")
2359  .arg(version).arg(VBOX_MIN_API_VERSION);
2360  delete vbox;
2361  return apiVersionErr;
2362 
2363  }
2364 
2365  delete vbox;
2366 
2367  return QString("V@Box TV Gateway - ID: %1, IP: %2, Tuner: %3-%4").arg(id)
2368  .arg(ip).arg(tunerNo).arg(tunerType);
2369 
2370 #else
2371  (void) id;
2372  (void) ip;
2373  (void) tunerNo;
2374  (void) tunerType;
2375 
2376  return connectErr;
2377 #endif
2378 }
2379 
2380 #ifdef USING_ASI
2381 static QString sys_dev(uint device_num, QString dev)
2382 {
2383  return QString("/sys/class/asi/asirx%1/%2").arg(device_num).arg(dev);
2384 }
2385 
2386 static QString read_sys(QString sys_dev)
2387 {
2388  QFile f(sys_dev);
2389  f.open(QIODevice::ReadOnly);
2390  QByteArray sdba = f.readAll();
2391  f.close();
2392  return sdba;
2393 }
2394 
2395 static bool write_sys(QString sys_dev, QString str)
2396 {
2397  QFile f(sys_dev);
2398  f.open(QIODevice::WriteOnly);
2399  QByteArray ba = str.toLocal8Bit();
2400  qint64 offset = 0;
2401  for (uint tries = 0; (offset < ba.size()) && tries < 5; tries++)
2402  {
2403  qint64 written = f.write(ba.data()+offset, ba.size()-offset);
2404  if (written < 0)
2405  return false;
2406  offset += written;
2407  }
2408  return true;
2409 }
2410 #endif
2411 
2412 int CardUtil::GetASIDeviceNumber(const QString &device, QString *error)
2413 {
2414 #ifdef USING_ASI
2415  // basic confirmation
2416  struct stat statbuf;
2417  memset(&statbuf, 0, sizeof(statbuf));
2418  if (stat(device.toLocal8Bit().constData(), &statbuf) < 0)
2419  {
2420  if (error)
2421  *error = QString("Unable to stat '%1'").arg(device) + ENO;
2422  return -1;
2423  }
2424 
2425  if (!S_ISCHR(statbuf.st_mode))
2426  {
2427  if (error)
2428  *error = QString("'%1' is not a character device").arg(device);
2429  return -1;
2430  }
2431 
2432  if (!(statbuf.st_rdev & 0x0080))
2433  {
2434  if (error)
2435  *error = QString("'%1' not a DVEO ASI receiver").arg(device);
2436  return -1;
2437  }
2438 
2439  int device_num = statbuf.st_rdev & 0x007f;
2440 
2441  // extra confirmation
2442  QString sys_dev_contents = read_sys(sys_dev(device_num, "dev"));
2443  QStringList sys_dev_clist = sys_dev_contents.split(":");
2444  if (2 != sys_dev_clist.size())
2445  {
2446  if (error)
2447  {
2448  *error = QString("Unable to read '%1'")
2449  .arg(sys_dev(device_num, "dev"));
2450  }
2451  return -1;
2452  }
2453  if (sys_dev_clist[0].toUInt() != (statbuf.st_rdev>>8))
2454  {
2455  if (error)
2456  *error = QString("'%1' not a DVEO ASI device").arg(device);
2457  return -1;
2458  }
2459 
2460  return device_num;
2461 #else
2462  (void) device;
2463  if (error)
2464  *error = "Not compiled with ASI support.";
2465  return -1;
2466 #endif
2467 }
2468 
2470 {
2471 #ifdef USING_ASI
2472  // get the buffer size
2473  QString sys_bufsize_contents = read_sys(sys_dev(device_num, "bufsize"));
2474  bool ok;
2475  uint buf_size = sys_bufsize_contents.toUInt(&ok);
2476  if (!ok)
2477  {
2478  if (error)
2479  {
2480  *error = QString("Failed to read buffer size from '%1'")
2481  .arg(sys_dev(device_num, "bufsize"));
2482  }
2483  return 0;
2484  }
2485  return buf_size;
2486 #else
2487  (void) device_num;
2488  if (error)
2489  *error = "Not compiled with ASI support.";
2490  return 0;
2491 #endif
2492 }
2493 
2495 {
2496 #ifdef USING_ASI
2497  // get the buffer size
2498  QString sys_numbuffers_contents = read_sys(sys_dev(device_num, "buffers"));
2499  bool ok;
2500  uint num_buffers = sys_numbuffers_contents.toUInt(&ok);
2501  if (!ok)
2502  {
2503  if (error)
2504  {
2505  *error = QString("Failed to read num buffers from '%1'")
2506  .arg(sys_dev(device_num, "buffers"));
2507  }
2508  return 0;
2509  }
2510  return num_buffers;
2511 #else
2512  (void) device_num;
2513  if (error)
2514  *error = "Not compiled with ASI support.";
2515  return 0;
2516 #endif
2517 }
2518 
2519 int CardUtil::GetASIMode(uint device_num, QString *error)
2520 {
2521 #ifdef USING_ASI
2522  QString sys_bufsize_contents = read_sys(sys_dev(device_num, "mode"));
2523  bool ok;
2524  uint mode = sys_bufsize_contents.toUInt(&ok);
2525  if (!ok)
2526  {
2527  if (error)
2528  {
2529  *error = QString("Failed to read mode from '%1'")
2530  .arg(sys_dev(device_num, "mode"));
2531  }
2532  return -1;
2533  }
2534  return mode;
2535 #else
2536  (void) device_num;
2537  if (error)
2538  *error = "Not compiled with ASI support.";
2539  return -1;
2540 #endif
2541 }
2542 
2543 bool CardUtil::SetASIMode(uint device_num, uint mode, QString *error)
2544 {
2545 #ifdef USING_ASI
2546  QString sys_bufsize_contents = read_sys(sys_dev(device_num, "mode"));
2547  bool ok;
2548  uint old_mode = sys_bufsize_contents.toUInt(&ok);
2549  if (ok && old_mode == mode)
2550  return true;
2551  ok = write_sys(sys_dev(device_num, "mode"), QString("%1\n").arg(mode));
2552  if (!ok && error)
2553  {
2554  *error = QString("Failed to set mode to %1 using '%2'")
2555  .arg(mode).arg(sys_dev(device_num, "mode"));
2556  }
2557  return ok;
2558 #else
2559  Q_UNUSED(device_num);
2560  Q_UNUSED(mode);
2561  if (error)
2562  *error = "Not compiled with ASI support.";
2563  return false;
2564 #endif
2565 }
2566 
2572 {
2573  // should only be called if inputtype == VBOX
2574  if (!inputid )
2575  {
2576  LOG(VB_GENERAL, LOG_ERR, QString("VBOX inputid (%1) not valid, redo mythtv-setup")
2577  .arg(inputid));
2578  return false;
2579  }
2580 
2581  // get sourceid and startchan from table capturecard for inputid
2582  uint chanid = 0;
2583  chanid = ChannelUtil::GetChannelValueInt("chanid",GetSourceID(inputid),GetStartingChannel(inputid));
2584  if (!chanid)
2585  {
2586  // no chanid, presume bad setup
2587  LOG(VB_GENERAL, LOG_ERR, QString("VBOX chanid (%1) not found for inputid (%2) , redo mythtv-setup")
2588  .arg(chanid).arg(inputid));
2589  return false;
2590  }
2591 
2592  // get timeouts for inputid
2593  uint signal_timeout = 0;
2594  uint tuning_timeout = 0;
2595  if (!GetTimeouts(inputid,signal_timeout,tuning_timeout))
2596  {
2597  LOG(VB_GENERAL, LOG_ERR, QString("Failed to get timeouts for inputid (%1)")
2598  .arg(inputid));
2599  return false;
2600  }
2601 
2602  signal_timeout = signal_timeout/1000; //convert to seconds
2603 
2604  // now get url from iptv_channel table
2605  QUrl url;
2606  MSqlQuery query(MSqlQuery::InitCon());
2607  query.prepare("SELECT url "
2608  "FROM iptv_channel "
2609  "WHERE chanid = :CHANID");
2610  query.bindValue(":CHANID", chanid);
2611 
2612  if (!query.exec())
2613  MythDB::DBError("CardUtil::IsVBoxPresent url", query);
2614  else if (query.next())
2615  url = query.value(0).toString();
2616 
2617  //now get just the IP address from the url
2618  QString ip ="";
2619  ip = url.host();
2620  LOG(VB_GENERAL, LOG_INFO, QString("VBOX IP found (%1) for inputid (%2)")
2621  .arg(ip).arg(inputid));
2622 
2623  if (!ping(ip,signal_timeout))
2624  {
2625  LOG(VB_GENERAL, LOG_ERR, QString("VBOX at IP (%1) failed to respond to network ping for inputid (%2) timeout (%3)")
2626  .arg(ip).arg(inputid).arg(signal_timeout));
2627  return false;
2628  }
2629 
2630  return true;
2631 }
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:798
uint scheduleOrder
Definition: inputinfo.h:77
dvb_dev_type_t
Definition: cardutil.h:29
static bool IsTunerSharingCapable(const QString &rawtype)
Definition: cardutil.h:163
static bool HasDVBCRCBug(const QString &device)
Returns true if and only if the device munges PAT/PMT tables, and then doesn&#39;t fix the CRC...
Definition: cardutil.cpp:698
int recPriority
Definition: inputinfo.h:76
static QStringList ProbeVideoDevices(const QString &rawtype)
Definition: cardutil.cpp:462
void bindValue(const QString &placeholder, const QVariant &val)
Definition: mythdbcon.cpp:897
DVB-S device settings class.
Definition: diseqc.h:37
static QString GetInputName(uint inputid)
Definition: cardutil.cpp:1192
DiSEqCDevDevice * Root(void)
Retrieves the root node in the tree.
Definition: diseqc.h:94
static uint GetASINumBuffers(uint device_num, QString *error=NULL)
Definition: cardutil.cpp:2494
static bool IsTunerShared(uint inputidA, uint inputidB)
Definition: cardutil.cpp:257
DiSEqCDevTree * FindTree(uint cardid)
Retrieve device tree.
Definition: diseqc.cpp:247
#define VBOX_MIN_API_VERSION
Definition: vboxutils.h:11
static const int kTunerTypeDVBT
static bool SetStartChannel(uint inputid, const QString &channum)
Definition: cardutil.cpp:1100
static QStringList GetVideoDevices(const QString &rawtype, QString hostname=QString::null)
Returns the videodevices of the matching inputs, duplicates removed.
Definition: cardutil.cpp:411
uint GetDeviceID(void) const
Definition: diseqc.h:164
#define O_NONBLOCK
Definition: mythmedia.cpp:25
static uint CreateInputGroup(const QString &name)
Definition: cardutil.cpp:1360
static QString ProbeDVBType(const QString &device)
Definition: cardutil.cpp:570
static vector< uint > GetInputIDs(QString videodevice=QString::null, QString rawtype=QString::null, QString inputname=QString::null, QString hostname=QString::null)
Returns all inputids of inputs that uses the specified videodevice if specified, and optionally rawty...
Definition: cardutil.cpp:784
static void ClearVideoDeviceCache()
Definition: cardutil.cpp:456
static vector< uint > GetConflictingInputs(uint inputid)
Definition: cardutil.cpp:1562
static QString GetDeviceLabel(const QString &inputtype, const QString &videodevice)
Definition: cardutil.cpp:1944
static QString ProbeSubTypeName(uint inputid)
Definition: cardutil.cpp:715
static void error(const char *str,...)
Definition: vbi.c:41
static int CreateCaptureCard(const QString &videodevice, const QString &audiodevice, const QString &vbidevice, const QString &inputtype, const uint audioratelimit, const QString &hostname, const uint dvb_swfilter, const uint dvb_sat_type, bool dvb_wait_for_seqstart, bool skipbtaudio, bool dvb_on_demand, const uint dvb_diseqc_type, const uint firewire_speed, const QString &firewire_model, const uint firewire_connection, const uint signal_timeout, const uint channel_timeout, const uint dvb_tuning_delay, const uint contrast, const uint brightness, const uint colour, const uint hue, const uint diseqcid, bool dvb_eitscan)
Definition: cardutil.cpp:1981
uint sourceid
associated channel listings source
Definition: inputinfo.h:71
bool IsInNeedOfConf(void) const
Definition: diseqc.cpp:851
bool checkVersion(QString &version)
Definition: vboxutils.cpp:221
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:652
static QStringList ProbeDVBInputs(QString device)
Definition: cardutil.cpp:1924
static bool IsInNeedOfExternalInputConf(uint inputid)
Definition: cardutil.cpp:1624
static bool write_sys(QString sys_dev, QString str)
Definition: cardutil.cpp:2395
QMap< QString, QString > InputTypes
Definition: cardutil.h:45
unsigned int uint
Definition: compat.h:136
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static bool GetInputInfo(InputInfo &info, vector< uint > *groupids=NULL)
Definition: cardutil.cpp:1118
static vector< uint > GetSchedInputList(void)
Definition: cardutil.cpp:2182
bool set_on_input(const QString &to_set, uint inputid, const QString &value)
Definition: cardutil.cpp:754
static bool DeleteInput(uint inputid)
Definition: cardutil.cpp:1319
static const int kTunerTypeDVBS1
static bool HasTuner(const QString &rawtype, const QString &device)
Definition: cardutil.cpp:234
bool Load(uint card_input_id)
Loads configuration chain from DB for specified card input id.
Definition: diseqc.cpp:137
bool Store(uint cardid, const QString &device="")
Stores the device tree to the database.
Definition: diseqc.cpp:433
static QString sys_dev(uint device_num, QString dev)
Definition: cardutil.cpp:2381
static bool GetV4LInfo(int videofd, QString &input, QString &driver, uint32_t &version, uint32_t &capabilities)
Definition: cardutil.cpp:1671
static vector< uint > GetLiveTVInputList(void)
Definition: cardutil.cpp:2204
static bool GetTimeouts(uint inputid, uint &signal_timeout, uint &channel_timeout)
Definition: cardutil.cpp:1602
QVariant value(int i) const
Definition: mythdbcon.h:182
static QString GetVBoxdesc(const QString &id, const QString &ip, const QString &tunerNo, const QString &tunerType)
Get a nicely formatted string describing the device.
Definition: cardutil.cpp:2340
QString displayName
Definition: inputinfo.h:75
voidpf uLong offset
Definition: ioapi.h:142
static const int kTunerTypeDVBS2
static QStringList ProbeAudioInputs(QString device, QString inputtype=QString::null)
Definition: cardutil.cpp:1847
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:83
static const int kTunerTypeDVBT2
#define close
Definition: compat.h:12
static InputNames ProbeV4LAudioInputs(int videofd, bool &ok)
Definition: cardutil.cpp:1776
void SetRoot(DiSEqCDevDevice *root)
Changes the root node of the tree.
Definition: diseqc.cpp:646
static bool IsDVBInputType(const QString &input_type)
Returns true iff the input_type is one of the DVB types.
Definition: cardutil.cpp:730
uint livetvorder
order for live TV use
Definition: inputinfo.h:78
static bool IsSingleInputType(const QString &rawtype)
Definition: cardutil.h:197
unsigned char t
Definition: ParseText.cpp:340
static QString GetDeviceName(dvb_dev_type_t, const QString &device)
Definition: cardutil.cpp:2226
static bool IsCableCardPresent(uint inputid, const QString &inputType)
Definition: cardutil.cpp:117
static uint GetDeviceInputGroup(uint inputid)
Definition: cardutil.cpp:1414
bool ping(const QString &host, int timeout)
Can we ping host within timeout seconds?
bool isActive(void) const
Definition: mythdbcon.h:188
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
bool download(const QString &url, const QString &dest, const bool reload=false)
Downloads a URL to a file in blocking mode.
bool Store(uint card_input_id) const
Stores configuration chain to DB for specified card input id.
Definition: diseqc.cpp:171
static QString GetFirewireChangerModel(uint inputid)
Definition: cardutil.cpp:1059
string hostname
Definition: caa.py:17
static vector< uint > GetGroupInputIDs(uint inputgroupid)
Definition: cardutil.cpp:1535
static int GetChannelValueInt(const QString &channel_field, uint sourceid, const QString &channum)
static vector< uint > GetChildInputIDs(uint inputid)
Definition: cardutil.cpp:852
static bool UnlinkInputGroup(uint inputid, uint inputgroupid)
Definition: cardutil.cpp:1478
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:555
static QList< InputInfo > GetAllInputInfo()
Definition: cardutil.cpp:1158
static InputNames GetConfiguredDVBInputs(const QString &device)
Definition: cardutil.cpp:1809
static uint GetQuickTuning(uint inputid, const QString &inputname)
Definition: cardutil.cpp:1636
static QMap< QString, QStringList > videoDeviceCache
Definition: cardutil.h:415
static bool HDHRdoesDVB(const QString &device)
If the device is valid, check if the model does DVB.
Definition: cardutil.cpp:2255
const char * name
Definition: ParseText.cpp:339
static uint GetASIBufferSize(uint device_num, QString *error=NULL)
Definition: cardutil.cpp:2469
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:98
uint inputid
unique key in DB for this input
Definition: inputinfo.h:72
static int GetASIDeviceNumber(const QString &device, QString *error=NULL)
Definition: cardutil.cpp:2412
static QStringList ProbeVideoInputs(QString device, QString inputtype=QString::null)
Definition: cardutil.cpp:1833
static bool SetASIMode(uint device_num, uint mode, QString *error=NULL)
Definition: cardutil.cpp:2543
static bool CloneCard(uint src_inputid, uint dst_inputid)
Definition: cardutil.cpp:1029
static vector< uint > GetInputList(void)
Definition: cardutil.cpp:2161
bool Load(const QString &device)
Loads the device tree from the database.
Definition: diseqc.cpp:329
static QString GetFirewireChangerNode(uint inputid)
Definition: cardutil.cpp:1042
static MythSystemLegacyManager * manager
static QString GetVideoDevice(uint inputid)
Definition: cardutil.h:274
#define LOC
Definition: cardutil.cpp:56
static QStringList probeDevices(void)
Definition: vboxutils.cpp:37
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:823
bool quickTune
Definition: inputinfo.h:79
static uint GetChildInputCount(uint inputid)
Definition: cardutil.cpp:828
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:38
bool HasTuner(void) const
Definition: v4l2util.cpp:701
static void GetDeviceInputNames(const QString &device, const QString &inputtype, QStringList &inputs)
Definition: cardutil.cpp:1967
static QStringList GetInputTypeNames(uint sourceid)
Get a list of card input types for a source id.
Definition: cardutil.cpp:383
QString name
input name
Definition: inputinfo.h:70
static uint clone_capturecard(uint src_inputid, uint orig_dst_inputid)
Definition: cardutil.cpp:880
static int CreateCardInput(const uint inputid, const uint sourceid, const QString &inputname, const QString &externalcommand, const QString &changer_device, const QString &changer_model, const QString &hostname, const QString &tunechan, const QString &startchan, const QString &displayname, bool dishnet_eit, const uint recpriority, const uint quicktune, const uint schedorder, const uint livetvorder)
Definition: cardutil.cpp:1259
static bool IsVBoxPresent(uint inputid)
Returns true if the VBox responds to a ping.
Definition: cardutil.cpp:2571
QString get_on_input(const QString &to_get, uint inputid)
Definition: cardutil.cpp:737
static bool DeleteCard(uint inputid)
Definition: cardutil.cpp:2075
QMap< int, QString > InputNames
Definition: cardutil.h:22
static uint GetSourceID(uint inputid)
Definition: cardutil.cpp:1240
static bool hasV4L2(int videofd)
Definition: cardutil.cpp:1657
const char int mode
Definition: ioapi.h:135
static QString GetRawInputType(uint inputid)
Definition: cardutil.h:272
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:623
bool checkConnection(void)
Definition: vboxutils.cpp:215
static bool LinkInputGroup(uint inputid, uint inputgroupid)
Definition: cardutil.cpp:1438
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:181
static bool DeleteAllCards(void)
Definition: cardutil.cpp:2151
QString GetHostName(void)
static InputTypes GetInputTypes(void)
Definition: cardutil.cpp:338
DVB-S device tree class.
Definition: diseqc.h:75
QString DriverName(void) const
Definition: v4l2util.h:45
static InputNames ProbeV4LVideoInputs(int videofd, bool &ok)
Definition: cardutil.cpp:1709
static int GetASIMode(uint device_num, QString *error=NULL)
Definition: cardutil.cpp:2519
static bool IsInputTypePresent(const QString &rawtype, QString hostname=QString::null)
Returns true if the input type is present and connected to an input.
Definition: cardutil.cpp:304
static QString GetHDHRdesc(const QString &device)
Get a nicely formatted string describing the device.
Definition: cardutil.cpp:2283
static QString GetScanableInputTypes(void)
Definition: cardutil.cpp:58
static vector< uint > GetInputGroups(uint inputid)
Definition: cardutil.cpp:1509
QString toString() const
static uint GetMinSignalMonitoringDelay(const QString &device)
Definition: cardutil.cpp:705
static uint CreateDeviceInputGroup(uint inputid, const QString &type, const QString &host, const QString &device)
Definition: cardutil.cpp:1402
static QString GetStartingChannel(uint inputid)
Definition: cardutil.cpp:1199
Main DVB-S device interface.
Definition: diseqc.h:52
static QString read_sys(QString sys_dev)
Definition: cardutil.cpp:2386
static QString GetDisplayName(uint inputid)
Definition: cardutil.cpp:1215