Skip to content

Commit

Permalink
Add support for the Service Relocated Descriptor
Browse files Browse the repository at this point in the history
Add the Service Relocated Descriptor as described in
the A038 Blue Book page 115.
This descriptor is used when a service is moved from one
transponder to another and in that time the service does
appear in both transports.
Such a service appeared twice in the channel list.
This is now fixed; the service in the old location is removed
when the "Remove duplicates" scan option is selected.
  • Loading branch information
kmdewaal committed May 24, 2020
1 parent 1146cfe commit 6f01dec
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 58 deletions.
5 changes: 5 additions & 0 deletions mythtv/libs/libmythtv/channelinfo.h
Expand Up @@ -252,6 +252,11 @@ class MTV_PUBLIC ChannelInsertInfo
bool m_isOpencable {false};
bool m_couldBeOpencable {false};
int m_decryptionStatus {0};

// Service relocated descriptor
uint m_oldOrigNetId {0};
uint m_oldTsId {0};
uint m_oldServiceId {0};
};
using ChannelInsertInfoList = vector<ChannelInsertInfo>;

Expand Down
146 changes: 97 additions & 49 deletions mythtv/libs/libmythtv/channelscan/channelimporter.cpp
Expand Up @@ -13,6 +13,7 @@

// Qt includes
#include <QTextStream>
#include <QElapsedTimer>

using namespace std;

Expand Down Expand Up @@ -126,6 +127,12 @@ void ChannelImporter::Process(const ScanDTVTransportList &_transports,
// Remove the channels that do not pass various criteria.
FilterServices(transports);

// Remove the channels that have been relocated.
if (m_removeDuplicates)
{
FilterRelocatedServices(transports);
}

// Pull in DB info in transports
// Channels not found in scan but only in DB are returned in db_trans
sourceid = transports[0].m_channels[0].m_sourceId;
Expand Down Expand Up @@ -178,7 +185,7 @@ void ChannelImporter::Process(const ScanDTVTransportList &_transports,

// Create summary
ssMsg << endl;
ssMsg << GetSummary(transports.size(), info, stats) << endl;
ssMsg << GetSummary(info, stats) << endl;

LOG(VB_GENERAL, LOG_INFO, LOC + msg);

Expand Down Expand Up @@ -485,7 +492,7 @@ void ChannelImporter::InsertChannels(
ssMsg << endl;
ssMsg << "Remaining channels (" << SimpleCountChannels(list) << "):" << endl;
ssMsg << FormatChannels(list).toLatin1().constData() << endl;
ssMsg << GetSummary(list.size(), ninfo, nstats).toLatin1().constData();
ssMsg << GetSummary(ninfo, nstats).toLatin1().constData();
}
LOG(VB_GENERAL, LOG_INFO, LOC + msg);
}
Expand Down Expand Up @@ -1018,74 +1025,113 @@ void ChannelImporter::FilterServices(ScanDTVTransportList &transports) const
for (auto & transport : transports)
{
ChannelInsertInfoList filtered;
for (size_t k = 0; k < transport.m_channels.size(); ++k)
for (auto & channel : transport.m_channels)
{
if (m_ftaOnly && transport.m_channels[k].m_isEncrypted &&
transport.m_channels[k].m_decryptionStatus != kEncDecrypted)
if (m_ftaOnly && channel.m_isEncrypted &&
channel.m_decryptionStatus != kEncDecrypted)
continue;

if (require_a && transport.m_channels[k].m_isDataService)
if (require_a && channel.m_isDataService)
continue;

if (require_av && transport.m_channels[k].m_isAudioService)
if (require_av && channel.m_isAudioService)
continue;

// Filter channels out that do not have a logical channel number
if (m_lcnOnly && transport.m_channels[k].m_chanNum.isEmpty())
if (m_lcnOnly && channel.m_chanNum.isEmpty())
{
QString msg = FormatChannel(transport, transport.m_channels[k]);
QString msg = FormatChannel(transport, channel);
LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("No LCN: %1").arg(msg));
continue;
}

// Filter channels out that are not present in PAT and PMT.
if (m_completeOnly &&
!(transport.m_channels[k].m_inPat &&
transport.m_channels[k].m_inPmt ))
!(channel.m_inPat &&
channel.m_inPmt ))
{
QString msg = FormatChannel(transport, transport.m_channels[k]);
QString msg = FormatChannel(transport, channel);
LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("Not in PAT/PMT: %1").arg(msg));
continue;
}

// Filter channels out that are not present in SDT and that are not ATSC
if (m_completeOnly &&
transport.m_channels[k].m_atscMajorChannel == 0 &&
transport.m_channels[k].m_atscMinorChannel == 0 &&
!(transport.m_channels[k].m_inPat &&
transport.m_channels[k].m_inPmt &&
transport.m_channels[k].m_inSdt &&
(transport.m_channels[k].m_patTsId ==
transport.m_channels[k].m_sdtTsId)))
channel.m_atscMajorChannel == 0 &&
channel.m_atscMinorChannel == 0 &&
!(channel.m_inPat &&
channel.m_inPmt &&
channel.m_inSdt &&
(channel.m_patTsId ==
channel.m_sdtTsId)))
{
QString msg = FormatChannel(transport, transport.m_channels[k]);
QString msg = FormatChannel(transport, channel);
LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("Not in PAT/PMT/SDT: %1").arg(msg));
continue;
}

// Filter channels out that do not have a name
if (m_completeOnly && transport.m_channels[k].m_serviceName.isEmpty())
if (m_completeOnly && channel.m_serviceName.isEmpty())
{
QString msg = FormatChannel(transport, transport.m_channels[k]);
QString msg = FormatChannel(transport, channel);
LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("No name: %1").arg(msg));
continue;
}

// Filter channels out only in channels.conf, i.e. not found
if (transport.m_channels[k].m_inChannelsConf &&
!(transport.m_channels[k].m_inPat ||
transport.m_channels[k].m_inPmt ||
transport.m_channels[k].m_inVct ||
transport.m_channels[k].m_inNit ||
transport.m_channels[k].m_inSdt))
if (channel.m_inChannelsConf &&
!(channel.m_inPat ||
channel.m_inPmt ||
channel.m_inVct ||
channel.m_inNit ||
channel.m_inSdt))
continue;

filtered.push_back(transport.m_channels[k]);
filtered.push_back(channel);
}
transport.m_channels = filtered;
}
}

void ChannelImporter::FilterRelocatedServices(ScanDTVTransportList &transports) const
{
QMap<uint64_t, bool> rs;
QElapsedTimer timer;
timer.start();

// Search all channels to find relocated services
for (auto & transport : transports)
{
for (auto & channel : transport.m_channels)
{
if (channel.m_oldOrigNetId > 0)
{
uint64_t key = ((uint64_t)channel.m_oldOrigNetId << 32) | (channel.m_oldTsId << 16) | channel.m_oldServiceId;
rs[key] = true;
}
}
}

// Remove all relocated services
for (auto & transport : transports)
{
ChannelInsertInfoList filtered;
for (auto & channel : transport.m_channels)
{
uint64_t key = ((uint64_t)channel.m_origNetId << 32) | (channel.m_sdtTsId << 16) | channel.m_serviceId;
if (rs.value(key, false))
{
QString msg = FormatChannel(transport, channel);
LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("Relocated: %1").arg(msg));
continue;
}
filtered.push_back(channel);
}
transport.m_channels = filtered;
}
LOG(VB_CHANSCAN, LOG_DEBUG, QString("%1 processing %2 milliseconds").arg(__func__).arg(timer.elapsed()));
}

/** \fn ChannelImporter::GetDBTransports(uint,ScanDTVTransportList&) const
* \brief Adds found channel info to transports list,
* returns channels in DB which were not found in scan
Expand Down Expand Up @@ -1341,7 +1387,6 @@ QString ChannelImporter::FormatChannel(
QString msg;
QTextStream ssMsg(&msg);

ssMsg << transport.m_modulation.toString().toLatin1().constData() << ":";
ssMsg << transport.m_frequency << ":";

QString si_standard = (chan.m_siStandard=="opencable") ?
Expand Down Expand Up @@ -1494,9 +1539,10 @@ QString ChannelImporter::FormatChannels(

for (auto & transport : transports)
{
for (size_t j = 0; j < transport.m_channels.size(); ++j)
msg += FormatChannel(transport, transport.m_channels[j],
info) + "\n";
for (auto & channel : transport.m_channels)
{
msg += FormatChannel(transport, channel, info) + "\n";
}
}

return msg;
Expand Down Expand Up @@ -1537,31 +1583,33 @@ QString ChannelImporter::FormatTransports(
}

QString ChannelImporter::GetSummary(
uint transport_count,
const ChannelImporterBasicStats &info,
const ChannelImporterUniquenessStats &stats)
{
//: %n is the number of transports
QString msg = tr("Found %n transport(s):\n", "", transport_count);
msg += tr("Channels: FTA Enc Dec\n") +
QString msg = tr("Channels: FTA Enc Dec\n") +
QString("ATSC %1 %2 %3\n")
.arg(info.m_atscChannels[0],3).arg(info.m_atscChannels[1],3)
.arg(info.m_atscChannels[2],3) +
.arg(info.m_atscChannels[0],3)
.arg(info.m_atscChannels[1],3)
.arg(info.m_atscChannels[2],3) +
QString("DVB %1 %2 %3\n")
.arg(info.m_dvbChannels [0],3).arg(info.m_dvbChannels [1],3)
.arg(info.m_dvbChannels [2],3) +
.arg(info.m_dvbChannels [0],3)
.arg(info.m_dvbChannels [1],3)
.arg(info.m_dvbChannels [2],3) +
QString("SCTE %1 %2 %3\n")
.arg(info.m_scteChannels[0],3).arg(info.m_scteChannels[1],3)
.arg(info.m_scteChannels[2],3) +
.arg(info.m_scteChannels[0],3)
.arg(info.m_scteChannels[1],3)
.arg(info.m_scteChannels[2],3) +
QString("MPEG %1 %2 %3\n")
.arg(info.m_mpegChannels[0],3).arg(info.m_mpegChannels[1],3)
.arg(info.m_mpegChannels[2],3) +
QString("NTSC %1\n").arg(info.m_ntscChannels[0],3) +
.arg(info.m_mpegChannels[0],3)
.arg(info.m_mpegChannels[1],3)
.arg(info.m_mpegChannels[2],3) +
QString("NTSC %1\n")
.arg(info.m_ntscChannels[0],3) +
tr("Unique: prog %1 atsc %2 atsc minor %3 channum %4\n")
.arg(stats.m_uniqueProgNum).arg(stats.m_uniqueAtscNum)
.arg(stats.m_uniqueAtscMin).arg(stats.m_uniqueChanNum) +
.arg(stats.m_uniqueProgNum).arg(stats.m_uniqueAtscNum)
.arg(stats.m_uniqueAtscMin).arg(stats.m_uniqueChanNum) +
tr("Max atsc major count: %1")
.arg(stats.m_maxAtscMajCnt);
.arg(stats.m_maxAtscMajCnt);

return msg;
}
Expand Down
3 changes: 2 additions & 1 deletion mythtv/libs/libmythtv/channelscan/channelimporter.h
Expand Up @@ -145,6 +145,8 @@ class MTV_PUBLIC ChannelImporter
static void MergeSameFrequency(ScanDTVTransportList &transports);
static void RemoveDuplicates(ScanDTVTransportList &transports, ScanDTVTransportList &duplicates);
void FilterServices(ScanDTVTransportList &transports) const;
void FilterRelocatedServices(ScanDTVTransportList &transports) const;

ScanDTVTransportList GetDBTransports(
uint sourceid, ScanDTVTransportList &transports) const;

Expand Down Expand Up @@ -229,7 +231,6 @@ class MTV_PUBLIC ChannelImporter
const ScanDTVTransportList &transports_in);

static QString GetSummary(
uint transport_count,
const ChannelImporterBasicStats &info,
const ChannelImporterUniquenessStats &stats);

Expand Down
39 changes: 33 additions & 6 deletions mythtv/libs/libmythtv/channelscan/channelscan_sm.cpp
Expand Up @@ -1054,7 +1054,7 @@ bool ChannelScanSM::UpdateChannelInfo(bool wait_until_complete)
}

LOG(VB_CHANSCAN, LOG_INFO, LOC +
QString("Adding %1 offset %2 ss %3 to m_channelList.")
QString("Adding %1 offset:%2 ss:%3")
.arg(item.m_tuning.toString()).arg(m_current.offset())
.arg(item.m_signalStrength));

Expand Down Expand Up @@ -1268,6 +1268,29 @@ static void update_info(ChannelInsertInfo &info,
info.m_sdtTsId << 16 | info.m_serviceId;
if (defAuthorities.contains(index))
info.m_defaultAuthority = defAuthorities[index];

// Is this service relocated from somewhere else?
ServiceRelocatedDescriptor *srdesc = sdt->GetServiceRelocatedDescriptor(i);
if (srdesc)
{
info.m_oldOrigNetId = srdesc->OldOriginalNetworkID();
info.m_oldTsId = srdesc->OldTransportID();
info.m_oldServiceId = srdesc->OldServiceID();

LOG(VB_CHANSCAN, LOG_DEBUG, "ChannelScanSM: " +
QString("Service '%1' onid:%2 tid:%3 sid:%4 ")
.arg(info.m_serviceName)
.arg(info.m_origNetId)
.arg(info.m_sdtTsId)
.arg(info.m_serviceId) +
QString(" relocated from onid:%1 tid:%2 sid:%3")
.arg(info.m_oldOrigNetId)
.arg(info.m_oldTsId)
.arg(info.m_oldServiceId));

delete srdesc;
}

}

uint ChannelScanSM::GetCurrentTransportInfo(
Expand Down Expand Up @@ -2466,7 +2489,7 @@ bool ChannelScanSM::AddToList(uint mplexid)
{
MSqlQuery query(MSqlQuery::InitCon());
query.prepare(
"SELECT sourceid, sistandard, transportid, frequency, modulation "
"SELECT sourceid, sistandard, transportid, frequency, modulation, mod_sys "
"FROM dtv_multiplex "
"WHERE mplexid = :MPLEXID");
query.bindValue(":MPLEXID", mplexid);
Expand All @@ -2486,14 +2509,18 @@ bool ChannelScanSM::AddToList(uint mplexid)
uint sourceid = query.value(0).toUInt();
QString sistandard = query.value(1).toString();
uint tsid = query.value(2).toUInt();
DTVTunerType tt(DTVTunerType::kTunerTypeUnknown);

uint frequency = query.value(3).toUInt();
QString modulation = query.value(4).toString();
QString mod_sys = query.value(5).toString();
DTVModulationSystem delsys;
delsys.Parse(mod_sys);
DTVTunerType tt = CardUtil::ConvertToTunerType(delsys);
QString fn = (tsid) ? QString("Transport ID %1").arg(tsid) :
QString("Multiplex #%1").arg(mplexid);

if (query.value(4).toString() == "8vsb")
if (modulation == "8vsb")
{
QString chan = QString("%1 Hz").arg(query.value(3).toInt());
QString chan = QString("%1 Hz").arg(frequency);
struct CHANLIST *curList = gChanLists[0].list;
int totalChannels = gChanLists[0].count;
int findFrequency = (query.value(3).toInt() / 1000) - 1750;
Expand Down
2 changes: 1 addition & 1 deletion mythtv/libs/libmythtv/channelutil.cpp
Expand Up @@ -100,7 +100,7 @@ static uint insert_dtv_multiplex(
QString("dbid:%1 std:'%2' ").arg(db_source_id).arg(sistandard) +
QString("freq:%1 mod:%2 ").arg(frequency).arg(modulation) +
QString("tid:%1 nid:%2 ").arg(transport_id).arg(network_id) +
QString("pol:%1 mod_sys:%2 ...)").arg(QChar(polarity)).arg(mod_sys) +
QString("pol:%1 msys:%2 ...)").arg(QChar(polarity)).arg(mod_sys) +
QString("mplexid:%1").arg(mplex));

bool isDVB = (sistandard.toLower() == "dvb");
Expand Down
2 changes: 1 addition & 1 deletion mythtv/libs/libmythtv/dtvmultiplex.cpp
Expand Up @@ -41,7 +41,7 @@ QString DTVMultiplex::toString() const
.arg(m_bandwidth.toString()).arg(m_transMode.toString())
.arg(m_guardInterval.toString()).arg(m_hierarchy.toString())
.arg(m_polarity.toString());
ret += QString(" fec:%1 msys:%2 rolloff:%3")
ret += QString(" fec:%1 msys:%2 ro:%3")
.arg(m_fec.toString(),-4).arg(m_modSys.toString(),-6).arg(m_rolloff.toString());

return ret;
Expand Down
15 changes: 15 additions & 0 deletions mythtv/libs/libmythtv/mpeg/dvbtables.cpp
Expand Up @@ -176,6 +176,21 @@ ServiceDescriptor *ServiceDescriptionTable::GetServiceDescriptor(uint i) const
return nullptr;
}

ServiceRelocatedDescriptor *ServiceDescriptionTable::GetServiceRelocatedDescriptor(uint i) const
{
desc_list_t parsed =
MPEGDescriptor::Parse(ServiceDescriptors(i),
ServiceDescriptorsLength(i));

const unsigned char *desc =
MPEGDescriptor::FindExtension(parsed, DescriptorID::service_relocated);

if (desc)
return new ServiceRelocatedDescriptor(desc);

return nullptr;
}

bool ServiceDescriptionTable::Mutate(void)
{
if (VerifyCRC())
Expand Down
1 change: 1 addition & 0 deletions mythtv/libs/libmythtv/mpeg/dvbtables.h
Expand Up @@ -158,6 +158,7 @@ class MTV_PUBLIC ServiceDescriptionTable : public PSIPTable
{ return m_ptrs[i]+5; }
// }
ServiceDescriptor *GetServiceDescriptor(uint i) const;
ServiceRelocatedDescriptor *GetServiceRelocatedDescriptor(uint i) const;

/// mutates a SDTo into a SDTa (vice versa) and recalculates the CRC
bool Mutate(void);
Expand Down

0 comments on commit 6f01dec

Please sign in to comment.