7#include "libmythbase/mythconfig.h"
9#if CONFIG_V4L2 || CONFIG_DVB
16#include <QRegularExpression>
42#include HDHOMERUN_HEADERFILE
57#include <dveo/master.h>
60#define LOC QString("CardUtil: ")
64 QStringList inputTypes {};
67 inputTypes +=
"'DVB'";
71 inputTypes +=
"'V4L'";
72 inputTypes +=
"'MPEG'";
76 inputTypes +=
"'FREEBOX'";
80 inputTypes +=
"'VBOX'";
84 inputTypes +=
"'HDHOMERUN'";
88 inputTypes +=
"'SATIP'";
92 inputTypes +=
"'ASI'";
96 inputTypes +=
"'CETON'";
100 inputTypes +=
"'EXTERNAL'";
103 if (inputTypes.isEmpty())
106 return QString(
"(%1)").arg(inputTypes.join(
','));
110 const QString &inputType)
112 if (inputType ==
"HDHOMERUN")
115 hdhomerun_tuner_status_t status {};
118 hdhomerun_device_create_from_str(device.toLatin1(),
nullptr);
122 int oob = hdhomerun_device_get_oob_status(hdhr,
nullptr, &status);
126 if (oob > 0 && (strncmp(status.channel,
"none", 4) != 0))
128 LOG(VB_GENERAL, LOG_INFO,
"Cardutil: HDHomeRun Cablecard Present.");
129 hdhomerun_device_destroy(hdhr);
133 hdhomerun_device_destroy(hdhr);
138 if (inputType ==
"CETON")
143 QStringList parts = device.split(
"-");
144 if (parts.size() != 2)
146 LOG(VB_GENERAL, LOG_ERR,
147 QString(
"CardUtil: Ceton invalid device id %1").arg(device));
151 const QString& ip_address = parts.at(0);
153 QStringList tuner_parts = parts.at(1).split(
".");
154 if (tuner_parts.size() != 2)
156 LOG(VB_GENERAL, LOG_ERR,
LOC +
157 QString(
"CardUtil: Ceton invalid device id %1").arg(device));
161 uint tuner = tuner_parts.at(1).toUInt();
164 params.addQueryItem(
"i", QString::number(tuner));
165 params.addQueryItem(
"s",
"cas");
166 params.addQueryItem(
"v",
"CardStatus");
169 url.setScheme(
"http");
170 url.setHost(ip_address);
171 url.setPath(
"/get_var.json");
172 url.setQuery(params);
174 auto *request =
new QNetworkRequest();
175 request->setAttribute(QNetworkRequest::CacheLoadControlAttribute,
176 QNetworkRequest::AlwaysNetwork);
177 request->setUrl(url);
182 if (!
manager->download(request, &data))
184 LOG(VB_GENERAL, LOG_ERR,
185 QString(
"CardUtil: Ceton http request failed %1").arg(device));
189 QString response = QString(data);
191 static const QRegularExpression regex {
"^\\{ \"?result\"?: \"(.*)\" \\}$"};
192 auto match = regex.match(response);
193 if (!match.hasMatch())
195 LOG(VB_GENERAL, LOG_ERR,
196 QString(
"CardUtil: Ceton unexpected http response: %1").arg(response));
200 QString result = match.captured(1);
202 if (result ==
"Inserted")
204 LOG(VB_GENERAL, LOG_DEBUG,
"Cardutil: Ceton CableCARD present.");
208 LOG(VB_GENERAL, LOG_DEBUG,
"Cardutil: Ceton CableCARD not present.");
218 [[maybe_unused]]
const QString & device)
220 if (rawtype ==
"DVB" || rawtype ==
"HDHOMERUN" ||
221 rawtype ==
"FREEBOX" || rawtype ==
"CETON" ||
222 rawtype ==
"VBOX" || rawtype ==
"SATIP")
226 if (rawtype ==
"V4L2ENC")
233 if (rawtype ==
"EXTERNAL")
244 LOG(VB_GENERAL, LOG_DEBUG, QString(
"IsTunerShared(%1,%2)")
245 .arg(inputidA).arg(inputidB));
248 query.
prepare(
"SELECT videodevice, hostname, cardtype "
250 "WHERE ( (cardid = :INPUTID_A) OR "
251 " (cardid = :INPUTID_B) )");
264 const QString vdevice = query.
value(0).toString();
266 const QString inputtype = query.
value(2).toString();
274 bool ret = ((vdevice == query.
value(0).toString()) &&
276 (inputtype == query.
value(2).toString()));
278 LOG(VB_RECORD, LOG_DEBUG, QString(
"IsTunerShared(%1,%2) -> %3")
279 .arg(inputidA).arg(inputidB).arg(ret));
296 "SELECT count(cardtype) "
298 "WHERE capturecard.hostname = :HOSTNAME ";
300 if (!rawtype.isEmpty())
301 qstr +=
" AND capturecard.cardtype = :INPUTTYPE";
305 if (!rawtype.isEmpty())
306 query.
bindValue(
":INPUTTYPE", rawtype.toUpper());
318 count = query.
value(0).toUInt();
328 query.
prepare(
"SELECT DISTINCT cardtype, videodevice "
341 cardtype = query.
value(0).toString();
342 if (cardtype !=
"V4L2ENC")
344 inputtypes[cardtype] =
"";
352 QString driver_name =
"V4L2:" + v4l2.
DriverName();
353 inputtypes[driver_name] = v4l2.
CardName();
373 query.
prepare(
"SELECT cardtype "
375 "WHERE capturecard.sourceid = :SOURCEID "
376 "GROUP BY cardtype");
386 list.push_back(query.
value(0).toString());
407 "SELECT videodevice "
409 "WHERE hostname = :HOSTNAME";
411 if (!rawtype.isEmpty())
412 qstr +=
" AND cardtype = :INPUTTYPE";
416 if (!rawtype.isEmpty())
417 query.
bindValue(
":INPUTTYPE", rawtype.toUpper());
427 QMap<QString,bool> dup;
430 QString videodevice = query.
value(0).toString();
431 if (dup[videodevice])
434 list.push_back(videodevice);
435 dup[videodevice] =
true;
456 if (rawtype.toUpper() ==
"DVB")
458 QDir dir(
"/dev/dvb",
"adapter*", QDir::Name, QDir::Dirs);
459 QFileInfoList entries = dir.entryInfoList();
460 for (
const auto & it : std::as_const(entries))
462 QDir subdir(it.filePath(),
"frontend*", QDir::Name, QDir::Files | QDir::System);
463 const QFileInfoList subil = subdir.entryInfoList();
467 for (
const auto & subit : std::as_const(subil))
468 devs.push_back(subit.filePath());
471 else if (rawtype.toUpper() ==
"ASI")
473 QDir dir(
"/dev/",
"asirx*", QDir::Name, QDir::System);
474 QFileInfoList entries = dir.entryInfoList();
475 for (
const auto & it : std::as_const(entries))
479 devs.push_back(it.filePath());
486 else if (rawtype.toUpper() ==
"HDHOMERUN")
488#if HDHOMERUN_VERSION >= 20221010
489 struct hdhomerun_debug_t *dbg = hdhomerun_debug_create();
490 struct hdhomerun_discover_t *ds = hdhomerun_discover_create(dbg);
493 uint32_t
type { HDHOMERUN_DEVICE_TYPE_TUNER };
494 uint32_t flags { HDHOMERUN_DISCOVER_FLAGS_IPV4_GENERAL |
495 HDHOMERUN_DISCOVER_FLAGS_IPV6_GENERAL };
497 hdhomerun_discover2_find_devices_broadcast(ds, flags, &
type, 1);
501 LOG(VB_GENERAL, LOG_ERR,
"Error finding HDHomerun devices");
503 else if (result == 0)
505 LOG(VB_GENERAL, LOG_INFO,
"No HDHomerun devices found.");
510 struct hdhomerun_discover2_device_t *device = hdhomerun_discover2_iter_device_first(ds);
513 uint8_t tuners = hdhomerun_discover2_device_get_tuner_count(device);
514 uint32_t device_id = hdhomerun_discover2_device_get_device_id(device);
515 QString
id = QString(
"%1").arg(device_id, 0, 16, QChar(
'0')).toUpper();
516 auto *dev1 = hdhomerun_device_create_from_str(
id.toLatin1(),
nullptr);
517 QString model = hdhomerun_device_get_model_str(dev1);
520 struct sockaddr_storage saddr {};
521 struct hdhomerun_discover2_device_if_t *device_if {
nullptr };
524 device_if = hdhomerun_discover2_iter_device_if_first(device);
527 hdhomerun_discover2_device_if_get_ip_addr(device_if, &saddr);
528 ip = QHostAddress((
struct sockaddr *)&saddr);
529 LOG(VB_GENERAL, LOG_DEBUG,
530 QString(
"HDHomerun %1 has IP %2").arg(
id, ip.toString()));
531 device_if = hdhomerun_discover2_iter_device_if_next(device_if);
535 device_if = hdhomerun_discover2_iter_device_if_first(device);
536 if (
nullptr == device_if)
538 LOG(VB_GENERAL, LOG_WARNING,
539 QString(
"HDHomerun %1 has no IP addresses").arg(
id));
542 hdhomerun_discover2_device_if_get_ip_addr(device_if, &saddr);
543 ip = QHostAddress((
struct sockaddr *)&saddr);
546 QString hdhrdev = QString(
"%1 %2 %3").arg(
id, ip.toString(), model);
547 devs.push_back(hdhrdev);
548 LOG(VB_GENERAL, LOG_INFO,
549 QString(
"HDHomerun %1: IP %2, model %3, %4 tuners")
550 .arg(
id, ip.toString(), model).arg(tuners));
552 device = hdhomerun_discover2_iter_device_next(device);
554 hdhomerun_discover_destroy(ds);
555 hdhomerun_debug_destroy(dbg);
558 uint32_t target_ip = 0;
559 uint32_t device_type = HDHOMERUN_DEVICE_TYPE_TUNER;
560 uint32_t device_id = HDHOMERUN_DEVICE_ID_WILDCARD;
561 const int max_count = 50;
562 std::array<hdhomerun_discover_device_t,max_count> result_list {};
564 int result = hdhomerun_discover_find_devices_custom_v2(
565 target_ip, device_type, device_id, result_list.data(), result_list.size());
569 LOG(VB_GENERAL, LOG_ERR,
"Error finding HDHomerun devices");
572 if (result >= max_count)
574 LOG(VB_GENERAL, LOG_WARNING,
575 "Warning: may be > 50 HDHomerun devices");
579 for (
int i = 0; i < result; i++)
581 QString
id = QString(
"%1").arg(result_list[i].device_id, 0, 16);
582 QString ip = QString(
"%1.%2.%3.%4")
583 .arg((result_list[i].ip_addr>>24) & 0xFF)
584 .arg((result_list[i].ip_addr>>16) & 0xFF)
585 .arg((result_list[i].ip_addr>> 8) & 0xFF)
586 .arg((result_list[i].ip_addr>> 0) & 0xFF);
590 result_list[i].device_id, 0, 0,
nullptr);
593 model = hdhomerun_device_get_model_str(device);
594 hdhomerun_device_destroy(device);
597 QString hdhrdev =
id.toUpper() +
" " + ip +
" " + model;
598 devs.push_back(hdhrdev);
604 else if (rawtype.toUpper() ==
"SATIP")
610 else if (rawtype.toUpper() ==
"VBOX")
616 else if (rawtype.toUpper() ==
"CETON")
619 LOG(VB_GENERAL, LOG_INFO,
"CardUtil::ProbeVideoDevices: "
620 "TODO Probe Ceton devices");
625 LOG(VB_GENERAL, LOG_ERR, QString(
"Raw Type: '%1' is not supported")
636 QStringList delsyslist;
645 struct dtv_property prop = {};
646 struct dtv_properties cmd = {};
648 prop.cmd = DTV_API_VERSION;
651 if (ioctl(fd_frontend, FE_GET_PROPERTY, &cmd) == 0)
653 LOG(VB_GENERAL, LOG_DEBUG,
654 QString(
"CardUtil(%1): ").arg(device) +
655 QString(
"dvb api version %1.%2").arg((prop.u.data>>8)&0xff).arg((prop.u.data)&0xff));
659 LOG(VB_GENERAL, LOG_ERR,
660 QString(
"CardUtil(%1) FE_GET_PROPERTY ioctl failed").arg(device) +
ENO);
667 QString msg =
"Delivery systems:";
668 for (
const auto & item : std::as_const(delsyslist))
673 LOG(VB_GENERAL, LOG_INFO, QString(
"CardUtil(%1): ").arg(device) + msg);
684 QStringList delsyslist;
687 struct dtv_property prop = {};
688 struct dtv_properties cmd = {};
690 prop.cmd = DTV_ENUM_DELSYS;
693 if (ioctl(fd_frontend, FE_GET_PROPERTY, &cmd) == 0)
695 for (
unsigned int i = 0; i < prop.u.buffer.len; i++)
702 LOG(VB_GENERAL, LOG_ERR,
LOC +
"FE_GET_PROPERTY ioctl failed " +
ENO);
727 QString ret =
"ERROR_UNKNOWN";
729 if (device.isEmpty())
734 ret = (
type.toString() !=
"UNKNOWN") ?
type.toString().toUpper() : ret;
736 LOG(VB_GENERAL, LOG_DEBUG,
LOC + QString(
"(%1) tuner type:%2 %3")
737 .arg(device).arg(
type).arg(ret));
748 QString ret =
"ERROR_UNKNOWN";
752 QByteArray dev = dvbdev.toLatin1();
753 int fd_frontend = open(dev.constData(), O_RDWR |
O_NONBLOCK);
757 struct dvb_frontend_info
info {};
758 int err = ioctl(fd_frontend, FE_GET_INFO, &
info);
762 return "ERROR_PROBE";
793 return ((name ==
"VLSI VES1x93 DVB-S") ||
794 (name ==
"ST STV0299 DVB-S"));
800 if (name.indexOf(
"DVB-S") >= 0)
802 if (name ==
"DiBcom 3000P/M-C DVB-T")
840 LOG(VB_GENERAL, LOG_ERR,
LOC +
841 QString(
"TODO Add to switch case delivery system:%2 %3")
842 .arg(delsys).arg(delsys.
toString()));
881 "FROM dtv_multiplex "
882 "WHERE dtv_multiplex.mplexid = :MPLEXID");
893 LOG(VB_GENERAL, LOG_ERR,
LOC +
894 QString(
"Could not find entry in dtv_multiplex for mplexid %1")
921 if (device.isEmpty())
930 LOG(VB_GENERAL, LOG_ERR,
LOC +
931 QString(
"open failed (%1)")
938 LOG(VB_GENERAL, LOG_DEBUG, QString(
"CardUtil(%1): delsys:%2 %3")
939 .arg(device).arg(delsys).arg(delsys.
toString()));
953 struct dtv_property prop = {};
954 struct dtv_properties cmd = {};
956 prop.cmd = DTV_DELIVERY_SYSTEM;
961 int ret = ioctl(fd_frontend, FE_GET_PROPERTY, &cmd);
964 LOG(VB_GENERAL, LOG_ERR,
LOC +
965 QString(
"FE_GET_PROPERTY ioctl failed (fd_frontend:%1)")
966 .arg(fd_frontend) +
ENO);
970 delsys = prop.u.data;
1002 LOG(VB_GENERAL, LOG_INFO,
1003 QString(
"CardUtil[%1]: ").arg(inputid) +
1004 QString(
"Update capturecard delivery system: %1").arg(delsys.
toString()));
1008 LOG(VB_GENERAL, LOG_ERR,
1009 QString(
"CardUtil[%1]: Error probing best delivery system").arg(inputid));
1010 return "ERROR_UNKNOWN";
1017 QString subtype =
"ERROR_UNKNOWN";
1023 LOG(VB_GENERAL, LOG_DEBUG,
1024 QString(
"CardUtil[%1]: subtype:%2").arg(inputid).arg(subtype));
1032 QString
t = inputType.toUpper();
1033 return (
t ==
"DVB") || (
t ==
"QPSK") || (
t ==
"QAM") || (
t ==
"OFDM") ||
1034 (
t ==
"ATSC") || (
t ==
"DVB_S2") || (
t ==
"DVB_T2");
1049 LOG(VB_GENERAL, LOG_INFO,
LOC +
1050 QString(
"Current delivery system: %1").arg(delsys.
toString()));
1053 QString msg =
"Supported delivery systems:";
1055 msg.append(delsyslist.join(
" "));
1056 LOG(VB_GENERAL, LOG_DEBUG,
LOC + msg);
1062 if (delsyslist.contains(newdelsys.
toString()))
1064 LOG(VB_GENERAL, LOG_INFO,
LOC +
1065 QString(
"Changing delivery system from %1 to %2")
1075 if (delsyslist.contains(newdelsys.
toString()))
1077 LOG(VB_GENERAL, LOG_INFO,
LOC +
1078 QString(
"Changing delivery system from %1 to %2")
1092 [[maybe_unused]]
int fd)
1107 LOG(VB_GENERAL, LOG_INFO,
1108 QString(
"CardUtil[%1]: ").arg(inputid) +
1109 QString(
"No capturecard delivery system in database, using: %1").arg(delsys.
toString()));
1120 [[maybe_unused]]
int fd)
1160 if (device.isEmpty())
1162 LOG(VB_GENERAL, LOG_DEBUG,
1163 QString(
"CardUtil[%1]: ").arg(inputid) +
1164 QString(
"inputid:%1 ").arg(inputid) +
1165 QString(
"delsys:%1").arg(delsys.toString()));
1170 if (fd_frontend < 0)
1172 LOG(VB_GENERAL, LOG_ERR,
1173 QString(
"CardUtil[%1]: ").arg(inputid) +
1174 QString(
"open failed (%1)").arg(device) +
ENO);
1187 [[maybe_unused]]
int fd)
1205 [[maybe_unused]]
int fd)
1210 LOG(VB_GENERAL, LOG_INFO,
1211 QString(
"CardUtil[%1]: ").arg(inputid) +
1212 QString(
"Set delivery system: %1").arg(delsys.toString()));
1214 struct dtv_property prop = {};
1215 struct dtv_properties cmd = {};
1217 prop.cmd = DTV_DELIVERY_SYSTEM;
1218 prop.u.data = delsys;
1222 ret = ioctl(fd, FE_SET_PROPERTY, &cmd);
1225 LOG(VB_GENERAL, LOG_ERR,
LOC +
1226 QString(
"[%1] FE_SET_PROPERTY ioctl failed")
1227 .arg(inputid) +
ENO);
1243 if (device.isEmpty())
1247 QByteArray dev = dvbdev.toLatin1();
1248 int fd_frontend = open(dev.constData(), O_RDWR |
O_NONBLOCK);
1249 if (fd_frontend < 0)
1251 LOG(VB_GENERAL, LOG_ERR,
LOC +
1252 QString(
"Can't open DVB frontend (%1) for %2.")
1253 .arg(dvbdev, device) +
ENO);
1262 QString(
"SELECT %1 ").arg(to_get) +
1264 "WHERE capturecard.cardid = :INPUTID");
1269 else if (query.
next())
1270 return query.
value(0).toString();
1283 QString(
"UPDATE capturecard SET %1 = :VALUE ").arg(to_set) +
1284 "WHERE cardid = :INPUTID");
1306 const QString& rawtype,
1307 const QString& inputname,
1310 std::vector<uint> list;
1319 "WHERE hostname = :HOSTNAME ";
1320 if (!videodevice.isEmpty())
1321 qstr +=
"AND videodevice = :DEVICE ";
1322 if (!inputname.isEmpty())
1323 qstr +=
"AND inputname = :INPUTNAME ";
1324 if (!rawtype.isEmpty())
1325 qstr +=
"AND cardtype = :INPUTTYPE ";
1326 qstr +=
"ORDER BY cardid";
1331 if (!videodevice.isEmpty())
1332 query.
bindValue(
":DEVICE", videodevice);
1333 if (!inputname.isEmpty())
1334 query.
bindValue(
":INPUTNAME", inputname);
1335 if (!rawtype.isEmpty())
1336 query.
bindValue(
":INPUTTYPE", rawtype.toUpper());
1342 while (query.
next())
1343 list.push_back(query.
value(0).toUInt());
1358 "WHERE parentid = :INPUTID";
1367 else if (query.
next())
1368 count = query.
value(0).toUInt();
1375 std::vector<uint> list;
1384 "WHERE parentid = :INPUTID "
1394 while (query.
next())
1395 list.push_back(query.
value(0).toUInt());
1403 uint dst_inputid = orig_dst_inputid;
1409 "DELETE FROM capturecard "
1410 "WHERE videodevice = 'temp_dummy'");
1419 "INSERT INTO capturecard "
1420 "SET videodevice = 'temp_dummy'");
1431 "WHERE videodevice = 'temp_dummy'");
1441 LOG(VB_GENERAL, LOG_ERR,
"clone_capturecard -- get temp id");
1445 dst_inputid = query.
value(0).toUInt();
1449 "SELECT videodevice, audiodevice, vbidevice, "
1450 " cardtype, hostname, signal_timeout, "
1451 " channel_timeout, dvb_wait_for_seqstart, dvb_on_demand, "
1452 " dvb_tuning_delay, dvb_diseqc_type, diseqcid, "
1453 " dvb_eitscan, inputname, sourceid, "
1454 " externalcommand, changer_device, changer_model, "
1455 " tunechan, startchan, displayname, "
1456 " dishnet_eit, recpriority, quicktune, "
1457 " livetvorder, reclimit, "
1459 " schedgroup, schedorder "
1461 "WHERE cardid = :INPUTID");
1462 query.
bindValue(
":INPUTID", src_inputid);
1471 LOG(VB_GENERAL, LOG_ERR,
"clone_cardinput -- get data 2");
1478 bool schedgroup = query.
value(26).toBool();
1479 uint schedorder = query.
value(27).toUInt();
1488 "UPDATE capturecard "
1489 "SET videodevice = :V0, "
1490 " audiodevice = :V1, "
1491 " vbidevice = :V2, "
1494 " signal_timeout = :V5, "
1495 " channel_timeout = :V6, "
1496 " dvb_wait_for_seqstart = :V7, "
1497 " dvb_on_demand = :V8, "
1498 " dvb_tuning_delay = :V9, "
1499 " dvb_diseqc_type = :V10, "
1501 " dvb_eitscan = :V12, "
1502 " inputname = :V13, "
1503 " sourceid = :V14, "
1504 " externalcommand = :V15, "
1505 " changer_device = :V16, "
1506 " changer_model = :V17, "
1507 " tunechan = :V18, "
1508 " startchan = :V19, "
1509 " displayname = :V20, "
1510 " dishnet_eit = :V21, "
1511 " recpriority = :V22, "
1512 " quicktune = :V23, "
1513 " livetvorder = :V24, "
1514 " reclimit = :V25, "
1515 " schedgroup = :SCHEDGROUP, "
1516 " schedorder = :SCHEDORDER, "
1517 " parentid = :PARENTID "
1518 "WHERE cardid = :INPUTID");
1519 for (
uint i = 0; i < 26; ++i)
1520 query2.
bindValue(QString(
":V%1").arg(i), query.
value(i).toString());
1521 query2.
bindValue(
":INPUTID", dst_inputid);
1522 query2.
bindValue(
":PARENTID", src_inputid);
1523 query2.
bindValue(
":SCHEDGROUP", schedgroup);
1524 query2.
bindValue(
":SCHEDORDER", schedorder);
1529 if (!orig_dst_inputid)
1537 for (
uint dst_grp : dst_grps)
1539 for (
uint src_grp : src_grps)
1544 if (diseqc.
Load(src_inputid))
1545 diseqc.
Store(dst_inputid);
1563 if (max_recordings < 1)
1565 LOG(VB_GENERAL, LOG_ERR,
LOC +
1566 "InputSetMaxRecording: max must be greater than zero.");
1573 for (
size_t i = cardids.size() + 1;
1574 (i > max_recordings) && !cardids.empty(); --i)
1581 for (
uint id : cardids)
1585 for (
size_t i = cardids.size() + 1; i < max_recordings; ++i)
1602 LOG(VB_GENERAL, LOG_INFO,
LOC +
1603 QString(
"Added child input %1 to parent %2")
1604 .arg(inputid).arg(parentid));
1606 query.
prepare(
"UPDATE capturecard "
1607 "SET reclimit = reclimit + 1 "
1608 "WHERE cardid = :PARENTID");
1615 LOG(VB_GENERAL, LOG_ERR,
LOC +
1616 QString(
"Failed to add child input to parent %1").arg(parentid));
1627 query.
prepare(
"SELECT changer_device "
1628 "FROM capturecard WHERE cardid = :INPUTID ");
1633 fwnode = query.
value(0).toString();
1644 query.
prepare(
"SELECT changer_model "
1645 "FROM capturecard WHERE cardid = :INPUTID ");
1650 fwnode = query.
value(0).toString();
1661 "SELECT DISTINCT cardid "
1663 "WHERE sourceid = :SOURCEID");
1666 std::vector<uint> list;
1674 while (query.
next())
1675 list.push_back(query.
value(0).toUInt());
1683 query.
prepare(
"UPDATE capturecard "
1684 "SET startchan = :CHANNUM "
1685 "WHERE cardid = :INPUTID");
1705 "inputname, sourceid, livetvorder, "
1706 "schedorder, displayname, recpriority, quicktune "
1708 "WHERE cardid = :INPUTID");
1737 QList<InputInfo> infoInputList;
1740 QString queryStr =
"SELECT cardid, "
1741 "inputname, sourceid, livetvorder, "
1742 "schedorder, displayname, recpriority, quicktune "
1746 queryStr.append(
" WHERE parentid = 0");
1752 return infoInputList;
1755 while (query.
next())
1767 infoInputList.push_back(input);
1770 return infoInputList;
1797 query.
prepare(
"SELECT startchan "
1799 "WHERE cardid = :INPUTID");
1806 else if (query.
next())
1808 startchan = query.
value(0).toString();
1812 if (!startchan.isEmpty())
1814 query.
prepare(
"SELECT channel.chanid "
1815 "FROM capturecard, channel "
1816 "WHERE capturecard.cardid = :INPUTID AND "
1817 " capturecard.sourceid = channel.sourceid AND "
1818 " channel.deleted IS NULL AND "
1819 " channel.visible > 0 AND "
1820 " channel.channum = :CHANNUM");
1828 else if (!query.
next())
1830 LOG(VB_GENERAL, LOG_WARNING,
1831 QString(
"CardUtil[%1]: ").arg(inputid) +
1832 QString(
"Channel %1 on inputid %2 is invalid").arg(startchan).arg(inputid));
1839 if (startchan.isEmpty())
1841 query.
prepare(
"SELECT channel.channum "
1842 "FROM capturecard, channel "
1843 "WHERE capturecard.cardid = :INPUTID AND "
1844 " capturecard.sourceid = channel.sourceid AND "
1845 " channel.deleted IS NULL AND "
1846 " channel.visible > 0 "
1854 else if (query.
next())
1856 startchan = query.
value(0).toString();
1860 if (startchan.isEmpty())
1862 LOG(VB_GENERAL, LOG_WARNING,
1863 QString(
"CardUtil[%1]: ").arg(inputid) +
1864 QString(
"No start channel found on inputid %1").arg(inputid));
1868 LOG(VB_GENERAL, LOG_DEBUG,
1869 QString(
"CardUtil[%1]: ").arg(inputid) +
1870 QString(
"Start channel %1 on inputid %2").arg(startchan).arg(inputid));
1882 query.
prepare(
"SELECT displayname "
1884 "WHERE cardid = :INPUTID");
1889 else if (query.
next())
1891 QString result = query.
value(0).toString();
1905 qsizetype idx = name.indexOf(
'/');
1908 matching = name.right(name.size() - idx -1);
1913 matching = name.right(2);
1918 query.
prepare(
"SELECT cardid, displayname "
1920 "WHERE parentid = 0 "
1921 " AND cardid <> :INPUTID ");
1922 query.
bindValue(
":INPUTID", exclude_inputid);
1930 while (query.
next())
1932 QString dn = query.
value(1).toString();
1933 idx = dn.indexOf(
'/');
1934 if (!two && idx >= 0)
1936 if (dn.right(dn.size() - idx - 1) == matching)
1939 else if (dn.right(2) == matching.right(2))
1954 "WHERE cardid = :INPUTID");
1958 else if (query.
next())
1959 return query.
value(0).toUInt();
1968 const uint sourceid,
1969 const QString &inputname,
1970 const QString &externalcommand,
1971 const QString &changer_device,
1972 const QString &changer_model,
1974 const QString &tunechan,
1975 const QString &startchan,
1976 const QString &displayname,
1978 const uint recpriority,
1979 const uint quicktune,
1980 const uint schedorder,
1981 const uint livetvorder)
1986 "UPDATE capturecard "
1987 "SET sourceid = :SOURCEID, "
1988 " inputname = :INPUTNAME, "
1989 " externalcommand = :EXTERNALCOMMAND, "
1990 " changer_device = :CHANGERDEVICE, "
1991 " changer_model = :CHANGERMODEL, "
1992 " tunechan = :TUNECHAN, "
1993 " startchan = :STARTCHAN, "
1994 " displayname = :DISPLAYNAME, "
1995 " dishnet_eit = :DISHNETEIT, "
1996 " recpriority = :RECPRIORITY, "
1997 " quicktune = :QUICKTUNE, "
1998 " schedorder = :SCHEDORDER, "
1999 " livetvorder = :LIVETVORDER "
2000 "WHERE cardid = :INPUTID AND "
2001 " inputname = 'None'");
2005 query.
bindValue(
":INPUTNAME", inputname);
2006 query.
bindValue(
":EXTERNALCOMMAND", externalcommand);
2007 query.
bindValue(
":CHANGERDEVICE", changer_device);
2008 query.
bindValue(
":CHANGERMODEL", changer_model);
2010 query.
bindValue(
":STARTCHAN", startchan);
2012 query.
bindValue(
":DISHNETEIT", dishnet_eit);
2013 query.
bindValue(
":RECPRIORITY", recpriority);
2014 query.
bindValue(
":QUICKTUNE", quicktune);
2015 query.
bindValue(
":SCHEDORDER", schedorder);
2016 query.
bindValue(
":LIVETVORDER", livetvorder);
2031 query.
prepare(
"SELECT inputgroupid FROM inputgroup "
2032 "WHERE inputgroupname = :GROUPNAME "
2042 return query.
value(0).toUInt();
2044 query.
prepare(
"SELECT MAX(inputgroupid) FROM inputgroup");
2051 uint inputgroupid = (query.
next()) ? query.
value(0).toUInt() + 1 : 1;
2054 "INSERT INTO inputgroup "
2055 " (cardinputid, inputgroupid, inputgroupname) "
2056 "VALUES (:INPUTID, :GROUPID, :GROUPNAME ) ");
2058 query.
bindValue(
":GROUPID", inputgroupid);
2066 return inputgroupid;
2070 const QString &
type,
2071 const QString &host,
2072 const QString &device)
2074 QString name = host +
'|' + device;
2075 if (
type ==
"FREEBOX" ||
type ==
"IMPORT" ||
2076 type ==
"DEMO" ||
type ==
"EXTERNAL" ||
2077 type ==
"HDHOMERUN" )
2078 name += QString(
"|%1").arg(inputid);
2086 "SELECT inputgroupid "
2088 "WHERE cardinputid = :INPUTID "
2089 " AND inputgroupname REGEXP '^[a-z_-]*\\\\|'");
2100 return query.
value(0).toUInt();
2111 "SELECT cardinputid, inputgroupid, inputgroupname "
2113 "WHERE inputgroupid = :GROUPID "
2114 "ORDER BY inputgroupid, cardinputid, inputgroupname");
2115 query.
bindValue(
":GROUPID", inputgroupid);
2124 while (query.
next()) {
2125 name = query.
value(2).toString();
2128 if (cardid == inputid)
2137 "INSERT INTO inputgroup "
2138 " (cardinputid, inputgroupid, inputgroupname) "
2139 "VALUES (:INPUTID, :GROUPID, :GROUPNAME ) ");
2142 query.
bindValue(
":GROUPID", inputgroupid);
2161 if (!inputid && !inputgroupid)
2164 "DELETE FROM inputgroup "
2165 "WHERE cardinputid NOT IN "
2166 "( SELECT cardid FROM capturecard )");
2171 "DELETE FROM inputgroup "
2172 "WHERE cardinputid = :INPUTID AND "
2173 " inputgroupid = :GROUPID ");
2176 query.
bindValue(
":GROUPID", inputgroupid);
2190 std::vector<uint> list;
2195 "SELECT inputgroupid "
2197 "WHERE cardinputid = :INPUTID "
2198 "ORDER BY inputgroupid, cardinputid, inputgroupname");
2208 while (query.
next())
2209 list.push_back(query.
value(0).toUInt());
2216 std::vector<uint> list;
2221 "SELECT DISTINCT cardid "
2222 "FROM capturecard, inputgroup "
2223 "WHERE inputgroupid = :GROUPID AND "
2224 " capturecard.cardid = inputgroup.cardinputid "
2227 query.
bindValue(
":GROUPID", inputgroupid);
2235 while (query.
next())
2236 list.push_back(query.
value(0).toUInt());
2243 std::vector<uint> inputids;
2248 "SELECT DISTINCT c.cardid "
2250 " SELECT inputgroupid "
2252 " WHERE cardinputid = :INPUTID1 "
2254 "JOIN inputgroup ig ON ig.inputgroupid = g.inputgroupid "
2255 "JOIN capturecard c ON c.cardid = ig.cardinputid "
2256 " AND c.cardid <> :INPUTID2 "
2257 " AND c.sourceid > 0 "
2258 "ORDER BY c.cardid");
2269 while (query.
next())
2271 inputids.push_back(query.
value(0).toUInt());
2276 QString msg = QString(
"CardUtil[%1]: GetConflictingInputs(%1) ").arg(inputid);
2278 for (
auto id : inputids)
2280 ids.append(QString::number(
id));
2282 msg.append(ids.join(
','));
2283 LOG(VB_RECORD, LOG_INFO, msg);
2289 std::chrono::milliseconds &signal_timeout,
2290 std::chrono::milliseconds &channel_timeout)
2294 "SELECT signal_timeout, channel_timeout "
2296 "WHERE cardid = :INPUTID");
2301 else if (query.
next())
2303 signal_timeout = std::max(std::chrono::milliseconds(query.
value(0).toInt()), 250ms);
2304 channel_timeout = std::max(std::chrono::milliseconds(query.
value(1).toInt()), 500ms);
2315 bool needsConf =
false;
2330 "WHERE cardid = :INPUTID AND "
2331 " inputname = :INPUTNAME");
2333 query.
bindValue(
":INPUTNAME", input_name);
2337 else if (query.
next())
2338 quicktune = query.
value(0).toUInt();
2346 struct v4l2_capability vcap {};
2348 return ((ioctl(videofd, VIDIOC_QUERYCAP, &vcap) >= 0) &&
2349 ((vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE) != 0U));
2356 int videofd, QString &input, QString &driver, uint32_t &
version,
2357 uint32_t &capabilities)
2368 struct v4l2_capability capability {};
2369 if (ioctl(videofd, VIDIOC_QUERYCAP, &capability) >= 0)
2371 input = QString::fromLatin1((
const char*)capability.card);
2372 driver = QString::fromLatin1((
const char*)capability.driver);
2374 capabilities = capability.capabilities;
2378 static const QRegularExpression kBracketedDigitRE { R
"(\[[0-9]\]$)" };
2379 if (!driver.isEmpty())
2380 driver.remove( kBracketedDigitRE );
2382 return !input.isEmpty();
2391 bool usingv4l2 =
hasV4L2(videofd);
2393 struct v4l2_input vin {};
2394 while (usingv4l2 && (ioctl(videofd, VIDIOC_ENUMINPUT, &vin) >= 0))
2396 QString input((
char *)vin.name);
2397 list[vin.index] = input;
2408 list[0] =
"Television";
2412 list[-1] += QObject::tr(
"ERROR, Compile with V4L support to query inputs");
2423 bool usingv4l2 =
hasV4L2(videofd);
2426 struct v4l2_audio ain {};
2427 while (usingv4l2 && (ioctl(videofd, VIDIOC_ENUMAUDIO, &ain) >= 0))
2429 QString input((
char *)ain.name);
2430 list[ain.index] = input;
2441 list[-1] += QObject::tr(
2442 "ERROR, Compile with V4L support to query audio inputs");
2452 "SELECT cardid, inputname "
2454 "WHERE hostname = :HOSTNAME "
2455 " AND videodevice = :DEVICE "
2456 " AND parentid = 0 "
2457 " AND inputname <> 'None'");
2465 while (query.
next())
2466 list[query.
value(0).toUInt()] = query.
value(1).toString();
2480 struct fe_caps_name {
2485 std::array<fe_caps_name,31> fe_caps_name {{
2486 { FE_CAN_2G_MODULATION,
"CAN_2G_MODULATION" },
2487 { FE_CAN_8VSB,
"CAN_8VSB" },
2488 { FE_CAN_16VSB,
"CAN_16VSB" },
2489 { FE_CAN_BANDWIDTH_AUTO,
"CAN_BANDWIDTH_AUTO" },
2490 { FE_CAN_FEC_1_2,
"CAN_FEC_1_2" },
2491 { FE_CAN_FEC_2_3,
"CAN_FEC_2_3" },
2492 { FE_CAN_FEC_3_4,
"CAN_FEC_3_4" },
2493 { FE_CAN_FEC_4_5,
"CAN_FEC_4_5" },
2494 { FE_CAN_FEC_5_6,
"CAN_FEC_5_6" },
2495 { FE_CAN_FEC_6_7,
"CAN_FEC_6_7" },
2496 { FE_CAN_FEC_7_8,
"CAN_FEC_7_8" },
2497 { FE_CAN_FEC_8_9,
"CAN_FEC_8_9" },
2498 { FE_CAN_FEC_AUTO,
"CAN_FEC_AUTO" },
2499 { FE_CAN_GUARD_INTERVAL_AUTO,
"CAN_GUARD_INTERVAL_AUTO" },
2500 { FE_CAN_HIERARCHY_AUTO,
"CAN_HIERARCHY_AUTO" },
2501 { FE_CAN_INVERSION_AUTO,
"CAN_INVERSION_AUTO" },
2502 { FE_CAN_MULTISTREAM,
"CAN_MULTISTREAM" },
2503 { FE_CAN_MUTE_TS,
"CAN_MUTE_TS" },
2504 { FE_CAN_QAM_16,
"CAN_QAM_16" },
2505 { FE_CAN_QAM_32,
"CAN_QAM_32" },
2506 { FE_CAN_QAM_64,
"CAN_QAM_64" },
2507 { FE_CAN_QAM_128,
"CAN_QAM_128" },
2508 { FE_CAN_QAM_256,
"CAN_QAM_256" },
2509 { FE_CAN_QAM_AUTO,
"CAN_QAM_AUTO" },
2510 { FE_CAN_QPSK,
"CAN_QPSK" },
2511 { FE_CAN_RECOVER,
"CAN_RECOVER" },
2512 { FE_CAN_TRANSMISSION_MODE_AUTO,
"CAN_TRANSMISSION_MODE_AUTO" },
2513 { FE_CAN_TURBO_FEC,
"CAN_TURBO_FEC" },
2514 { FE_HAS_EXTENDED_CAPS,
"HAS_EXTENDED_CAPS" },
2515 { FE_IS_STUPID,
"IS_STUPID" },
2516 { FE_NEEDS_BENDING,
"NEEDS_BENDING" },
2519 for (
const auto & cap : fe_caps_name)
2521 if (capabilities & cap.idx)
2522 caps.append(cap.name);
2534 else if (
"DVB" == inputtype)
2544 LOG(VB_GENERAL, LOG_DEBUG, QString(
"ProbeAudioInputs(%1,%2)")
2545 .arg(device, inputtype));
2548 if (
"HDPVR" == inputtype ||
2549 "V4L2" == inputtype)
2559 QByteArray dev = device.toLatin1();
2560 int videofd = open(dev.constData(), O_RDWR);
2563 ret += QObject::tr(
"Could not open '%1' "
2564 "to probe its inputs.").arg(device);
2576 InputNames::iterator it;
2577 for (it = list.begin(); it != list.end(); ++it)
2588 LOG(VB_GENERAL, LOG_DEBUG, QString(
"ProbeV4LAudioInputs(%1)").arg(device));
2592 int videofd = open(device.toLatin1().constData(), O_RDWR);
2595 LOG(VB_GENERAL, LOG_ERR,
"ProbeAudioInputs() -> couldn't open device");
2596 ret += QObject::tr(
"Could not open '%1' to probe its inputs.")
2609 InputNames::iterator it;
2610 for (it = list.begin(); it != list.end(); ++it)
2625 InputNames::iterator it;
2626 for (it = list.begin(); it != list.end(); ++it)
2632 ret += QObject::tr(
"ERROR, Compile with DVB support to query inputs");
2639 const QString &videodevice)
2641 return QString(
"[ %1 : %2 ]").arg(inputtype, videodevice);
2647 query.
prepare(
"SELECT cardtype, videodevice "
2648 "FROM capturecard WHERE cardid = :INPUTID ");
2654 query.
value(1).toString());
2657 return "[ UNKNOWN ]";
2661 const QString &device,
2662 const QString &inputtype,
2663 QStringList &inputs)
2667 inputs +=
"MPEG2TS";
2668 else if (inputtype ==
"DVB")
2669 inputs +=
"DVBInput";
2675 const QString &audiodevice,
2676 const QString &vbidevice,
2677 const QString &inputtype,
2678 const uint audioratelimit,
2680 const uint dvb_swfilter,
2681 const uint dvb_sat_type,
2682 bool dvb_wait_for_seqstart,
2685 const uint dvb_diseqc_type,
2686 const uint firewire_speed,
2687 const QString &firewire_model,
2688 const uint firewire_connection,
2689 const std::chrono::milliseconds signal_timeout,
2690 const std::chrono::milliseconds channel_timeout,
2691 const uint dvb_tuning_delay,
2692 const uint contrast,
2693 const uint brightness,
2696 const uint diseqcid,
2702 "INSERT INTO capturecard "
2703 "(videodevice, audiodevice, vbidevice, cardtype, "
2704 "audioratelimit, hostname, dvb_swfilter, dvb_sat_type, "
2705 "dvb_wait_for_seqstart, skipbtaudio, dvb_on_demand, dvb_diseqc_type, "
2706 "firewire_speed, firewire_model, firewire_connection, signal_timeout, "
2707 "channel_timeout, dvb_tuning_delay, contrast, brightness, colour, "
2708 "hue, diseqcid, dvb_eitscan) "
2709 "VALUES (:VIDEODEVICE, :AUDIODEVICE, :VBIDEVICE, :INPUTTYPE, "
2710 ":AUDIORATELIMIT, :HOSTNAME, :DVBSWFILTER, :DVBSATTYPE, "
2711 ":DVBWAITFORSEQSTART, :SKIPBTAUDIO, :DVBONDEMAND, :DVBDISEQCTYPE, "
2712 ":FIREWIRESPEED, :FIREWIREMODEL, :FIREWIRECONNECTION, :SIGNALTIMEOUT, "
2713 ":CHANNELTIMEOUT, :DVBTUNINGDELAY, :CONTRAST, :BRIGHTNESS, :COLOUR, "
2714 ":HUE, :DISEQCID, :DVBEITSCAN ) ");
2716 query.
bindValue(
":VIDEODEVICE", videodevice);
2717 if (audiodevice.length() == 0)
2719#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2720 query.
bindValue(
":AUDIODEVICE", QVariant(QVariant::String));
2722 query.
bindValue(
":AUDIODEVICE", QVariant(QMetaType(QMetaType::QString)));
2727 query.
bindValue(
":AUDIODEVICE", audiodevice);
2729 if (vbidevice.length() == 0)
2731#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2732 query.
bindValue(
":VBIDEVICE", QVariant(QVariant::String));
2734 query.
bindValue(
":VBIDEVICE", QVariant(QMetaType(QMetaType::QString)));
2739 query.
bindValue(
":VBIDEVICE", vbidevice);
2741 query.
bindValue(
":INPUTTYPE", inputtype);
2742 if (audioratelimit == 0)
2744#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2745 query.
bindValue(
":AUDIORATELIMIT", QVariant(QVariant::UInt));
2747 query.
bindValue(
":AUDIORATELIMIT", QVariant(QMetaType(QMetaType::UInt)));
2752 query.
bindValue(
":AUDIORATELIMIT", audioratelimit);
2755 query.
bindValue(
":DVBSWFILTER", dvb_swfilter);
2756 query.
bindValue(
":DVBSATTYPE", dvb_sat_type);
2757 query.
bindValue(
":DVBWAITFORSEQSTART", dvb_wait_for_seqstart);
2758 query.
bindValue(
":SKIPBTAUDIO", skipbtaudio);
2759 query.
bindValue(
":DVBONDEMAND", dvb_on_demand);
2760 query.
bindValue(
":DVBDISEQCTYPE", dvb_diseqc_type);
2761 query.
bindValue(
":FIREWIRESPEED", firewire_speed);
2762 if (firewire_model.length() == 0)
2764#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2765 query.
bindValue(
":FIREWIREMODEL", QVariant(QVariant::String));
2767 query.
bindValue(
":FIREWIREMODEL", QVariant(QMetaType(QMetaType::QString)));
2772 query.
bindValue(
":FIREWIREMODEL", firewire_model);
2774 query.
bindValue(
":FIREWIRECONNECTION", firewire_connection);
2775 query.
bindValue(
":SIGNALTIMEOUT",
static_cast<qint64
>(signal_timeout.count()));
2776 query.
bindValue(
":CHANNELTIMEOUT",
static_cast<qint64
>(channel_timeout.count()));
2777 query.
bindValue(
":DVBTUNINGDELAY", dvb_tuning_delay);
2779 query.
bindValue(
":BRIGHTNESS", brightness);
2784#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2785 query.
bindValue(
":DISEQCID", QVariant(QVariant::UInt));
2787 query.
bindValue(
":DISEQCID", QVariant(QMetaType(QMetaType::UInt)));
2794 query.
bindValue(
":DVBEITSCAN", dvb_eitscan);
2802 query.
prepare(
"SELECT MAX(cardid) FROM capturecard");
2814 inputid = query.
value(0).toInt();
2826 for (
uint childid : childids)
2830 LOG(VB_GENERAL, LOG_ERR,
LOC +
2831 QString(
"CardUtil: Failed to delete child input %1")
2843 query.
prepare(
"DELETE FROM capturecard WHERE cardid = :INPUTID");
2852 query.
prepare(
"UPDATE capturecard SET reclimit=reclimit-1 "
2853 "WHERE cardid = :INPUTID");
2862 query.
prepare(
"DELETE FROM inputgroup WHERE cardinputid = :INPUTID");
2873 query.
prepare(
"SELECT cardid FROM capturecard "
2874 "WHERE diseqcid = :DISEQCID LIMIT 1");
2880 else if (!query.
next())
2883 tree.
Store(inputid);
2896 return (query.
exec(
"TRUNCATE TABLE inputgroup") &&
2897 query.
exec(
"TRUNCATE TABLE diseqc_config") &&
2898 query.
exec(
"TRUNCATE TABLE diseqc_tree") &&
2899 query.
exec(
"TRUNCATE TABLE capturecard"));
2904 std::vector<uint> list;
2916 while (query.
next())
2917 list.push_back(query.
value(0).toUInt());
2925 std::vector<uint> list;
2929 "SELECT DISTINCT cardid "
2931 "WHERE schedorder <> 0 "
2932 "ORDER BY schedorder, cardid");
2938 while (query.
next())
2939 list.push_back(query.
value(0).toUInt());
2947 std::vector<uint> list;
2951 "SELECT DISTINCT cardid "
2953 "WHERE livetvorder <> 0 "
2954 "ORDER BY livetvorder, cardid");
2960 while (query.
next())
2961 list.push_back(query.
value(0).toUInt());
2969 QString devname = QString(device);
2971 LOG(VB_RECORD, LOG_DEBUG,
LOC + QString(
"DVB Device (%1)").arg(devname));
2981 QString
tmp = devname;
2982 tmp =
tmp.replace(
tmp.indexOf(
"frontend"), 8,
"dvr");
2985 LOG(VB_RECORD, LOG_DEBUG,
LOC +
2986 QString(
"Adapter Frontend dvr number matches (%1)").arg(
tmp));
2992 tmp =
tmp.replace(
tmp.indexOf(
"frontend"), 9,
"dvr0");
2995 LOG(VB_RECORD, LOG_DEBUG,
LOC +
2996 QString(
"Adapter Frontend dvr number not matching, using dvr0 instead (%1)").arg(
tmp));
3000 LOG(VB_RECORD, LOG_DEBUG,
LOC +
3001 QString(
"Adapter Frontend no dvr device found for (%1)").arg(devname));
3007 QString
tmp = devname;
3008 tmp =
tmp.replace(
tmp.indexOf(
"frontend"), 8,
"demux");
3011 LOG(VB_RECORD, LOG_DEBUG,
LOC +
3012 QString(
"Adapter Frontend demux number matches (%1)").arg(
tmp));
3018 tmp =
tmp.replace(
tmp.indexOf(
"frontend"), 9,
"demux0");
3021 LOG(VB_RECORD, LOG_DEBUG,
LOC +
3022 QString(
"Adapter Frontend demux number not matching, using demux0 instead (%1)").arg(
tmp));
3026 LOG(VB_RECORD, LOG_DEBUG,
LOC +
3027 QString(
"Adapter Frontend no demux device found for (%1)").arg(devname));
3033 QString
tmp = devname;
3034 tmp =
tmp.replace(
tmp.indexOf(
"frontend"), 8,
"ca");
3037 LOG(VB_RECORD, LOG_DEBUG,
LOC +
3038 QString(
"Adapter Frontend ca number matches (%1)").arg(
tmp));
3044 tmp =
tmp.replace(
tmp.indexOf(
"frontend"), 9,
"ca0");
3047 LOG(VB_RECORD, LOG_DEBUG,
LOC +
3048 QString(
"Adapter Frontend ca number not matching, using ca0 instead (%1)").arg(
tmp));
3052 LOG(VB_RECORD, LOG_DEBUG,
LOC +
3053 QString(
"Adapter Frontend no ca device found for (%1)").arg(devname));
3059 return devname.replace(devname.indexOf(
"frontend"), 8,
"audio");
3064 return devname.replace(devname.indexOf(
"frontend"), 8,
"video");
3084 hdhomerun_device_create_from_str(device.toLatin1(),
nullptr);
3088 const char *model = hdhomerun_device_get_model_str(hdhr);
3089 if (model && strstr(model,
"dvb"))
3091 hdhomerun_device_destroy(hdhr);
3095 hdhomerun_device_destroy(hdhr);
3110 hdhomerun_device_create_from_str(device.toLatin1(),
nullptr);
3114 const char *model = hdhomerun_device_get_model_str(hdhr);
3115 if (model && strstr(model,
"dvbc"))
3117 hdhomerun_device_destroy(hdhr);
3121 hdhomerun_device_destroy(hdhr);
3134 QString connectErr = QObject::tr(
"Unable to connect to device.");
3137 [[maybe_unused]]
bool deviceIsIP =
false;
3139 if (device.contains(
'.'))
3143 bool validID =
false;
3145 uint32_t dev = device.toUInt(&validID, 16);
3146 if (!validID || !hdhomerun_discover_validate_device_id(dev))
3147 return QObject::tr(
"Invalid Device ID");
3150 LOG(VB_GENERAL, LOG_INFO,
"CardUtil::GetHDHRdescription(" + device +
3151 ") - trying to locate device");
3154 hdhomerun_device_create_from_str(device.toLatin1(),
nullptr);
3156 return QObject::tr(
"Invalid Device ID or address.");
3158 const char *model = hdhomerun_device_get_model_str(hdhr);
3161 hdhomerun_device_destroy(hdhr);
3166 QString description = model;
3167 char *sVersion =
nullptr;
3168 uint32_t iVersion = 0;
3170 if (hdhomerun_device_get_version(hdhr, &sVersion, &iVersion))
3171 description += QObject::tr(
", firmware: %2").arg(sVersion);
3173 hdhomerun_device_destroy(hdhr);
3186 [[maybe_unused]]
const QString &ip,
3187 [[maybe_unused]]
const QString &tunerNo,
3188 [[maybe_unused]]
const QString &tunerType)
3190 QString connectErr = QObject::tr(
"Unable to connect to device.");
3205 QString apiVersionErr = QObject::tr(
"The VBox software version is too old (%1), we require %2")
3208 return apiVersionErr;
3214 return QString(
"V@Box TV Gateway - ID: %1, IP: %2, Tuner: %3-%4")
3215 .arg(
id, ip, tunerNo, tunerType);
3223static QString sys_dev(
uint device_num,
const QString& dev)
3225 return QString(
"/sys/class/asi/asirx%1/%2").arg(device_num).arg(dev);
3228static QString read_sys(
const QString& sys_dev)
3231 f.open(QIODevice::ReadOnly);
3232 QByteArray sdba = f.readAll();
3237static bool write_sys(
const QString& sys_dev,
const QString& str)
3240 f.open(QIODevice::WriteOnly);
3241 QByteArray ba = str.toLocal8Bit();
3243 for (
uint tries = 0; (offset < ba.size()) && tries < 5; tries++)
3245 qint64 written = f.write(ba.data()+offset, ba.size()-offset);
3258 struct stat statbuf {};
3259 if (stat(device.toLocal8Bit().constData(), &statbuf) < 0)
3262 *
error = QString(
"Unable to stat '%1'").arg(device) +
ENO;
3266 if (!S_ISCHR(statbuf.st_mode))
3269 *
error = QString(
"'%1' is not a character device").arg(device);
3273 if (!(statbuf.st_rdev & 0x0080))
3276 *
error = QString(
"'%1' not a DVEO ASI receiver").arg(device);
3280 int device_num = statbuf.st_rdev & 0x007f;
3283 QString sys_dev_contents = read_sys(sys_dev(device_num,
"dev"));
3284 QStringList sys_dev_clist = sys_dev_contents.split(
":");
3285 if (2 != sys_dev_clist.size())
3289 *
error = QString(
"Unable to read '%1'")
3290 .arg(sys_dev(device_num,
"dev"));
3294 if (sys_dev_clist[0].toUInt() != (statbuf.st_rdev>>8))
3297 *
error = QString(
"'%1' not a DVEO ASI device").arg(device);
3304 *
error =
"Not compiled with ASI support.";
3313 QString sys_bufsize_contents = read_sys(sys_dev(device_num,
"bufsize"));
3315 uint buf_size = sys_bufsize_contents.toUInt(&ok);
3320 *
error = QString(
"Failed to read buffer size from '%1'")
3321 .arg(sys_dev(device_num,
"bufsize"));
3328 *
error =
"Not compiled with ASI support.";
3337 QString sys_numbuffers_contents = read_sys(sys_dev(device_num,
"buffers"));
3339 uint num_buffers = sys_numbuffers_contents.toUInt(&ok);
3344 *
error = QString(
"Failed to read num buffers from '%1'")
3345 .arg(sys_dev(device_num,
"buffers"));
3352 *
error =
"Not compiled with ASI support.";
3360 QString sys_bufsize_contents = read_sys(sys_dev(device_num,
"mode"));
3362 uint mode = sys_bufsize_contents.toUInt(&ok);
3367 *
error = QString(
"Failed to read mode from '%1'")
3368 .arg(sys_dev(device_num,
"mode"));
3375 *
error =
"Not compiled with ASI support.";
3381 [[maybe_unused]]
uint mode,
3385 QString sys_bufsize_contents = read_sys(sys_dev(device_num,
"mode"));
3387 uint old_mode = sys_bufsize_contents.toUInt(&ok);
3388 if (ok && old_mode == mode)
3390 ok = write_sys(sys_dev(device_num,
"mode"), QString(
"%1\n").arg(mode));
3393 *
error = QString(
"Failed to set mode to %1 using '%2'")
3394 .arg(mode).arg(sys_dev(device_num,
"mode"));
3399 *
error =
"Not compiled with ASI support.";
3409bool CardUtil::IsVBoxPresent(
uint inputid)
3414 LOG(VB_GENERAL, LOG_ERR, QString(
"VBOX inputid (%1) not valid, redo mythtv-setup")
3425 LOG(VB_GENERAL, LOG_ERR, QString(
"VBOX chanid (%1) not found for inputid (%2), redo mythtv-setup")
3426 .arg(chanid).arg(inputid));
3431 std::chrono::milliseconds signal_timeout = 0ms;
3432 std::chrono::milliseconds tuning_timeout = 0ms;
3433 if (!
GetTimeouts(inputid,signal_timeout,tuning_timeout))
3435 LOG(VB_GENERAL, LOG_ERR, QString(
"Failed to get timeouts for inputid (%1)")
3443 query.prepare(
"SELECT url "
3444 "FROM iptv_channel "
3445 "WHERE chanid = :CHANID");
3446 query.bindValue(
":CHANID", chanid);
3450 else if (query.next())
3451 url = query.value(0).toString();
3454 QString ip = url.host();
3455 LOG(VB_GENERAL, LOG_INFO, QString(
"VBOX IP found (%1) for inputid (%2)")
3456 .arg(ip).arg(inputid));
3458 if (!
ping(ip,signal_timeout))
3460 LOG(VB_GENERAL, LOG_ERR, QString(
"VBOX at IP (%1) failed to respond to network ping for inputid (%2) timeout (%3)")
3461 .arg(ip).arg(inputid).arg(signal_timeout.count()));
3474bool CardUtil::IsSatIPPresent(
uint inputid)
3479 LOG(VB_GENERAL, LOG_ERR, QString(
"SatIP inputid (%1) not valid, redo mythtv-setup")
3490 LOG(VB_GENERAL, LOG_ERR, QString(
"SatIP chanid (%1) not found for inputid (%2), redo mythtv-setup")
3491 .arg(chanid).arg(inputid));
3496 std::chrono::milliseconds signal_timeout = 0ms;
3497 std::chrono::milliseconds tuning_timeout = 0ms;
3498 if (!
GetTimeouts(inputid,signal_timeout,tuning_timeout))
3500 LOG(VB_GENERAL, LOG_ERR, QString(
"Failed to get timeouts for inputid (%1)")
3507 QStringList devinfo = device.split(
":");
3508 if (devinfo.value(0).toUpper() ==
"UUID")
3510 QString deviceId = QString(
"uuid:%1").arg(devinfo.value(1));
3512 LOG(VB_GENERAL, LOG_INFO, QString(
"SatIP[%1] IP address %2 device %3")
3513 .arg(inputid).arg(ip, device));
3515 if (!
ping(ip, signal_timeout))
3517 LOG(VB_GENERAL, LOG_ERR, QString(
"SatIP[%1] at IP %2 failed to respond to network ping (timeout %3)")
3518 .arg(inputid).arg(ip).arg(signal_timeout.count()));
static uint clone_capturecard(uint src_inputid, uint orig_dst_inputid)
QString get_on_input(const QString &to_get, uint inputid)
bool set_on_input(const QString &to_set, uint inputid, const QString &value)
QMap< int, QString > InputNames
static uint GetQuickTuning(uint inputid, const QString &input_name)
static int GetASIDeviceNumber(const QString &device, QString *error=nullptr)
static int OpenVideoDevice(int inputid)
static bool IsTunerShared(uint inputidA, uint inputidB)
static bool IsTunerSharingCapable(const QString &rawtype)
static void GetDeviceInputNames(const QString &device, const QString &inputtype, QStringList &inputs)
static InputNames ProbeV4LVideoInputs(int videofd, bool &ok)
static int SetDefaultDeliverySystem(uint inputid, int fd)
static uint CreateInputGroup(const QString &name)
static QString GetFirewireChangerModel(uint inputid)
static QString GetRawInputType(uint inputid)
static std::vector< uint > GetSchedInputList(void)
static bool HDHRdoesDVB(const QString &device)
If the device is valid, check if the model does DVB.
static QString ProbeDVBFrontendName(const QString &device)
Returns the input type from the video device.
static QString ProbeDVBType(const QString &device)
static bool GetInputInfo(InputInfo &input, std::vector< uint > *groupids=nullptr)
static bool InputSetMaxRecordings(uint parentid, uint max_recordings)
static QStringList ProbeDeliverySystems(const QString &device)
static DTVModulationSystem GetDeliverySystem(uint inputid)
static uint CreateDeviceInputGroup(uint inputid, const QString &type, const QString &host, const QString &device)
static QString GetStartChannel(uint inputid)
static QString GetDeviceLabel(const QString &inputtype, const QString &videodevice)
static DTVModulationSystem ProbeBestDeliverySystem(int fd)
static int SetDeliverySystem(uint inputid)
static QString GetVBoxdesc(const QString &id, const QString &ip, const QString &tunerNo, const QString &tunerType)
Get a nicely formatted string describing the device.
static std::vector< uint > GetInputList(void)
static DTVModulationSystem GetOrProbeDeliverySystem(uint inputid, int fd)
static QStringList GetVideoDevices(const QString &rawtype, QString hostname=QString())
Returns the videodevices of the matching inputs, duplicates removed.
static QStringList GetInputTypeNames(uint sourceid)
Get a list of card input types for a source id.
static QMap< QString, QStringList > s_videoDeviceCache
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.
static bool IsInputTypePresent(const QString &rawtype, QString hostname=QString())
Returns true if the input type is present and connected to an input.
static uint GetASINumBuffers(uint device_num, QString *error=nullptr)
static bool HasTuner(const QString &rawtype, const QString &device)
static uint GetASIBufferSize(uint device_num, QString *error=nullptr)
static QString GetInputName(uint inputid)
static DTVTunerType GetTunerType(uint inputid)
static QString GetDeliverySystemFromDB(uint inputid)
static QStringList ProbeDVBInputs(const QString &device)
static bool UnlinkInputGroup(uint inputid, uint inputgroupid)
static bool LinkInputGroup(uint inputid, uint inputgroupid)
static InputNames ProbeV4LAudioInputs(int videofd, bool &ok)
static QString GetVideoDevice(uint inputid)
static bool GetTimeouts(uint inputid, std::chrono::milliseconds &signal_timeout, std::chrono::milliseconds &channel_timeout)
static bool IsInNeedOfExternalInputConf(uint inputid)
static std::chrono::milliseconds GetMinSignalMonitoringDelay(const QString &device)
static uint AddChildInput(uint parentid)
static DTVTunerType ProbeTunerType(int fd_frontend)
static void ClearVideoDeviceCache()
static QString GetHDHRdesc(const QString &device)
Get a nicely formatted string describing the device.
static uint CloneCard(uint src_inputid, uint dst_inputid)
static std::vector< uint > GetInputGroups(uint inputid)
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...
static std::vector< uint > GetChildInputIDs(uint inputid)
static std::vector< uint > GetLiveTVInputList(void)
static InputNames GetConfiguredDVBInputs(const QString &device)
static bool HDHRdoesDVBC(const QString &device)
If the device is valid, check if the model does DVB-C.
static uint GetSourceID(uint inputid)
static QString GetDeviceName(dvb_dev_type_t type, const QString &device)
static QStringList CapabilitiesToString(uint64_t capabilities)
static QString GetFirewireChangerNode(uint inputid)
static std::vector< uint > GetGroupInputIDs(uint inputgroupid)
static bool SetStartChannel(uint inputid, const QString &channum)
static QString GetDisplayName(uint inputid)
static bool DeleteInput(uint inputid)
static int GetASIMode(uint device_num, QString *error=nullptr)
static QString ProbeDefaultDeliverySystem(const QString &device)
static QStringList ProbeVideoDevices(const QString &rawtype)
static bool IsUniqueDisplayName(const QString &name, uint exclude_inputid)
static QString GetScanableInputTypes(void)
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)
static bool SetASIMode(uint device_num, uint mode, QString *error=nullptr)
static DTVModulationSystem ProbeCurrentDeliverySystem(const QString &device)
static std::vector< uint > GetConflictingInputs(uint inputid)
static uint GetChildInputCount(uint inputid)
static bool IsCableCardPresent(uint inputid, const QString &inputType)
static bool GetV4LInfo(int videofd, QString &input, QString &driver, uint32_t &version, uint32_t &capabilities)
static bool IsSingleInputType(const QString &rawtype)
static DTVTunerType GetTunerTypeFromMultiplex(uint mplexid)
static QList< InputInfo > GetAllInputInfo(bool virtTuners)
static DTVTunerType ConvertToTunerType(DTVModulationSystem delsys)
static QString ProbeSubTypeName(uint inputid)
QMap< QString, QString > InputTypes
static bool hasV4L2(int videofd)
static QStringList ProbeVideoInputs(const QString &device, const QString &inputtype=QString())
static bool IsDVBInputType(const QString &inputType)
Returns true iff the input_type is one of the DVB types.
static bool DeleteAllInputs(void)
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)
static InputTypes GetInputTypes(void)
static QStringList ProbeAudioInputs(const QString &device, const QString &inputtype=QString())
static uint GetDeviceInputGroup(uint inputid)
static int GetChannelValueInt(const QString &channel_field, uint sourceid, const QString &channum)
@ kModulationSystem_DVBS2
@ kModulationSystem_DVBT2
@ kModulationSystem_DVBC_ANNEX_A
@ kModulationSystem_UNDEFINED
@ kModulationSystem_DVBC_ANNEX_C
@ kModulationSystem_DVBC_ANNEX_B
bool Parse(const QString &_value)
static const int kTunerTypeDVBS2
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
DVB-S device settings class.
bool Store(uint card_input_id) const
Stores configuration chain to DB for specified card input id.
bool Load(uint card_input_id)
Loads configuration chain from DB for specified card input id.
bool Load(const QString &device)
Loads the device tree from the database.
void SetRoot(DiSEqCDevDevice *root)
Changes the root node of the tree.
bool Store(uint cardid, const QString &device="")
Stores the device tree to the database.
DiSEqCDevDevice * Root(void)
Retrieves the root node in the tree.
bool IsInNeedOfConf(void) const
static DiSEqCDevTree * FindTree(uint cardid)
Retrieve device tree.
QSqlQuery wrapper that fetches a DB connection from the connection pool.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
QVariant value(int i) const
bool isActive(void) const
void bindValueNoNull(const QString &placeholder, const QVariant &val)
Add a single binding, taking care not to set a NULL value.
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
QString GetHostName(void)
static void DBError(const QString &where, const MSqlQuery &query)
static QString findDeviceIP(const QString &deviceuuid)
static QStringList probeDevices(void)
bool HasTuner(void) const
QString DriverName(void) const
QString CardName(void) const
bool checkVersion(QString &version)
static QStringList probeDevices(void)
bool checkConnection(void)
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)
#define ENO
This can be appended to the LOG args with "+".
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
bool ping(const QString &host, std::chrono::milliseconds timeout)
Can we ping host within timeout seconds?
static MythSystemLegacyManager * manager
static constexpr const char * VBOX_MIN_API_VERSION