7 #if defined(USING_V4L2) || defined(USING_DVB)
16 #include "libmythbase/mythconfig.h"
37 #ifdef USING_HDHOMERUN
38 #include HDHOMERUN_HEADERFILE
50 #include <sys/types.h>
53 #include <dveo/master.h>
56 #define LOC QString("CardUtil: ")
60 QStringList inputTypes {};
63 inputTypes +=
"'DVB'";
67 inputTypes +=
"'V4L'";
68 inputTypes +=
"'MPEG'";
72 inputTypes +=
"'FREEBOX'";
76 inputTypes +=
"'VBOX'";
79 #ifdef USING_HDHOMERUN
80 inputTypes +=
"'HDHOMERUN'";
81 #endif // USING_HDHOMERUN
84 inputTypes +=
"'SATIP'";
88 inputTypes +=
"'ASI'";
92 inputTypes +=
"'CETON'";
95 #if !defined( USING_MINGW ) && !defined( _MSC_VER )
96 inputTypes +=
"'EXTERNAL'";
99 if (inputTypes.isEmpty())
102 return QString(
"(%1)").arg(inputTypes.join(
','));
106 const QString &inputType)
108 if (inputType ==
"HDHOMERUN")
110 #ifdef USING_HDHOMERUN
111 hdhomerun_tuner_status_t status {};
113 hdhomerun_device_t *hdhr =
114 hdhomerun_device_create_from_str(device.toLatin1(),
nullptr);
118 int oob = hdhomerun_device_get_oob_status(hdhr,
nullptr, &status);
122 if (oob > 0 && (strncmp(status.channel,
"none", 4) != 0))
124 LOG(VB_GENERAL, LOG_INFO,
"Cardutil: HDHomeRun Cablecard Present.");
125 hdhomerun_device_destroy(hdhr);
129 hdhomerun_device_destroy(hdhr);
134 if (inputType ==
"CETON")
139 QStringList parts = device.split(
"-");
140 if (parts.size() != 2)
142 LOG(VB_GENERAL, LOG_ERR,
143 QString(
"CardUtil: Ceton invalid device id %1").arg(device));
147 const QString& ip_address = parts.at(0);
149 QStringList tuner_parts = parts.at(1).split(
".");
150 if (tuner_parts.size() != 2)
152 LOG(VB_GENERAL, LOG_ERR,
LOC +
153 QString(
"CardUtil: Ceton invalid device id %1").arg(device));
157 uint tuner = tuner_parts.at(1).toUInt();
160 params.addQueryItem(
"i", QString::number(tuner));
161 params.addQueryItem(
"s",
"cas");
162 params.addQueryItem(
"v",
"CardStatus");
165 url.setScheme(
"http");
166 url.setHost(ip_address);
167 url.setPath(
"/get_var.json");
168 url.setQuery(params);
170 auto *request =
new QNetworkRequest();
171 request->setAttribute(QNetworkRequest::CacheLoadControlAttribute,
172 QNetworkRequest::AlwaysNetwork);
173 request->setUrl(url);
178 if (!
manager->download(request, &data))
180 LOG(VB_GENERAL, LOG_ERR,
181 QString(
"CardUtil: Ceton http request failed %1").arg(device));
185 QString response = QString(data);
187 static const QRegularExpression regex {
"^\\{ \"?result\"?: \"(.*)\" \\}$"};
188 auto match = regex.match(response);
189 if (!match.hasMatch())
191 LOG(VB_GENERAL, LOG_ERR,
192 QString(
"CardUtil: Ceton unexpected http response: %1").arg(response));
196 QString result = match.captured(1);
198 if (result ==
"Inserted")
200 LOG(VB_GENERAL, LOG_DEBUG,
"Cardutil: Ceton CableCARD present.");
204 LOG(VB_GENERAL, LOG_DEBUG,
"Cardutil: Ceton CableCARD not present.");
214 [[maybe_unused]]
const QString & device)
216 if (rawtype ==
"DVB" || rawtype ==
"HDHOMERUN" ||
217 rawtype ==
"FREEBOX" || rawtype ==
"CETON" ||
218 rawtype ==
"VBOX" || rawtype ==
"SATIP")
222 if (rawtype ==
"V4L2ENC")
229 if (rawtype ==
"EXTERNAL")
240 LOG(VB_GENERAL, LOG_DEBUG, QString(
"IsTunerShared(%1,%2)")
241 .arg(inputidA).arg(inputidB));
244 query.
prepare(
"SELECT videodevice, hostname, cardtype "
246 "WHERE ( (cardid = :INPUTID_A) OR "
247 " (cardid = :INPUTID_B) )");
260 const QString vdevice = query.
value(0).toString();
262 const QString inputtype = query.
value(2).toString();
270 bool ret = ((vdevice == query.
value(0).toString()) &&
272 (inputtype == query.
value(2).toString()));
274 LOG(VB_RECORD, LOG_DEBUG, QString(
"IsTunerShared(%1,%2) -> %3")
275 .arg(inputidA).arg(inputidB).arg(ret));
292 "SELECT count(cardtype) "
294 "WHERE capturecard.hostname = :HOSTNAME ";
296 if (!rawtype.isEmpty())
297 qstr +=
" AND capturecard.cardtype = :INPUTTYPE";
301 if (!rawtype.isEmpty())
302 query.
bindValue(
":INPUTTYPE", rawtype.toUpper());
314 count = query.
value(0).toUInt();
324 query.
prepare(
"SELECT DISTINCT cardtype, videodevice "
337 cardtype = query.
value(0).toString();
338 if (cardtype !=
"V4L2ENC")
340 inputtypes[cardtype] =
"";
348 QString driver_name =
"V4L2:" + v4l2.
DriverName();
349 inputtypes[driver_name] = v4l2.
CardName();
369 query.
prepare(
"SELECT cardtype "
371 "WHERE capturecard.sourceid = :SOURCEID "
372 "GROUP BY cardtype");
382 list.push_back(query.
value(0).toString());
403 "SELECT videodevice "
405 "WHERE hostname = :HOSTNAME";
407 if (!rawtype.isEmpty())
408 qstr +=
" AND cardtype = :INPUTTYPE";
412 if (!rawtype.isEmpty())
413 query.
bindValue(
":INPUTTYPE", rawtype.toUpper());
423 QMap<QString,bool> dup;
426 QString videodevice = query.
value(0).toString();
427 if (dup[videodevice])
430 list.push_back(videodevice);
431 dup[videodevice] =
true;
452 if (rawtype.toUpper() ==
"DVB")
454 QDir dir(
"/dev/dvb",
"adapter*", QDir::Name, QDir::Dirs);
455 QFileInfoList entries = dir.entryInfoList();
456 for (
const auto & it : qAsConst(entries))
458 QDir subdir(it.filePath(),
"frontend*", QDir::Name, QDir::Files | QDir::System);
459 const QFileInfoList subil = subdir.entryInfoList();
463 for (
const auto & subit : qAsConst(subil))
464 devs.push_back(subit.filePath());
467 else if (rawtype.toUpper() ==
"ASI")
469 QDir dir(
"/dev/",
"asirx*", QDir::Name, QDir::System);
470 QFileInfoList entries = dir.entryInfoList();
471 for (
const auto & it : qAsConst(entries))
475 devs.push_back(it.filePath());
481 #ifdef USING_HDHOMERUN
482 else if (rawtype.toUpper() ==
"HDHOMERUN")
484 #if HDHOMERUN_VERSION >= 20221010
485 struct hdhomerun_debug_t *dbg = hdhomerun_debug_create();
486 struct hdhomerun_discover_t *ds = hdhomerun_discover_create(dbg);
489 uint32_t
type { HDHOMERUN_DEVICE_TYPE_TUNER };
490 uint32_t flags { HDHOMERUN_DISCOVER_FLAGS_IPV4_GENERAL |
491 HDHOMERUN_DISCOVER_FLAGS_IPV6_GENERAL };
493 hdhomerun_discover2_find_devices_broadcast(ds, flags, &
type, 1);
497 LOG(VB_GENERAL, LOG_ERR,
"Error finding HDHomerun devices");
499 else if (result == 0)
501 LOG(VB_GENERAL, LOG_INFO,
"No HDHomerun devices found.");
506 struct hdhomerun_discover2_device_t *device = hdhomerun_discover2_iter_device_first(ds);
509 uint8_t tuners = hdhomerun_discover2_device_get_tuner_count(device);
510 uint32_t device_id = hdhomerun_discover2_device_get_device_id(device);
511 QString
id = QString(
"%1").arg(device_id, 0, 16, QChar(
'0')).toUpper();
512 auto *dev1 = hdhomerun_device_create_from_str(
id.toLatin1(),
nullptr);
513 QString model = hdhomerun_device_get_model_str(dev1);
516 struct sockaddr_storage saddr {};
517 struct hdhomerun_discover2_device_if_t *device_if {
nullptr };
520 device_if = hdhomerun_discover2_iter_device_if_first(device);
523 hdhomerun_discover2_device_if_get_ip_addr(device_if, &saddr);
524 ip = QHostAddress((
struct sockaddr *)&saddr);
525 LOG(VB_GENERAL, LOG_DEBUG,
526 QString(
"HDHomerun %1 has IP %2").arg(
id, ip.toString()));
527 device_if = hdhomerun_discover2_iter_device_if_next(device_if);
531 device_if = hdhomerun_discover2_iter_device_if_first(device);
532 if (
nullptr == device_if)
534 LOG(VB_GENERAL, LOG_WARNING,
535 QString(
"HDHomerun %1 has no IP addresses").arg(
id));
538 hdhomerun_discover2_device_if_get_ip_addr(device_if, &saddr);
539 ip = QHostAddress((
struct sockaddr *)&saddr);
542 QString hdhrdev = QString(
"%1 %2 %3").arg(
id, ip.toString(), model);
543 devs.push_back(hdhrdev);
544 LOG(VB_GENERAL, LOG_INFO,
545 QString(
"HDHomerun %1: IP %2, model %3, %4 tuners")
546 .arg(
id, ip.toString(), model).arg(tuners));
548 device = hdhomerun_discover2_iter_device_next(device);
550 hdhomerun_discover_destroy(ds);
551 hdhomerun_debug_destroy(dbg);
553 #else // HDHOMERUN_VERSION >= 20221010
554 uint32_t target_ip = 0;
555 uint32_t device_type = HDHOMERUN_DEVICE_TYPE_TUNER;
556 uint32_t device_id = HDHOMERUN_DEVICE_ID_WILDCARD;
557 const int max_count = 50;
558 std::array<hdhomerun_discover_device_t,max_count> result_list {};
560 int result = hdhomerun_discover_find_devices_custom_v2(
561 target_ip, device_type, device_id, result_list.data(), result_list.size());
565 LOG(VB_GENERAL, LOG_ERR,
"Error finding HDHomerun devices");
568 if (result >= max_count)
570 LOG(VB_GENERAL, LOG_WARNING,
571 "Warning: may be > 50 HDHomerun devices");
575 for (
int i = 0; i < result; i++)
577 QString
id = QString(
"%1").arg(result_list[i].device_id, 0, 16);
578 QString ip = QString(
"%1.%2.%3.%4")
579 .arg((result_list[i].ip_addr>>24) & 0xFF)
580 .arg((result_list[i].ip_addr>>16) & 0xFF)
581 .arg((result_list[i].ip_addr>> 8) & 0xFF)
582 .arg((result_list[i].ip_addr>> 0) & 0xFF);
585 hdhomerun_device_t *device = hdhomerun_device_create(
586 result_list[i].device_id, 0, 0,
nullptr);
589 model = hdhomerun_device_get_model_str(device);
590 hdhomerun_device_destroy(device);
593 QString hdhrdev =
id.toUpper() +
" " + ip +
" " + model;
594 devs.push_back(hdhrdev);
596 #endif // HDHOMERUN_VERSION >= 20221010
598 #endif // USING_HDHOMERUN
600 else if (rawtype.toUpper() ==
"SATIP")
604 #endif // USING_SATIP
606 else if (rawtype.toUpper() ==
"VBOX")
612 else if (rawtype.toUpper() ==
"CETON")
615 LOG(VB_GENERAL, LOG_INFO,
"CardUtil::ProbeVideoDevices: "
616 "TODO Probe Ceton devices");
618 #endif // USING_CETON
621 LOG(VB_GENERAL, LOG_ERR, QString(
"Raw Type: '%1' is not supported")
632 QStringList delsyslist;
641 struct dtv_property prop = {};
642 struct dtv_properties cmd = {};
644 prop.cmd = DTV_API_VERSION;
647 if (ioctl(fd_frontend, FE_GET_PROPERTY, &cmd) == 0)
649 LOG(VB_GENERAL, LOG_DEBUG,
650 QString(
"CardUtil(%1): ").arg(device) +
651 QString(
"dvb api version %1.%2").arg((prop.u.data>>8)&0xff).arg((prop.u.data)&0xff));
655 LOG(VB_GENERAL, LOG_ERR,
656 QString(
"CardUtil(%1) FE_GET_PROPERTY ioctl failed").arg(device) +
ENO);
663 QString msg =
"Delivery systems:";
664 for (
const auto & item : qAsConst(delsyslist))
669 LOG(VB_GENERAL, LOG_INFO, QString(
"CardUtil(%1): ").arg(device) + msg);
680 QStringList delsyslist;
683 struct dtv_property prop = {};
684 struct dtv_properties cmd = {};
686 prop.cmd = DTV_ENUM_DELSYS;
689 if (ioctl(fd_frontend, FE_GET_PROPERTY, &cmd) == 0)
691 for (
unsigned int i = 0; i < prop.u.buffer.len; i++)
698 LOG(VB_GENERAL, LOG_ERR,
LOC +
"FE_GET_PROPERTY ioctl failed " +
ENO);
723 QString ret =
"ERROR_UNKNOWN";
725 if (device.isEmpty())
730 ret = (
type.toString() !=
"UNKNOWN") ?
type.toString().toUpper() : ret;
732 LOG(VB_GENERAL, LOG_DEBUG,
LOC + QString(
"(%1) tuner type:%2 %3")
733 .arg(device).arg(
type).arg(ret));
744 QString ret =
"ERROR_UNKNOWN";
748 QByteArray dev = dvbdev.toLatin1();
749 int fd_frontend = open(dev.constData(), O_RDWR |
O_NONBLOCK);
753 struct dvb_frontend_info info {};
754 int err = ioctl(fd_frontend, FE_GET_INFO, &info);
758 return "ERROR_PROBE";
789 return ((name ==
"VLSI VES1x93 DVB-S") ||
790 (name ==
"ST STV0299 DVB-S"));
796 if (name.indexOf(
"DVB-S") >= 0)
798 if (name ==
"DiBcom 3000P/M-C DVB-T")
836 LOG(VB_GENERAL, LOG_ERR,
LOC +
837 QString(
"TODO Add to switch case delivery system:%2 %3")
838 .arg(delsys).arg(delsys.
toString()));
877 "FROM dtv_multiplex "
878 "WHERE dtv_multiplex.mplexid = :MPLEXID");
889 LOG(VB_GENERAL, LOG_ERR,
LOC +
890 QString(
"Could not find entry in dtv_multiplex for mplexid %1")
917 if (device.isEmpty())
926 LOG(VB_GENERAL, LOG_ERR,
LOC +
927 QString(
"open failed (%1)")
934 LOG(VB_GENERAL, LOG_DEBUG, QString(
"CardUtil(%1): delsys:%2 %3")
935 .arg(device).arg(delsys).arg(delsys.
toString()));
949 struct dtv_property prop = {};
950 struct dtv_properties cmd = {};
952 prop.cmd = DTV_DELIVERY_SYSTEM;
957 int ret = ioctl(fd_frontend, FE_GET_PROPERTY, &cmd);
960 LOG(VB_GENERAL, LOG_ERR,
LOC +
961 QString(
"FE_GET_PROPERTY ioctl failed (fd_frontend:%1)")
962 .arg(fd_frontend) +
ENO);
966 delsys = prop.u.data;
998 LOG(VB_GENERAL, LOG_INFO,
999 QString(
"CardUtil[%1]: ").arg(inputid) +
1000 QString(
"Update capturecard delivery system: %1").arg(delsys.
toString()));
1004 LOG(VB_GENERAL, LOG_ERR,
1005 QString(
"CardUtil[%1]: Error probing best delivery system").arg(inputid));
1006 return "ERROR_UNKNOWN";
1013 QString subtype =
"ERROR_UNKNOWN";
1019 LOG(VB_GENERAL, LOG_DEBUG,
1020 QString(
"CardUtil[%1]: subtype:%2").arg(inputid).arg(subtype));
1028 QString
t = inputType.toUpper();
1029 return (
t ==
"DVB") || (
t ==
"QPSK") || (
t ==
"QAM") || (
t ==
"OFDM") ||
1030 (
t ==
"ATSC") || (
t ==
"DVB_S2") || (
t ==
"DVB_T2");
1045 LOG(VB_GENERAL, LOG_INFO,
LOC +
1046 QString(
"Current delivery system: %1").arg(delsys.
toString()));
1049 QString msg =
"Supported delivery systems:";
1051 msg.append(delsyslist.join(
" "));
1052 LOG(VB_GENERAL, LOG_DEBUG,
LOC + msg);
1058 if (delsyslist.contains(newdelsys.
toString()))
1060 LOG(VB_GENERAL, LOG_INFO,
LOC +
1061 QString(
"Changing delivery system from %1 to %2")
1071 if (delsyslist.contains(newdelsys.
toString()))
1073 LOG(VB_GENERAL, LOG_INFO,
LOC +
1074 QString(
"Changing delivery system from %1 to %2")
1088 [[maybe_unused]]
int fd)
1103 LOG(VB_GENERAL, LOG_INFO,
1104 QString(
"CardUtil[%1]: ").arg(inputid) +
1105 QString(
"No capturecard delivery system in database, using: %1").arg(delsys.
toString()));
1116 [[maybe_unused]]
int fd)
1156 if (device.isEmpty())
1158 LOG(VB_GENERAL, LOG_DEBUG,
1159 QString(
"CardUtil[%1]: ").arg(inputid) +
1160 QString(
"inputid:%1 ").arg(inputid) +
1161 QString(
"delsys:%1").arg(delsys.toString()));
1166 if (fd_frontend < 0)
1168 LOG(VB_GENERAL, LOG_ERR,
1169 QString(
"CardUtil[%1]: ").arg(inputid) +
1170 QString(
"open failed (%1)").arg(device) +
ENO);
1183 [[maybe_unused]]
int fd)
1201 [[maybe_unused]]
int fd)
1206 LOG(VB_GENERAL, LOG_INFO,
1207 QString(
"CardUtil[%1]: ").arg(inputid) +
1208 QString(
"Set delivery system: %1").arg(delsys.toString()));
1210 struct dtv_property prop = {};
1211 struct dtv_properties cmd = {};
1213 prop.cmd = DTV_DELIVERY_SYSTEM;
1214 prop.u.data = delsys;
1218 ret = ioctl(fd, FE_SET_PROPERTY, &cmd);
1221 LOG(VB_GENERAL, LOG_ERR,
LOC +
1222 QString(
"[%1] FE_SET_PROPERTY ioctl failed")
1223 .arg(inputid) +
ENO);
1239 if (device.isEmpty())
1243 QByteArray dev = dvbdev.toLatin1();
1244 int fd_frontend = open(dev.constData(), O_RDWR |
O_NONBLOCK);
1245 if (fd_frontend < 0)
1247 LOG(VB_GENERAL, LOG_ERR,
LOC +
1248 QString(
"Can't open DVB frontend (%1) for %2.")
1249 .arg(dvbdev, device) +
ENO);
1258 QString(
"SELECT %1 ").arg(to_get) +
1260 "WHERE capturecard.cardid = :INPUTID");
1265 else if (query.
next())
1266 return query.
value(0).toString();
1279 QString(
"UPDATE capturecard SET %1 = :VALUE ").arg(to_set) +
1280 "WHERE cardid = :INPUTID");
1302 const QString& rawtype,
1303 const QString& inputname,
1306 std::vector<uint> list;
1315 "WHERE hostname = :HOSTNAME ";
1316 if (!videodevice.isEmpty())
1317 qstr +=
"AND videodevice = :DEVICE ";
1318 if (!inputname.isEmpty())
1319 qstr +=
"AND inputname = :INPUTNAME ";
1320 if (!rawtype.isEmpty())
1321 qstr +=
"AND cardtype = :INPUTTYPE ";
1322 qstr +=
"ORDER BY cardid";
1327 if (!videodevice.isEmpty())
1328 query.
bindValue(
":DEVICE", videodevice);
1329 if (!inputname.isEmpty())
1330 query.
bindValue(
":INPUTNAME", inputname);
1331 if (!rawtype.isEmpty())
1332 query.
bindValue(
":INPUTTYPE", rawtype.toUpper());
1338 while (query.
next())
1339 list.push_back(query.
value(0).toUInt());
1354 "WHERE parentid = :INPUTID";
1363 else if (query.
next())
1364 count = query.
value(0).toUInt();
1371 std::vector<uint> list;
1380 "WHERE parentid = :INPUTID "
1390 while (query.
next())
1391 list.push_back(query.
value(0).toUInt());
1399 uint dst_inputid = orig_dst_inputid;
1405 "DELETE FROM capturecard "
1406 "WHERE videodevice = 'temp_dummy'");
1415 "INSERT INTO capturecard "
1416 "SET videodevice = 'temp_dummy'");
1427 "WHERE videodevice = 'temp_dummy'");
1437 LOG(VB_GENERAL, LOG_ERR,
"clone_capturecard -- get temp id");
1441 dst_inputid = query.
value(0).toUInt();
1445 "SELECT videodevice, audiodevice, vbidevice, "
1446 " cardtype, hostname, signal_timeout, "
1447 " channel_timeout, dvb_wait_for_seqstart, dvb_on_demand, "
1448 " dvb_tuning_delay, dvb_diseqc_type, diseqcid, "
1449 " dvb_eitscan, inputname, sourceid, "
1450 " externalcommand, changer_device, changer_model, "
1451 " tunechan, startchan, displayname, "
1452 " dishnet_eit, recpriority, quicktune, "
1453 " livetvorder, reclimit, "
1455 " schedgroup, schedorder "
1457 "WHERE cardid = :INPUTID");
1458 query.
bindValue(
":INPUTID", src_inputid);
1467 LOG(VB_GENERAL, LOG_ERR,
"clone_cardinput -- get data 2");
1474 bool schedgroup = query.
value(26).toBool();
1475 uint schedorder = query.
value(27).toUInt();
1484 "UPDATE capturecard "
1485 "SET videodevice = :V0, "
1486 " audiodevice = :V1, "
1487 " vbidevice = :V2, "
1490 " signal_timeout = :V5, "
1491 " channel_timeout = :V6, "
1492 " dvb_wait_for_seqstart = :V7, "
1493 " dvb_on_demand = :V8, "
1494 " dvb_tuning_delay = :V9, "
1495 " dvb_diseqc_type = :V10, "
1497 " dvb_eitscan = :V12, "
1498 " inputname = :V13, "
1499 " sourceid = :V14, "
1500 " externalcommand = :V15, "
1501 " changer_device = :V16, "
1502 " changer_model = :V17, "
1503 " tunechan = :V18, "
1504 " startchan = :V19, "
1505 " displayname = :V20, "
1506 " dishnet_eit = :V21, "
1507 " recpriority = :V22, "
1508 " quicktune = :V23, "
1509 " livetvorder = :V24, "
1510 " reclimit = :V25, "
1511 " schedgroup = :SCHEDGROUP, "
1512 " schedorder = :SCHEDORDER, "
1513 " parentid = :PARENTID "
1514 "WHERE cardid = :INPUTID");
1515 for (
uint i = 0; i < 26; ++i)
1516 query2.
bindValue(QString(
":V%1").arg(i), query.
value(i).toString());
1517 query2.
bindValue(
":INPUTID", dst_inputid);
1518 query2.
bindValue(
":PARENTID", src_inputid);
1519 query2.
bindValue(
":SCHEDGROUP", schedgroup);
1520 query2.
bindValue(
":SCHEDORDER", schedorder);
1525 if (!orig_dst_inputid)
1533 for (
uint dst_grp : dst_grps)
1535 for (
uint src_grp : src_grps)
1540 if (diseqc.
Load(src_inputid))
1541 diseqc.
Store(dst_inputid);
1559 if (max_recordings < 1)
1561 LOG(VB_GENERAL, LOG_ERR,
LOC +
1562 "InputSetMaxRecording: max must be greater than zero.");
1569 for (
size_t i = cardids.size() + 1;
1570 (i > max_recordings) && !cardids.empty(); --i)
1577 for (
uint id : cardids)
1581 for (
size_t i = cardids.size() + 1; i < max_recordings; ++i)
1598 LOG(VB_GENERAL, LOG_INFO,
LOC +
1599 QString(
"Added child input %1 to parent %2")
1600 .arg(inputid).arg(parentid));
1602 query.
prepare(
"UPDATE capturecard "
1603 "SET reclimit = reclimit + 1 "
1604 "WHERE cardid = :PARENTID");
1611 LOG(VB_GENERAL, LOG_ERR,
LOC +
1612 QString(
"Failed to add child input to parent %1").arg(parentid));
1623 query.
prepare(
"SELECT changer_device "
1624 "FROM capturecard WHERE cardid = :INPUTID ");
1629 fwnode = query.
value(0).toString();
1640 query.
prepare(
"SELECT changer_model "
1641 "FROM capturecard WHERE cardid = :INPUTID ");
1646 fwnode = query.
value(0).toString();
1657 "SELECT DISTINCT cardid "
1659 "WHERE sourceid = :SOURCEID");
1662 std::vector<uint> list;
1670 while (query.
next())
1671 list.push_back(query.
value(0).toUInt());
1679 query.
prepare(
"UPDATE capturecard "
1680 "SET startchan = :CHANNUM "
1681 "WHERE cardid = :INPUTID");
1701 "inputname, sourceid, livetvorder, "
1702 "schedorder, displayname, recpriority, quicktune "
1704 "WHERE cardid = :INPUTID");
1732 QList<InputInfo> infoInputList;
1735 query.
prepare(
"SELECT cardid, "
1736 "inputname, sourceid, livetvorder, "
1737 "schedorder, displayname, recpriority, quicktune "
1738 "FROM capturecard");
1743 return infoInputList;
1746 while (query.
next())
1758 infoInputList.push_back(input);
1761 return infoInputList;
1766 InputInfo info(
"None", 0, inputid, 0, 0, 0);
1788 query.
prepare(
"SELECT startchan "
1790 "WHERE cardid = :INPUTID");
1797 else if (query.
next())
1799 startchan = query.
value(0).toString();
1803 if (!startchan.isEmpty())
1805 query.
prepare(
"SELECT channel.chanid "
1806 "FROM capturecard, channel "
1807 "WHERE capturecard.cardid = :INPUTID AND "
1808 " capturecard.sourceid = channel.sourceid AND "
1809 " channel.deleted IS NULL AND "
1810 " channel.visible > 0 AND "
1811 " channel.channum = :CHANNUM");
1819 else if (!query.
next())
1821 LOG(VB_GENERAL, LOG_WARNING,
1822 QString(
"CardUtil[%1]: ").arg(inputid) +
1823 QString(
"Channel %1 on inputid %2 is invalid").arg(startchan).arg(inputid));
1830 if (startchan.isEmpty())
1832 query.
prepare(
"SELECT channel.channum "
1833 "FROM capturecard, channel "
1834 "WHERE capturecard.cardid = :INPUTID AND "
1835 " capturecard.sourceid = channel.sourceid AND "
1836 " channel.deleted IS NULL AND "
1837 " channel.visible > 0 "
1845 else if (query.
next())
1847 startchan = query.
value(0).toString();
1851 if (startchan.isEmpty())
1853 LOG(VB_GENERAL, LOG_WARNING,
1854 QString(
"CardUtil[%1]: ").arg(inputid) +
1855 QString(
"No start channel found on inputid %1").arg(inputid));
1859 LOG(VB_GENERAL, LOG_DEBUG,
1860 QString(
"CardUtil[%1]: ").arg(inputid) +
1861 QString(
"Start channel %1 on inputid %2").arg(startchan).arg(inputid));
1873 query.
prepare(
"SELECT displayname "
1875 "WHERE cardid = :INPUTID");
1880 else if (query.
next())
1882 QString result = query.
value(0).toString();
1894 qsizetype idx { 0 };
1897 if ((idx = name.indexOf(
'/')) >= 0)
1899 matching = name.right(name.size() - idx -1);
1904 matching = name.right(2);
1909 query.
prepare(
"SELECT cardid, displayname "
1911 "WHERE parentid = 0 "
1912 " AND cardid <> :INPUTID ");
1913 query.
bindValue(
":INPUTID", exclude_inputid);
1921 while (query.
next())
1923 QString dn = query.
value(1).toString();
1924 if (!two && (idx = dn.indexOf(
'/')) >= 0)
1926 if (dn.right(dn.size() - idx - 1) == matching)
1929 else if (dn.right(2) == matching.right(2))
1942 "WHERE cardid = :INPUTID");
1946 else if (query.
next())
1947 return query.
value(0).toUInt();
1956 const uint sourceid,
1957 const QString &inputname,
1958 const QString &externalcommand,
1959 const QString &changer_device,
1960 const QString &changer_model,
1962 const QString &tunechan,
1963 const QString &startchan,
1964 const QString &displayname,
1966 const uint recpriority,
1967 const uint quicktune,
1968 const uint schedorder,
1969 const uint livetvorder)
1974 "UPDATE capturecard "
1975 "SET sourceid = :SOURCEID, "
1976 " inputname = :INPUTNAME, "
1977 " externalcommand = :EXTERNALCOMMAND, "
1978 " changer_device = :CHANGERDEVICE, "
1979 " changer_model = :CHANGERMODEL, "
1980 " tunechan = :TUNECHAN, "
1981 " startchan = :STARTCHAN, "
1982 " displayname = :DISPLAYNAME, "
1983 " dishnet_eit = :DISHNETEIT, "
1984 " recpriority = :RECPRIORITY, "
1985 " quicktune = :QUICKTUNE, "
1986 " schedorder = :SCHEDORDER, "
1987 " livetvorder = :LIVETVORDER "
1988 "WHERE cardid = :INPUTID AND "
1989 " inputname = 'None'");
1993 query.
bindValue(
":INPUTNAME", inputname);
1994 query.
bindValue(
":EXTERNALCOMMAND", externalcommand);
1995 query.
bindValue(
":CHANGERDEVICE", changer_device);
1996 query.
bindValue(
":CHANGERMODEL", changer_model);
1998 query.
bindValue(
":STARTCHAN", startchan);
2000 query.
bindValue(
":DISHNETEIT", dishnet_eit);
2001 query.
bindValue(
":RECPRIORITY", recpriority);
2002 query.
bindValue(
":QUICKTUNE", quicktune);
2003 query.
bindValue(
":SCHEDORDER", schedorder);
2004 query.
bindValue(
":LIVETVORDER", livetvorder);
2019 query.
prepare(
"SELECT inputgroupid FROM inputgroup "
2020 "WHERE inputgroupname = :GROUPNAME "
2030 return query.
value(0).toUInt();
2032 query.
prepare(
"SELECT MAX(inputgroupid) FROM inputgroup");
2039 uint inputgroupid = (query.
next()) ? query.
value(0).toUInt() + 1 : 1;
2042 "INSERT INTO inputgroup "
2043 " (cardinputid, inputgroupid, inputgroupname) "
2044 "VALUES (:INPUTID, :GROUPID, :GROUPNAME ) ");
2046 query.
bindValue(
":GROUPID", inputgroupid);
2054 return inputgroupid;
2058 const QString &
type,
2059 const QString &host,
2060 const QString &device)
2062 QString name = host +
'|' + device;
2063 if (
type ==
"FREEBOX" ||
type ==
"IMPORT" ||
2064 type ==
"DEMO" ||
type ==
"EXTERNAL" ||
2065 type ==
"HDHOMERUN" )
2066 name += QString(
"|%1").arg(inputid);
2074 "SELECT inputgroupid "
2076 "WHERE cardinputid = :INPUTID "
2077 " AND inputgroupname REGEXP '^[a-z_-]*\\\\|'");
2088 return query.
value(0).toUInt();
2099 "SELECT cardinputid, inputgroupid, inputgroupname "
2101 "WHERE inputgroupid = :GROUPID "
2102 "ORDER BY inputgroupid, cardinputid, inputgroupname");
2103 query.
bindValue(
":GROUPID", inputgroupid);
2112 while (query.
next()) {
2113 name = query.
value(2).toString();
2116 if (cardid == inputid)
2125 "INSERT INTO inputgroup "
2126 " (cardinputid, inputgroupid, inputgroupname) "
2127 "VALUES (:INPUTID, :GROUPID, :GROUPNAME ) ");
2130 query.
bindValue(
":GROUPID", inputgroupid);
2149 if (!inputid && !inputgroupid)
2152 "DELETE FROM inputgroup "
2153 "WHERE cardinputid NOT IN "
2154 "( SELECT cardid FROM capturecard )");
2159 "DELETE FROM inputgroup "
2160 "WHERE cardinputid = :INPUTID AND "
2161 " inputgroupid = :GROUPID ");
2164 query.
bindValue(
":GROUPID", inputgroupid);
2178 std::vector<uint> list;
2183 "SELECT inputgroupid "
2185 "WHERE cardinputid = :INPUTID "
2186 "ORDER BY inputgroupid, cardinputid, inputgroupname");
2196 while (query.
next())
2197 list.push_back(query.
value(0).toUInt());
2204 std::vector<uint> list;
2209 "SELECT DISTINCT cardid "
2210 "FROM capturecard, inputgroup "
2211 "WHERE inputgroupid = :GROUPID AND "
2212 " capturecard.cardid = inputgroup.cardinputid "
2215 query.
bindValue(
":GROUPID", inputgroupid);
2223 while (query.
next())
2224 list.push_back(query.
value(0).toUInt());
2231 std::vector<uint> inputids;
2236 "SELECT DISTINCT c.cardid "
2238 " SELECT inputgroupid "
2240 " WHERE cardinputid = :INPUTID1 "
2242 "JOIN inputgroup ig ON ig.inputgroupid = g.inputgroupid "
2243 "JOIN capturecard c ON c.cardid = ig.cardinputid "
2244 " AND c.cardid <> :INPUTID2 "
2245 "ORDER BY c.cardid");
2256 while (query.
next())
2258 inputids.push_back(query.
value(0).toUInt());
2263 QString msg = QString(
"CardUtil[%1]: GetConflictingInputs(%1) ").arg(inputid);
2265 for (
auto id : inputids)
2267 ids.append(QString::number(
id));
2269 msg.append(ids.join(
','));
2270 LOG(VB_RECORD, LOG_INFO, msg);
2276 std::chrono::milliseconds &signal_timeout,
2277 std::chrono::milliseconds &channel_timeout)
2281 "SELECT signal_timeout, channel_timeout "
2283 "WHERE cardid = :INPUTID");
2288 else if (query.
next())
2290 signal_timeout = std::max(std::chrono::milliseconds(query.
value(0).toInt()), 250ms);
2291 channel_timeout = std::max(std::chrono::milliseconds(query.
value(1).toInt()), 500ms);
2302 bool needsConf =
false;
2317 "WHERE cardid = :INPUTID AND "
2318 " inputname = :INPUTNAME");
2320 query.
bindValue(
":INPUTNAME", input_name);
2324 else if (query.
next())
2325 quicktune = query.
value(0).toUInt();
2333 struct v4l2_capability vcap {};
2335 return ((ioctl(videofd, VIDIOC_QUERYCAP, &vcap) >= 0) &&
2336 ((vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE) != 0U));
2337 #else // if !USING_V4L2
2339 #endif // !USING_V4L2
2343 int videofd, QString &input, QString &driver, uint32_t &
version,
2344 uint32_t &capabilities)
2355 struct v4l2_capability capability {};
2356 if (ioctl(videofd, VIDIOC_QUERYCAP, &capability) >= 0)
2358 input = QString::fromLatin1((
const char*)capability.card);
2359 driver = QString::fromLatin1((
const char*)capability.driver);
2361 capabilities = capability.capabilities;
2363 #endif // USING_V4L2
2365 static const QRegularExpression kBracketedDigitRE { R
"(\[[0-9]\]$)" };
2366 if (!driver.isEmpty())
2367 driver.remove( kBracketedDigitRE );
2369 return !input.isEmpty();
2378 bool usingv4l2 =
hasV4L2(videofd);
2380 struct v4l2_input vin {};
2381 while (usingv4l2 && (ioctl(videofd, VIDIOC_ENUMINPUT, &vin) >= 0))
2383 QString input((
char *)vin.name);
2384 list[vin.index] = input;
2395 list[0] =
"Television";
2398 #else // if !USING_V4L2
2399 list[-1] += QObject::tr(
"ERROR, Compile with V4L support to query inputs");
2400 #endif // !USING_V4L2
2410 bool usingv4l2 =
hasV4L2(videofd);
2413 struct v4l2_audio ain {};
2414 while (usingv4l2 && (ioctl(videofd, VIDIOC_ENUMAUDIO, &ain) >= 0))
2416 QString input((
char *)ain.name);
2417 list[ain.index] = input;
2427 #else // if !USING_V4L2
2428 list[-1] += QObject::tr(
2429 "ERROR, Compile with V4L support to query audio inputs");
2430 #endif // !USING_V4L2
2439 "SELECT cardid, inputname "
2441 "WHERE hostname = :HOSTNAME "
2442 " AND videodevice = :DEVICE "
2443 " AND parentid = 0 "
2444 " AND inputname <> 'None'");
2452 while (query.
next())
2453 list[query.
value(0).toUInt()] = query.
value(1).toString();
2467 struct fe_caps_name {
2472 std::array<fe_caps_name,31> fe_caps_name {{
2473 { FE_CAN_2G_MODULATION,
"CAN_2G_MODULATION" },
2474 { FE_CAN_8VSB,
"CAN_8VSB" },
2475 { FE_CAN_16VSB,
"CAN_16VSB" },
2476 { FE_CAN_BANDWIDTH_AUTO,
"CAN_BANDWIDTH_AUTO" },
2477 { FE_CAN_FEC_1_2,
"CAN_FEC_1_2" },
2478 { FE_CAN_FEC_2_3,
"CAN_FEC_2_3" },
2479 { FE_CAN_FEC_3_4,
"CAN_FEC_3_4" },
2480 { FE_CAN_FEC_4_5,
"CAN_FEC_4_5" },
2481 { FE_CAN_FEC_5_6,
"CAN_FEC_5_6" },
2482 { FE_CAN_FEC_6_7,
"CAN_FEC_6_7" },
2483 { FE_CAN_FEC_7_8,
"CAN_FEC_7_8" },
2484 { FE_CAN_FEC_8_9,
"CAN_FEC_8_9" },
2485 { FE_CAN_FEC_AUTO,
"CAN_FEC_AUTO" },
2486 { FE_CAN_GUARD_INTERVAL_AUTO,
"CAN_GUARD_INTERVAL_AUTO" },
2487 { FE_CAN_HIERARCHY_AUTO,
"CAN_HIERARCHY_AUTO" },
2488 { FE_CAN_INVERSION_AUTO,
"CAN_INVERSION_AUTO" },
2489 { FE_CAN_MULTISTREAM,
"CAN_MULTISTREAM" },
2490 { FE_CAN_MUTE_TS,
"CAN_MUTE_TS" },
2491 { FE_CAN_QAM_16,
"CAN_QAM_16" },
2492 { FE_CAN_QAM_32,
"CAN_QAM_32" },
2493 { FE_CAN_QAM_64,
"CAN_QAM_64" },
2494 { FE_CAN_QAM_128,
"CAN_QAM_128" },
2495 { FE_CAN_QAM_256,
"CAN_QAM_256" },
2496 { FE_CAN_QAM_AUTO,
"CAN_QAM_AUTO" },
2497 { FE_CAN_QPSK,
"CAN_QPSK" },
2498 { FE_CAN_RECOVER,
"CAN_RECOVER" },
2499 { FE_CAN_TRANSMISSION_MODE_AUTO,
"CAN_TRANSMISSION_MODE_AUTO" },
2500 { FE_CAN_TURBO_FEC,
"CAN_TURBO_FEC" },
2501 { FE_HAS_EXTENDED_CAPS,
"HAS_EXTENDED_CAPS" },
2502 { FE_IS_STUPID,
"IS_STUPID" },
2503 { FE_NEEDS_BENDING,
"NEEDS_BENDING" },
2506 for (
const auto & cap : fe_caps_name)
2508 if (capabilities & cap.idx)
2509 caps.append(cap.name);
2521 else if (
"DVB" == inputtype)
2531 LOG(VB_GENERAL, LOG_DEBUG, QString(
"ProbeAudioInputs(%1,%2)")
2532 .arg(device, inputtype));
2535 if (
"HDPVR" == inputtype ||
2536 "V4L2" == inputtype)
2546 QByteArray dev = device.toLatin1();
2547 int videofd = open(dev.constData(), O_RDWR);
2550 ret += QObject::tr(
"Could not open '%1' "
2551 "to probe its inputs.").arg(device);
2563 InputNames::iterator it;
2564 for (it = list.begin(); it != list.end(); ++it)
2575 LOG(VB_GENERAL, LOG_DEBUG, QString(
"ProbeV4LAudioInputs(%1)").arg(device));
2579 int videofd = open(device.toLatin1().constData(), O_RDWR);
2582 LOG(VB_GENERAL, LOG_ERR,
"ProbeAudioInputs() -> couldn't open device");
2583 ret += QObject::tr(
"Could not open '%1' to probe its inputs.")
2596 InputNames::iterator it;
2597 for (it = list.begin(); it != list.end(); ++it)
2612 InputNames::iterator it;
2613 for (it = list.begin(); it != list.end(); ++it)
2619 ret += QObject::tr(
"ERROR, Compile with DVB support to query inputs");
2626 const QString &videodevice)
2628 return QString(
"[ %1 : %2 ]").arg(inputtype, videodevice);
2634 query.
prepare(
"SELECT cardtype, videodevice "
2635 "FROM capturecard WHERE cardid = :INPUTID ");
2641 query.
value(1).toString());
2644 return "[ UNKNOWN ]";
2648 const QString &device,
2649 const QString &inputtype,
2650 QStringList &inputs)
2654 inputs +=
"MPEG2TS";
2655 else if (inputtype ==
"DVB")
2656 inputs +=
"DVBInput";
2662 const QString &audiodevice,
2663 const QString &vbidevice,
2664 const QString &inputtype,
2665 const uint audioratelimit,
2667 const uint dvb_swfilter,
2668 const uint dvb_sat_type,
2669 bool dvb_wait_for_seqstart,
2672 const uint dvb_diseqc_type,
2673 const uint firewire_speed,
2674 const QString &firewire_model,
2675 const uint firewire_connection,
2676 const std::chrono::milliseconds signal_timeout,
2677 const std::chrono::milliseconds channel_timeout,
2678 const uint dvb_tuning_delay,
2679 const uint contrast,
2680 const uint brightness,
2683 const uint diseqcid,
2689 "INSERT INTO capturecard "
2690 "(videodevice, audiodevice, vbidevice, cardtype, "
2691 "audioratelimit, hostname, dvb_swfilter, dvb_sat_type, "
2692 "dvb_wait_for_seqstart, skipbtaudio, dvb_on_demand, dvb_diseqc_type, "
2693 "firewire_speed, firewire_model, firewire_connection, signal_timeout, "
2694 "channel_timeout, dvb_tuning_delay, contrast, brightness, colour, "
2695 "hue, diseqcid, dvb_eitscan) "
2696 "VALUES (:VIDEODEVICE, :AUDIODEVICE, :VBIDEVICE, :INPUTTYPE, "
2697 ":AUDIORATELIMIT, :HOSTNAME, :DVBSWFILTER, :DVBSATTYPE, "
2698 ":DVBWAITFORSEQSTART, :SKIPBTAUDIO, :DVBONDEMAND, :DVBDISEQCTYPE, "
2699 ":FIREWIRESPEED, :FIREWIREMODEL, :FIREWIRECONNECTION, :SIGNALTIMEOUT, "
2700 ":CHANNELTIMEOUT, :DVBTUNINGDELAY, :CONTRAST, :BRIGHTNESS, :COLOUR, "
2701 ":HUE, :DISEQCID, :DVBEITSCAN ) ");
2703 query.
bindValue(
":VIDEODEVICE", videodevice);
2704 if (audiodevice.length() == 0)
2706 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2707 query.
bindValue(
":AUDIODEVICE", QVariant(QVariant::String));
2709 query.
bindValue(
":AUDIODEVICE", QVariant(QMetaType(QMetaType::QString)));
2713 query.
bindValue(
":AUDIODEVICE", audiodevice);
2714 if (vbidevice.length() == 0)
2716 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2717 query.
bindValue(
":VBIDEVICE", QVariant(QVariant::String));
2719 query.
bindValue(
":VBIDEVICE", QVariant(QMetaType(QMetaType::QString)));
2723 query.
bindValue(
":VBIDEVICE", vbidevice);
2724 query.
bindValue(
":INPUTTYPE", inputtype);
2725 if (audioratelimit == 0)
2727 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2728 query.
bindValue(
":AUDIORATELIMIT", QVariant(QVariant::UInt));
2730 query.
bindValue(
":AUDIORATELIMIT", QVariant(QMetaType(QMetaType::UInt)));
2734 query.
bindValue(
":AUDIORATELIMIT", audioratelimit);
2736 query.
bindValue(
":DVBSWFILTER", dvb_swfilter);
2737 query.
bindValue(
":DVBSATTYPE", dvb_sat_type);
2738 query.
bindValue(
":DVBWAITFORSEQSTART", dvb_wait_for_seqstart);
2739 query.
bindValue(
":SKIPBTAUDIO", skipbtaudio);
2740 query.
bindValue(
":DVBONDEMAND", dvb_on_demand);
2741 query.
bindValue(
":DVBDISEQCTYPE", dvb_diseqc_type);
2742 query.
bindValue(
":FIREWIRESPEED", firewire_speed);
2743 if (firewire_model.length() == 0)
2745 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2746 query.
bindValue(
":FIREWIREMODEL", QVariant(QVariant::String));
2748 query.
bindValue(
":FIREWIREMODEL", QVariant(QMetaType(QMetaType::QString)));
2752 query.
bindValue(
":FIREWIREMODEL", firewire_model);
2753 query.
bindValue(
":FIREWIRECONNECTION", firewire_connection);
2754 query.
bindValue(
":SIGNALTIMEOUT",
static_cast<qint64
>(signal_timeout.count()));
2755 query.
bindValue(
":CHANNELTIMEOUT",
static_cast<qint64
>(channel_timeout.count()));
2756 query.
bindValue(
":DVBTUNINGDELAY", dvb_tuning_delay);
2758 query.
bindValue(
":BRIGHTNESS", brightness);
2763 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2764 query.
bindValue(
":DISEQCID", QVariant(QVariant::UInt));
2766 query.
bindValue(
":DISEQCID", QVariant(QMetaType(QMetaType::UInt)));
2771 query.
bindValue(
":DVBEITSCAN", dvb_eitscan);
2779 query.
prepare(
"SELECT MAX(cardid) FROM capturecard");
2791 inputid = query.
value(0).toInt();
2803 for (
uint childid : childids)
2807 LOG(VB_GENERAL, LOG_ERR,
LOC +
2808 QString(
"CardUtil: Failed to delete child input %1")
2820 query.
prepare(
"DELETE FROM capturecard WHERE cardid = :INPUTID");
2829 query.
prepare(
"UPDATE capturecard SET reclimit=reclimit-1 "
2830 "WHERE cardid = :INPUTID");
2839 query.
prepare(
"DELETE FROM inputgroup WHERE cardinputid = :INPUTID");
2850 query.
prepare(
"SELECT cardid FROM capturecard "
2851 "WHERE diseqcid = :DISEQCID LIMIT 1");
2857 else if (!query.
next())
2860 tree.
Store(inputid);
2873 return (query.
exec(
"TRUNCATE TABLE inputgroup") &&
2874 query.
exec(
"TRUNCATE TABLE diseqc_config") &&
2875 query.
exec(
"TRUNCATE TABLE diseqc_tree") &&
2876 query.
exec(
"TRUNCATE TABLE capturecard") &&
2877 query.
exec(
"TRUNCATE TABLE iptv_channel"));
2882 std::vector<uint> list;
2894 while (query.
next())
2895 list.push_back(query.
value(0).toUInt());
2903 std::vector<uint> list;
2907 "SELECT DISTINCT cardid "
2909 "WHERE schedorder <> 0 "
2910 "ORDER BY schedorder, cardid");
2916 while (query.
next())
2917 list.push_back(query.
value(0).toUInt());
2925 std::vector<uint> list;
2929 "SELECT DISTINCT cardid "
2931 "WHERE livetvorder <> 0 "
2932 "ORDER BY livetvorder, cardid");
2938 while (query.
next())
2939 list.push_back(query.
value(0).toUInt());
2947 QString devname = QString(device);
2949 LOG(VB_RECORD, LOG_DEBUG,
LOC + QString(
"DVB Device (%1)").arg(devname));
2959 QString
tmp = devname;
2960 tmp =
tmp.replace(
tmp.indexOf(
"frontend"), 8,
"dvr");
2961 if (QFile::exists(
tmp))
2963 LOG(VB_RECORD, LOG_DEBUG,
LOC +
2964 QString(
"Adapter Frontend dvr number matches (%1)").arg(
tmp));
2970 tmp =
tmp.replace(
tmp.indexOf(
"frontend"), 9,
"dvr0");
2971 if (QFile::exists(
tmp))
2973 LOG(VB_RECORD, LOG_DEBUG,
LOC +
2974 QString(
"Adapter Frontend dvr number not matching, using dvr0 instead (%1)").arg(
tmp));
2978 LOG(VB_RECORD, LOG_DEBUG,
LOC +
2979 QString(
"Adapter Frontend no dvr device found for (%1)").arg(devname));
2985 QString
tmp = devname;
2986 tmp =
tmp.replace(
tmp.indexOf(
"frontend"), 8,
"demux");
2987 if (QFile::exists(
tmp))
2989 LOG(VB_RECORD, LOG_DEBUG,
LOC +
2990 QString(
"Adapter Frontend demux number matches (%1)").arg(
tmp));
2996 tmp =
tmp.replace(
tmp.indexOf(
"frontend"), 9,
"demux0");
2997 if (QFile::exists(
tmp))
2999 LOG(VB_RECORD, LOG_DEBUG,
LOC +
3000 QString(
"Adapter Frontend demux number not matching, using demux0 instead (%1)").arg(
tmp));
3004 LOG(VB_RECORD, LOG_DEBUG,
LOC +
3005 QString(
"Adapter Frontend no demux device found for (%1)").arg(devname));
3011 QString
tmp = devname;
3012 tmp =
tmp.replace(
tmp.indexOf(
"frontend"), 8,
"ca");
3013 if (QFile::exists(
tmp))
3015 LOG(VB_RECORD, LOG_DEBUG,
LOC +
3016 QString(
"Adapter Frontend ca number matches (%1)").arg(
tmp));
3022 tmp =
tmp.replace(
tmp.indexOf(
"frontend"), 9,
"ca0");
3023 if (QFile::exists(
tmp))
3025 LOG(VB_RECORD, LOG_DEBUG,
LOC +
3026 QString(
"Adapter Frontend ca number not matching, using ca0 instead (%1)").arg(
tmp));
3030 LOG(VB_RECORD, LOG_DEBUG,
LOC +
3031 QString(
"Adapter Frontend no ca device found for (%1)").arg(devname));
3037 return devname.replace(devname.indexOf(
"frontend"), 8,
"audio");
3042 return devname.replace(devname.indexOf(
"frontend"), 8,
"video");
3060 #ifdef USING_HDHOMERUN
3061 hdhomerun_device_t *hdhr =
3062 hdhomerun_device_create_from_str(device.toLatin1(),
nullptr);
3066 const char *model = hdhomerun_device_get_model_str(hdhr);
3067 if (model && strstr(model,
"dvb"))
3069 hdhomerun_device_destroy(hdhr);
3073 hdhomerun_device_destroy(hdhr);
3086 #ifdef USING_HDHOMERUN
3087 hdhomerun_device_t *hdhr =
3088 hdhomerun_device_create_from_str(device.toLatin1(),
nullptr);
3092 const char *model = hdhomerun_device_get_model_str(hdhr);
3093 if (model && strstr(model,
"dvbc"))
3095 hdhomerun_device_destroy(hdhr);
3099 hdhomerun_device_destroy(hdhr);
3112 QString connectErr = QObject::tr(
"Unable to connect to device.");
3114 #ifdef USING_HDHOMERUN
3115 [[maybe_unused]]
bool deviceIsIP =
false;
3117 if (device.contains(
'.'))
3121 bool validID =
false;
3123 uint32_t dev = device.toUInt(&validID, 16);
3124 if (!validID || !hdhomerun_discover_validate_device_id(dev))
3125 return QObject::tr(
"Invalid Device ID");
3128 LOG(VB_GENERAL, LOG_INFO,
"CardUtil::GetHDHRdescription(" + device +
3129 ") - trying to locate device");
3131 hdhomerun_device_t *hdhr =
3132 hdhomerun_device_create_from_str(device.toLatin1(),
nullptr);
3134 return QObject::tr(
"Invalid Device ID or address.");
3136 const char *model = hdhomerun_device_get_model_str(hdhr);
3139 hdhomerun_device_destroy(hdhr);
3144 QString description = model;
3145 char *sVersion =
nullptr;
3146 uint32_t iVersion = 0;
3148 if (hdhomerun_device_get_version(hdhr, &sVersion, &iVersion))
3149 description += QObject::tr(
", firmware: %2").arg(sVersion);
3151 hdhomerun_device_destroy(hdhr);
3164 [[maybe_unused]]
const QString &ip,
3165 [[maybe_unused]]
const QString &tunerNo,
3166 [[maybe_unused]]
const QString &tunerType)
3168 QString connectErr = QObject::tr(
"Unable to connect to device.");
3183 QString apiVersionErr = QObject::tr(
"The VBox software version is too old (%1), we require %2")
3186 return apiVersionErr;
3192 return QString(
"V@Box TV Gateway - ID: %1, IP: %2, Tuner: %3-%4")
3193 .arg(
id, ip, tunerNo, tunerType);
3203 return QString(
"/sys/class/asi/asirx%1/%2").arg(device_num).arg(dev);
3209 f.open(QIODevice::ReadOnly);
3210 QByteArray sdba = f.readAll();
3218 f.open(QIODevice::WriteOnly);
3219 QByteArray ba = str.toLocal8Bit();
3221 for (
uint tries = 0; (offset < ba.size()) && tries < 5; tries++)
3223 qint64 written = f.write(ba.data()+offset, ba.size()-offset);
3236 struct stat statbuf {};
3237 if (stat(device.toLocal8Bit().constData(), &statbuf) < 0)
3240 *
error = QString(
"Unable to stat '%1'").arg(device) +
ENO;
3244 if (!S_ISCHR(statbuf.st_mode))
3247 *
error = QString(
"'%1' is not a character device").arg(device);
3251 if (!(statbuf.st_rdev & 0x0080))
3254 *
error = QString(
"'%1' not a DVEO ASI receiver").arg(device);
3258 int device_num = statbuf.st_rdev & 0x007f;
3262 QStringList sys_dev_clist = sys_dev_contents.split(
":");
3263 if (2 != sys_dev_clist.size())
3267 *
error = QString(
"Unable to read '%1'")
3268 .arg(
sys_dev(device_num,
"dev"));
3272 if (sys_dev_clist[0].toUInt() != (statbuf.st_rdev>>8))
3275 *
error = QString(
"'%1' not a DVEO ASI device").arg(device);
3282 *
error =
"Not compiled with ASI support.";
3291 QString sys_bufsize_contents =
read_sys(
sys_dev(device_num,
"bufsize"));
3293 uint buf_size = sys_bufsize_contents.toUInt(&ok);
3298 *
error = QString(
"Failed to read buffer size from '%1'")
3299 .arg(
sys_dev(device_num,
"bufsize"));
3306 *
error =
"Not compiled with ASI support.";
3315 QString sys_numbuffers_contents =
read_sys(
sys_dev(device_num,
"buffers"));
3317 uint num_buffers = sys_numbuffers_contents.toUInt(&ok);
3322 *
error = QString(
"Failed to read num buffers from '%1'")
3323 .arg(
sys_dev(device_num,
"buffers"));
3330 *
error =
"Not compiled with ASI support.";
3340 uint mode = sys_bufsize_contents.toUInt(&ok);
3345 *
error = QString(
"Failed to read mode from '%1'")
3346 .arg(
sys_dev(device_num,
"mode"));
3353 *
error =
"Not compiled with ASI support.";
3359 [[maybe_unused]]
uint mode,
3365 uint old_mode = sys_bufsize_contents.toUInt(&ok);
3366 if (ok && old_mode == mode)
3371 *
error = QString(
"Failed to set mode to %1 using '%2'")
3372 .arg(mode).arg(
sys_dev(device_num,
"mode"));
3377 *
error =
"Not compiled with ASI support.";
3387 bool CardUtil::IsVBoxPresent(
uint inputid)
3392 LOG(VB_GENERAL, LOG_ERR, QString(
"VBOX inputid (%1) not valid, redo mythtv-setup")
3403 LOG(VB_GENERAL, LOG_ERR, QString(
"VBOX chanid (%1) not found for inputid (%2), redo mythtv-setup")
3404 .arg(chanid).arg(inputid));
3409 std::chrono::milliseconds signal_timeout = 0ms;
3410 std::chrono::milliseconds tuning_timeout = 0ms;
3411 if (!
GetTimeouts(inputid,signal_timeout,tuning_timeout))
3413 LOG(VB_GENERAL, LOG_ERR, QString(
"Failed to get timeouts for inputid (%1)")
3421 query.prepare(
"SELECT url "
3422 "FROM iptv_channel "
3423 "WHERE chanid = :CHANID");
3424 query.bindValue(
":CHANID", chanid);
3428 else if (query.next())
3429 url = query.value(0).toString();
3432 QString ip = url.host();
3433 LOG(VB_GENERAL, LOG_INFO, QString(
"VBOX IP found (%1) for inputid (%2)")
3434 .arg(ip).arg(inputid));
3436 if (!
ping(ip,signal_timeout))
3438 LOG(VB_GENERAL, LOG_ERR, QString(
"VBOX at IP (%1) failed to respond to network ping for inputid (%2) timeout (%3)")
3439 .arg(ip).arg(inputid).arg(signal_timeout.count()));
3452 bool CardUtil::IsSatIPPresent(
uint inputid)
3457 LOG(VB_GENERAL, LOG_ERR, QString(
"SatIP inputid (%1) not valid, redo mythtv-setup")
3468 LOG(VB_GENERAL, LOG_ERR, QString(
"SatIP chanid (%1) not found for inputid (%2), redo mythtv-setup")
3469 .arg(chanid).arg(inputid));
3474 std::chrono::milliseconds signal_timeout = 0ms;
3475 std::chrono::milliseconds tuning_timeout = 0ms;
3476 if (!
GetTimeouts(inputid,signal_timeout,tuning_timeout))
3478 LOG(VB_GENERAL, LOG_ERR, QString(
"Failed to get timeouts for inputid (%1)")
3485 QStringList devinfo = device.split(
":");
3486 if (devinfo.value(0).toUpper() ==
"UUID")
3488 QString deviceId = QString(
"uuid:%1").arg(devinfo.value(1));
3490 LOG(VB_GENERAL, LOG_INFO, QString(
"SatIP[%1] IP address %2 device %3")
3491 .arg(inputid).arg(ip, device));
3493 if (!
ping(ip, signal_timeout))
3495 LOG(VB_GENERAL, LOG_ERR, QString(
"SatIP[%1] at IP %2 failed to respond to network ping (timeout %3)")
3496 .arg(inputid).arg(ip).arg(signal_timeout.count()));