7 #if defined(USING_V4L2) || defined(USING_DVB)
14 #include <QRegularExpression>
18 #include "libmythbase/mythconfig.h"
39 #ifdef USING_HDHOMERUN
40 #include HDHOMERUN_HEADERFILE
52 #include <sys/types.h>
55 #include <dveo/master.h>
58 #define LOC QString("CardUtil: ")
62 QStringList inputTypes {};
65 inputTypes +=
"'DVB'";
69 inputTypes +=
"'V4L'";
70 inputTypes +=
"'MPEG'";
74 inputTypes +=
"'FREEBOX'";
78 inputTypes +=
"'VBOX'";
81 #ifdef USING_HDHOMERUN
82 inputTypes +=
"'HDHOMERUN'";
83 #endif // USING_HDHOMERUN
86 inputTypes +=
"'SATIP'";
90 inputTypes +=
"'ASI'";
94 inputTypes +=
"'CETON'";
97 #if !defined( USING_MINGW ) && !defined( _MSC_VER )
98 inputTypes +=
"'EXTERNAL'";
101 if (inputTypes.isEmpty())
104 return QString(
"(%1)").arg(inputTypes.join(
','));
108 const QString &inputType)
110 if (inputType ==
"HDHOMERUN")
112 #ifdef USING_HDHOMERUN
113 hdhomerun_tuner_status_t status {};
115 hdhomerun_device_t *hdhr =
116 hdhomerun_device_create_from_str(device.toLatin1(),
nullptr);
120 int oob = hdhomerun_device_get_oob_status(hdhr,
nullptr, &status);
124 if (oob > 0 && (strncmp(status.channel,
"none", 4) != 0))
126 LOG(VB_GENERAL, LOG_INFO,
"Cardutil: HDHomeRun Cablecard Present.");
127 hdhomerun_device_destroy(hdhr);
131 hdhomerun_device_destroy(hdhr);
136 if (inputType ==
"CETON")
141 QStringList parts = device.split(
"-");
142 if (parts.size() != 2)
144 LOG(VB_GENERAL, LOG_ERR,
145 QString(
"CardUtil: Ceton invalid device id %1").arg(device));
149 const QString& ip_address = parts.at(0);
151 QStringList tuner_parts = parts.at(1).split(
".");
152 if (tuner_parts.size() != 2)
154 LOG(VB_GENERAL, LOG_ERR,
LOC +
155 QString(
"CardUtil: Ceton invalid device id %1").arg(device));
159 uint tuner = tuner_parts.at(1).toUInt();
162 params.addQueryItem(
"i", QString::number(tuner));
163 params.addQueryItem(
"s",
"cas");
164 params.addQueryItem(
"v",
"CardStatus");
167 url.setScheme(
"http");
168 url.setHost(ip_address);
169 url.setPath(
"/get_var.json");
170 url.setQuery(params);
172 auto *request =
new QNetworkRequest();
173 request->setAttribute(QNetworkRequest::CacheLoadControlAttribute,
174 QNetworkRequest::AlwaysNetwork);
175 request->setUrl(url);
180 if (!
manager->download(request, &data))
182 LOG(VB_GENERAL, LOG_ERR,
183 QString(
"CardUtil: Ceton http request failed %1").arg(device));
187 QString response = QString(data);
189 static const QRegularExpression regex {
"^\\{ \"?result\"?: \"(.*)\" \\}$"};
190 auto match = regex.match(response);
191 if (!match.hasMatch())
193 LOG(VB_GENERAL, LOG_ERR,
194 QString(
"CardUtil: Ceton unexpected http response: %1").arg(response));
198 QString result = match.captured(1);
200 if (result ==
"Inserted")
202 LOG(VB_GENERAL, LOG_DEBUG,
"Cardutil: Ceton CableCARD present.");
206 LOG(VB_GENERAL, LOG_DEBUG,
"Cardutil: Ceton CableCARD not present.");
216 [[maybe_unused]]
const QString & device)
218 if (rawtype ==
"DVB" || rawtype ==
"HDHOMERUN" ||
219 rawtype ==
"FREEBOX" || rawtype ==
"CETON" ||
220 rawtype ==
"VBOX" || rawtype ==
"SATIP")
224 if (rawtype ==
"V4L2ENC")
231 if (rawtype ==
"EXTERNAL")
242 LOG(VB_GENERAL, LOG_DEBUG, QString(
"IsTunerShared(%1,%2)")
243 .arg(inputidA).arg(inputidB));
246 query.
prepare(
"SELECT videodevice, hostname, cardtype "
248 "WHERE ( (cardid = :INPUTID_A) OR "
249 " (cardid = :INPUTID_B) )");
262 const QString vdevice = query.
value(0).toString();
264 const QString inputtype = query.
value(2).toString();
272 bool ret = ((vdevice == query.
value(0).toString()) &&
274 (inputtype == query.
value(2).toString()));
276 LOG(VB_RECORD, LOG_DEBUG, QString(
"IsTunerShared(%1,%2) -> %3")
277 .arg(inputidA).arg(inputidB).arg(ret));
294 "SELECT count(cardtype) "
296 "WHERE capturecard.hostname = :HOSTNAME ";
298 if (!rawtype.isEmpty())
299 qstr +=
" AND capturecard.cardtype = :INPUTTYPE";
303 if (!rawtype.isEmpty())
304 query.
bindValue(
":INPUTTYPE", rawtype.toUpper());
316 count = query.
value(0).toUInt();
326 query.
prepare(
"SELECT DISTINCT cardtype, videodevice "
339 cardtype = query.
value(0).toString();
340 if (cardtype !=
"V4L2ENC")
342 inputtypes[cardtype] =
"";
350 QString driver_name =
"V4L2:" + v4l2.
DriverName();
351 inputtypes[driver_name] = v4l2.
CardName();
371 query.
prepare(
"SELECT cardtype "
373 "WHERE capturecard.sourceid = :SOURCEID "
374 "GROUP BY cardtype");
384 list.push_back(query.
value(0).toString());
405 "SELECT videodevice "
407 "WHERE hostname = :HOSTNAME";
409 if (!rawtype.isEmpty())
410 qstr +=
" AND cardtype = :INPUTTYPE";
414 if (!rawtype.isEmpty())
415 query.
bindValue(
":INPUTTYPE", rawtype.toUpper());
425 QMap<QString,bool> dup;
428 QString videodevice = query.
value(0).toString();
429 if (dup[videodevice])
432 list.push_back(videodevice);
433 dup[videodevice] =
true;
454 if (rawtype.toUpper() ==
"DVB")
456 QDir dir(
"/dev/dvb",
"adapter*", QDir::Name, QDir::Dirs);
457 QFileInfoList entries = dir.entryInfoList();
458 for (
const auto & it : std::as_const(entries))
460 QDir subdir(it.filePath(),
"frontend*", QDir::Name, QDir::Files | QDir::System);
461 const QFileInfoList subil = subdir.entryInfoList();
465 for (
const auto & subit : std::as_const(subil))
466 devs.push_back(subit.filePath());
469 else if (rawtype.toUpper() ==
"ASI")
471 QDir dir(
"/dev/",
"asirx*", QDir::Name, QDir::System);
472 QFileInfoList entries = dir.entryInfoList();
473 for (
const auto & it : std::as_const(entries))
477 devs.push_back(it.filePath());
483 #ifdef USING_HDHOMERUN
484 else if (rawtype.toUpper() ==
"HDHOMERUN")
486 #if HDHOMERUN_VERSION >= 20221010
487 struct hdhomerun_debug_t *dbg = hdhomerun_debug_create();
488 struct hdhomerun_discover_t *ds = hdhomerun_discover_create(dbg);
491 uint32_t
type { HDHOMERUN_DEVICE_TYPE_TUNER };
492 uint32_t flags { HDHOMERUN_DISCOVER_FLAGS_IPV4_GENERAL |
493 HDHOMERUN_DISCOVER_FLAGS_IPV6_GENERAL };
495 hdhomerun_discover2_find_devices_broadcast(ds, flags, &
type, 1);
499 LOG(VB_GENERAL, LOG_ERR,
"Error finding HDHomerun devices");
501 else if (result == 0)
503 LOG(VB_GENERAL, LOG_INFO,
"No HDHomerun devices found.");
508 struct hdhomerun_discover2_device_t *device = hdhomerun_discover2_iter_device_first(ds);
511 uint8_t tuners = hdhomerun_discover2_device_get_tuner_count(device);
512 uint32_t device_id = hdhomerun_discover2_device_get_device_id(device);
513 QString
id = QString(
"%1").arg(device_id, 0, 16, QChar(
'0')).toUpper();
514 auto *dev1 = hdhomerun_device_create_from_str(
id.toLatin1(),
nullptr);
515 QString model = hdhomerun_device_get_model_str(dev1);
518 struct sockaddr_storage saddr {};
519 struct hdhomerun_discover2_device_if_t *device_if {
nullptr };
522 device_if = hdhomerun_discover2_iter_device_if_first(device);
525 hdhomerun_discover2_device_if_get_ip_addr(device_if, &saddr);
526 ip = QHostAddress((
struct sockaddr *)&saddr);
527 LOG(VB_GENERAL, LOG_DEBUG,
528 QString(
"HDHomerun %1 has IP %2").arg(
id, ip.toString()));
529 device_if = hdhomerun_discover2_iter_device_if_next(device_if);
533 device_if = hdhomerun_discover2_iter_device_if_first(device);
534 if (
nullptr == device_if)
536 LOG(VB_GENERAL, LOG_WARNING,
537 QString(
"HDHomerun %1 has no IP addresses").arg(
id));
540 hdhomerun_discover2_device_if_get_ip_addr(device_if, &saddr);
541 ip = QHostAddress((
struct sockaddr *)&saddr);
544 QString hdhrdev = QString(
"%1 %2 %3").arg(
id, ip.toString(), model);
545 devs.push_back(hdhrdev);
546 LOG(VB_GENERAL, LOG_INFO,
547 QString(
"HDHomerun %1: IP %2, model %3, %4 tuners")
548 .arg(
id, ip.toString(), model).arg(tuners));
550 device = hdhomerun_discover2_iter_device_next(device);
552 hdhomerun_discover_destroy(ds);
553 hdhomerun_debug_destroy(dbg);
555 #else // HDHOMERUN_VERSION >= 20221010
556 uint32_t target_ip = 0;
557 uint32_t device_type = HDHOMERUN_DEVICE_TYPE_TUNER;
558 uint32_t device_id = HDHOMERUN_DEVICE_ID_WILDCARD;
559 const int max_count = 50;
560 std::array<hdhomerun_discover_device_t,max_count> result_list {};
562 int result = hdhomerun_discover_find_devices_custom_v2(
563 target_ip, device_type, device_id, result_list.data(), result_list.size());
567 LOG(VB_GENERAL, LOG_ERR,
"Error finding HDHomerun devices");
570 if (result >= max_count)
572 LOG(VB_GENERAL, LOG_WARNING,
573 "Warning: may be > 50 HDHomerun devices");
577 for (
int i = 0; i < result; i++)
579 QString
id = QString(
"%1").arg(result_list[i].device_id, 0, 16);
580 QString ip = QString(
"%1.%2.%3.%4")
581 .arg((result_list[i].ip_addr>>24) & 0xFF)
582 .arg((result_list[i].ip_addr>>16) & 0xFF)
583 .arg((result_list[i].ip_addr>> 8) & 0xFF)
584 .arg((result_list[i].ip_addr>> 0) & 0xFF);
587 hdhomerun_device_t *device = hdhomerun_device_create(
588 result_list[i].device_id, 0, 0,
nullptr);
591 model = hdhomerun_device_get_model_str(device);
592 hdhomerun_device_destroy(device);
595 QString hdhrdev =
id.toUpper() +
" " + ip +
" " + model;
596 devs.push_back(hdhrdev);
598 #endif // HDHOMERUN_VERSION >= 20221010
600 #endif // USING_HDHOMERUN
602 else if (rawtype.toUpper() ==
"SATIP")
606 #endif // USING_SATIP
608 else if (rawtype.toUpper() ==
"VBOX")
614 else if (rawtype.toUpper() ==
"CETON")
617 LOG(VB_GENERAL, LOG_INFO,
"CardUtil::ProbeVideoDevices: "
618 "TODO Probe Ceton devices");
620 #endif // USING_CETON
623 LOG(VB_GENERAL, LOG_ERR, QString(
"Raw Type: '%1' is not supported")
634 QStringList delsyslist;
643 struct dtv_property prop = {};
644 struct dtv_properties cmd = {};
646 prop.cmd = DTV_API_VERSION;
649 if (ioctl(fd_frontend, FE_GET_PROPERTY, &cmd) == 0)
651 LOG(VB_GENERAL, LOG_DEBUG,
652 QString(
"CardUtil(%1): ").arg(device) +
653 QString(
"dvb api version %1.%2").arg((prop.u.data>>8)&0xff).arg((prop.u.data)&0xff));
657 LOG(VB_GENERAL, LOG_ERR,
658 QString(
"CardUtil(%1) FE_GET_PROPERTY ioctl failed").arg(device) +
ENO);
665 QString msg =
"Delivery systems:";
666 for (
const auto & item : std::as_const(delsyslist))
671 LOG(VB_GENERAL, LOG_INFO, QString(
"CardUtil(%1): ").arg(device) + msg);
682 QStringList delsyslist;
685 struct dtv_property prop = {};
686 struct dtv_properties cmd = {};
688 prop.cmd = DTV_ENUM_DELSYS;
691 if (ioctl(fd_frontend, FE_GET_PROPERTY, &cmd) == 0)
693 for (
unsigned int i = 0; i < prop.u.buffer.len; i++)
700 LOG(VB_GENERAL, LOG_ERR,
LOC +
"FE_GET_PROPERTY ioctl failed " +
ENO);
725 QString ret =
"ERROR_UNKNOWN";
727 if (device.isEmpty())
732 ret = (
type.toString() !=
"UNKNOWN") ?
type.toString().toUpper() : ret;
734 LOG(VB_GENERAL, LOG_DEBUG,
LOC + QString(
"(%1) tuner type:%2 %3")
735 .arg(device).arg(
type).arg(ret));
746 QString ret =
"ERROR_UNKNOWN";
750 QByteArray dev = dvbdev.toLatin1();
751 int fd_frontend = open(dev.constData(), O_RDWR |
O_NONBLOCK);
755 struct dvb_frontend_info info {};
756 int err = ioctl(fd_frontend, FE_GET_INFO, &info);
760 return "ERROR_PROBE";
791 return ((name ==
"VLSI VES1x93 DVB-S") ||
792 (name ==
"ST STV0299 DVB-S"));
798 if (name.indexOf(
"DVB-S") >= 0)
800 if (name ==
"DiBcom 3000P/M-C DVB-T")
838 LOG(VB_GENERAL, LOG_ERR,
LOC +
839 QString(
"TODO Add to switch case delivery system:%2 %3")
840 .arg(delsys).arg(delsys.
toString()));
879 "FROM dtv_multiplex "
880 "WHERE dtv_multiplex.mplexid = :MPLEXID");
891 LOG(VB_GENERAL, LOG_ERR,
LOC +
892 QString(
"Could not find entry in dtv_multiplex for mplexid %1")
919 if (device.isEmpty())
928 LOG(VB_GENERAL, LOG_ERR,
LOC +
929 QString(
"open failed (%1)")
936 LOG(VB_GENERAL, LOG_DEBUG, QString(
"CardUtil(%1): delsys:%2 %3")
937 .arg(device).arg(delsys).arg(delsys.
toString()));
951 struct dtv_property prop = {};
952 struct dtv_properties cmd = {};
954 prop.cmd = DTV_DELIVERY_SYSTEM;
959 int ret = ioctl(fd_frontend, FE_GET_PROPERTY, &cmd);
962 LOG(VB_GENERAL, LOG_ERR,
LOC +
963 QString(
"FE_GET_PROPERTY ioctl failed (fd_frontend:%1)")
964 .arg(fd_frontend) +
ENO);
968 delsys = prop.u.data;
1000 LOG(VB_GENERAL, LOG_INFO,
1001 QString(
"CardUtil[%1]: ").arg(inputid) +
1002 QString(
"Update capturecard delivery system: %1").arg(delsys.
toString()));
1006 LOG(VB_GENERAL, LOG_ERR,
1007 QString(
"CardUtil[%1]: Error probing best delivery system").arg(inputid));
1008 return "ERROR_UNKNOWN";
1015 QString subtype =
"ERROR_UNKNOWN";
1021 LOG(VB_GENERAL, LOG_DEBUG,
1022 QString(
"CardUtil[%1]: subtype:%2").arg(inputid).arg(subtype));
1030 QString
t = inputType.toUpper();
1031 return (
t ==
"DVB") || (
t ==
"QPSK") || (
t ==
"QAM") || (
t ==
"OFDM") ||
1032 (
t ==
"ATSC") || (
t ==
"DVB_S2") || (
t ==
"DVB_T2");
1047 LOG(VB_GENERAL, LOG_INFO,
LOC +
1048 QString(
"Current delivery system: %1").arg(delsys.
toString()));
1051 QString msg =
"Supported delivery systems:";
1053 msg.append(delsyslist.join(
" "));
1054 LOG(VB_GENERAL, LOG_DEBUG,
LOC + msg);
1060 if (delsyslist.contains(newdelsys.
toString()))
1062 LOG(VB_GENERAL, LOG_INFO,
LOC +
1063 QString(
"Changing delivery system from %1 to %2")
1073 if (delsyslist.contains(newdelsys.
toString()))
1075 LOG(VB_GENERAL, LOG_INFO,
LOC +
1076 QString(
"Changing delivery system from %1 to %2")
1090 [[maybe_unused]]
int fd)
1105 LOG(VB_GENERAL, LOG_INFO,
1106 QString(
"CardUtil[%1]: ").arg(inputid) +
1107 QString(
"No capturecard delivery system in database, using: %1").arg(delsys.
toString()));
1118 [[maybe_unused]]
int fd)
1158 if (device.isEmpty())
1160 LOG(VB_GENERAL, LOG_DEBUG,
1161 QString(
"CardUtil[%1]: ").arg(inputid) +
1162 QString(
"inputid:%1 ").arg(inputid) +
1163 QString(
"delsys:%1").arg(delsys.toString()));
1168 if (fd_frontend < 0)
1170 LOG(VB_GENERAL, LOG_ERR,
1171 QString(
"CardUtil[%1]: ").arg(inputid) +
1172 QString(
"open failed (%1)").arg(device) +
ENO);
1185 [[maybe_unused]]
int fd)
1203 [[maybe_unused]]
int fd)
1208 LOG(VB_GENERAL, LOG_INFO,
1209 QString(
"CardUtil[%1]: ").arg(inputid) +
1210 QString(
"Set delivery system: %1").arg(delsys.toString()));
1212 struct dtv_property prop = {};
1213 struct dtv_properties cmd = {};
1215 prop.cmd = DTV_DELIVERY_SYSTEM;
1216 prop.u.data = delsys;
1220 ret = ioctl(fd, FE_SET_PROPERTY, &cmd);
1223 LOG(VB_GENERAL, LOG_ERR,
LOC +
1224 QString(
"[%1] FE_SET_PROPERTY ioctl failed")
1225 .arg(inputid) +
ENO);
1241 if (device.isEmpty())
1245 QByteArray dev = dvbdev.toLatin1();
1246 int fd_frontend = open(dev.constData(), O_RDWR |
O_NONBLOCK);
1247 if (fd_frontend < 0)
1249 LOG(VB_GENERAL, LOG_ERR,
LOC +
1250 QString(
"Can't open DVB frontend (%1) for %2.")
1251 .arg(dvbdev, device) +
ENO);
1260 QString(
"SELECT %1 ").arg(to_get) +
1262 "WHERE capturecard.cardid = :INPUTID");
1267 else if (query.
next())
1268 return query.
value(0).toString();
1281 QString(
"UPDATE capturecard SET %1 = :VALUE ").arg(to_set) +
1282 "WHERE cardid = :INPUTID");
1304 const QString& rawtype,
1305 const QString& inputname,
1308 std::vector<uint> list;
1317 "WHERE hostname = :HOSTNAME ";
1318 if (!videodevice.isEmpty())
1319 qstr +=
"AND videodevice = :DEVICE ";
1320 if (!inputname.isEmpty())
1321 qstr +=
"AND inputname = :INPUTNAME ";
1322 if (!rawtype.isEmpty())
1323 qstr +=
"AND cardtype = :INPUTTYPE ";
1324 qstr +=
"ORDER BY cardid";
1329 if (!videodevice.isEmpty())
1330 query.
bindValue(
":DEVICE", videodevice);
1331 if (!inputname.isEmpty())
1332 query.
bindValue(
":INPUTNAME", inputname);
1333 if (!rawtype.isEmpty())
1334 query.
bindValue(
":INPUTTYPE", rawtype.toUpper());
1340 while (query.
next())
1341 list.push_back(query.
value(0).toUInt());
1356 "WHERE parentid = :INPUTID";
1365 else if (query.
next())
1366 count = query.
value(0).toUInt();
1373 std::vector<uint> list;
1382 "WHERE parentid = :INPUTID "
1392 while (query.
next())
1393 list.push_back(query.
value(0).toUInt());
1401 uint dst_inputid = orig_dst_inputid;
1407 "DELETE FROM capturecard "
1408 "WHERE videodevice = 'temp_dummy'");
1417 "INSERT INTO capturecard "
1418 "SET videodevice = 'temp_dummy'");
1429 "WHERE videodevice = 'temp_dummy'");
1439 LOG(VB_GENERAL, LOG_ERR,
"clone_capturecard -- get temp id");
1443 dst_inputid = query.
value(0).toUInt();
1447 "SELECT videodevice, audiodevice, vbidevice, "
1448 " cardtype, hostname, signal_timeout, "
1449 " channel_timeout, dvb_wait_for_seqstart, dvb_on_demand, "
1450 " dvb_tuning_delay, dvb_diseqc_type, diseqcid, "
1451 " dvb_eitscan, inputname, sourceid, "
1452 " externalcommand, changer_device, changer_model, "
1453 " tunechan, startchan, displayname, "
1454 " dishnet_eit, recpriority, quicktune, "
1455 " livetvorder, reclimit, "
1457 " schedgroup, schedorder "
1459 "WHERE cardid = :INPUTID");
1460 query.
bindValue(
":INPUTID", src_inputid);
1469 LOG(VB_GENERAL, LOG_ERR,
"clone_cardinput -- get data 2");
1476 bool schedgroup = query.
value(26).toBool();
1477 uint schedorder = query.
value(27).toUInt();
1486 "UPDATE capturecard "
1487 "SET videodevice = :V0, "
1488 " audiodevice = :V1, "
1489 " vbidevice = :V2, "
1492 " signal_timeout = :V5, "
1493 " channel_timeout = :V6, "
1494 " dvb_wait_for_seqstart = :V7, "
1495 " dvb_on_demand = :V8, "
1496 " dvb_tuning_delay = :V9, "
1497 " dvb_diseqc_type = :V10, "
1499 " dvb_eitscan = :V12, "
1500 " inputname = :V13, "
1501 " sourceid = :V14, "
1502 " externalcommand = :V15, "
1503 " changer_device = :V16, "
1504 " changer_model = :V17, "
1505 " tunechan = :V18, "
1506 " startchan = :V19, "
1507 " displayname = :V20, "
1508 " dishnet_eit = :V21, "
1509 " recpriority = :V22, "
1510 " quicktune = :V23, "
1511 " livetvorder = :V24, "
1512 " reclimit = :V25, "
1513 " schedgroup = :SCHEDGROUP, "
1514 " schedorder = :SCHEDORDER, "
1515 " parentid = :PARENTID "
1516 "WHERE cardid = :INPUTID");
1517 for (
uint i = 0; i < 26; ++i)
1518 query2.
bindValue(QString(
":V%1").arg(i), query.
value(i).toString());
1519 query2.
bindValue(
":INPUTID", dst_inputid);
1520 query2.
bindValue(
":PARENTID", src_inputid);
1521 query2.
bindValue(
":SCHEDGROUP", schedgroup);
1522 query2.
bindValue(
":SCHEDORDER", schedorder);
1527 if (!orig_dst_inputid)
1535 for (
uint dst_grp : dst_grps)
1537 for (
uint src_grp : src_grps)
1542 if (diseqc.
Load(src_inputid))
1543 diseqc.
Store(dst_inputid);
1561 if (max_recordings < 1)
1563 LOG(VB_GENERAL, LOG_ERR,
LOC +
1564 "InputSetMaxRecording: max must be greater than zero.");
1571 for (
size_t i = cardids.size() + 1;
1572 (i > max_recordings) && !cardids.empty(); --i)
1579 for (
uint id : cardids)
1583 for (
size_t i = cardids.size() + 1; i < max_recordings; ++i)
1600 LOG(VB_GENERAL, LOG_INFO,
LOC +
1601 QString(
"Added child input %1 to parent %2")
1602 .arg(inputid).arg(parentid));
1604 query.
prepare(
"UPDATE capturecard "
1605 "SET reclimit = reclimit + 1 "
1606 "WHERE cardid = :PARENTID");
1613 LOG(VB_GENERAL, LOG_ERR,
LOC +
1614 QString(
"Failed to add child input to parent %1").arg(parentid));
1625 query.
prepare(
"SELECT changer_device "
1626 "FROM capturecard WHERE cardid = :INPUTID ");
1631 fwnode = query.
value(0).toString();
1642 query.
prepare(
"SELECT changer_model "
1643 "FROM capturecard WHERE cardid = :INPUTID ");
1648 fwnode = query.
value(0).toString();
1659 "SELECT DISTINCT cardid "
1661 "WHERE sourceid = :SOURCEID");
1664 std::vector<uint> list;
1672 while (query.
next())
1673 list.push_back(query.
value(0).toUInt());
1681 query.
prepare(
"UPDATE capturecard "
1682 "SET startchan = :CHANNUM "
1683 "WHERE cardid = :INPUTID");
1703 "inputname, sourceid, livetvorder, "
1704 "schedorder, displayname, recpriority, quicktune "
1706 "WHERE cardid = :INPUTID");
1735 QList<InputInfo> infoInputList;
1738 QString queryStr =
"SELECT cardid, "
1739 "inputname, sourceid, livetvorder, "
1740 "schedorder, displayname, recpriority, quicktune "
1744 queryStr.append(
" WHERE parentid = 0");
1750 return infoInputList;
1753 while (query.
next())
1765 infoInputList.push_back(input);
1768 return infoInputList;
1773 InputInfo info(
"None", 0, inputid, 0, 0, 0);
1795 query.
prepare(
"SELECT startchan "
1797 "WHERE cardid = :INPUTID");
1804 else if (query.
next())
1806 startchan = query.
value(0).toString();
1810 if (!startchan.isEmpty())
1812 query.
prepare(
"SELECT channel.chanid "
1813 "FROM capturecard, channel "
1814 "WHERE capturecard.cardid = :INPUTID AND "
1815 " capturecard.sourceid = channel.sourceid AND "
1816 " channel.deleted IS NULL AND "
1817 " channel.visible > 0 AND "
1818 " channel.channum = :CHANNUM");
1826 else if (!query.
next())
1828 LOG(VB_GENERAL, LOG_WARNING,
1829 QString(
"CardUtil[%1]: ").arg(inputid) +
1830 QString(
"Channel %1 on inputid %2 is invalid").arg(startchan).arg(inputid));
1837 if (startchan.isEmpty())
1839 query.
prepare(
"SELECT channel.channum "
1840 "FROM capturecard, channel "
1841 "WHERE capturecard.cardid = :INPUTID AND "
1842 " capturecard.sourceid = channel.sourceid AND "
1843 " channel.deleted IS NULL AND "
1844 " channel.visible > 0 "
1852 else if (query.
next())
1854 startchan = query.
value(0).toString();
1858 if (startchan.isEmpty())
1860 LOG(VB_GENERAL, LOG_WARNING,
1861 QString(
"CardUtil[%1]: ").arg(inputid) +
1862 QString(
"No start channel found on inputid %1").arg(inputid));
1866 LOG(VB_GENERAL, LOG_DEBUG,
1867 QString(
"CardUtil[%1]: ").arg(inputid) +
1868 QString(
"Start channel %1 on inputid %2").arg(startchan).arg(inputid));
1880 query.
prepare(
"SELECT displayname "
1882 "WHERE cardid = :INPUTID");
1887 else if (query.
next())
1889 QString result = query.
value(0).toString();
1901 qsizetype idx { 0 };
1904 if ((idx = name.indexOf(
'/')) >= 0)
1906 matching = name.right(name.size() - idx -1);
1911 matching = name.right(2);
1916 query.
prepare(
"SELECT cardid, displayname "
1918 "WHERE parentid = 0 "
1919 " AND cardid <> :INPUTID ");
1920 query.
bindValue(
":INPUTID", exclude_inputid);
1928 while (query.
next())
1930 QString dn = query.
value(1).toString();
1931 if (!two && (idx = dn.indexOf(
'/')) >= 0)
1933 if (dn.right(dn.size() - idx - 1) == matching)
1936 else if (dn.right(2) == matching.right(2))
1949 "WHERE cardid = :INPUTID");
1953 else if (query.
next())
1954 return query.
value(0).toUInt();
1963 const uint sourceid,
1964 const QString &inputname,
1965 const QString &externalcommand,
1966 const QString &changer_device,
1967 const QString &changer_model,
1969 const QString &tunechan,
1970 const QString &startchan,
1971 const QString &displayname,
1973 const uint recpriority,
1974 const uint quicktune,
1975 const uint schedorder,
1976 const uint livetvorder)
1981 "UPDATE capturecard "
1982 "SET sourceid = :SOURCEID, "
1983 " inputname = :INPUTNAME, "
1984 " externalcommand = :EXTERNALCOMMAND, "
1985 " changer_device = :CHANGERDEVICE, "
1986 " changer_model = :CHANGERMODEL, "
1987 " tunechan = :TUNECHAN, "
1988 " startchan = :STARTCHAN, "
1989 " displayname = :DISPLAYNAME, "
1990 " dishnet_eit = :DISHNETEIT, "
1991 " recpriority = :RECPRIORITY, "
1992 " quicktune = :QUICKTUNE, "
1993 " schedorder = :SCHEDORDER, "
1994 " livetvorder = :LIVETVORDER "
1995 "WHERE cardid = :INPUTID AND "
1996 " inputname = 'None'");
2000 query.
bindValue(
":INPUTNAME", inputname);
2001 query.
bindValue(
":EXTERNALCOMMAND", externalcommand);
2002 query.
bindValue(
":CHANGERDEVICE", changer_device);
2003 query.
bindValue(
":CHANGERMODEL", changer_model);
2005 query.
bindValue(
":STARTCHAN", startchan);
2007 query.
bindValue(
":DISHNETEIT", dishnet_eit);
2008 query.
bindValue(
":RECPRIORITY", recpriority);
2009 query.
bindValue(
":QUICKTUNE", quicktune);
2010 query.
bindValue(
":SCHEDORDER", schedorder);
2011 query.
bindValue(
":LIVETVORDER", livetvorder);
2026 query.
prepare(
"SELECT inputgroupid FROM inputgroup "
2027 "WHERE inputgroupname = :GROUPNAME "
2037 return query.
value(0).toUInt();
2039 query.
prepare(
"SELECT MAX(inputgroupid) FROM inputgroup");
2046 uint inputgroupid = (query.
next()) ? query.
value(0).toUInt() + 1 : 1;
2049 "INSERT INTO inputgroup "
2050 " (cardinputid, inputgroupid, inputgroupname) "
2051 "VALUES (:INPUTID, :GROUPID, :GROUPNAME ) ");
2053 query.
bindValue(
":GROUPID", inputgroupid);
2061 return inputgroupid;
2065 const QString &
type,
2066 const QString &host,
2067 const QString &device)
2069 QString name = host +
'|' + device;
2070 if (
type ==
"FREEBOX" ||
type ==
"IMPORT" ||
2071 type ==
"DEMO" ||
type ==
"EXTERNAL" ||
2072 type ==
"HDHOMERUN" )
2073 name += QString(
"|%1").arg(inputid);
2081 "SELECT inputgroupid "
2083 "WHERE cardinputid = :INPUTID "
2084 " AND inputgroupname REGEXP '^[a-z_-]*\\\\|'");
2095 return query.
value(0).toUInt();
2106 "SELECT cardinputid, inputgroupid, inputgroupname "
2108 "WHERE inputgroupid = :GROUPID "
2109 "ORDER BY inputgroupid, cardinputid, inputgroupname");
2110 query.
bindValue(
":GROUPID", inputgroupid);
2119 while (query.
next()) {
2120 name = query.
value(2).toString();
2123 if (cardid == inputid)
2132 "INSERT INTO inputgroup "
2133 " (cardinputid, inputgroupid, inputgroupname) "
2134 "VALUES (:INPUTID, :GROUPID, :GROUPNAME ) ");
2137 query.
bindValue(
":GROUPID", inputgroupid);
2156 if (!inputid && !inputgroupid)
2159 "DELETE FROM inputgroup "
2160 "WHERE cardinputid NOT IN "
2161 "( SELECT cardid FROM capturecard )");
2166 "DELETE FROM inputgroup "
2167 "WHERE cardinputid = :INPUTID AND "
2168 " inputgroupid = :GROUPID ");
2171 query.
bindValue(
":GROUPID", inputgroupid);
2185 std::vector<uint> list;
2190 "SELECT inputgroupid "
2192 "WHERE cardinputid = :INPUTID "
2193 "ORDER BY inputgroupid, cardinputid, inputgroupname");
2203 while (query.
next())
2204 list.push_back(query.
value(0).toUInt());
2211 std::vector<uint> list;
2216 "SELECT DISTINCT cardid "
2217 "FROM capturecard, inputgroup "
2218 "WHERE inputgroupid = :GROUPID AND "
2219 " capturecard.cardid = inputgroup.cardinputid "
2222 query.
bindValue(
":GROUPID", inputgroupid);
2230 while (query.
next())
2231 list.push_back(query.
value(0).toUInt());
2238 std::vector<uint> inputids;
2243 "SELECT DISTINCT c.cardid "
2245 " SELECT inputgroupid "
2247 " WHERE cardinputid = :INPUTID1 "
2249 "JOIN inputgroup ig ON ig.inputgroupid = g.inputgroupid "
2250 "JOIN capturecard c ON c.cardid = ig.cardinputid "
2251 " AND c.cardid <> :INPUTID2 "
2252 " AND c.sourceid > 0 "
2253 "ORDER BY c.cardid");
2264 while (query.
next())
2266 inputids.push_back(query.
value(0).toUInt());
2271 QString msg = QString(
"CardUtil[%1]: GetConflictingInputs(%1) ").arg(inputid);
2273 for (
auto id : inputids)
2275 ids.append(QString::number(
id));
2277 msg.append(ids.join(
','));
2278 LOG(VB_RECORD, LOG_INFO, msg);
2284 std::chrono::milliseconds &signal_timeout,
2285 std::chrono::milliseconds &channel_timeout)
2289 "SELECT signal_timeout, channel_timeout "
2291 "WHERE cardid = :INPUTID");
2296 else if (query.
next())
2298 signal_timeout = std::max(std::chrono::milliseconds(query.
value(0).toInt()), 250ms);
2299 channel_timeout = std::max(std::chrono::milliseconds(query.
value(1).toInt()), 500ms);
2310 bool needsConf =
false;
2325 "WHERE cardid = :INPUTID AND "
2326 " inputname = :INPUTNAME");
2328 query.
bindValue(
":INPUTNAME", input_name);
2332 else if (query.
next())
2333 quicktune = query.
value(0).toUInt();
2341 struct v4l2_capability vcap {};
2343 return ((ioctl(videofd, VIDIOC_QUERYCAP, &vcap) >= 0) &&
2344 ((vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE) != 0U));
2345 #else // if !USING_V4L2
2347 #endif // !USING_V4L2
2351 int videofd, QString &input, QString &driver, uint32_t &
version,
2352 uint32_t &capabilities)
2363 struct v4l2_capability capability {};
2364 if (ioctl(videofd, VIDIOC_QUERYCAP, &capability) >= 0)
2366 input = QString::fromLatin1((
const char*)capability.card);
2367 driver = QString::fromLatin1((
const char*)capability.driver);
2369 capabilities = capability.capabilities;
2371 #endif // USING_V4L2
2373 static const QRegularExpression kBracketedDigitRE { R
"(\[[0-9]\]$)" };
2374 if (!driver.isEmpty())
2375 driver.remove( kBracketedDigitRE );
2377 return !input.isEmpty();
2386 bool usingv4l2 =
hasV4L2(videofd);
2388 struct v4l2_input vin {};
2389 while (usingv4l2 && (ioctl(videofd, VIDIOC_ENUMINPUT, &vin) >= 0))
2391 QString input((
char *)vin.name);
2392 list[vin.index] = input;
2403 list[0] =
"Television";
2406 #else // if !USING_V4L2
2407 list[-1] += QObject::tr(
"ERROR, Compile with V4L support to query inputs");
2408 #endif // !USING_V4L2
2418 bool usingv4l2 =
hasV4L2(videofd);
2421 struct v4l2_audio ain {};
2422 while (usingv4l2 && (ioctl(videofd, VIDIOC_ENUMAUDIO, &ain) >= 0))
2424 QString input((
char *)ain.name);
2425 list[ain.index] = input;
2435 #else // if !USING_V4L2
2436 list[-1] += QObject::tr(
2437 "ERROR, Compile with V4L support to query audio inputs");
2438 #endif // !USING_V4L2
2447 "SELECT cardid, inputname "
2449 "WHERE hostname = :HOSTNAME "
2450 " AND videodevice = :DEVICE "
2451 " AND parentid = 0 "
2452 " AND inputname <> 'None'");
2460 while (query.
next())
2461 list[query.
value(0).toUInt()] = query.
value(1).toString();
2475 struct fe_caps_name {
2480 std::array<fe_caps_name,31> fe_caps_name {{
2481 { FE_CAN_2G_MODULATION,
"CAN_2G_MODULATION" },
2482 { FE_CAN_8VSB,
"CAN_8VSB" },
2483 { FE_CAN_16VSB,
"CAN_16VSB" },
2484 { FE_CAN_BANDWIDTH_AUTO,
"CAN_BANDWIDTH_AUTO" },
2485 { FE_CAN_FEC_1_2,
"CAN_FEC_1_2" },
2486 { FE_CAN_FEC_2_3,
"CAN_FEC_2_3" },
2487 { FE_CAN_FEC_3_4,
"CAN_FEC_3_4" },
2488 { FE_CAN_FEC_4_5,
"CAN_FEC_4_5" },
2489 { FE_CAN_FEC_5_6,
"CAN_FEC_5_6" },
2490 { FE_CAN_FEC_6_7,
"CAN_FEC_6_7" },
2491 { FE_CAN_FEC_7_8,
"CAN_FEC_7_8" },
2492 { FE_CAN_FEC_8_9,
"CAN_FEC_8_9" },
2493 { FE_CAN_FEC_AUTO,
"CAN_FEC_AUTO" },
2494 { FE_CAN_GUARD_INTERVAL_AUTO,
"CAN_GUARD_INTERVAL_AUTO" },
2495 { FE_CAN_HIERARCHY_AUTO,
"CAN_HIERARCHY_AUTO" },
2496 { FE_CAN_INVERSION_AUTO,
"CAN_INVERSION_AUTO" },
2497 { FE_CAN_MULTISTREAM,
"CAN_MULTISTREAM" },
2498 { FE_CAN_MUTE_TS,
"CAN_MUTE_TS" },
2499 { FE_CAN_QAM_16,
"CAN_QAM_16" },
2500 { FE_CAN_QAM_32,
"CAN_QAM_32" },
2501 { FE_CAN_QAM_64,
"CAN_QAM_64" },
2502 { FE_CAN_QAM_128,
"CAN_QAM_128" },
2503 { FE_CAN_QAM_256,
"CAN_QAM_256" },
2504 { FE_CAN_QAM_AUTO,
"CAN_QAM_AUTO" },
2505 { FE_CAN_QPSK,
"CAN_QPSK" },
2506 { FE_CAN_RECOVER,
"CAN_RECOVER" },
2507 { FE_CAN_TRANSMISSION_MODE_AUTO,
"CAN_TRANSMISSION_MODE_AUTO" },
2508 { FE_CAN_TURBO_FEC,
"CAN_TURBO_FEC" },
2509 { FE_HAS_EXTENDED_CAPS,
"HAS_EXTENDED_CAPS" },
2510 { FE_IS_STUPID,
"IS_STUPID" },
2511 { FE_NEEDS_BENDING,
"NEEDS_BENDING" },
2514 for (
const auto & cap : fe_caps_name)
2516 if (capabilities & cap.idx)
2517 caps.append(cap.name);
2529 else if (
"DVB" == inputtype)
2539 LOG(VB_GENERAL, LOG_DEBUG, QString(
"ProbeAudioInputs(%1,%2)")
2540 .arg(device, inputtype));
2543 if (
"HDPVR" == inputtype ||
2544 "V4L2" == inputtype)
2554 QByteArray dev = device.toLatin1();
2555 int videofd = open(dev.constData(), O_RDWR);
2558 ret += QObject::tr(
"Could not open '%1' "
2559 "to probe its inputs.").arg(device);
2571 InputNames::iterator it;
2572 for (it = list.begin(); it != list.end(); ++it)
2583 LOG(VB_GENERAL, LOG_DEBUG, QString(
"ProbeV4LAudioInputs(%1)").arg(device));
2587 int videofd = open(device.toLatin1().constData(), O_RDWR);
2590 LOG(VB_GENERAL, LOG_ERR,
"ProbeAudioInputs() -> couldn't open device");
2591 ret += QObject::tr(
"Could not open '%1' to probe its inputs.")
2604 InputNames::iterator it;
2605 for (it = list.begin(); it != list.end(); ++it)
2620 InputNames::iterator it;
2621 for (it = list.begin(); it != list.end(); ++it)
2627 ret += QObject::tr(
"ERROR, Compile with DVB support to query inputs");
2634 const QString &videodevice)
2636 return QString(
"[ %1 : %2 ]").arg(inputtype, videodevice);
2642 query.
prepare(
"SELECT cardtype, videodevice "
2643 "FROM capturecard WHERE cardid = :INPUTID ");
2649 query.
value(1).toString());
2652 return "[ UNKNOWN ]";
2656 const QString &device,
2657 const QString &inputtype,
2658 QStringList &inputs)
2662 inputs +=
"MPEG2TS";
2663 else if (inputtype ==
"DVB")
2664 inputs +=
"DVBInput";
2670 const QString &audiodevice,
2671 const QString &vbidevice,
2672 const QString &inputtype,
2673 const uint audioratelimit,
2675 const uint dvb_swfilter,
2676 const uint dvb_sat_type,
2677 bool dvb_wait_for_seqstart,
2680 const uint dvb_diseqc_type,
2681 const uint firewire_speed,
2682 const QString &firewire_model,
2683 const uint firewire_connection,
2684 const std::chrono::milliseconds signal_timeout,
2685 const std::chrono::milliseconds channel_timeout,
2686 const uint dvb_tuning_delay,
2687 const uint contrast,
2688 const uint brightness,
2691 const uint diseqcid,
2697 "INSERT INTO capturecard "
2698 "(videodevice, audiodevice, vbidevice, cardtype, "
2699 "audioratelimit, hostname, dvb_swfilter, dvb_sat_type, "
2700 "dvb_wait_for_seqstart, skipbtaudio, dvb_on_demand, dvb_diseqc_type, "
2701 "firewire_speed, firewire_model, firewire_connection, signal_timeout, "
2702 "channel_timeout, dvb_tuning_delay, contrast, brightness, colour, "
2703 "hue, diseqcid, dvb_eitscan) "
2704 "VALUES (:VIDEODEVICE, :AUDIODEVICE, :VBIDEVICE, :INPUTTYPE, "
2705 ":AUDIORATELIMIT, :HOSTNAME, :DVBSWFILTER, :DVBSATTYPE, "
2706 ":DVBWAITFORSEQSTART, :SKIPBTAUDIO, :DVBONDEMAND, :DVBDISEQCTYPE, "
2707 ":FIREWIRESPEED, :FIREWIREMODEL, :FIREWIRECONNECTION, :SIGNALTIMEOUT, "
2708 ":CHANNELTIMEOUT, :DVBTUNINGDELAY, :CONTRAST, :BRIGHTNESS, :COLOUR, "
2709 ":HUE, :DISEQCID, :DVBEITSCAN ) ");
2711 query.
bindValue(
":VIDEODEVICE", videodevice);
2712 if (audiodevice.length() == 0)
2714 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2715 query.
bindValue(
":AUDIODEVICE", QVariant(QVariant::String));
2717 query.
bindValue(
":AUDIODEVICE", QVariant(QMetaType(QMetaType::QString)));
2721 query.
bindValue(
":AUDIODEVICE", audiodevice);
2722 if (vbidevice.length() == 0)
2724 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2725 query.
bindValue(
":VBIDEVICE", QVariant(QVariant::String));
2727 query.
bindValue(
":VBIDEVICE", QVariant(QMetaType(QMetaType::QString)));
2731 query.
bindValue(
":VBIDEVICE", vbidevice);
2732 query.
bindValue(
":INPUTTYPE", inputtype);
2733 if (audioratelimit == 0)
2735 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2736 query.
bindValue(
":AUDIORATELIMIT", QVariant(QVariant::UInt));
2738 query.
bindValue(
":AUDIORATELIMIT", QVariant(QMetaType(QMetaType::UInt)));
2742 query.
bindValue(
":AUDIORATELIMIT", audioratelimit);
2744 query.
bindValue(
":DVBSWFILTER", dvb_swfilter);
2745 query.
bindValue(
":DVBSATTYPE", dvb_sat_type);
2746 query.
bindValue(
":DVBWAITFORSEQSTART", dvb_wait_for_seqstart);
2747 query.
bindValue(
":SKIPBTAUDIO", skipbtaudio);
2748 query.
bindValue(
":DVBONDEMAND", dvb_on_demand);
2749 query.
bindValue(
":DVBDISEQCTYPE", dvb_diseqc_type);
2750 query.
bindValue(
":FIREWIRESPEED", firewire_speed);
2751 if (firewire_model.length() == 0)
2753 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2754 query.
bindValue(
":FIREWIREMODEL", QVariant(QVariant::String));
2756 query.
bindValue(
":FIREWIREMODEL", QVariant(QMetaType(QMetaType::QString)));
2760 query.
bindValue(
":FIREWIREMODEL", firewire_model);
2761 query.
bindValue(
":FIREWIRECONNECTION", firewire_connection);
2762 query.
bindValue(
":SIGNALTIMEOUT",
static_cast<qint64
>(signal_timeout.count()));
2763 query.
bindValue(
":CHANNELTIMEOUT",
static_cast<qint64
>(channel_timeout.count()));
2764 query.
bindValue(
":DVBTUNINGDELAY", dvb_tuning_delay);
2766 query.
bindValue(
":BRIGHTNESS", brightness);
2771 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2772 query.
bindValue(
":DISEQCID", QVariant(QVariant::UInt));
2774 query.
bindValue(
":DISEQCID", QVariant(QMetaType(QMetaType::UInt)));
2779 query.
bindValue(
":DVBEITSCAN", dvb_eitscan);
2787 query.
prepare(
"SELECT MAX(cardid) FROM capturecard");
2799 inputid = query.
value(0).toInt();
2811 for (
uint childid : childids)
2815 LOG(VB_GENERAL, LOG_ERR,
LOC +
2816 QString(
"CardUtil: Failed to delete child input %1")
2828 query.
prepare(
"DELETE FROM capturecard WHERE cardid = :INPUTID");
2837 query.
prepare(
"UPDATE capturecard SET reclimit=reclimit-1 "
2838 "WHERE cardid = :INPUTID");
2847 query.
prepare(
"DELETE FROM inputgroup WHERE cardinputid = :INPUTID");
2858 query.
prepare(
"SELECT cardid FROM capturecard "
2859 "WHERE diseqcid = :DISEQCID LIMIT 1");
2865 else if (!query.
next())
2868 tree.
Store(inputid);
2881 return (query.
exec(
"TRUNCATE TABLE inputgroup") &&
2882 query.
exec(
"TRUNCATE TABLE diseqc_config") &&
2883 query.
exec(
"TRUNCATE TABLE diseqc_tree") &&
2884 query.
exec(
"TRUNCATE TABLE capturecard") &&
2885 query.
exec(
"TRUNCATE TABLE iptv_channel"));
2890 std::vector<uint> list;
2902 while (query.
next())
2903 list.push_back(query.
value(0).toUInt());
2911 std::vector<uint> list;
2915 "SELECT DISTINCT cardid "
2917 "WHERE schedorder <> 0 "
2918 "ORDER BY schedorder, cardid");
2924 while (query.
next())
2925 list.push_back(query.
value(0).toUInt());
2933 std::vector<uint> list;
2937 "SELECT DISTINCT cardid "
2939 "WHERE livetvorder <> 0 "
2940 "ORDER BY livetvorder, cardid");
2946 while (query.
next())
2947 list.push_back(query.
value(0).toUInt());
2955 QString devname = QString(device);
2957 LOG(VB_RECORD, LOG_DEBUG,
LOC + QString(
"DVB Device (%1)").arg(devname));
2967 QString
tmp = devname;
2968 tmp =
tmp.replace(
tmp.indexOf(
"frontend"), 8,
"dvr");
2969 if (QFile::exists(
tmp))
2971 LOG(VB_RECORD, LOG_DEBUG,
LOC +
2972 QString(
"Adapter Frontend dvr number matches (%1)").arg(
tmp));
2978 tmp =
tmp.replace(
tmp.indexOf(
"frontend"), 9,
"dvr0");
2979 if (QFile::exists(
tmp))
2981 LOG(VB_RECORD, LOG_DEBUG,
LOC +
2982 QString(
"Adapter Frontend dvr number not matching, using dvr0 instead (%1)").arg(
tmp));
2986 LOG(VB_RECORD, LOG_DEBUG,
LOC +
2987 QString(
"Adapter Frontend no dvr device found for (%1)").arg(devname));
2993 QString
tmp = devname;
2994 tmp =
tmp.replace(
tmp.indexOf(
"frontend"), 8,
"demux");
2995 if (QFile::exists(
tmp))
2997 LOG(VB_RECORD, LOG_DEBUG,
LOC +
2998 QString(
"Adapter Frontend demux number matches (%1)").arg(
tmp));
3004 tmp =
tmp.replace(
tmp.indexOf(
"frontend"), 9,
"demux0");
3005 if (QFile::exists(
tmp))
3007 LOG(VB_RECORD, LOG_DEBUG,
LOC +
3008 QString(
"Adapter Frontend demux number not matching, using demux0 instead (%1)").arg(
tmp));
3012 LOG(VB_RECORD, LOG_DEBUG,
LOC +
3013 QString(
"Adapter Frontend no demux device found for (%1)").arg(devname));
3019 QString
tmp = devname;
3020 tmp =
tmp.replace(
tmp.indexOf(
"frontend"), 8,
"ca");
3021 if (QFile::exists(
tmp))
3023 LOG(VB_RECORD, LOG_DEBUG,
LOC +
3024 QString(
"Adapter Frontend ca number matches (%1)").arg(
tmp));
3030 tmp =
tmp.replace(
tmp.indexOf(
"frontend"), 9,
"ca0");
3031 if (QFile::exists(
tmp))
3033 LOG(VB_RECORD, LOG_DEBUG,
LOC +
3034 QString(
"Adapter Frontend ca number not matching, using ca0 instead (%1)").arg(
tmp));
3038 LOG(VB_RECORD, LOG_DEBUG,
LOC +
3039 QString(
"Adapter Frontend no ca device found for (%1)").arg(devname));
3045 return devname.replace(devname.indexOf(
"frontend"), 8,
"audio");
3050 return devname.replace(devname.indexOf(
"frontend"), 8,
"video");
3068 #ifdef USING_HDHOMERUN
3069 hdhomerun_device_t *hdhr =
3070 hdhomerun_device_create_from_str(device.toLatin1(),
nullptr);
3074 const char *model = hdhomerun_device_get_model_str(hdhr);
3075 if (model && strstr(model,
"dvb"))
3077 hdhomerun_device_destroy(hdhr);
3081 hdhomerun_device_destroy(hdhr);
3094 #ifdef USING_HDHOMERUN
3095 hdhomerun_device_t *hdhr =
3096 hdhomerun_device_create_from_str(device.toLatin1(),
nullptr);
3100 const char *model = hdhomerun_device_get_model_str(hdhr);
3101 if (model && strstr(model,
"dvbc"))
3103 hdhomerun_device_destroy(hdhr);
3107 hdhomerun_device_destroy(hdhr);
3120 QString connectErr = QObject::tr(
"Unable to connect to device.");
3122 #ifdef USING_HDHOMERUN
3123 [[maybe_unused]]
bool deviceIsIP =
false;
3125 if (device.contains(
'.'))
3129 bool validID =
false;
3131 uint32_t dev = device.toUInt(&validID, 16);
3132 if (!validID || !hdhomerun_discover_validate_device_id(dev))
3133 return QObject::tr(
"Invalid Device ID");
3136 LOG(VB_GENERAL, LOG_INFO,
"CardUtil::GetHDHRdescription(" + device +
3137 ") - trying to locate device");
3139 hdhomerun_device_t *hdhr =
3140 hdhomerun_device_create_from_str(device.toLatin1(),
nullptr);
3142 return QObject::tr(
"Invalid Device ID or address.");
3144 const char *model = hdhomerun_device_get_model_str(hdhr);
3147 hdhomerun_device_destroy(hdhr);
3152 QString description = model;
3153 char *sVersion =
nullptr;
3154 uint32_t iVersion = 0;
3156 if (hdhomerun_device_get_version(hdhr, &sVersion, &iVersion))
3157 description += QObject::tr(
", firmware: %2").arg(sVersion);
3159 hdhomerun_device_destroy(hdhr);
3172 [[maybe_unused]]
const QString &ip,
3173 [[maybe_unused]]
const QString &tunerNo,
3174 [[maybe_unused]]
const QString &tunerType)
3176 QString connectErr = QObject::tr(
"Unable to connect to device.");
3191 QString apiVersionErr = QObject::tr(
"The VBox software version is too old (%1), we require %2")
3194 return apiVersionErr;
3200 return QString(
"V@Box TV Gateway - ID: %1, IP: %2, Tuner: %3-%4")
3201 .arg(
id, ip, tunerNo, tunerType);
3211 return QString(
"/sys/class/asi/asirx%1/%2").arg(device_num).arg(dev);
3217 f.open(QIODevice::ReadOnly);
3218 QByteArray sdba = f.readAll();
3226 f.open(QIODevice::WriteOnly);
3227 QByteArray ba = str.toLocal8Bit();
3229 for (
uint tries = 0; (offset < ba.size()) && tries < 5; tries++)
3231 qint64 written = f.write(ba.data()+offset, ba.size()-offset);
3244 struct stat statbuf {};
3245 if (stat(device.toLocal8Bit().constData(), &statbuf) < 0)
3248 *
error = QString(
"Unable to stat '%1'").arg(device) +
ENO;
3252 if (!S_ISCHR(statbuf.st_mode))
3255 *
error = QString(
"'%1' is not a character device").arg(device);
3259 if (!(statbuf.st_rdev & 0x0080))
3262 *
error = QString(
"'%1' not a DVEO ASI receiver").arg(device);
3266 int device_num = statbuf.st_rdev & 0x007f;
3270 QStringList sys_dev_clist = sys_dev_contents.split(
":");
3271 if (2 != sys_dev_clist.size())
3275 *
error = QString(
"Unable to read '%1'")
3276 .arg(
sys_dev(device_num,
"dev"));
3280 if (sys_dev_clist[0].toUInt() != (statbuf.st_rdev>>8))
3283 *
error = QString(
"'%1' not a DVEO ASI device").arg(device);
3290 *
error =
"Not compiled with ASI support.";
3299 QString sys_bufsize_contents =
read_sys(
sys_dev(device_num,
"bufsize"));
3301 uint buf_size = sys_bufsize_contents.toUInt(&ok);
3306 *
error = QString(
"Failed to read buffer size from '%1'")
3307 .arg(
sys_dev(device_num,
"bufsize"));
3314 *
error =
"Not compiled with ASI support.";
3323 QString sys_numbuffers_contents =
read_sys(
sys_dev(device_num,
"buffers"));
3325 uint num_buffers = sys_numbuffers_contents.toUInt(&ok);
3330 *
error = QString(
"Failed to read num buffers from '%1'")
3331 .arg(
sys_dev(device_num,
"buffers"));
3338 *
error =
"Not compiled with ASI support.";
3348 uint mode = sys_bufsize_contents.toUInt(&ok);
3353 *
error = QString(
"Failed to read mode from '%1'")
3354 .arg(
sys_dev(device_num,
"mode"));
3361 *
error =
"Not compiled with ASI support.";
3367 [[maybe_unused]]
uint mode,
3373 uint old_mode = sys_bufsize_contents.toUInt(&ok);
3374 if (ok && old_mode == mode)
3379 *
error = QString(
"Failed to set mode to %1 using '%2'")
3380 .arg(mode).arg(
sys_dev(device_num,
"mode"));
3385 *
error =
"Not compiled with ASI support.";
3395 bool CardUtil::IsVBoxPresent(
uint inputid)
3400 LOG(VB_GENERAL, LOG_ERR, QString(
"VBOX inputid (%1) not valid, redo mythtv-setup")
3411 LOG(VB_GENERAL, LOG_ERR, QString(
"VBOX chanid (%1) not found for inputid (%2), redo mythtv-setup")
3412 .arg(chanid).arg(inputid));
3417 std::chrono::milliseconds signal_timeout = 0ms;
3418 std::chrono::milliseconds tuning_timeout = 0ms;
3419 if (!
GetTimeouts(inputid,signal_timeout,tuning_timeout))
3421 LOG(VB_GENERAL, LOG_ERR, QString(
"Failed to get timeouts for inputid (%1)")
3429 query.prepare(
"SELECT url "
3430 "FROM iptv_channel "
3431 "WHERE chanid = :CHANID");
3432 query.bindValue(
":CHANID", chanid);
3436 else if (query.next())
3437 url = query.value(0).toString();
3440 QString ip = url.host();
3441 LOG(VB_GENERAL, LOG_INFO, QString(
"VBOX IP found (%1) for inputid (%2)")
3442 .arg(ip).arg(inputid));
3444 if (!
ping(ip,signal_timeout))
3446 LOG(VB_GENERAL, LOG_ERR, QString(
"VBOX at IP (%1) failed to respond to network ping for inputid (%2) timeout (%3)")
3447 .arg(ip).arg(inputid).arg(signal_timeout.count()));
3460 bool CardUtil::IsSatIPPresent(
uint inputid)
3465 LOG(VB_GENERAL, LOG_ERR, QString(
"SatIP inputid (%1) not valid, redo mythtv-setup")
3476 LOG(VB_GENERAL, LOG_ERR, QString(
"SatIP chanid (%1) not found for inputid (%2), redo mythtv-setup")
3477 .arg(chanid).arg(inputid));
3482 std::chrono::milliseconds signal_timeout = 0ms;
3483 std::chrono::milliseconds tuning_timeout = 0ms;
3484 if (!
GetTimeouts(inputid,signal_timeout,tuning_timeout))
3486 LOG(VB_GENERAL, LOG_ERR, QString(
"Failed to get timeouts for inputid (%1)")
3493 QStringList devinfo = device.split(
":");
3494 if (devinfo.value(0).toUpper() ==
"UUID")
3496 QString deviceId = QString(
"uuid:%1").arg(devinfo.value(1));
3498 LOG(VB_GENERAL, LOG_INFO, QString(
"SatIP[%1] IP address %2 device %3")
3499 .arg(inputid).arg(ip, device));
3501 if (!
ping(ip, signal_timeout))
3503 LOG(VB_GENERAL, LOG_ERR, QString(
"SatIP[%1] at IP %2 failed to respond to network ping (timeout %3)")
3504 .arg(inputid).arg(ip).arg(signal_timeout.count()));