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