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