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