Skip to content

Commit

Permalink
OpenTV logical channel numbers
Browse files Browse the repository at this point in the history
Support for OpenTV logical channel numbers as used
by Sky (US/IT/NZ/AU), Dishnet(US) and others.
The Bouquet ID and the Region ID, needed to get the correct
tables, can be defined in the video source  similar to how it
is done for the Freesat and Sky UK channel numbers.
Scanning is activated by a new "Scan OpenTV" channel scanning option.
As a next step, when the applicable network ID's and private descriptor
identifiers are known, the identification of a stream with OpenTV
channel numbers can be done automatically.

Refs #13472
Fixes #13547

Signed-off-by: Klaas de Waal <klaas@kldo.nl>
  • Loading branch information
mspieth authored and kmdewaal committed Jan 9, 2020
1 parent c116014 commit efbc436
Show file tree
Hide file tree
Showing 16 changed files with 240 additions and 2 deletions.
84 changes: 83 additions & 1 deletion mythtv/libs/libmythtv/channelscan/channelscan_sm.cpp
Expand Up @@ -118,6 +118,7 @@ class ScannedChannelInfo
nit_vec_t m_nits;
sdt_map_t m_sdts;
bat_vec_t m_bats;
QMap<uint64_t,uint64_t> m_opentv_channels;
};

/** \class ChannelScanSM
Expand Down Expand Up @@ -158,6 +159,9 @@ ChannelScanSM::ChannelScanSM(ScanMonitor *_scan_monitor,
m_channelTimeout(channel_timeout),
m_inputName(std::move(_inputname)),
m_testDecryption(test_decryption),
m_scanOpenTVBouquet(0),
m_scanOpenTVRegion(0),
m_scanOpenTVRegionMask(0),
// Misc
m_analogSignalHandler(new AnalogSignalHandler(this))
{
Expand Down Expand Up @@ -192,7 +196,7 @@ ChannelScanSM::ChannelScanSM(ScanMonitor *_scan_monitor,
}

LOG(VB_CHANSCAN, LOG_INFO, LOC +
QString("Freesat/BSkyB bouquet_id:%1 region_id:%2")
QString("Freesat/BSkyB/OpenTV bouquet_id:%1 region_id:%2")
.arg(m_bouquetId).arg(m_regionId));

dtvSigMon->SetStreamData(data);
Expand Down Expand Up @@ -251,6 +255,27 @@ void ChannelScanSM::SetAnalog(bool is_analog)
m_signalMonitor->AddListener(m_analogSignalHandler);
}

void ChannelScanSM::SetOpenTV(void)
{
if (m_bouquetId > 0)
{
m_scanOpenTVBouquet = m_bouquetId;
m_scanOpenTVRegion = m_regionId;
if (m_regionId)
m_scanOpenTVRegionMask = 1 << m_regionId;
else
m_scanOpenTVRegionMask = (uint)-1;
LOG(VB_GENERAL, LOG_INFO, LOC +
QString("Scan for OpenTV channels on bouquet ID %1 and region ID %2")
.arg(m_scanOpenTVBouquet).arg(m_scanOpenTVRegion));
}
else
{
LOG(VB_GENERAL, LOG_WARNING, LOC +
QString("Cannot scan for OpenTV channels, bouquet ID is 0"));
}
}

void ChannelScanSM::HandleAllGood(void)
{
QMutexLocker locker(&m_lock);
Expand Down Expand Up @@ -546,6 +571,44 @@ void ChannelScanSM::HandleBAT(const BouquetAssociationTable *bat)
m_defAuthorities[index] = authority.DefaultAuthority();
}
}

if (m_scanOpenTVBouquet > 0)
{
const unsigned char *otv_chan_list =
MPEGDescriptor::Find(parsed, PrivateDescriptorID::opentv_channel_list);
if (otv_chan_list)
{
OpenTVChannelListDescriptor opentvChannelList(otv_chan_list);

uint64_t regionMask = (uint64_t)-1;
if (opentvChannelList.RegionID() > 0 && opentvChannelList.RegionID() < 32)
regionMask = ((uint64_t)1) << opentvChannelList.RegionID();

for (uint j = 0; j < opentvChannelList.ChannelCount(); j++)
{
uint64_t index = ((uint64_t)netid << 32) |
bat->BouquetID() << 16 |
opentvChannelList.ServiceID(j);
if (!m_currentInfo)
m_currentInfo = new ScannedChannelInfo();
if (m_currentInfo->m_opentv_channels.contains(index))
{
m_currentInfo->m_opentv_channels[index] |= regionMask << 32;
}
else
{
m_currentInfo->m_opentv_channels[index] =
regionMask << 32 |
(opentvChannelList.ChannelID(j) << 16);
}
if (regionMask & m_scanOpenTVRegionMask)
{
m_currentInfo->m_opentv_channels[index] |=
opentvChannelList.ChannelNumber(j);
}
}
}
}
}
UpdateChannelInfo(true);
}
Expand Down Expand Up @@ -1654,6 +1717,25 @@ ChannelScanSM::GetChannelList(transport_scan_items_it_t trans_info,
}
}

// OpenTV Logical Channel Numbers
if (info.m_chanNum.isEmpty())
{
if (m_scanOpenTVBouquet > 0)
{
qlonglong key =
((qlonglong)info.m_origNetId<<32) | m_scanOpenTVBouquet << 16 | info.m_serviceId;
QMap<uint64_t, uint64_t>::const_iterator it = scan_info->m_opentv_channels.find(key);
if (it != scan_info->m_opentv_channels.end())
{
uint chanNum = (*it) & 0xffff;
if (chanNum > 0)
{
info.m_chanNum = chanNum;
}
}
}
}

LOG(VB_CHANSCAN, LOG_INFO, LOC +
QString("GetChannelList: service %1 (0x%2) chan_num '%3' callsign '%4'")
.arg(info.m_serviceId).arg(info.m_serviceId,4,16,QChar('0'))
Expand Down
6 changes: 6 additions & 0 deletions mythtv/libs/libmythtv/channelscan/channelscan_sm.h
Expand Up @@ -121,6 +121,7 @@ class ChannelScanSM : public MPEGStreamListener,
void SetChannelTimeout(uint val) { m_channelTimeout = val; }
void SetScanDTVTunerType(DTVTunerType t) { m_scanDTVTunerType = t; }
void SetScanDTVTunerType(int t) { m_scanDTVTunerType = DTVTunerType(t); }
void SetOpenTV(void);

uint GetSignalTimeout(void) const { return m_signalTimeout; }
uint GetChannelTimeout(void) const { return m_channelTimeout; }
Expand Down Expand Up @@ -225,6 +226,11 @@ class ChannelScanSM : public MPEGStreamListener,
// Optional info
DTVTunerType m_scanDTVTunerType {DTVTunerType::kTunerTypeUnknown};

// OpenTV
uint m_scanOpenTVBouquet {0};
uint m_scanOpenTVRegion {0};
uint m_scanOpenTVRegionMask {0};

/// The big lock
mutable QMutex m_lock;

Expand Down
12 changes: 12 additions & 0 deletions mythtv/libs/libmythtv/channelscan/channelscanmiscsettings.h
Expand Up @@ -201,6 +201,18 @@ class TrustEncSISetting : public TransMythUICheckBoxSetting
}
};

class ScanOpenTV: public TransMythUICheckBoxSetting
{
public:
ScanOpenTV()
{
setLabel(QObject::tr("Scan OpenTV"));
setHelpText(QObject::tr("Scan for OpenTV channel numbers.\n"
"Set the Bouquet ID and Region ID in the Video Source."));
setValue(false);
};
};

class ScanFrequencykHz: public TransTextEditSetting
{
public:
Expand Down
7 changes: 7 additions & 0 deletions mythtv/libs/libmythtv/channelscan/channelscanner.cpp
Expand Up @@ -121,6 +121,7 @@ void ChannelScanner::Scan(
bool do_complete_only,
bool do_full_channel_search,
bool do_add_full_ts,
bool do_scanOpenTV,
ServiceRequirements service_requirements,
// stuff needed for particular scans
uint mplexid /* TransportScan */,
Expand All @@ -136,6 +137,7 @@ void ChannelScanner::Scan(
m_completeOnly = do_complete_only;
m_fullSearch = do_full_channel_search;
m_addFullTS = do_add_full_ts;
m_scanOpenTV = do_scanOpenTV;
m_serviceRequirements = service_requirements;
m_sourceid = sourceid;

Expand All @@ -155,6 +157,11 @@ void ChannelScanner::Scan(

bool ok = false;

if (do_scanOpenTV)
{
m_sigmonScanner->SetOpenTV();
}

if ((ScanTypeSetting::FullScan_ATSC == scantype) ||
(ScanTypeSetting::FullScan_DVBC == scantype) ||
(ScanTypeSetting::FullScan_DVBT == scantype) ||
Expand Down
4 changes: 4 additions & 0 deletions mythtv/libs/libmythtv/channelscan/channelscanner.h
Expand Up @@ -80,6 +80,7 @@ class MTV_PUBLIC ChannelScanner
bool do_complete_only,
bool do_full_channel_search,
bool do_add_full_ts,
bool do_scanOpenTV,
ServiceRequirements service_requirements,
// stuff needed for particular scans
uint mplexid,
Expand Down Expand Up @@ -150,6 +151,9 @@ class MTV_PUBLIC ChannelScanner
/// Add MPTS "full transport stream" channels
bool m_addFullTS {false};

/// Scan for OpenTV channel numbers
bool m_scanOpenTV {false};

int m_sourceid {-1};

/// Services desired post scan
Expand Down
7 changes: 7 additions & 0 deletions mythtv/libs/libmythtv/channelscan/scanwizardconfig.cpp
Expand Up @@ -37,6 +37,7 @@ void ScanWizard::SetupConfig(
m_fullSearch = new FullChannelSearch();
m_addFullTS = new AddFullTS();
m_trustEncSI = new TrustEncSISetting();
m_scanOpenTV = new ScanOpenTV();

setLabel(tr("Channel Scan"));

Expand All @@ -47,6 +48,7 @@ void ScanWizard::SetupConfig(
addChild(m_fullSearch);
addChild(m_addFullTS);
addChild(m_trustEncSI);
addChild(m_scanOpenTV);

addChild(m_videoSource);
addChild(m_input);
Expand Down Expand Up @@ -110,6 +112,11 @@ bool ScanWizard::DoTestDecryption(void) const
return m_trustEncSI->boolValue();
}

bool ScanWizard::DoScanOpenTV(void) const
{
return m_scanOpenTV->boolValue();
}

void ScanWizard::SetPaneDefaults(const QString &cardid_inputname)
{
const int sourceid = m_videoSource->getValue().toInt();
Expand Down
1 change: 1 addition & 0 deletions mythtv/libs/libmythtv/channelscan/scanwizardconfig.h
Expand Up @@ -49,6 +49,7 @@ class CompleteChannelsOnly;
class FullChannelSearch;
class AddFullTS;
class TrustEncSISetting;
class ScanOpenTV;

class PaneAll;
class PaneATSC;
Expand Down
11 changes: 11 additions & 0 deletions mythtv/libs/libmythtv/mpeg/dvbdescriptors.cpp
Expand Up @@ -682,6 +682,17 @@ QString FreesatCallsignDescriptor::toString(void) const
return ret;
}

QString OpenTVChannelListDescriptor::toString() const
{
QString ret = QString("OpenTV ChannelList Descriptor region: %1 sid->chan_num(id): ").arg(RegionID());
for (uint i = 0; i < ChannelCount(); i++)
{
ret += QString("%1->%2(%3)").arg(ServiceID(i)).arg(ChannelNumber(i)).arg(ChannelID(i));
ret += (i+1<ChannelCount()) ? ", " : "";
}
return ret;
}

QString CAIdentifierDescriptor::toString(void) const
{
QString ret = QString("CAIdentifierDescriptor ");
Expand Down
32 changes: 32 additions & 0 deletions mythtv/libs/libmythtv/mpeg/dvbdescriptors.h
Expand Up @@ -2327,6 +2327,38 @@ class BSkyBLCNDescriptor : public MPEGDescriptor
QString toString(void) const override; // MPEGDescriptor
};

class OpenTVChannelListDescriptor : public MPEGDescriptor
{
public:
OpenTVChannelListDescriptor(const unsigned char *data, int len = 300) :
MPEGDescriptor(data, len, PrivateDescriptorID::opentv_channel_list) { }
// Name bits loc expected value
// descriptor_tag 8 0.0 0xB1
// descriptor_length 8 1.0

uint ChannelCount(void) const { return (DescriptorLength() - 2)/9; }

uint RegionID() const
{ return (m_data[2] << 8) | m_data[3]; }

uint ServiceID(uint i) const
{ return (m_data[4 + 0 + (i*9)] << 8) | m_data[4 + 1 + (i*9)]; }

uint ChannelType(uint i) const
{ return m_data[4 + 2 + (i*9)]; }

uint ChannelID(uint i) const
{ return ((m_data[4 + 3 + (i*9)] << 8) | m_data[4 + 5 + (i*9)]); }

uint ChannelNumber(uint i) const
{ return ((m_data[4 + 5 + (i*9)] << 8) | m_data[4 + 6 + (i*9)]); }

uint Flags(uint i) const
{ return ((m_data[4 + 7 + (i*9)] << 8) | m_data[4 + 8 + (i*9)]) & 0xf; }

QString toString(void) const override;
};

// ETSI TS 102 323 (TV Anytime)
class DVBContentIdentifierDescriptor : public MPEGDescriptor
{
Expand Down
36 changes: 36 additions & 0 deletions mythtv/libs/libmythtv/mpeg/dvbstreamdata.cpp
Expand Up @@ -470,6 +470,24 @@ bool DVBStreamData::GetEITPIDChanges(const uint_vec_t &cur_pids,
add_pids.push_back(DVB_BVLONG_EIT_PID);
}

// opentv eit pids
if (m_dvbEitDishnetLong)
{
uint pid;
for(pid=OTV_EIT_TIT_PID_START; pid <= OTV_EIT_TIT_PID_END; pid++)
{
if (find(cur_pids.begin(), cur_pids.end(),
pid) == cur_pids.end())
add_pids.push_back(pid);
}
for(pid=OTV_EIT_SUP_PID_START; pid <= OTV_EIT_SUP_PID_END; pid++)
{
if (find(cur_pids.begin(), cur_pids.end(),
pid) == cur_pids.end())
add_pids.push_back(pid);
}
}

if (m_desiredNetId == PREMIERE_ONID &&
find(cur_pids.begin(), cur_pids.end(),
(uint) PREMIERE_EIT_DIREKT_PID) == cur_pids.end())
Expand Down Expand Up @@ -520,6 +538,24 @@ bool DVBStreamData::GetEITPIDChanges(const uint_vec_t &cur_pids,
del_pids.push_back(DVB_BVLONG_EIT_PID);
}

// opentv eit pids
if (m_dvbEitDishnetLong)
{
uint pid;
for(pid=OTV_EIT_TIT_PID_START; pid <= OTV_EIT_TIT_PID_END; pid++)
{
if (find(cur_pids.begin(), cur_pids.end(),
pid) != cur_pids.end())
del_pids.push_back(pid);
}
for(pid=OTV_EIT_SUP_PID_START; pid <= OTV_EIT_SUP_PID_END; pid++)
{
if (find(cur_pids.begin(), cur_pids.end(),
pid) != cur_pids.end())
del_pids.push_back(pid);
}
}

if (m_desiredNetId == PREMIERE_ONID &&
find(cur_pids.begin(), cur_pids.end(),
(uint) PREMIERE_EIT_DIREKT_PID) != cur_pids.end())
Expand Down
8 changes: 8 additions & 0 deletions mythtv/libs/libmythtv/mpeg/mpegdescriptors.cpp
Expand Up @@ -372,6 +372,9 @@ QString MPEGDescriptor::DescriptorTagString(void) const
case PrivateDescriptorID::premiere_content_transmission: /* 0xF2 */
comma_list_append(str, "Possibly Premiere DE Content Transmission");
break;
case PrivateDescriptorID::opentv_channel_list: /* 0xB1 */
comma_list_append(str, "Possibly DVB OpenTV Channel List");
break;
}

if (str.isEmpty())
Expand Down Expand Up @@ -557,6 +560,11 @@ QString MPEGDescriptor::toStringPD(uint priv_dsid) const
{
SET_STRING(ComponentNameDescriptor);
}
// OpenTV descriptors
else if (PrivateDescriptorID::opentv_channel_list == DescriptorTag())
{
SET_STRING(OpenTVChannelListDescriptor);
}
// POSSIBLY UNSAFE ! -- end
else
{
Expand Down
11 changes: 11 additions & 0 deletions mythtv/libs/libmythtv/mpeg/mpegdescriptors.h
Expand Up @@ -218,6 +218,17 @@ class PrivateDescriptorID
premiere_content_order = 0xF0,
premiere_parental_information = 0xF1,
premiere_content_transmission = 0xF2,

// Private -- OpenTV
opentv_titles_1 = 0xA0, /* implemented */
opentv_titles_2 = 0xA1, /* implemented */
opentv_titles_3 = 0xA2, /* implemented */
opentv_titles_4 = 0xA3, /* implemented */
opentv_summaries_1 = 0xA8, /* implemented */
opentv_summaries_2 = 0xA9, /* implemented */
opentv_summaries_3 = 0xAA, /* implemented */
opentv_summaries_4 = 0xAB, /* implemented */
opentv_channel_list = 0xB1, /* partial */
};
};

Expand Down

0 comments on commit efbc436

Please sign in to comment.