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 MythDB::DBError("CardUtil::GetInputIDs(videodevice...)", query);
1346 else
1347 {
1348 while (query.next())
1349 list.push_back(query.value(0).toUInt());
1350 }
1351
1352 return list;
1353}
1354
1356{
1357 if (!inputid)
1358 return 0;
1359
1361 QString qstr =
1362 "SELECT COUNT(*) "
1363 "FROM capturecard "
1364 "WHERE parentid = :INPUTID";
1365
1366 query.prepare(qstr);
1367 query.bindValue(":INPUTID", inputid);
1368
1369 uint count = 0;
1370
1371 if (!query.exec())
1372 MythDB::DBError("CardUtil::GetChildInputCount()", query);
1373 else if (query.next())
1374 count = query.value(0).toUInt();
1375
1376 return count;
1377}
1378
1379std::vector<uint> CardUtil::GetChildInputIDs(uint inputid)
1380{
1381 std::vector<uint> list;
1382
1383 if (!inputid)
1384 return list;
1385
1387 QString qstr =
1388 "SELECT cardid "
1389 "FROM capturecard "
1390 "WHERE parentid = :INPUTID "
1391 "ORDER BY cardid";
1392
1393 query.prepare(qstr);
1394 query.bindValue(":INPUTID", inputid);
1395
1396 if (!query.exec())
1397 MythDB::DBError("CardUtil::GetChildInputIDs()", query);
1398 else
1399 {
1400 while (query.next())
1401 list.push_back(query.value(0).toUInt());
1402 }
1403
1404 return list;
1405}
1406
1407static uint clone_capturecard(uint src_inputid, uint orig_dst_inputid)
1408{
1409 uint dst_inputid = orig_dst_inputid;
1410
1412 if (!dst_inputid)
1413 {
1414 query.prepare(
1415 "DELETE FROM capturecard "
1416 "WHERE videodevice = 'temp_dummy'");
1417
1418 if (!query.exec())
1419 {
1420 MythDB::DBError("clone_capturecard -- delete temp", query);
1421 return 0;
1422 }
1423
1424 query.prepare(
1425 "INSERT INTO capturecard "
1426 "SET videodevice = 'temp_dummy'");
1427
1428 if (!query.exec())
1429 {
1430 MythDB::DBError("clone_capturecard -- insert temp", query);
1431 return 0;
1432 }
1433
1434 query.prepare(
1435 "SELECT cardid "
1436 "FROM capturecard "
1437 "WHERE videodevice = 'temp_dummy'");
1438
1439 if (!query.exec())
1440 {
1441 MythDB::DBError("clone_capturecard -- get temp id", query);
1442 return 0;
1443 }
1444
1445 if (!query.next())
1446 {
1447 LOG(VB_GENERAL, LOG_ERR, "clone_capturecard -- get temp id");
1448 return 0;
1449 }
1450
1451 dst_inputid = query.value(0).toUInt();
1452 }
1453
1454 query.prepare(
1455 "SELECT videodevice, audiodevice, vbidevice, "
1456 " cardtype, hostname, signal_timeout, "
1457 " channel_timeout, dvb_wait_for_seqstart, dvb_on_demand, "
1458 " dvb_tuning_delay, dvb_diseqc_type, diseqcid, "
1459 " dvb_eitscan, inputname, sourceid, "
1460 " externalcommand, changer_device, changer_model, "
1461 " tunechan, startchan, displayname, "
1462 " dishnet_eit, recpriority, quicktune, "
1463 " livetvorder, reclimit, "
1464 // See below for special handling of the following.
1465 " schedgroup, schedorder "
1466 "FROM capturecard "
1467 "WHERE cardid = :INPUTID");
1468 query.bindValue(":INPUTID", src_inputid);
1469
1470 if (!query.exec())
1471 {
1472 MythDB::DBError("clone_capturecard -- get data", query);
1473 return 0;
1474 }
1475 if (!query.next())
1476 {
1477 LOG(VB_GENERAL, LOG_ERR, "clone_cardinput -- get data 2");
1478 return 0;
1479 }
1480
1481 // Handle schedgroup and schedorder specially. If schedgroup is
1482 // set, schedgroup and schedorder should be false and 0,
1483 // respectively, for all children.
1484 bool schedgroup = query.value(26).toBool();
1485 uint schedorder = query.value(27).toUInt();
1486 if (schedgroup)
1487 {
1488 schedgroup = false;
1489 schedorder = 0;
1490 }
1491
1492 MSqlQuery query2(MSqlQuery::InitCon());
1493 query2.prepare(
1494 "UPDATE capturecard "
1495 "SET videodevice = :V0, "
1496 " audiodevice = :V1, "
1497 " vbidevice = :V2, "
1498 " cardtype = :V3, "
1499 " hostname = :V4, "
1500 " signal_timeout = :V5, "
1501 " channel_timeout = :V6, "
1502 " dvb_wait_for_seqstart = :V7, "
1503 " dvb_on_demand = :V8, "
1504 " dvb_tuning_delay = :V9, "
1505 " dvb_diseqc_type = :V10, "
1506 " diseqcid = :V11,"
1507 " dvb_eitscan = :V12, "
1508 " inputname = :V13, "
1509 " sourceid = :V14, "
1510 " externalcommand = :V15, "
1511 " changer_device = :V16, "
1512 " changer_model = :V17, "
1513 " tunechan = :V18, "
1514 " startchan = :V19, "
1515 " displayname = :V20, "
1516 " dishnet_eit = :V21, "
1517 " recpriority = :V22, "
1518 " quicktune = :V23, "
1519 " livetvorder = :V24, "
1520 " reclimit = :V25, "
1521 " schedgroup = :SCHEDGROUP, "
1522 " schedorder = :SCHEDORDER, "
1523 " parentid = :PARENTID "
1524 "WHERE cardid = :INPUTID");
1525 for (uint i = 0; i < 26; ++i)
1526 query2.bindValue(QString(":V%1").arg(i), query.value(i).toString());
1527 query2.bindValue(":INPUTID", dst_inputid);
1528 query2.bindValue(":PARENTID", src_inputid);
1529 query2.bindValue(":SCHEDGROUP", schedgroup);
1530 query2.bindValue(":SCHEDORDER", schedorder);
1531
1532 if (!query2.exec())
1533 {
1534 MythDB::DBError("clone_capturecard -- save data", query2);
1535 if (!orig_dst_inputid)
1536 CardUtil::DeleteInput(dst_inputid);
1537 return 0;
1538 }
1539
1540 // copy input group linkages
1541 std::vector<uint> src_grps = CardUtil::GetInputGroups(src_inputid);
1542 std::vector<uint> dst_grps = CardUtil::GetInputGroups(dst_inputid);
1543 for (uint dst_grp : dst_grps)
1544 CardUtil::UnlinkInputGroup(dst_inputid, dst_grp);
1545 for (uint src_grp : src_grps)
1546 CardUtil::LinkInputGroup(dst_inputid, src_grp);
1547
1548 // clone diseqc_config (just points to the same diseqc_tree row)
1549 DiSEqCDevSettings diseqc;
1550 if (diseqc.Load(src_inputid))
1551 diseqc.Store(dst_inputid);
1552
1553 return dst_inputid;
1554}
1555
1556uint CardUtil::CloneCard(uint src_inputid, uint orig_dst_inputid)
1557{
1558 QString type = CardUtil::GetRawInputType(src_inputid);
1560 return 0;
1561
1562 uint dst_inputid = clone_capturecard(src_inputid, orig_dst_inputid);
1563 return dst_inputid;
1564}
1565
1566
1567bool CardUtil::InputSetMaxRecordings(uint parentid, uint max_recordings)
1568{
1569 if (max_recordings < 1)
1570 {
1571 LOG(VB_GENERAL, LOG_ERR, LOC +
1572 "InputSetMaxRecording: max must be greater than zero.");
1573 return false;
1574 }
1575
1576 std::vector<uint> cardids = CardUtil::GetChildInputIDs(parentid);
1577
1578 // Delete old clone cards as required.
1579 for (size_t i = cardids.size() + 1;
1580 (i > max_recordings) && !cardids.empty(); --i)
1581 {
1582 CardUtil::DeleteInput(cardids.back());
1583 cardids.pop_back();
1584 }
1585
1586 // Clone this config to existing clone cards.
1587 for (uint id : cardids)
1588 CardUtil::CloneCard(parentid, id);
1589
1590 // Create new clone cards as required.
1591 for (size_t i = cardids.size() + 1; i < max_recordings; ++i)
1592 {
1593 CardUtil::CloneCard(parentid, 0);
1594 }
1595
1596 return true;
1597}
1598
1599
1601{
1602 uint inputid = CloneCard(parentid, 0);
1603
1604 // Update the reclimit for the parent and all children so the new
1605 // child doesn't get removed the next time mythtv-setup is run.
1606 if (inputid)
1607 {
1608 LOG(VB_GENERAL, LOG_INFO, LOC +
1609 QString("Added child input %1 to parent %2")
1610 .arg(inputid).arg(parentid));
1612 query.prepare("UPDATE capturecard "
1613 "SET reclimit = reclimit + 1 "
1614 "WHERE cardid = :PARENTID");
1615 query.bindValue(":PARENTID", parentid);
1616 if (!query.exec())
1617 MythDB::DBError("CardUtil::AddChildInput", query);
1618 }
1619 else
1620 {
1621 LOG(VB_GENERAL, LOG_ERR, LOC +
1622 QString("Failed to add child input to parent %1").arg(parentid));
1623 }
1624
1625 return inputid;
1626}
1627
1629{
1630 QString fwnode;
1631
1633 query.prepare("SELECT changer_device "
1634 "FROM capturecard WHERE cardid = :INPUTID ");
1635 query.bindValue(":INPUTID", inputid);
1636
1637 if (query.exec() && query.next())
1638 {
1639 fwnode = query.value(0).toString();
1640 }
1641
1642 return fwnode;
1643}
1644
1646{
1647 QString fwnode;
1648
1650 query.prepare("SELECT changer_model "
1651 "FROM capturecard WHERE cardid = :INPUTID ");
1652 query.bindValue(":INPUTID", inputid);
1653
1654 if (query.exec() && query.next())
1655 {
1656 fwnode = query.value(0).toString();
1657 }
1658
1659 return fwnode;
1660}
1661
1662std::vector<uint> CardUtil::GetInputIDs(uint sourceid)
1663{
1665
1666 query.prepare(
1667 "SELECT DISTINCT cardid "
1668 "FROM capturecard "
1669 "WHERE sourceid = :SOURCEID");
1670 query.bindValue(":SOURCEID", sourceid);
1671
1672 std::vector<uint> list;
1673
1674 if (!query.exec())
1675 {
1676 MythDB::DBError("CardUtil::GetInputIDs(sourceid)", query);
1677 return list;
1678 }
1679
1680 while (query.next())
1681 list.push_back(query.value(0).toUInt());
1682
1683 return list;
1684}
1685
1686bool CardUtil::SetStartChannel(uint inputid, const QString &channum)
1687{
1689 query.prepare("UPDATE capturecard "
1690 "SET startchan = :CHANNUM "
1691 "WHERE cardid = :INPUTID");
1692 query.bindValue(":CHANNUM", channum);
1693 query.bindValue(":INPUTID", inputid);
1694
1695 if (!query.exec())
1696 {
1697 MythDB::DBError("CardUtil::SetStartChannel", query);
1698 return false;
1699 }
1700
1701 return true;
1702}
1703
1704bool CardUtil::GetInputInfo(InputInfo &input, std::vector<uint> *groupids)
1705{
1706 if (!input.m_inputId)
1707 return false;
1708
1710 query.prepare("SELECT "
1711 "inputname, sourceid, livetvorder, "
1712 "schedorder, displayname, recpriority, quicktune "
1713 "FROM capturecard "
1714 "WHERE cardid = :INPUTID");
1715 query.bindValue(":INPUTID", input.m_inputId);
1716
1717 if (!query.exec())
1718 {
1719 MythDB::DBError("CardUtil::GetInputInfo()", query);
1720 return false;
1721 }
1722
1723 if (!query.next())
1724 return false;
1725
1726 input.m_name = query.value(0).toString();
1727 input.m_sourceId = query.value(1).toUInt();
1728 input.m_liveTvOrder = query.value(2).toUInt();
1729 input.m_scheduleOrder = query.value(3).toUInt();
1730 input.m_displayName = query.value(4).toString();
1731 input.m_recPriority = query.value(5).toInt();
1732 input.m_quickTune = query.value(6).toBool();
1733
1734 if (groupids)
1735 *groupids = GetInputGroups(input.m_inputId);
1736
1737 return true;
1738}
1739
1740// virtTuners to include virtual tuners in the list
1741QList<InputInfo> CardUtil::GetAllInputInfo(bool virtTuners)
1742{
1743 QList<InputInfo> infoInputList;
1744
1746 QString queryStr = "SELECT cardid, "
1747 "inputname, sourceid, livetvorder, "
1748 "schedorder, displayname, recpriority, quicktune "
1749 "FROM capturecard";
1750
1751 if (!virtTuners)
1752 queryStr.append(" WHERE parentid = 0");
1753 query.prepare(queryStr);
1754
1755 if (!query.exec())
1756 {
1757 MythDB::DBError("CardUtil::GetAllInputInfo()", query);
1758 return infoInputList;
1759 }
1760
1761 while (query.next())
1762 {
1763 InputInfo input;
1764 input.m_inputId = query.value(0).toUInt();
1765 input.m_name = query.value(1).toString();
1766 input.m_sourceId = query.value(2).toUInt();
1767 input.m_liveTvOrder = query.value(3).toUInt();
1768 input.m_scheduleOrder = query.value(4).toUInt();
1769 input.m_displayName = query.value(5).toString();
1770 input.m_recPriority = query.value(6).toInt();
1771 input.m_quickTune = query.value(7).toBool();
1772
1773 infoInputList.push_back(input);
1774 }
1775
1776 return infoInputList;
1777}
1778
1780{
1781 InputInfo info("None", 0, inputid, 0, 0, 0);
1783 return info.m_name;
1784}
1785
1786// Get start channel for a capture card
1787//
1788// The start channel is:
1789// - the last channel used for Live TV as found in field startchan
1790// - if that is not filled in, the first visible channel from
1791// the video source of the capture card
1792//
1793// The start channel is maintained per capture card instance so
1794// different capture cards connected to the same video source
1795// and different multirec instances of the same capture card
1796// can have a different starting channel value.
1797//
1799{
1800 QString startchan;
1801
1803 query.prepare("SELECT startchan "
1804 "FROM capturecard "
1805 "WHERE cardid = :INPUTID");
1806 query.bindValue(":INPUTID", inputid);
1807
1808 if (!query.exec() || !query.isActive())
1809 {
1810 MythDB::DBError("CardUtil::GetStartChannel#1", query);
1811 }
1812 else if (query.next())
1813 {
1814 startchan = query.value(0).toString();
1815 }
1816
1817 // Check if starting channel is valid; skip invisible channels.
1818 if (!startchan.isEmpty())
1819 {
1820 query.prepare("SELECT channel.chanid "
1821 "FROM capturecard, channel "
1822 "WHERE capturecard.cardid = :INPUTID AND "
1823 " capturecard.sourceid = channel.sourceid AND "
1824 " channel.deleted IS NULL AND "
1825 " channel.visible > 0 AND "
1826 " channel.channum = :CHANNUM");
1827 query.bindValue(":INPUTID", inputid);
1828 query.bindValue(":CHANNUM", startchan);
1829
1830 if (!query.exec() || !query.isActive())
1831 {
1832 MythDB::DBError("CardUtil::GetStartChannel#2", query);
1833 }
1834 else if (!query.next())
1835 {
1836 LOG(VB_GENERAL, LOG_WARNING,
1837 QString("CardUtil[%1]: ").arg(inputid) +
1838 QString("Channel %1 on inputid %2 is invalid").arg(startchan).arg(inputid));
1839 startchan.clear();
1840 }
1841 }
1842
1843 // If we do not have a start channel yet then use the first one available.
1844 // Any visible channel will do so there is no "order by" done.
1845 if (startchan.isEmpty())
1846 {
1847 query.prepare("SELECT channel.channum "
1848 "FROM capturecard, channel "
1849 "WHERE capturecard.cardid = :INPUTID AND "
1850 " capturecard.sourceid = channel.sourceid AND "
1851 " channel.deleted IS NULL AND "
1852 " channel.visible > 0 "
1853 "LIMIT 1");
1854 query.bindValue(":INPUTID", inputid);
1855
1856 if (!query.exec() || !query.isActive())
1857 {
1858 MythDB::DBError("CardUtil::GetStartChannel#3", query);
1859 }
1860 else if (query.next())
1861 {
1862 startchan = query.value(0).toString();
1863 }
1864 }
1865
1866 if (startchan.isEmpty())
1867 {
1868 LOG(VB_GENERAL, LOG_WARNING,
1869 QString("CardUtil[%1]: ").arg(inputid) +
1870 QString("No start channel found on inputid %1").arg(inputid));
1871 }
1872 else
1873 {
1874 LOG(VB_GENERAL, LOG_DEBUG,
1875 QString("CardUtil[%1]: ").arg(inputid) +
1876 QString("Start channel %1 on inputid %2").arg(startchan).arg(inputid));
1877 }
1878
1879 return startchan;
1880}
1881
1883{
1884 if (!inputid)
1885 return {};
1886
1888 query.prepare("SELECT displayname "
1889 "FROM capturecard "
1890 "WHERE cardid = :INPUTID");
1891 query.bindValue(":INPUTID", inputid);
1892
1893 if (!query.exec())
1894 MythDB::DBError("CardUtil::GetDisplayName(uint)", query);
1895 else if (query.next())
1896 {
1897 QString result = query.value(0).toString();
1898 return result;
1899 }
1900
1901 return {};
1902}
1903
1904bool CardUtil::IsUniqueDisplayName(const QString &name, uint exclude_inputid)
1905{
1906 if (name.isEmpty())
1907 return false;
1908
1909 QString matching;
1910 bool two { false };
1911 qsizetype idx = name.indexOf('/');
1912 if (idx >= 0)
1913 {
1914 matching = name.right(name.size() - idx -1);
1915 two = false;
1916 }
1917 else
1918 {
1919 matching = name.right(2);
1920 two = true;
1921 }
1922
1924 query.prepare("SELECT cardid, displayname "
1925 "FROM capturecard "
1926 "WHERE parentid = 0 "
1927 " AND cardid <> :INPUTID ");
1928 query.bindValue(":INPUTID", exclude_inputid);
1929
1930 if (!query.exec())
1931 {
1932 MythDB::DBError("CardUtil::IsUniqueDisplayName()", query);
1933 return false;
1934 }
1935
1936 while (query.next())
1937 {
1938 QString dn = query.value(1).toString();
1939 idx = dn.indexOf('/');
1940 if (!two && idx >= 0)
1941 {
1942 if (dn.right(dn.size() - idx - 1) == matching)
1943 return false;
1944 }
1945 else if (dn.right(2) == matching.right(2))
1946 {
1947 return false;
1948 }
1949 }
1950
1951 return true;
1952}
1953
1955{
1957 query.prepare(
1958 "SELECT sourceid "
1959 "FROM capturecard "
1960 "WHERE cardid = :INPUTID");
1961 query.bindValue(":INPUTID", inputid);
1962 if (!query.exec() || !query.isActive())
1963 MythDB::DBError("CardUtil::GetSourceID()", query);
1964 else if (query.next())
1965 return query.value(0).toUInt();
1966
1967 return 0;
1968}
1969
1970// Is this intentionally leaving out the hostname when updating the
1971// capturecard table? The hostname value does get set when inserting
1972// into the capturecard table. (Code written in 2011.)
1974 const uint sourceid,
1975 const QString &inputname,
1976 const QString &externalcommand,
1977 const QString &changer_device,
1978 const QString &changer_model,
1979 const QString &/*hostname*/,
1980 const QString &tunechan,
1981 const QString &startchan,
1982 const QString &displayname,
1983 bool dishnet_eit,
1984 const uint recpriority,
1985 const uint quicktune,
1986 const uint schedorder,
1987 const uint livetvorder)
1988{
1990
1991 query.prepare(
1992 "UPDATE capturecard "
1993 "SET sourceid = :SOURCEID, "
1994 " inputname = :INPUTNAME, "
1995 " externalcommand = :EXTERNALCOMMAND, "
1996 " changer_device = :CHANGERDEVICE, "
1997 " changer_model = :CHANGERMODEL, "
1998 " tunechan = :TUNECHAN, "
1999 " startchan = :STARTCHAN, "
2000 " displayname = :DISPLAYNAME, "
2001 " dishnet_eit = :DISHNETEIT, "
2002 " recpriority = :RECPRIORITY, "
2003 " quicktune = :QUICKTUNE, "
2004 " schedorder = :SCHEDORDER, "
2005 " livetvorder = :LIVETVORDER "
2006 "WHERE cardid = :INPUTID AND "
2007 " inputname = 'None'");
2008
2009 query.bindValue(":INPUTID", inputid);
2010 query.bindValue(":SOURCEID", sourceid);
2011 query.bindValue(":INPUTNAME", inputname);
2012 query.bindValue(":EXTERNALCOMMAND", externalcommand);
2013 query.bindValue(":CHANGERDEVICE", changer_device);
2014 query.bindValue(":CHANGERMODEL", changer_model);
2015 query.bindValue(":TUNECHAN", tunechan);
2016 query.bindValue(":STARTCHAN", startchan);
2017 query.bindValueNoNull(":DISPLAYNAME", displayname);
2018 query.bindValue(":DISHNETEIT", dishnet_eit);
2019 query.bindValue(":RECPRIORITY", recpriority);
2020 query.bindValue(":QUICKTUNE", quicktune);
2021 query.bindValue(":SCHEDORDER", schedorder);
2022 query.bindValue(":LIVETVORDER", livetvorder);
2023
2024 if (!query.exec())
2025 {
2026 MythDB::DBError("CardUtil::CreateCardInput()", query);
2027 return -1;
2028 }
2029
2030 return inputid;
2031}
2032
2034{
2036
2037 query.prepare("SELECT inputgroupid FROM inputgroup "
2038 "WHERE inputgroupname = :GROUPNAME "
2039 "LIMIT 1");
2040 query.bindValue(":GROUPNAME", name);
2041 if (!query.exec())
2042 {
2043 MythDB::DBError("CardUtil::CreateNewInputGroup 0", query);
2044 return 0;
2045 }
2046
2047 if (query.next())
2048 return query.value(0).toUInt();
2049
2050 query.prepare("SELECT MAX(inputgroupid) FROM inputgroup");
2051 if (!query.exec())
2052 {
2053 MythDB::DBError("CardUtil::CreateNewInputGroup 1", query);
2054 return 0;
2055 }
2056
2057 uint inputgroupid = (query.next()) ? query.value(0).toUInt() + 1 : 1;
2058
2059 query.prepare(
2060 "INSERT INTO inputgroup "
2061 " (cardinputid, inputgroupid, inputgroupname) "
2062 "VALUES (:INPUTID, :GROUPID, :GROUPNAME ) ");
2063 query.bindValue(":INPUTID", 0);
2064 query.bindValue(":GROUPID", inputgroupid);
2065 query.bindValue(":GROUPNAME", name);
2066 if (!query.exec())
2067 {
2068 MythDB::DBError("CardUtil::CreateNewInputGroup 2", query);
2069 return 0;
2070 }
2071
2072 return inputgroupid;
2073}
2074
2076 const QString &type,
2077 const QString &host,
2078 const QString &device)
2079{
2080 QString name = host + '|' + device;
2081 if (type == "FREEBOX" || type == "IMPORT" ||
2082 type == "DEMO" || type == "EXTERNAL" ||
2083 type == "HDHOMERUN" )
2084 name += QString("|%1").arg(inputid);
2085 return CreateInputGroup(name);
2086}
2087
2089{
2091 query.prepare(
2092 "SELECT inputgroupid "
2093 "FROM inputgroup "
2094 "WHERE cardinputid = :INPUTID "
2095 " AND inputgroupname REGEXP '^[a-z_-]*\\\\|'");
2096 query.bindValue(":INPUTID", inputid);
2097
2098 if (!query.exec())
2099 {
2100 MythDB::DBError("CardUtil::GetDeviceInputGroup()", query);
2101 return 0;
2102 }
2103
2104 if (query.next())
2105 {
2106 return query.value(0).toUInt();
2107 }
2108
2109 return 0;
2110}
2111
2112bool CardUtil::LinkInputGroup(uint inputid, uint inputgroupid)
2113{
2115
2116 query.prepare(
2117 "SELECT cardinputid, inputgroupid, inputgroupname "
2118 "FROM inputgroup "
2119 "WHERE inputgroupid = :GROUPID "
2120 "ORDER BY inputgroupid, cardinputid, inputgroupname");
2121 query.bindValue(":GROUPID", inputgroupid);
2122
2123 if (!query.exec())
2124 {
2125 MythDB::DBError("CardUtil::LinkInputGroup() 1", query);
2126 return false;
2127 }
2128
2129 QString name;
2130 while (query.next()) {
2131 name = query.value(2).toString();
2132 uint cardid = query.value(0).toUInt();
2133 // Already linked
2134 if (cardid == inputid)
2135 return true;
2136 }
2137
2138 // Invalid group id
2139 if (name.isEmpty())
2140 return false;
2141
2142 query.prepare(
2143 "INSERT INTO inputgroup "
2144 " (cardinputid, inputgroupid, inputgroupname) "
2145 "VALUES (:INPUTID, :GROUPID, :GROUPNAME ) ");
2146
2147 query.bindValue(":INPUTID", inputid);
2148 query.bindValue(":GROUPID", inputgroupid);
2149 query.bindValue(":GROUPNAME", name);
2150
2151 if (!query.exec())
2152 {
2153 MythDB::DBError("CardUtil::LinkInputGroup() 2", query);
2154 return false;
2155 }
2156
2157 // Now that there is a proper linkage, unlink temporary cardid 0
2158 UnlinkInputGroup(0, inputgroupid);
2159
2160 return true;
2161}
2162
2163bool CardUtil::UnlinkInputGroup(uint inputid, uint inputgroupid)
2164{
2166
2167 if (!inputid && !inputgroupid)
2168 {
2169 query.prepare(
2170 "DELETE FROM inputgroup "
2171 "WHERE cardinputid NOT IN "
2172 "( SELECT cardid FROM capturecard )");
2173 }
2174 else
2175 {
2176 query.prepare(
2177 "DELETE FROM inputgroup "
2178 "WHERE cardinputid = :INPUTID AND "
2179 " inputgroupid = :GROUPID ");
2180
2181 query.bindValue(":INPUTID", inputid);
2182 query.bindValue(":GROUPID", inputgroupid);
2183 }
2184
2185 if (!query.exec())
2186 {
2187 MythDB::DBError("CardUtil::DeleteInputGroup()", query);
2188 return false;
2189 }
2190
2191 return true;
2192}
2193
2194std::vector<uint> CardUtil::GetInputGroups(uint inputid)
2195{
2196 std::vector<uint> list;
2197
2199
2200 query.prepare(
2201 "SELECT inputgroupid "
2202 "FROM inputgroup "
2203 "WHERE cardinputid = :INPUTID "
2204 "ORDER BY inputgroupid, cardinputid, inputgroupname");
2205
2206 query.bindValue(":INPUTID", inputid);
2207
2208 if (!query.exec())
2209 {
2210 MythDB::DBError("CardUtil::GetInputGroups()", query);
2211 return list;
2212 }
2213
2214 while (query.next())
2215 list.push_back(query.value(0).toUInt());
2216
2217 return list;
2218}
2219
2220std::vector<uint> CardUtil::GetGroupInputIDs(uint inputgroupid)
2221{
2222 std::vector<uint> list;
2223
2225
2226 query.prepare(
2227 "SELECT DISTINCT cardid "
2228 "FROM capturecard, inputgroup "
2229 "WHERE inputgroupid = :GROUPID AND "
2230 " capturecard.cardid = inputgroup.cardinputid "
2231 "ORDER BY cardid");
2232
2233 query.bindValue(":GROUPID", inputgroupid);
2234
2235 if (!query.exec())
2236 {
2237 MythDB::DBError("CardUtil::GetGroupInputIDs()", query);
2238 return list;
2239 }
2240
2241 while (query.next())
2242 list.push_back(query.value(0).toUInt());
2243
2244 return list;
2245}
2246
2247std::vector<uint> CardUtil::GetConflictingInputs(uint inputid)
2248{
2249 std::vector<uint> inputids;
2250
2252
2253 query.prepare(
2254 "SELECT DISTINCT c.cardid "
2255 "FROM ( "
2256 " SELECT inputgroupid "
2257 " FROM inputgroup "
2258 " WHERE cardinputid = :INPUTID1 "
2259 ") g "
2260 "JOIN inputgroup ig ON ig.inputgroupid = g.inputgroupid "
2261 "JOIN capturecard c ON c.cardid = ig.cardinputid "
2262 " AND c.cardid <> :INPUTID2 "
2263 " AND c.sourceid > 0 "
2264 "ORDER BY c.cardid");
2265
2266 query.bindValue(":INPUTID1", inputid);
2267 query.bindValue(":INPUTID2", inputid);
2268
2269 if (!query.exec())
2270 {
2271 MythDB::DBError("CardUtil::GetConflictingInputs()", query);
2272 return inputids;
2273 }
2274
2275 while (query.next())
2276 {
2277 inputids.push_back(query.value(0).toUInt());
2278 }
2279
2280 if (VERBOSE_LEVEL_CHECK(VB_RECORD, LOG_INFO))
2281 {
2282 QString msg = QString("CardUtil[%1]: GetConflictingInputs(%1) ").arg(inputid);
2283 QStringList ids;
2284 for (auto id : inputids)
2285 {
2286 ids.append(QString::number(id));
2287 }
2288 msg.append(ids.join(','));
2289 LOG(VB_RECORD, LOG_INFO, msg);
2290 }
2291 return inputids;
2292}
2293
2295 std::chrono::milliseconds &signal_timeout,
2296 std::chrono::milliseconds &channel_timeout)
2297{
2299 query.prepare(
2300 "SELECT signal_timeout, channel_timeout "
2301 "FROM capturecard "
2302 "WHERE cardid = :INPUTID");
2303 query.bindValue(":INPUTID", inputid);
2304
2305 if (!query.exec() || !query.isActive())
2306 MythDB::DBError("CardUtil::GetTimeouts()", query);
2307 else if (query.next())
2308 {
2309 signal_timeout = std::max(std::chrono::milliseconds(query.value(0).toInt()), 250ms);
2310 channel_timeout = std::max(std::chrono::milliseconds(query.value(1).toInt()), 500ms);
2311 return true;
2312 }
2313
2314 return false;
2315}
2316
2318{
2319 DiSEqCDevTree *diseqc_tree = DiSEqCDev::FindTree(inputid);
2320
2321 bool needsConf = false;
2322 if (diseqc_tree)
2323 needsConf = diseqc_tree->IsInNeedOfConf();
2324
2325 return needsConf;
2326}
2327
2328uint CardUtil::GetQuickTuning(uint inputid, const QString &input_name)
2329{
2330 uint quicktune = 0;
2331
2333 query.prepare(
2334 "SELECT quicktune "
2335 "FROM capturecard "
2336 "WHERE cardid = :INPUTID AND "
2337 " inputname = :INPUTNAME");
2338 query.bindValue(":INPUTID", inputid);
2339 query.bindValue(":INPUTNAME", input_name);
2340
2341 if (!query.exec() || !query.isActive())
2342 MythDB::DBError("CardUtil::GetQuickTuning()", query);
2343 else if (query.next())
2344 quicktune = query.value(0).toUInt();
2345
2346 return quicktune;
2347}
2348
2349bool CardUtil::hasV4L2([[maybe_unused]] int videofd)
2350{
2351#if CONFIG_V4L2
2352 struct v4l2_capability vcap {};
2353
2354 return ((ioctl(videofd, VIDIOC_QUERYCAP, &vcap) >= 0) &&
2355 ((vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE) != 0U));
2356#else // if !CONFIG_V4L2
2357 return false;
2358#endif // !CONFIG_V4L2
2359}
2360
2362 int videofd, QString &input, QString &driver, uint32_t &version,
2363 uint32_t &capabilities)
2364{
2365 input.clear();
2366 driver.clear();
2367 version = 0;
2368 capabilities = 0;
2369
2370 if (videofd < 0)
2371 return false;
2372
2373#if CONFIG_V4L2
2374 struct v4l2_capability capability {};
2375 if (ioctl(videofd, VIDIOC_QUERYCAP, &capability) >= 0)
2376 {
2377 input = QString::fromLatin1((const char*)capability.card);
2378 driver = QString::fromLatin1((const char*)capability.driver);
2379 version = capability.version;
2380 capabilities = capability.capabilities;
2381 }
2382#endif // CONFIG_V4L2
2383
2384 static const QRegularExpression kBracketedDigitRE { R"(\[[0-9]\]$)" };
2385 if (!driver.isEmpty())
2386 driver.remove( kBracketedDigitRE );
2387
2388 return !input.isEmpty();
2389}
2390
2391InputNames CardUtil::ProbeV4LVideoInputs([[maybe_unused]] int videofd, bool &ok)
2392{
2393 InputNames list;
2394 ok = false;
2395
2396#if CONFIG_V4L2
2397 bool usingv4l2 = hasV4L2(videofd);
2398
2399 struct v4l2_input vin {};
2400 while (usingv4l2 && (ioctl(videofd, VIDIOC_ENUMINPUT, &vin) >= 0))
2401 {
2402 QString input((char *)vin.name);
2403 list[vin.index] = input;
2404 vin.index++;
2405 }
2406 if (vin.index)
2407 {
2408 ok = true;
2409 return list;
2410 }
2411
2412 // Create an input when none are advertised
2413 if (list.isEmpty())
2414 list[0] = "Television";
2415
2416 ok = true;
2417#else // if !CONFIG_V4L2
2418 list[-1] += QObject::tr("ERROR, Compile with V4L support to query inputs");
2419#endif // !CONFIG_V4L2
2420 return list;
2421}
2422
2423InputNames CardUtil::ProbeV4LAudioInputs([[maybe_unused]] int videofd, bool &ok)
2424{
2425 InputNames list;
2426 ok = false;
2427
2428#if CONFIG_V4L2
2429 bool usingv4l2 = hasV4L2(videofd);
2430
2431 // V4L v2 query
2432 struct v4l2_audio ain {};
2433 while (usingv4l2 && (ioctl(videofd, VIDIOC_ENUMAUDIO, &ain) >= 0))
2434 {
2435 QString input((char *)ain.name);
2436 list[ain.index] = input;
2437 ain.index++;
2438 }
2439 if (ain.index)
2440 {
2441 ok = true;
2442 return list;
2443 }
2444
2445 ok = true;
2446#else // if !CONFIG_V4L2
2447 list[-1] += QObject::tr(
2448 "ERROR, Compile with V4L support to query audio inputs");
2449#endif // !CONFIG_V4L2
2450 return list;
2451}
2452
2454{
2455 InputNames list;
2457 query.prepare(
2458 "SELECT cardid, inputname "
2459 "FROM capturecard "
2460 "WHERE hostname = :HOSTNAME "
2461 " AND videodevice = :DEVICE "
2462 " AND parentid = 0 "
2463 " AND inputname <> 'None'");
2464 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
2465 query.bindValue(":DEVICE", device);
2466
2467 if (!query.exec() || !query.isActive())
2468 MythDB::DBError("CardUtil::GetConfiguredDVBInputs", query);
2469 else
2470 {
2471 while (query.next())
2472 list[query.value(0).toUInt()] = query.value(1).toString();
2473 }
2474 return list;
2475}
2476
2477// Convert DVBv5 frontend capabilities from bitmask to string
2478//
2479// Source linuxtv.org v4l-utils/lib/libdvbv5/dvb-v5.c
2480//
2481QStringList CardUtil::CapabilitiesToString([[maybe_unused]] uint64_t capabilities)
2482{
2483 QStringList caps;
2484#if CONFIG_DVB
2485
2486 struct fe_caps_name {
2487 unsigned idx;
2488 const char *name;
2489 };
2490
2491 std::array<fe_caps_name,31> fe_caps_name {{
2492 { FE_CAN_2G_MODULATION, "CAN_2G_MODULATION" },
2493 { FE_CAN_8VSB, "CAN_8VSB" },
2494 { FE_CAN_16VSB, "CAN_16VSB" },
2495 { FE_CAN_BANDWIDTH_AUTO, "CAN_BANDWIDTH_AUTO" },
2496 { FE_CAN_FEC_1_2, "CAN_FEC_1_2" },
2497 { FE_CAN_FEC_2_3, "CAN_FEC_2_3" },
2498 { FE_CAN_FEC_3_4, "CAN_FEC_3_4" },
2499 { FE_CAN_FEC_4_5, "CAN_FEC_4_5" },
2500 { FE_CAN_FEC_5_6, "CAN_FEC_5_6" },
2501 { FE_CAN_FEC_6_7, "CAN_FEC_6_7" },
2502 { FE_CAN_FEC_7_8, "CAN_FEC_7_8" },
2503 { FE_CAN_FEC_8_9, "CAN_FEC_8_9" },
2504 { FE_CAN_FEC_AUTO, "CAN_FEC_AUTO" },
2505 { FE_CAN_GUARD_INTERVAL_AUTO, "CAN_GUARD_INTERVAL_AUTO" },
2506 { FE_CAN_HIERARCHY_AUTO, "CAN_HIERARCHY_AUTO" },
2507 { FE_CAN_INVERSION_AUTO, "CAN_INVERSION_AUTO" },
2508 { FE_CAN_MULTISTREAM, "CAN_MULTISTREAM" },
2509 { FE_CAN_MUTE_TS, "CAN_MUTE_TS" },
2510 { FE_CAN_QAM_16, "CAN_QAM_16" },
2511 { FE_CAN_QAM_32, "CAN_QAM_32" },
2512 { FE_CAN_QAM_64, "CAN_QAM_64" },
2513 { FE_CAN_QAM_128, "CAN_QAM_128" },
2514 { FE_CAN_QAM_256, "CAN_QAM_256" },
2515 { FE_CAN_QAM_AUTO, "CAN_QAM_AUTO" },
2516 { FE_CAN_QPSK, "CAN_QPSK" },
2517 { FE_CAN_RECOVER, "CAN_RECOVER" },
2518 { FE_CAN_TRANSMISSION_MODE_AUTO, "CAN_TRANSMISSION_MODE_AUTO" },
2519 { FE_CAN_TURBO_FEC, "CAN_TURBO_FEC" },
2520 { FE_HAS_EXTENDED_CAPS, "HAS_EXTENDED_CAPS" },
2521 { FE_IS_STUPID, "IS_STUPID" },
2522 { FE_NEEDS_BENDING, "NEEDS_BENDING" },
2523 }};
2524
2525 for (const auto & cap : fe_caps_name)
2526 {
2527 if (capabilities & cap.idx)
2528 caps.append(cap.name);
2529 }
2530#endif // CONFIG_DVB
2531 return caps;
2532}
2533
2534QStringList CardUtil::ProbeVideoInputs(const QString& device, const QString& inputtype)
2535{
2536 QStringList ret;
2537
2538 if (IsSingleInputType(inputtype))
2539 ret += "MPEG2TS";
2540 else if ("DVB" == inputtype)
2541 ret += ProbeDVBInputs(device);
2542 else
2543 ret += ProbeV4LVideoInputs(device);
2544
2545 return ret;
2546}
2547
2548QStringList CardUtil::ProbeAudioInputs(const QString& device, const QString& inputtype)
2549{
2550 LOG(VB_GENERAL, LOG_DEBUG, QString("ProbeAudioInputs(%1,%2)")
2551 .arg(device, inputtype));
2552 QStringList ret;
2553
2554 if ("HDPVR" == inputtype ||
2555 "V4L2" == inputtype)
2556 ret += ProbeV4LAudioInputs(device);
2557
2558 return ret;
2559}
2560
2561QStringList CardUtil::ProbeV4LVideoInputs(const QString& device)
2562{
2563 bool ok = false;
2564 QStringList ret;
2565 QByteArray dev = device.toLatin1();
2566 int videofd = open(dev.constData(), O_RDWR);
2567 if (videofd < 0)
2568 {
2569 ret += QObject::tr("Could not open '%1' "
2570 "to probe its inputs.").arg(device);
2571 return ret;
2572 }
2573 InputNames list = CardUtil::ProbeV4LVideoInputs(videofd, ok);
2574 close(videofd);
2575
2576 if (!ok)
2577 {
2578 ret += list[-1];
2579 return ret;
2580 }
2581
2582 InputNames::iterator it;
2583 for (it = list.begin(); it != list.end(); ++it)
2584 {
2585 if (it.key() >= 0)
2586 ret += *it;
2587 }
2588
2589 return ret;
2590}
2591
2592QStringList CardUtil::ProbeV4LAudioInputs(const QString& device)
2593{
2594 LOG(VB_GENERAL, LOG_DEBUG, QString("ProbeV4LAudioInputs(%1)").arg(device));
2595
2596 bool ok = false;
2597 QStringList ret;
2598 int videofd = open(device.toLatin1().constData(), O_RDWR);
2599 if (videofd < 0)
2600 {
2601 LOG(VB_GENERAL, LOG_ERR, "ProbeAudioInputs() -> couldn't open device");
2602 ret += QObject::tr("Could not open '%1' to probe its inputs.")
2603 .arg(device);
2604 return ret;
2605 }
2606 InputNames list = CardUtil::ProbeV4LAudioInputs(videofd, ok);
2607 close(videofd);
2608
2609 if (!ok)
2610 {
2611 ret += list[-1];
2612 return ret;
2613 }
2614
2615 InputNames::iterator it;
2616 for (it = list.begin(); it != list.end(); ++it)
2617 {
2618 if (it.key() >= 0)
2619 ret += *it;
2620 }
2621
2622 return ret;
2623}
2624
2625QStringList CardUtil::ProbeDVBInputs([[maybe_unused]] const QString& device)
2626{
2627 QStringList ret;
2628
2629#if CONFIG_DVB
2630 InputNames list = GetConfiguredDVBInputs(device);
2631 InputNames::iterator it;
2632 for (it = list.begin(); it != list.end(); ++it)
2633 {
2634 if (it.key())
2635 ret += *it;
2636 }
2637#else
2638 ret += QObject::tr("ERROR, Compile with DVB support to query inputs");
2639#endif
2640
2641 return ret;
2642}
2643
2644QString CardUtil::GetDeviceLabel(const QString &inputtype,
2645 const QString &videodevice)
2646{
2647 return QString("[ %1 : %2 ]").arg(inputtype, videodevice);
2648}
2649
2651{
2653 query.prepare("SELECT cardtype, videodevice "
2654 "FROM capturecard WHERE cardid = :INPUTID ");
2655 query.bindValue(":INPUTID", inputid);
2656
2657 if (query.exec() && query.next())
2658 {
2659 return GetDeviceLabel(query.value(0).toString(),
2660 query.value(1).toString());
2661 }
2662
2663 return "[ UNKNOWN ]";
2664}
2665
2667 const QString &device,
2668 const QString &inputtype,
2669 QStringList &inputs)
2670{
2671 inputs.clear();
2672 if (IsSingleInputType(inputtype))
2673 inputs += "MPEG2TS";
2674 else if (inputtype == "DVB")
2675 inputs += "DVBInput";
2676 else
2677 inputs += ProbeV4LVideoInputs(device);
2678}
2679
2680int CardUtil::CreateCaptureCard(const QString &videodevice,
2681 const QString &audiodevice,
2682 const QString &vbidevice,
2683 const QString &inputtype,
2684 const uint audioratelimit,
2685 const QString &hostname,
2686 const uint dvb_swfilter,
2687 const uint dvb_sat_type,
2688 bool dvb_wait_for_seqstart,
2689 bool skipbtaudio,
2690 bool dvb_on_demand,
2691 const uint dvb_diseqc_type,
2692 const uint firewire_speed,
2693 const QString &firewire_model,
2694 const uint firewire_connection,
2695 const std::chrono::milliseconds signal_timeout,
2696 const std::chrono::milliseconds channel_timeout,
2697 const uint dvb_tuning_delay,
2698 const uint contrast,
2699 const uint brightness,
2700 const uint colour,
2701 const uint hue,
2702 const uint diseqcid,
2703 bool dvb_eitscan)
2704{
2706
2707 query.prepare(
2708 "INSERT INTO capturecard "
2709 "(videodevice, audiodevice, vbidevice, cardtype, "
2710 "audioratelimit, hostname, dvb_swfilter, dvb_sat_type, "
2711 "dvb_wait_for_seqstart, skipbtaudio, dvb_on_demand, dvb_diseqc_type, "
2712 "firewire_speed, firewire_model, firewire_connection, signal_timeout, "
2713 "channel_timeout, dvb_tuning_delay, contrast, brightness, colour, "
2714 "hue, diseqcid, dvb_eitscan) "
2715 "VALUES (:VIDEODEVICE, :AUDIODEVICE, :VBIDEVICE, :INPUTTYPE, "
2716 ":AUDIORATELIMIT, :HOSTNAME, :DVBSWFILTER, :DVBSATTYPE, "
2717 ":DVBWAITFORSEQSTART, :SKIPBTAUDIO, :DVBONDEMAND, :DVBDISEQCTYPE, "
2718 ":FIREWIRESPEED, :FIREWIREMODEL, :FIREWIRECONNECTION, :SIGNALTIMEOUT, "
2719 ":CHANNELTIMEOUT, :DVBTUNINGDELAY, :CONTRAST, :BRIGHTNESS, :COLOUR, "
2720 ":HUE, :DISEQCID, :DVBEITSCAN ) ");
2721
2722 query.bindValue(":VIDEODEVICE", videodevice);
2723 if (audiodevice.length() == 0) // Empty string is set to null
2724 {
2725#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2726 query.bindValue(":AUDIODEVICE", QVariant(QVariant::String));
2727#else
2728 query.bindValue(":AUDIODEVICE", QVariant(QMetaType(QMetaType::QString)));
2729#endif
2730 }
2731 else
2732 {
2733 query.bindValue(":AUDIODEVICE", audiodevice);
2734 }
2735 if (vbidevice.length() == 0) // Empty string is set to null
2736 {
2737#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2738 query.bindValue(":VBIDEVICE", QVariant(QVariant::String));
2739#else
2740 query.bindValue(":VBIDEVICE", QVariant(QMetaType(QMetaType::QString)));
2741#endif
2742 }
2743 else
2744 {
2745 query.bindValue(":VBIDEVICE", vbidevice);
2746 }
2747 query.bindValue(":INPUTTYPE", inputtype);
2748 if (audioratelimit == 0) // Value 0 is set to null
2749 {
2750#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2751 query.bindValue(":AUDIORATELIMIT", QVariant(QVariant::UInt));
2752#else
2753 query.bindValue(":AUDIORATELIMIT", QVariant(QMetaType(QMetaType::UInt)));
2754#endif
2755 }
2756 else
2757 {
2758 query.bindValue(":AUDIORATELIMIT", audioratelimit);
2759 }
2760 query.bindValue(":HOSTNAME", hostname);
2761 query.bindValue(":DVBSWFILTER", dvb_swfilter);
2762 query.bindValue(":DVBSATTYPE", dvb_sat_type);
2763 query.bindValue(":DVBWAITFORSEQSTART", dvb_wait_for_seqstart);
2764 query.bindValue(":SKIPBTAUDIO", skipbtaudio);
2765 query.bindValue(":DVBONDEMAND", dvb_on_demand);
2766 query.bindValue(":DVBDISEQCTYPE", dvb_diseqc_type);
2767 query.bindValue(":FIREWIRESPEED", firewire_speed);
2768 if (firewire_model.length() == 0) // Empty string is set to null
2769 {
2770#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2771 query.bindValue(":FIREWIREMODEL", QVariant(QVariant::String));
2772#else
2773 query.bindValue(":FIREWIREMODEL", QVariant(QMetaType(QMetaType::QString)));
2774#endif
2775 }
2776 else
2777 {
2778 query.bindValue(":FIREWIREMODEL", firewire_model);
2779 }
2780 query.bindValue(":FIREWIRECONNECTION", firewire_connection);
2781 query.bindValue(":SIGNALTIMEOUT", static_cast<qint64>(signal_timeout.count()));
2782 query.bindValue(":CHANNELTIMEOUT", static_cast<qint64>(channel_timeout.count()));
2783 query.bindValue(":DVBTUNINGDELAY", dvb_tuning_delay);
2784 query.bindValue(":CONTRAST", contrast);
2785 query.bindValue(":BRIGHTNESS", brightness);
2786 query.bindValue(":COLOUR", colour);
2787 query.bindValue(":HUE", hue);
2788 if (diseqcid == 0) // Value 0 is set to null
2789 {
2790#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2791 query.bindValue(":DISEQCID", QVariant(QVariant::UInt));
2792#else
2793 query.bindValue(":DISEQCID", QVariant(QMetaType(QMetaType::UInt)));
2794#endif
2795 }
2796 else
2797 {
2798 query.bindValue(":DISEQCID", diseqcid);
2799 }
2800 query.bindValue(":DVBEITSCAN", dvb_eitscan);
2801
2802 if (!query.exec())
2803 {
2804 MythDB::DBError("CreateCaptureCard", query);
2805 return -1;
2806 }
2807
2808 query.prepare("SELECT MAX(cardid) FROM capturecard");
2809
2810 if (!query.exec())
2811 {
2812 MythDB::DBError("CreateCaptureCard maxinput", query);
2813 return -1;
2814 }
2815
2816 int inputid = -1; /* must be int not uint because of return type. */
2817
2818 if (query.next())
2819 {
2820 inputid = query.value(0).toInt();
2821 uint groupid = CardUtil::CreateDeviceInputGroup(inputid, inputtype,
2822 hostname, videodevice);
2823 CardUtil::LinkInputGroup(inputid, groupid);
2824 }
2825
2826 return inputid;
2827}
2828
2830{
2831 std::vector<uint> childids = GetChildInputIDs(inputid);
2832 for (uint childid : childids)
2833 {
2834 if (!DeleteInput(childid))
2835 {
2836 LOG(VB_GENERAL, LOG_ERR, LOC +
2837 QString("CardUtil: Failed to delete child input %1")
2838 .arg(childid));
2839 return false;
2840 }
2841 }
2842
2844
2845 DiSEqCDevTree tree;
2846 tree.Load(inputid);
2847
2848 // Delete the capturecard row for this input
2849 query.prepare("DELETE FROM capturecard WHERE cardid = :INPUTID");
2850 query.bindValue(":INPUTID", inputid);
2851 if (!query.exec())
2852 {
2853 MythDB::DBError("DeleteCard -- delete capturecard", query);
2854 return false;
2855 }
2856
2857 // Update the reclimit of the parent input
2858 query.prepare("UPDATE capturecard SET reclimit=reclimit-1 "
2859 "WHERE cardid = :INPUTID");
2860 query.bindValue(":INPUTID", inputid);
2861 if (!query.exec())
2862 {
2863 MythDB::DBError("DeleteCard -- update capturecard", query);
2864 return false;
2865 }
2866
2867 // Delete the inputgroup rows for this input
2868 query.prepare("DELETE FROM inputgroup WHERE cardinputid = :INPUTID");
2869 query.bindValue(":INPUTID", inputid);
2870 if (!query.exec())
2871 {
2872 MythDB::DBError("DeleteCard -- delete inputgroup", query);
2873 return false;
2874 }
2875
2876 // Delete the diseqc tree if no more inputs reference it.
2877 if (tree.Root())
2878 {
2879 query.prepare("SELECT cardid FROM capturecard "
2880 "WHERE diseqcid = :DISEQCID LIMIT 1");
2881 query.bindValue(":DISEQCID", tree.Root()->GetDeviceID());
2882 if (!query.exec())
2883 {
2884 MythDB::DBError("DeleteCard -- find diseqc tree", query);
2885 }
2886 else if (!query.next())
2887 {
2888 tree.SetRoot(nullptr);
2889 tree.Store(inputid);
2890 }
2891 }
2892
2893 // Delete any unused input groups
2894 UnlinkInputGroup(0, 0);
2895
2896 return true;
2897}
2898
2900{
2902 return (query.exec("TRUNCATE TABLE inputgroup") &&
2903 query.exec("TRUNCATE TABLE diseqc_config") &&
2904 query.exec("TRUNCATE TABLE diseqc_tree") &&
2905 query.exec("TRUNCATE TABLE capturecard"));
2906}
2907
2908std::vector<uint> CardUtil::GetInputList(void)
2909{
2910 std::vector<uint> list;
2911
2913 query.prepare(
2914 "SELECT cardid "
2915 "FROM capturecard "
2916 "ORDER BY cardid");
2917
2918 if (!query.exec())
2919 MythDB::DBError("CardUtil::GetInputList()", query);
2920 else
2921 {
2922 while (query.next())
2923 list.push_back(query.value(0).toUInt());
2924 }
2925
2926 return list;
2927}
2928
2929std::vector<uint> CardUtil::GetSchedInputList(void)
2930{
2931 std::vector<uint> list;
2932
2934 query.prepare(
2935 "SELECT DISTINCT cardid "
2936 "FROM capturecard "
2937 "WHERE schedorder <> 0 "
2938 "ORDER BY schedorder, cardid");
2939
2940 if (!query.exec())
2941 MythDB::DBError("CardUtil::GetSchedInputList()", query);
2942 else
2943 {
2944 while (query.next())
2945 list.push_back(query.value(0).toUInt());
2946 }
2947
2948 return list;
2949}
2950
2951std::vector<uint> CardUtil::GetLiveTVInputList(void)
2952{
2953 std::vector<uint> list;
2954
2956 query.prepare(
2957 "SELECT DISTINCT cardid "
2958 "FROM capturecard "
2959 "WHERE livetvorder <> 0 "
2960 "ORDER BY livetvorder, cardid");
2961
2962 if (!query.exec())
2963 MythDB::DBError("CardUtil::GetLiveTVInputList()", query);
2964 else
2965 {
2966 while (query.next())
2967 list.push_back(query.value(0).toUInt());
2968 }
2969
2970 return list;
2971}
2972
2973QString CardUtil::GetDeviceName(dvb_dev_type_t type, const QString &device)
2974{
2975 QString devname = QString(device);
2976#if 0
2977 LOG(VB_RECORD, LOG_DEBUG, LOC + QString("DVB Device (%1)").arg(devname));
2978#endif
2979
2980 if (DVB_DEV_FRONTEND == type)
2981 {
2982 return devname;
2983 }
2984
2985 if (DVB_DEV_DVR == type)
2986 {
2987 QString tmp = devname;
2988 tmp = tmp.replace(tmp.indexOf("frontend"), 8, "dvr");
2989 if (QFile::exists(tmp))
2990 {
2991 LOG(VB_RECORD, LOG_DEBUG, LOC +
2992 QString("Adapter Frontend dvr number matches (%1)").arg(tmp));
2993 return tmp;
2994 }
2995
2996 // Use dvr0, allows multi-standard frontends which only have one dvr
2997 tmp = devname;
2998 tmp = tmp.replace(tmp.indexOf("frontend"), 9, "dvr0");
2999 if (QFile::exists(tmp))
3000 {
3001 LOG(VB_RECORD, LOG_DEBUG, LOC +
3002 QString("Adapter Frontend dvr number not matching, using dvr0 instead (%1)").arg(tmp));
3003 return tmp;
3004 }
3005
3006 LOG(VB_RECORD, LOG_DEBUG, LOC +
3007 QString("Adapter Frontend no dvr device found for (%1)").arg(devname));
3008 return "";
3009 }
3010
3011 if (DVB_DEV_DEMUX == type)
3012 {
3013 QString tmp = devname;
3014 tmp = tmp.replace(tmp.indexOf("frontend"), 8, "demux");
3015 if (QFile::exists(tmp))
3016 {
3017 LOG(VB_RECORD, LOG_DEBUG, LOC +
3018 QString("Adapter Frontend demux number matches (%1)").arg(tmp));
3019 return tmp;
3020 }
3021
3022 // Use demux0, allows multi-standard frontends, which only have one demux
3023 tmp = devname;
3024 tmp = tmp.replace(tmp.indexOf("frontend"), 9, "demux0");
3025 if (QFile::exists(tmp))
3026 {
3027 LOG(VB_RECORD, LOG_DEBUG, LOC +
3028 QString("Adapter Frontend demux number not matching, using demux0 instead (%1)").arg(tmp));
3029 return tmp;
3030 }
3031
3032 LOG(VB_RECORD, LOG_DEBUG, LOC +
3033 QString("Adapter Frontend no demux device found for (%1)").arg(devname));
3034 return "";
3035 }
3036
3037 if (DVB_DEV_CA == type)
3038 {
3039 QString tmp = devname;
3040 tmp = tmp.replace(tmp.indexOf("frontend"), 8, "ca");
3041 if (QFile::exists(tmp))
3042 {
3043 LOG(VB_RECORD, LOG_DEBUG, LOC +
3044 QString("Adapter Frontend ca number matches (%1)").arg(tmp));
3045 return tmp;
3046 }
3047
3048 // Use ca0, allows multi-standard frontends, which only have one ca
3049 tmp = devname;
3050 tmp = tmp.replace(tmp.indexOf("frontend"), 9, "ca0");
3051 if (QFile::exists(tmp))
3052 {
3053 LOG(VB_RECORD, LOG_DEBUG, LOC +
3054 QString("Adapter Frontend ca number not matching, using ca0 instead (%1)").arg(tmp));
3055 return tmp;
3056 }
3057
3058 LOG(VB_RECORD, LOG_DEBUG, LOC +
3059 QString("Adapter Frontend no ca device found for (%1)").arg(devname));
3060 return "";
3061 }
3062
3063 if (DVB_DEV_AUDIO == type)
3064 {
3065 return devname.replace(devname.indexOf("frontend"), 8, "audio");
3066 }
3067
3068 if (DVB_DEV_VIDEO == type)
3069 {
3070 return devname.replace(devname.indexOf("frontend"), 8, "video");
3071 }
3072
3073 return "";
3074}
3075
3076
3086bool CardUtil::HDHRdoesDVB([[maybe_unused]] const QString &device)
3087{
3088#if CONFIG_HDHOMERUN
3089 hdhomerun_device_t *hdhr =
3090 hdhomerun_device_create_from_str(device.toLatin1().constData(), nullptr);
3091 if (!hdhr)
3092 return false;
3093
3094 const char *model = hdhomerun_device_get_model_str(hdhr);
3095 if (model && strstr(model, "dvb"))
3096 {
3097 hdhomerun_device_destroy(hdhr);
3098 return true;
3099 }
3100
3101 hdhomerun_device_destroy(hdhr);
3102
3103#endif
3104
3105 return false;
3106}
3107
3112bool CardUtil::HDHRdoesDVBC([[maybe_unused]] const QString &device)
3113{
3114#if CONFIG_HDHOMERUN
3115 hdhomerun_device_t *hdhr =
3116 hdhomerun_device_create_from_str(device.toLatin1().constData(), nullptr);
3117 if (!hdhr)
3118 return false;
3119
3120 const char *model = hdhomerun_device_get_model_str(hdhr);
3121 if (model && strstr(model, "dvbc"))
3122 {
3123 hdhomerun_device_destroy(hdhr);
3124 return true;
3125 }
3126
3127 hdhomerun_device_destroy(hdhr);
3128
3129#endif
3130
3131 return false;
3132}
3133
3138QString CardUtil::GetHDHRdesc([[maybe_unused]] const QString &device)
3139{
3140 QString connectErr = QObject::tr("Unable to connect to device.");
3141
3142#if CONFIG_HDHOMERUN
3143 [[maybe_unused]] bool deviceIsIP = false;
3144
3145 if (device.contains('.')) // Simplistic check, but also allows DNS names
3146 deviceIsIP = true;
3147 else
3148 {
3149 bool validID = false;
3150
3151 uint32_t dev = device.toUInt(&validID, 16);
3152 if (!validID || !hdhomerun_discover_validate_device_id(dev))
3153 return QObject::tr("Invalid Device ID");
3154 }
3155
3156 LOG(VB_GENERAL, LOG_INFO, "CardUtil::GetHDHRdescription(" + device +
3157 ") - trying to locate device");
3158
3159 hdhomerun_device_t *hdhr =
3160 hdhomerun_device_create_from_str(device.toLatin1().constData(), nullptr);
3161 if (!hdhr)
3162 return QObject::tr("Invalid Device ID or address.");
3163
3164 const char *model = hdhomerun_device_get_model_str(hdhr);
3165 if (!model)
3166 {
3167 hdhomerun_device_destroy(hdhr);
3168 return connectErr;
3169 }
3170
3171
3172 QString description = model;
3173 char *sVersion = nullptr;
3174 uint32_t iVersion = 0;
3175
3176 if (hdhomerun_device_get_version(hdhr, &sVersion, &iVersion))
3177 description += QObject::tr(", firmware: %2").arg(sVersion);
3178
3179 hdhomerun_device_destroy(hdhr);
3180
3181 return description;
3182#else
3183 return connectErr;
3184#endif
3185}
3186
3191QString CardUtil::GetVBoxdesc([[maybe_unused]] const QString &id,
3192 [[maybe_unused]] const QString &ip,
3193 [[maybe_unused]] const QString &tunerNo,
3194 [[maybe_unused]] const QString &tunerType)
3195{
3196 QString connectErr = QObject::tr("Unable to connect to device.");
3197
3198#if CONFIG_VBOX
3199 VBox *vbox = new VBox(ip);
3200
3201 if (!vbox->checkConnection())
3202 {
3203 delete vbox;
3204 return connectErr;
3205 }
3206
3207 QString version;
3208
3209 if (!vbox->checkVersion(version))
3210 {
3211 QString apiVersionErr = QObject::tr("The VBox software version is too old (%1), we require %2")
3213 delete vbox;
3214 return apiVersionErr;
3215
3216 }
3217
3218 delete vbox;
3219
3220 return QString("V@Box TV Gateway - ID: %1, IP: %2, Tuner: %3-%4")
3221 .arg(id, ip, tunerNo, tunerType);
3222
3223#else
3224 return connectErr;
3225#endif
3226}
3227
3228#if CONFIG_ASI
3229static QString sys_dev(uint device_num, const QString& dev)
3230{
3231 return QString("/sys/class/asi/asirx%1/%2").arg(device_num).arg(dev);
3232}
3233
3234static QString read_sys(const QString& sys_dev)
3235{
3236 QFile f(sys_dev);
3237 if (!f.open(QIODevice::ReadOnly))
3238 return {};
3239 QByteArray sdba = f.readAll();
3240 f.close();
3241 return sdba;
3242}
3243
3244static bool write_sys(const QString& sys_dev, const QString& str)
3245{
3246 QFile f(sys_dev);
3247 if (!f.open(QIODevice::WriteOnly))
3248 return false;
3249 QByteArray ba = str.toLocal8Bit();
3250 qint64 offset = 0;
3251 for (uint tries = 0; (offset < ba.size()) && tries < 5; tries++)
3252 {
3253 qint64 written = f.write(ba.data()+offset, ba.size()-offset);
3254 if (written < 0)
3255 return false;
3256 offset += written;
3257 }
3258 return true;
3259}
3260#endif
3261
3262int CardUtil::GetASIDeviceNumber([[maybe_unused]] const QString &device, QString *error)
3263{
3264#if CONFIG_ASI
3265 // basic confirmation
3266 struct stat statbuf {};
3267 if (stat(device.toLocal8Bit().constData(), &statbuf) < 0)
3268 {
3269 if (error)
3270 *error = QString("Unable to stat '%1'").arg(device) + ENO;
3271 return -1;
3272 }
3273
3274 if (!S_ISCHR(statbuf.st_mode))
3275 {
3276 if (error)
3277 *error = QString("'%1' is not a character device").arg(device);
3278 return -1;
3279 }
3280
3281 if (!(statbuf.st_rdev & 0x0080))
3282 {
3283 if (error)
3284 *error = QString("'%1' not a DVEO ASI receiver").arg(device);
3285 return -1;
3286 }
3287
3288 int device_num = statbuf.st_rdev & 0x007f;
3289
3290 // extra confirmation
3291 QString sys_dev_contents = read_sys(sys_dev(device_num, "dev"));
3292 QStringList sys_dev_clist = sys_dev_contents.split(":");
3293 if (2 != sys_dev_clist.size())
3294 {
3295 if (error)
3296 {
3297 *error = QString("Unable to read '%1'")
3298 .arg(sys_dev(device_num, "dev"));
3299 }
3300 return -1;
3301 }
3302 if (sys_dev_clist[0].toUInt() != (statbuf.st_rdev>>8))
3303 {
3304 if (error)
3305 *error = QString("'%1' not a DVEO ASI device").arg(device);
3306 return -1;
3307 }
3308
3309 return device_num;
3310#else
3311 if (error)
3312 *error = "Not compiled with ASI support.";
3313 return -1;
3314#endif
3315}
3316
3317uint CardUtil::GetASIBufferSize([[maybe_unused]] uint device_num, QString *error)
3318{
3319#if CONFIG_ASI
3320 // get the buffer size
3321 QString sys_bufsize_contents = read_sys(sys_dev(device_num, "bufsize"));
3322 bool ok = false;
3323 uint buf_size = sys_bufsize_contents.toUInt(&ok);
3324 if (!ok)
3325 {
3326 if (error)
3327 {
3328 *error = QString("Failed to read buffer size from '%1'")
3329 .arg(sys_dev(device_num, "bufsize"));
3330 }
3331 return 0;
3332 }
3333 return buf_size;
3334#else
3335 if (error)
3336 *error = "Not compiled with ASI support.";
3337 return 0;
3338#endif
3339}
3340
3341uint CardUtil::GetASINumBuffers([[maybe_unused]] uint device_num, QString *error)
3342{
3343#if CONFIG_ASI
3344 // get the buffer size
3345 QString sys_numbuffers_contents = read_sys(sys_dev(device_num, "buffers"));
3346 bool ok = false;
3347 uint num_buffers = sys_numbuffers_contents.toUInt(&ok);
3348 if (!ok)
3349 {
3350 if (error)
3351 {
3352 *error = QString("Failed to read num buffers from '%1'")
3353 .arg(sys_dev(device_num, "buffers"));
3354 }
3355 return 0;
3356 }
3357 return num_buffers;
3358#else
3359 if (error)
3360 *error = "Not compiled with ASI support.";
3361 return 0;
3362#endif
3363}
3364
3365int CardUtil::GetASIMode([[maybe_unused]] uint device_num, QString *error)
3366{
3367#if CONFIG_ASI
3368 QString sys_bufsize_contents = read_sys(sys_dev(device_num, "mode"));
3369 bool ok = false;
3370 uint mode = sys_bufsize_contents.toUInt(&ok);
3371 if (!ok)
3372 {
3373 if (error)
3374 {
3375 *error = QString("Failed to read mode from '%1'")
3376 .arg(sys_dev(device_num, "mode"));
3377 }
3378 return -1;
3379 }
3380 return mode;
3381#else
3382 if (error)
3383 *error = "Not compiled with ASI support.";
3384 return -1;
3385#endif
3386}
3387
3388bool CardUtil::SetASIMode([[maybe_unused]] uint device_num,
3389 [[maybe_unused]] uint mode,
3390 QString *error)
3391{
3392#if CONFIG_ASI
3393 QString sys_bufsize_contents = read_sys(sys_dev(device_num, "mode"));
3394 bool ok = false;
3395 uint old_mode = sys_bufsize_contents.toUInt(&ok);
3396 if (ok && old_mode == mode)
3397 return true;
3398 ok = write_sys(sys_dev(device_num, "mode"), QString("%1\n").arg(mode));
3399 if (!ok && error)
3400 {
3401 *error = QString("Failed to set mode to %1 using '%2'")
3402 .arg(mode).arg(sys_dev(device_num, "mode"));
3403 }
3404 return ok;
3405#else
3406 if (error)
3407 *error = "Not compiled with ASI support.";
3408 return false;
3409#endif
3410}
3411
3412#if CONFIG_VBOX
3417bool CardUtil::IsVBoxPresent(uint inputid)
3418{
3419 // should only be called if inputtype == VBOX
3420 if (!inputid )
3421 {
3422 LOG(VB_GENERAL, LOG_ERR, QString("VBOX inputid (%1) not valid, redo mythtv-setup")
3423 .arg(inputid));
3424 return false;
3425 }
3426
3427 // get sourceid and startchan from table capturecard for inputid
3428 uint chanid = 0;
3429 chanid = ChannelUtil::GetChannelValueInt("chanid",GetSourceID(inputid),GetStartChannel(inputid));
3430 if (!chanid)
3431 {
3432 // no chanid, presume bad setup
3433 LOG(VB_GENERAL, LOG_ERR, QString("VBOX chanid (%1) not found for inputid (%2), redo mythtv-setup")
3434 .arg(chanid).arg(inputid));
3435 return false;
3436 }
3437
3438 // get timeouts for inputid
3439 std::chrono::milliseconds signal_timeout = 0ms;
3440 std::chrono::milliseconds tuning_timeout = 0ms;
3441 if (!GetTimeouts(inputid,signal_timeout,tuning_timeout))
3442 {
3443 LOG(VB_GENERAL, LOG_ERR, QString("Failed to get timeouts for inputid (%1)")
3444 .arg(inputid));
3445 return false;
3446 }
3447
3448 // now get url from iptv_channel table
3449 QUrl url;
3451 query.prepare("SELECT url "
3452 "FROM iptv_channel "
3453 "WHERE chanid = :CHANID");
3454 query.bindValue(":CHANID", chanid);
3455
3456 if (!query.exec())
3457 MythDB::DBError("CardUtil::IsVBoxPresent url", query);
3458 else if (query.next())
3459 url = QUrl(query.value(0).toString());
3460
3461 // now get just the IP address from the url
3462 if (!url.isValid())
3463 {
3464 LOG(VB_GENERAL, LOG_ERR, QString("VBOX invalid url: %1")
3465 .arg(url.toString()));
3466 return false;
3467 }
3468 QString ip = url.host();
3469 LOG(VB_GENERAL, LOG_INFO, QString("VBOX IP found (%1) for inputid (%2)")
3470 .arg(ip).arg(inputid));
3471
3472 if (!ping(ip,signal_timeout))
3473 {
3474 LOG(VB_GENERAL, LOG_ERR, QString("VBOX at IP (%1) failed to respond to network ping for inputid (%2) timeout (%3)")
3475 .arg(ip).arg(inputid).arg(signal_timeout.count()));
3476 return false;
3477 }
3478
3479 return true;
3480}
3481#endif
3482
3483#if CONFIG_SATIP
3488bool CardUtil::IsSatIPPresent(uint inputid)
3489{
3490 // Should only be called if inputtype == SATIP
3491 if (!inputid )
3492 {
3493 LOG(VB_GENERAL, LOG_ERR, QString("SatIP inputid (%1) not valid, redo mythtv-setup")
3494 .arg(inputid));
3495 return false;
3496 }
3497
3498 // Get sourceid and startchan from table capturecard for inputid
3499 uint chanid = 0;
3500 chanid = ChannelUtil::GetChannelValueInt("chanid", GetSourceID(inputid), GetStartChannel(inputid));
3501 if (!chanid)
3502 {
3503 // no chanid, presume bad setup
3504 LOG(VB_GENERAL, LOG_ERR, QString("SatIP chanid (%1) not found for inputid (%2), redo mythtv-setup")
3505 .arg(chanid).arg(inputid));
3506 return false;
3507 }
3508
3509 // Get timeouts for inputid
3510 std::chrono::milliseconds signal_timeout = 0ms;
3511 std::chrono::milliseconds tuning_timeout = 0ms;
3512 if (!GetTimeouts(inputid,signal_timeout,tuning_timeout))
3513 {
3514 LOG(VB_GENERAL, LOG_ERR, QString("Failed to get timeouts for inputid (%1)")
3515 .arg(inputid));
3516 return false;
3517 }
3518
3519 // Ping the SatIP box to see if it can be reached on the network
3520 QString device = CardUtil::GetVideoDevice(inputid);
3521 QStringList devinfo = device.split(":");
3522 if (devinfo.value(0).toUpper() == "UUID")
3523 {
3524 QString deviceId = QString("uuid:%1").arg(devinfo.value(1));
3525 QString ip = SatIP::findDeviceIP(deviceId);
3526 LOG(VB_GENERAL, LOG_INFO, QString("SatIP[%1] IP address %2 device %3")
3527 .arg(inputid).arg(ip, device));
3528
3529 if (!ping(ip, signal_timeout))
3530 {
3531 LOG(VB_GENERAL, LOG_ERR, QString("SatIP[%1] at IP %2 failed to respond to network ping (timeout %3)")
3532 .arg(inputid).arg(ip).arg(signal_timeout.count()));
3533 return false;
3534 }
3535 return true;
3536 }
3537 return false;
3538}
3539#endif
#define LOC
Definition: cardutil.cpp:64
static uint clone_capturecard(uint src_inputid, uint orig_dst_inputid)
Definition: cardutil.cpp:1407
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:2328
static int GetASIDeviceNumber(const QString &device, QString *error=nullptr)
Definition: cardutil.cpp:3262
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:2666
static InputNames ProbeV4LVideoInputs(int videofd, bool &ok)
Definition: cardutil.cpp:2391
static int SetDefaultDeliverySystem(uint inputid, int fd)
Definition: cardutil.cpp:1125
static uint CreateInputGroup(const QString &name)
Definition: cardutil.cpp:2033
static QString GetFirewireChangerModel(uint inputid)
Definition: cardutil.cpp:1645
static QString GetRawInputType(uint inputid)
Definition: cardutil.h:294
static std::vector< uint > GetSchedInputList(void)
Definition: cardutil.cpp:2929
static bool HDHRdoesDVB(const QString &device)
If the device is valid, check if the model does DVB.
Definition: cardutil.cpp:3086
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:1704
static bool InputSetMaxRecordings(uint parentid, uint max_recordings)
Definition: cardutil.cpp:1567
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:2075
static QString GetStartChannel(uint inputid)
Definition: cardutil.cpp:1798
static QString GetDeviceLabel(const QString &inputtype, const QString &videodevice)
Definition: cardutil.cpp:2644
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:3191
static std::vector< uint > GetInputList(void)
Definition: cardutil.cpp:2908
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:3341
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:3317
static QString GetInputName(uint inputid)
Definition: cardutil.cpp:1779
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:2625
static bool UnlinkInputGroup(uint inputid, uint inputgroupid)
Definition: cardutil.cpp:2163
static bool LinkInputGroup(uint inputid, uint inputgroupid)
Definition: cardutil.cpp:2112
static InputNames ProbeV4LAudioInputs(int videofd, bool &ok)
Definition: cardutil.cpp:2423
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:2294
static bool IsInNeedOfExternalInputConf(uint inputid)
Definition: cardutil.cpp:2317
static std::chrono::milliseconds GetMinSignalMonitoringDelay(const QString &device)
Definition: cardutil.cpp:803
static uint AddChildInput(uint parentid)
Definition: cardutil.cpp:1600
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:3138
static uint CloneCard(uint src_inputid, uint dst_inputid)
Definition: cardutil.cpp:1556
static std::vector< uint > GetInputGroups(uint inputid)
Definition: cardutil.cpp:2194
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:1379
static std::vector< uint > GetLiveTVInputList(void)
Definition: cardutil.cpp:2951
static InputNames GetConfiguredDVBInputs(const QString &device)
Definition: cardutil.cpp:2453
static bool HDHRdoesDVBC(const QString &device)
If the device is valid, check if the model does DVB-C.
Definition: cardutil.cpp:3112
static uint GetSourceID(uint inputid)
Definition: cardutil.cpp:1954
static QString GetDeviceName(dvb_dev_type_t type, const QString &device)
Definition: cardutil.cpp:2973
static QStringList CapabilitiesToString(uint64_t capabilities)
Definition: cardutil.cpp:2481
static QString GetFirewireChangerNode(uint inputid)
Definition: cardutil.cpp:1628
static std::vector< uint > GetGroupInputIDs(uint inputgroupid)
Definition: cardutil.cpp:2220
static bool SetStartChannel(uint inputid, const QString &channum)
Definition: cardutil.cpp:1686
static QString GetDisplayName(uint inputid)
Definition: cardutil.cpp:1882
static bool DeleteInput(uint inputid)
Definition: cardutil.cpp:2829
static int GetASIMode(uint device_num, QString *error=nullptr)
Definition: cardutil.cpp:3365
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:1904
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:1973
static bool SetASIMode(uint device_num, uint mode, QString *error=nullptr)
Definition: cardutil.cpp:3388
static DTVModulationSystem ProbeCurrentDeliverySystem(const QString &device)
Definition: cardutil.cpp:923
static std::vector< uint > GetConflictingInputs(uint inputid)
Definition: cardutil.cpp:2247
static uint GetChildInputCount(uint inputid)
Definition: cardutil.cpp:1355
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:2361
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:1741
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:2349
static QStringList ProbeVideoInputs(const QString &device, const QString &inputtype=QString())
Definition: cardutil.cpp:2534
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:2899
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:2680
static InputTypes GetInputTypes(void)
Definition: cardutil.cpp:327
static QStringList ProbeAudioInputs(const QString &device, const QString &inputtype=QString())
Definition: cardutil.cpp:2548
static uint GetDeviceInputGroup(uint inputid)
Definition: cardutil.cpp:2088
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:164
bool Load(uint card_input_id)
Loads configuration chain from DB for specified card input id.
Definition: diseqc.cpp:130
DVB-S device tree class.
Definition: diseqc.h:75
bool Load(const QString &device)
Loads the device tree from the database.
Definition: diseqc.cpp:313
void SetRoot(DiSEqCDevDevice *root)
Changes the root node of the tree.
Definition: diseqc.cpp:634
bool Store(uint cardid, const QString &device="")
Stores the device tree to the database.
Definition: diseqc.cpp:424
DiSEqCDevDevice * Root(void)
Retrieves the root node in the tree.
Definition: diseqc.h:93
bool IsInNeedOfConf(void) const
Definition: diseqc.cpp:836
static DiSEqCDevTree * FindTree(uint cardid)
Retrieve device tree.
Definition: diseqc.cpp:239
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:690
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