10 #include <QStringList>
11 #include <QDomDocument>
13 #ifdef USING_HDHOMERUN
14 #include HDHOMERUN_HEADERFILE
26 #define LOC QString("HDHRChanFetch: ")
31 {
"http://{IP}/lineup.xml?tuning" };
33 QString getFirstText(QDomElement &element)
35 for (QDomNode dname = element.firstChild(); !dname.isNull();
36 dname = dname.nextSibling())
38 QDomText
t = dname.toText();
45 QString getStrValue(
const QDomElement &element,
const QString &name,
int index=0)
47 QDomNodeList nodes = element.elementsByTagName(name);
50 if (index >= nodes.count())
52 QDomElement e = nodes.at(index).toElement();
53 return getFirstText(e);
58 int getIntValue(
const QDomElement &element,
const QString &name,
int index=0)
60 QString value = getStrValue(element, name, index);
64 bool sendQuery(
const QString& query, QDomDocument* xmlDoc)
75 if (!xmlDoc->setContent(result,
false, &errorMsg, &errorLine, &errorColumn))
77 LOG(VB_GENERAL, LOG_ERR,
LOC +
78 QString(
"Error parsing: %1\nat line: %2 column: %3 msg: %4").
79 arg(query).arg(errorLine).arg(errorColumn).arg(errorMsg));
84 QDomNodeList statusNodes = xmlDoc->elementsByTagName(
"Status");
86 if (!statusNodes.count())
87 statusNodes = xmlDoc->elementsByTagName(
"Error");
89 if (statusNodes.count())
91 QDomElement elem = statusNodes.at(0).toElement();
94 int errorCode = getIntValue(elem,
"ErrorCode");
95 QString errorDesc = getStrValue(elem,
"ErrorDescription");
100 LOG(VB_GENERAL, LOG_ERR,
LOC +
101 QString(
"API Error: %1 - %2, Query was: %3").arg(errorCode).arg(errorDesc, query));
114 auto *xmlDoc =
new QDomDocument();
117 query.replace(
"{IP}", ip);
119 if (!sendQuery(query, xmlDoc))
126 QDomNodeList chanNodes = xmlDoc->elementsByTagName(
"Program");
128 for (
int x = 0; x < chanNodes.count(); x++)
130 QDomElement chanElem = chanNodes.at(x).toElement();
131 QString guideName = getStrValue(chanElem,
"GuideName");
132 QString guideNumber = getStrValue(chanElem,
"GuideNumber");
133 QString url = getStrValue(chanElem,
"URL");
134 QString modulation = getStrValue(chanElem,
"Modulation");
135 QString videoCodec = getStrValue(chanElem,
"VideoCodec");
136 QString audioCodec = getStrValue(chanElem,
"AudioCodec");
137 uint frequency = getStrValue(chanElem,
"Frequency").toUInt();
138 uint serviceID = getStrValue(chanElem,
"ProgramNumber").toUInt();
139 uint transportID = getStrValue(chanElem,
"TransportStreamID").toUInt();
140 QString onid = getStrValue(chanElem,
"OriginalNetworkID");
145 onid.replace(
":",
"");
146 uint originalNetworkID = onid.toUInt();
148 LOG(VB_CHANSCAN, LOG_DEBUG,
LOC + QString(
"ONID/TID/SID %1 %2 %3")
149 .arg(originalNetworkID).arg(transportID).arg(serviceID));
151 HDHRChannelInfo chanInfo(guideName, guideNumber, url, modulation, videoCodec,
152 audioCodec, frequency, serviceID, originalNetworkID, transportID);
154 result->insert(guideNumber, chanInfo);
159 QString HDHRIPv4Address([[maybe_unused]]
const QString &device)
161 #ifdef USING_HDHOMERUN
162 hdhomerun_device_t *hdhr =
163 hdhomerun_device_create_from_str(device.toLatin1(),
nullptr);
167 uint32_t ipv4 = hdhomerun_device_get_device_ip(hdhr);
168 hdhomerun_device_destroy(hdhr);
173 return QString(
"%1.%2.%3.%4").arg(ipv4>>24&0xff).arg(ipv4>>16&0xff).arg(ipv4>>8&0xff).arg(ipv4&0xff);
182 if (hdhrmod.contains(
"dvbt2"))
184 if (hdhrmod.contains(
"dvbt"))
186 if (hdhrmod.startsWith(
"a8qam"))
188 if (hdhrmod.contains(
"vsb"))
190 if (hdhrmod.contains(
"psk"))
195 signed char HDHRMod2Bandwidth(
const QString& hdhrmod)
197 if (hdhrmod.startsWith(
"t8") || hdhrmod.startsWith(
"a8"))
199 if (hdhrmod.startsWith(
"t7") || hdhrmod.startsWith(
"a7"))
201 if (hdhrmod.startsWith(
"t6") || hdhrmod.startsWith(
"a6"))
206 uint HDHRMod2SymbolRate(
const QString& hdhrmod)
208 static const QRegularExpression re(R
"(^(a8qam\d+-)(\d+))");
209 QRegularExpressionMatch match = re.match(hdhrmod);
210 if (match.hasMatch())
212 QString matched = match.captured(2);
213 return matched.toUInt() * 1000;
218 QString HDHRMod2Modulation(
const QString& hdhrmod)
220 if (hdhrmod.contains(
"qam256"))
222 if (hdhrmod.contains(
"qam128"))
224 if (hdhrmod.contains(
"qam64"))
226 if (hdhrmod.contains(
"qam16"))
228 if (hdhrmod.contains(
"qpsk"))
230 if (hdhrmod.contains(
"8vsb"))
235 void HDHRMajorMinorChannel(QString channum,
uint &atsc_major_channel,
uint &atsc_minor_channel)
237 if (channum.contains(
"."))
240 QTextStream(&channum) >> atsc_major_channel >> dot >> atsc_minor_channel;
248 m_scanMonitor(monitor),
250 m_inputName(
std::move(inputname)),
251 m_sourceId(sourceid),
252 m_serviceType(serviceType),
253 m_thread(new
MThread(
"HDHRChannelFetcher", this))
255 LOG(VB_CHANSCAN, LOG_INFO,
LOC + QString(
"Has ScanMonitor %1")
256 .arg(monitor?
"true":
"false"));
293 LOG(VB_CHANSCAN, LOG_INFO,
LOC + QString(
"Found %1 channels")
313 QString ip = HDHRIPv4Address(dev);
317 LOG(VB_CHANNEL, LOG_INFO,
LOC +
318 QString(
"Failed to get IP address from videodev (%1)").arg(dev));
319 QMutexLocker locker(&
m_lock);
324 LOG(VB_CHANNEL, LOG_INFO,
LOC + QString(
"HDHomeRun IP: %1").arg(ip));
344 QMutexLocker locker(&
m_lock);
357 LOG(VB_CHANSCAN, LOG_INFO,
LOC + QString(
"Found %1 channels").arg(
m_channels->size()));
360 hdhr_chan_map_t::const_iterator it =
m_channels->cbegin();
363 const QString& channum = it.key();
364 QString name = (*it).m_name;
365 uint serviceID = (*it).m_serviceID;
366 QString channelType = (*it).m_channelType;
367 QString hdhrmod = (*it).m_modulation;
368 uint networkID = (*it).m_networkID;
369 uint transportID = (*it).m_transportID;
370 uint frequency = (*it).m_frequency;
373 QString modulation = HDHRMod2Modulation(hdhrmod);
374 uint symbolrate = HDHRMod2SymbolRate(hdhrmod);
375 signed char bandwidth = HDHRMod2Bandwidth(hdhrmod);
376 uint atsc_major_channel = 0;
377 uint atsc_minor_channel = 0;
378 HDHRMajorMinorChannel(channum, atsc_major_channel, atsc_minor_channel);
379 QString sistandard = (atsc_major_channel > 0 && atsc_minor_channel > 0) ?
"atsc" :
"dvb";
381 bool use_on_air_guide =
true;
383 QString msg = tr(
"%1 channel %2: %3").arg(channelType).arg(channum, -5, QChar(
' ')).arg(name, -15, QChar(
' '));
384 LOG(VB_CHANSCAN, LOG_INFO, QString(
"Found %1").arg(msg));
406 bool adding_channel = chanid <= 0;
425 transportID, networkID, symbolrate, bandwidth,
426 'v',
'a',
'a', QString(), QString(),
'a', QString(),
427 QString(), QString(), modsys.
toString(),
"0.35");
430 LOG(VB_GENERAL, LOG_ERR, QString(
"No multiplex for %1 sid:%2 freq:%3 url:%4")
431 .arg(msg).arg(serviceID, -5, 10, QChar(
' ')).arg(frequency).arg((*it).m_tuning.GetDataURL().toString()));
438 channum, serviceID, atsc_major_channel, atsc_minor_channel,
440 QString(),
"Default", QString());
447 channum, serviceID, atsc_major_channel, atsc_minor_channel,
449 QString(),
"Default", QString());
453 LOG(VB_GENERAL, LOG_INFO, QString(
"%1 sid:%2 freq:%3 url:%4")
454 .arg(msg).arg(serviceID, -5, 10, QChar(
' ')).arg(frequency).arg((*it).m_tuning.GetDataURL().toString()));
466 QMutexLocker locker(&
m_lock);
474 uint range = 100 - minval;