418 changes: 316 additions & 102 deletions mythtv/libs/libmythtv/mpeg/atscstreamdata.cpp

Large diffs are not rendered by default.

118 changes: 81 additions & 37 deletions mythtv/libs/libmythtv/mpeg/atscstreamdata.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,16 @@ class RatingRegionTable;
class DirectedChannelChangeTable;
class DirectedChannelChangeSelectionCodeTable;

class ATSCStreamData : public MPEGStreamData
typedef vector<uint> uint_vec_t;
typedef QMap<uint, uint_vec_t> pid_tsid_vec_t;
typedef TerrestrialVirtualChannelTable* tvct_ptr_t;
typedef CableVirtualChannelTable* cvct_ptr_t;
typedef vector<const TerrestrialVirtualChannelTable*> tvct_vec_t;
typedef vector<const CableVirtualChannelTable*> cvct_vec_t;
typedef QMap<uint, tvct_ptr_t> tvct_cache_t;
typedef QMap<uint, cvct_ptr_t> cvct_cache_t;

class ATSCStreamData : virtual public MPEGStreamData
{
Q_OBJECT
public:
Expand All @@ -33,65 +42,100 @@ class ATSCStreamData : public MPEGStreamData
bool cacheTables = false);

void Reset(int desiredMajorChannel = -1, int desiredMinorChannel = -1);
void SetDesiredChannel(int major, int minor)
{
_desired_major_channel = major;
_desired_minor_channel = minor;
}

void SetVersionMGT(int version) { _mgt_version = version; }
void SetVersionTVCT(uint tsid, int version) { _tvct_version[tsid] = version; }
void SetVersionCVCT(uint tsid, int version) { _cvct_version[tsid] = version; }
void SetVersionEIT(uint pid, int version) { _eit_version[pid] = version; }
void SetVersionETT(uint pid, int version) { _ett_version[pid] = version; }

int VersionMGT() const { return _mgt_version; }
void SetDesiredChannel(int major, int minor);

// Table processing
virtual bool HandleTables(uint pid, const PSIPTable &psip);
bool IsRedundant(const PSIPTable&) const;

/// Current UTC to GPS time offset in seconds
uint GPSOffset() const { return _GPS_UTC_offset; }

// Table versions
void SetVersionMGT(int version)
{ _mgt_version = version; }
void SetVersionTVCT(uint tsid, int version)
{ _tvct_version[tsid] = version; }
void SetVersionCVCT(uint tsid, int version)
{ _cvct_version[tsid] = version; }
void SetVersionEIT(uint pid, int version)
{ _eit_version[pid] = version; }
void SetVersionETT(uint pid, int version)
{ _ett_version[pid] = version; }

int VersionMGT() const { return _mgt_version; }
inline int VersionTVCT(uint tsid) const;
inline int VersionCVCT(uint tsid) const;
inline int VersionEIT(uint pid) const;
inline int VersionETT(uint pid) const;

void HandleTables(const TSPacket* tspacket);
bool IsRedundant(const PSIPTable&) const;
// Caching
bool HasCachedMGT(bool current = true) const;
bool HasCachedTVCT(uint pid, bool current = true) const;
bool HasCachedCVCT(uint pid, bool current = true) const;
bool HasCachedAllTVCTs(bool current = true) const;
bool HasCachedAllCVCTs(bool current = true) const;
bool HasCachedAllVCTs(bool current = true) const
{ return HasCachedAllTVCTs(current) && HasCachedAllCVCTs(current); }

uint GPSOffset() const { return _GPS_UTC_offset; }
const MasterGuideTable *GetCachedMGT(bool current = true) const;
const tvct_ptr_t GetCachedTVCT(uint pid, bool current = true) const;
const cvct_ptr_t GetCachedCVCT(uint pid, bool current = true) const;

tvct_vec_t GetAllCachedTVCTs(bool current = true) const;
cvct_vec_t GetAllCachedCVCTs(bool current = true) const;

void ReturnCachedTables(tvct_vec_t&) const;
void ReturnCachedTables(cvct_vec_t&) const;

// Single channel stuff
int DesiredMajorChannel() const { return _desired_major_channel; }
int DesiredMinorChannel() const { return _desired_minor_channel; }
int DesiredMajorChannel(void) const { return _desired_major_channel; }
int DesiredMinorChannel(void) const { return _desired_minor_channel; }
signals:
void UpdateMGT(const MasterGuideTable*);
void UpdateSTT(const SystemTimeTable*);
void UpdateRRT(const RatingRegionTable*);
void UpdateDCCT(const DirectedChannelChangeTable*);
void UpdateMGT( const MasterGuideTable*);
void UpdateSTT( const SystemTimeTable*);
void UpdateRRT( const RatingRegionTable*);
void UpdateDCCT( const DirectedChannelChangeTable*);
void UpdateDCCSCT(const DirectedChannelChangeSelectionCodeTable*);

void UpdateVCT(uint tsid, const VirtualChannelTable*);
void UpdateTVCT(uint tsid, const TerrestrialVirtualChannelTable*);
void UpdateCVCT(uint tsid, const CableVirtualChannelTable*);
void UpdateEIT(uint pid, const EventInformationTable*);
void UpdateETT(uint pid, const ExtendedTextTable*);
void UpdateVCT( uint pid, const VirtualChannelTable*);
void UpdateTVCT(uint pid, const TerrestrialVirtualChannelTable*);
void UpdateCVCT(uint pid, const CableVirtualChannelTable*);
void UpdateEIT( uint pid, const EventInformationTable*);
void UpdateETT( uint pid, const ExtendedTextTable*);

private slots:
void PrintMGT(const MasterGuideTable*) const;
void PrintSTT(const SystemTimeTable*) const;
void PrintRRT(const RatingRegionTable*) const;
void PrintDCCT(const DirectedChannelChangeTable*) const;
void PrintMGT( const MasterGuideTable*) const;
void PrintSTT( const SystemTimeTable*) const;
void PrintRRT( const RatingRegionTable*) const;
void PrintDCCT( const DirectedChannelChangeTable*) const;
void PrintDCCSCT(const DirectedChannelChangeSelectionCodeTable*) const;

void PrintTVCT(uint tsid, const TerrestrialVirtualChannelTable*) const;
void PrintCVCT(uint tsid, const CableVirtualChannelTable*) const;
void PrintEIT(uint pid, const EventInformationTable*) const;
void PrintETT(uint pid, const ExtendedTextTable*) const;
void PrintTVCT(uint pid, const TerrestrialVirtualChannelTable*) const;
void PrintCVCT(uint pid, const CableVirtualChannelTable*) const;
void PrintEIT( uint pid, const EventInformationTable*) const;
void PrintETT( uint pid, const ExtendedTextTable*) const;

private:
// Caching
void CacheMGT(MasterGuideTable*);
void CacheTVCT(uint pid, TerrestrialVirtualChannelTable*);
void CacheCVCT(uint pid, CableVirtualChannelTable*);

private:
int _mgt_version;
uint _GPS_UTC_offset;
int _mgt_version;
QMap<uint, int> _tvct_version;
QMap<uint, int> _cvct_version;
QMap<uint, int> _eit_version;
QMap<uint, int> _ett_version;

// Caching
MasterGuideTable *_cached_mgt;
tvct_cache_t _cached_tvcts; // pid->tvct
cvct_cache_t _cached_cvcts; // pid->cvct
pid_tsid_vec_t _cached_pid_tsids;

// Single program variables
int _desired_major_channel;
int _desired_minor_channel;
Expand Down
50 changes: 30 additions & 20 deletions mythtv/libs/libmythtv/mpeg/atsctables.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// -*- Mode: c++ -*-
// Copyright (c) 2003-2004, Daniel Thor Kristjansson
#include <iostream>
#include "atsctables.h"
#include "atscdescriptors.h"
#include "qstring.h"
Expand Down Expand Up @@ -84,23 +83,23 @@ QString MasterGuideTable::toString(void) const
if (0 != TableDescriptorsLength(i))
{
vector<const unsigned char*> desc =
ATSCDescriptor::Parse(TableDescriptors(i),
MPEGDescriptor::Parse(TableDescriptors(i),
TableDescriptorsLength(i));
for (uint i = 0; i < desc.size(); i++)
str.append(QString(" %1\n")
.arg(ATSCDescriptor(desc[i]).toString()));
.arg(MPEGDescriptor(desc[i]).toString()));
}
}
if (0 != GlobalDescriptorsLength())
{
str.append(QString("global descriptors length: %1\n")
str.append(QString("Global descriptors length: %1\n")
.arg(GlobalDescriptorsLength()));
vector<const unsigned char*> desc =
ATSCDescriptor::Parse(GlobalDescriptors(),
MPEGDescriptor::Parse(GlobalDescriptors(),
GlobalDescriptorsLength());
for (uint i = 0; i < desc.size(); i++)
str.append(QString(" %1\n")
.arg(ATSCDescriptor(desc[i]).toString()));
.arg(MPEGDescriptor(desc[i]).toString()));
}
return str;
}
Expand Down Expand Up @@ -139,11 +138,11 @@ QString TerrestrialVirtualChannelTable::toString(int chan) const
str.append(QString(" descriptors length(%1) ")
.arg(DescriptorsLength(chan)));
vector<const unsigned char*> desc =
ATSCDescriptor::Parse(Descriptors(chan), DescriptorsLength(chan));
MPEGDescriptor::Parse(Descriptors(chan), DescriptorsLength(chan));
str.append(QString("count(%1)\n").arg(desc.size()));
for (uint i = 0; i < desc.size(); i++)
str.append(QString(" %1\n")
.arg(ATSCDescriptor(desc[i]).toString()));
.arg(MPEGDescriptor(desc[i]).toString()));
}
return str;
}
Expand All @@ -163,12 +162,12 @@ QString TerrestrialVirtualChannelTable::toString(void) const
str.append(QString("global descriptors length: %1\n")
.arg(GlobalDescriptorsLength()));
vector<const unsigned char*> desc =
ATSCDescriptor::Parse(GlobalDescriptors(),
MPEGDescriptor::Parse(GlobalDescriptors(),
GlobalDescriptorsLength());
str.append(QString("global descriptors count: %1\n").arg(desc.size()));
for (uint i = 0; i < desc.size(); i++)
str.append(QString(" %1\n")
.arg(ATSCDescriptor(desc[i]).toString()));
.arg(MPEGDescriptor(desc[i]).toString()));
}
return str;
}
Expand Down Expand Up @@ -210,11 +209,11 @@ QString CableVirtualChannelTable::toString(int chan) const
str.append(QString(" descriptors length(%1) ")
.arg(DescriptorsLength(chan)));
vector<const unsigned char*> desc =
ATSCDescriptor::Parse(Descriptors(chan), DescriptorsLength(chan));
MPEGDescriptor::Parse(Descriptors(chan), DescriptorsLength(chan));
str.append(QString("count(%1)\n").arg(desc.size()));
for (uint i = 0; i < desc.size(); i++)
str.append(QString(" %1\n")
.arg(ATSCDescriptor(desc[i]).toString()));
.arg(MPEGDescriptor(desc[i]).toString()));
}
return str;
}
Expand All @@ -234,13 +233,13 @@ QString CableVirtualChannelTable::toString(void) const
str.append(QString("global descriptors length: %1\n")
.arg(GlobalDescriptorsLength()));
vector<const unsigned char*> desc =
ATSCDescriptor::Parse(GlobalDescriptors(),
MPEGDescriptor::Parse(GlobalDescriptors(),
GlobalDescriptorsLength());
str.append(QString("global descriptors count: %1\n").arg(desc.size()));
for (uint i = 0; i < desc.size(); i++)
{
str.append(QString(" %1\n")
.arg(ATSCDescriptor(desc[i]).toString()));
.arg(MPEGDescriptor(desc[i]).toString()));
}
}
return str;
Expand All @@ -264,10 +263,10 @@ QString EventInformationTable::toString(void) const
if (0 != DescriptorsLength(i))
{
vector<const unsigned char*> desc =
ATSCDescriptor::Parse(Descriptors(i), DescriptorsLength(i));
MPEGDescriptor::Parse(Descriptors(i), DescriptorsLength(i));
for (uint j=0; j<desc.size(); j++)
str.append(QString("%1\n")
.arg(ATSCDescriptor(desc[j]).toString()));
.arg(MPEGDescriptor(desc[j]).toString()));
}
}
return str;
Expand All @@ -286,11 +285,22 @@ QString ExtendedTextTable::toString(void) const

int VirtualChannelTable::Find(int major, int minor) const
{
for (uint i = 0; i < ChannelCount(); i++)
if (major>0)
{
for (uint i = 0; i < ChannelCount(); i++)
{
if ((MajorChannel(i) == (uint)major) &&
(MinorChannel(i) == (uint)minor))
return (int)i;
}
}
else if (minor>0)
{
if ((MajorChannel(i) == (uint)major) &&
(MinorChannel(i) == (uint)minor))
return (int)i;
for (uint i = 0; i < ChannelCount(); i++)
{
if (MinorChannel(i) == (uint)minor)
return (int)i;
}
}
return -1;
}
182 changes: 121 additions & 61 deletions mythtv/libs/libmythtv/mpeg/atsctables.h

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions mythtv/libs/libmythtv/mpeg/dvbdescriptors.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include "dvbdescriptors.h"
1,356 changes: 1,356 additions & 0 deletions mythtv/libs/libmythtv/mpeg/dvbdescriptors.h

Large diffs are not rendered by default.

196 changes: 191 additions & 5 deletions mythtv/libs/libmythtv/mpeg/dvbstreamdata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,216 @@
#include "dvbstreamdata.h"
#include "dvbtables.h"

DVBStreamData::DVBStreamData(bool cacheTables)
: QObject(NULL, "DVBStreamData"),
MPEGStreamData(-1, cacheTables), _nit_version(-1), _cached_nit(NULL)
{
AddListeningPID(DVB_NIT_PID);
AddListeningPID(DVB_SDT_PID);
}

/** \fn DVBStreamData::IsRedundant(const PSIPTable&) const
* \brief Returns true if table already seen.
* \todo This is just a stub.
*/
bool DVBStreamData::IsRedundant(const PSIPTable &psip) const
{
return MPEGStreamData::IsRedundant(psip);
if (MPEGStreamData::IsRedundant(psip))
return true;

return false;
}

/** \fn DVBStreamData::HandleTables(const TSPacket*)
void DVBStreamData::Reset()
{
MPEGStreamData::Reset(-1);
_nit_version = -1;
_sdt_versions.clear();

{
_cache_lock.lock();

DeleteCachedTable(_cached_nit);
_cached_nit = NULL;

sdt_cache_t::iterator it = _cached_sdts.begin();
for (; it != _cached_sdts.end(); ++it)
DeleteCachedTable(*it);
_cached_sdts.clear();

_cache_lock.unlock();
}
}

/** \fn DVBStreamData::HandleTables(uint pid, const PSIPTable&)
* \brief Assembles PSIP packets and processes them.
* \todo This is just a stub.
*/
void DVBStreamData::HandleTables(const TSPacket* tspacket)
bool DVBStreamData::HandleTables(uint pid, const PSIPTable &psip)
{
if (MPEGStreamData::HandleTables(pid, psip))
return true;

switch (psip.TableID())
{
case TableID::NIT:
{
SetVersionNIT(psip.Version());
if (_cache_tables)
{
NetworkInformationTable *nit =
new NetworkInformationTable(psip);
CacheNIT(nit);
emit UpdateNIT(nit);
}
else
{
NetworkInformationTable nit(psip);
emit UpdateNIT(&nit);
}
return true;
}
case TableID::SDT:
{
uint tsid = psip.TableIDExtension();
SetVersionSDT(tsid, psip.Version());
if (_cache_tables)
{
ServiceDescriptionTable *sdt =
new ServiceDescriptionTable(psip);
CacheSDT(tsid, sdt);
emit UpdateSDT(tsid, sdt);
}
else
{
ServiceDescriptionTable sdt(psip);
emit UpdateSDT(tsid, &sdt);
}
return true;
}
}
return false;
}

bool DVBStreamData::HasCachedNIT(bool current) const
{
if (!current)
VERBOSE(VB_IMPORTANT, "Currently we ignore \'current\' param");

return (bool)(_cached_nit);
}

bool DVBStreamData::HasCachedSDT(uint tsid, bool current) const
{
if (!current)
VERBOSE(VB_IMPORTANT, "Currently we ignore \'current\' param");

_cache_lock.lock();
sdt_cache_t::const_iterator it = _cached_sdts.find(tsid);
bool exists = (it != _cached_sdts.end());
_cache_lock.unlock();
return exists;
}

bool DVBStreamData::HasCachedAllSDTs(bool current) const
{
if (!current)
VERBOSE(VB_IMPORTANT, "Currently we ignore \'current\' param");

if (!_cached_nit)
return false;

_cache_lock.lock();
bool ret = (bool)(_cached_nit);
for (uint i = 0; ret && (i < _cached_nit->TransportStreamCount()); ++i)
ret &= HasCachedSDT(_cached_nit->TSID(i));
_cache_lock.unlock();

return ret;
}

const NetworkInformationTable *DVBStreamData::GetCachedNIT(bool current) const
{
if (!current)
VERBOSE(VB_IMPORTANT, "Currently we ignore \'current\' param");

_cache_lock.lock();
const NetworkInformationTable *nit = _cached_nit;
IncrementRefCnt(nit);
_cache_lock.unlock();

return nit;
}

const sdt_ptr_t DVBStreamData::GetCachedSDT(uint tsid, bool current) const
{
if (!current)
VERBOSE(VB_IMPORTANT, "Currently we ignore \'current\' param");

ServiceDescriptionTable *sdt = NULL;

_cache_lock.lock();
sdt_cache_t::const_iterator it = _cached_sdts.find(tsid);
if (it != _cached_sdts.end())
IncrementRefCnt(sdt = *it);
_cache_lock.unlock();

return sdt;
}

sdt_vec_t DVBStreamData::GetAllCachedSDTs(bool current) const
{
if (!current)
VERBOSE(VB_IMPORTANT, "Currently we ignore \'current\' param");

vector<const ServiceDescriptionTable*> sdts;

_cache_lock.lock();
sdt_cache_t::const_iterator it = _cached_sdts.begin();
for (; it != _cached_sdts.end(); ++it)
{
ServiceDescriptionTable* sdt = *it;
IncrementRefCnt(sdt);
sdts.push_back(sdt);
}
_cache_lock.unlock();

return sdts;
}

void DVBStreamData::ReturnCachedTables(sdt_vec_t &sdts) const
{
for (sdt_vec_t::iterator it = sdts.begin(); it != sdts.end(); ++it)
ReturnCachedTable(*it);
sdts.clear();
}

void DVBStreamData::CacheNIT(NetworkInformationTable *nit)
{
_cache_lock.lock();
DeleteCachedTable(_cached_nit);
_cached_nit = nit;
_cache_lock.unlock();
}

void DVBStreamData::CacheSDT(uint tsid, ServiceDescriptionTable *sdt)
{
DVBStreamData::HandleTables(tspacket);
_cache_lock.lock();
sdt_cache_t::iterator it = _cached_sdts.find(tsid);
if (it != _cached_sdts.end())
DeleteCachedTable(*it);
_cached_sdts[tsid] = sdt;
_cache_lock.unlock();
}

void DVBStreamData::PrintNIT(const NetworkInformationTable* nit) const
{
VERBOSE(VB_RECORD, nit->toString());
}

void DVBStreamData::PrintSDT(const ServiceDescriptionTable* sdt) const
void DVBStreamData::PrintSDT(uint tsid,
const ServiceDescriptionTable* sdt) const
{
(void) tsid;
VERBOSE(VB_RECORD, sdt->toString());
}
50 changes: 44 additions & 6 deletions mythtv/libs/libmythtv/mpeg/dvbstreamdata.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,64 @@ class PSIPTable;
class NetworkInformationTable;
class ServiceDescriptionTable;

class DVBStreamData : public MPEGStreamData
typedef ServiceDescriptionTable* sdt_ptr_t;
typedef vector<const ServiceDescriptionTable*> sdt_vec_t;
typedef QMap<uint, sdt_ptr_t> sdt_cache_t;

class DVBStreamData : virtual public MPEGStreamData
{
Q_OBJECT
public:
DVBStreamData(bool cacheTables = false) :
MPEGStreamData(-1, cacheTables) { ; }
DVBStreamData(bool cacheTables = false);

void Reset();

void HandleTables(const TSPacket* tspacket);
// Table processing
bool HandleTables(uint pid, const PSIPTable&);
bool IsRedundant(const PSIPTable&) const;

// Table versions
void SetVersionNIT(int version) { _nit_version = version; }
int VersionNIT() const { return _nit_version; }
void SetVersionSDT(uint tsid, int version) { _sdt_versions[tsid] = version; }
inline int VersionSDT(uint tsid) const;

// Caching
bool HasCachedNIT(bool current = true) const;
bool HasCachedSDT(uint tsid, bool current = true) const;
bool HasCachedAllSDTs(bool current = true) const;

const NetworkInformationTable *GetCachedNIT(bool current = true) const;
const sdt_ptr_t GetCachedSDT(uint tsid, bool current = true) const;
sdt_vec_t GetAllCachedSDTs(bool current = true) const;

void ReturnCachedTables(sdt_vec_t&) const;

signals:
void UpdateNIT(const NetworkInformationTable*);
void UpdateSDT(const ServiceDescriptionTable*);
void UpdateSDT(uint tsid, const ServiceDescriptionTable*);

private slots:
void PrintNIT(const NetworkInformationTable*) const;
void PrintSDT(const ServiceDescriptionTable*) const;
void PrintSDT(uint tsid, const ServiceDescriptionTable*) const;

private:
// Caching
void CacheNIT(NetworkInformationTable *);
void CacheSDT(uint tsid, ServiceDescriptionTable*);

// Table versions
int _nit_version;
QMap<uint, int> _sdt_versions;
// Caching
NetworkInformationTable *_cached_nit;
sdt_cache_t _cached_sdts; // pid->sdt
};

inline int DVBStreamData::VersionSDT(uint tsid) const
{
const QMap<uint, int>::const_iterator it = _sdt_versions.find(tsid);
return (it == _sdt_versions.end()) ? -1 : *it;
}

#endif // DVBSTREAMDATA_H_
100 changes: 98 additions & 2 deletions mythtv/libs/libmythtv/mpeg/dvbtables.cpp
Original file line number Diff line number Diff line change
@@ -1,23 +1,119 @@
// -*- Mode: c++ -*-
// Copyright (c) 2005, Daniel Thor Kristjansson
#include "dvbtables.h"
#include "dvbdescriptors.h"
#include "qstring.h"

void NetworkInformationTable::Parse(void) const
{
_ptrs.clear();
_tsc_ptr = pesdata() + 11 + NetworkDescriptorsLength();
_ptrs.push_back(_tsc_ptr + 2);
for (uint i = 0; i < TransportStreamCount(); i++)
_ptrs.push_back(_ptrs[i] + 6 + TransportDescriptorsLength(i));

}

QString NetworkInformationTable::toString(void) const
{
return "NIT";
QString str = QString("NIT: NetID(%1) tranports(%2)\n")
.arg(NetworkID()).arg(TransportStreamCount());

if (0 != NetworkDescriptorsLength())
{
str.append(QString("Network descriptors length: %1\n")
.arg(NetworkDescriptorsLength()));
vector<const unsigned char*> desc =
MPEGDescriptor::Parse(NetworkDescriptors(),
NetworkDescriptorsLength());
for (uint i = 0; i < desc.size(); i++)
str.append(QString(" %1\n")
.arg(MPEGDescriptor(desc[i]).toString()));
}

for (uint i = 0; i < TransportStreamCount(); i++)
{
str.append(QString(" Transport #%1 TSID(0x%1) ")
.arg(i, 2, 10).arg(TSID(i), 0, 16));
str.append(QString("original_network_id(0x%2) desc_len(%3)\n")
.arg(OriginalNetworkID(i), 0, 16)
.arg(TransportDescriptorsLength(i)));

if (0 != TransportDescriptorsLength(i))
{
str.append(QString(" Transport descriptors length: %1\n")
.arg(TransportDescriptorsLength(i)));
vector<const unsigned char*> desc =
MPEGDescriptor::Parse(TransportDescriptors(i),
TransportDescriptorsLength(i));
for (uint i = 0; i < desc.size(); i++)
str.append(QString(" %1\n")
.arg(MPEGDescriptor(desc[i]).toString()));
}
}
return str;
}

QString NetworkInformationTable::NetworkName() const
{
if (_cached_network_name == QString::null)
{
desc_list_t parsed =
MPEGDescriptor::Parse(NetworkDescriptors(),
NetworkDescriptorsLength());

const unsigned char *desc =
MPEGDescriptor::Find(parsed, DescriptorID::network_name);

if (desc)
_cached_network_name = NetworkNameDescriptor(desc).Name();
else
_cached_network_name = QString("Net ID 0x%1")
.arg(NetworkID(), 0, 16);
}
return _cached_network_name;
}


void ServiceDescriptionTable::Parse(void) const
{
_ptrs.clear();
_ptrs.push_back(pesdata() + 12);
uint i = 0;
while ((_ptrs[i] + 5) < (pesdata() + Length()))
{
_ptrs.push_back(_ptrs[i] + 5 + ServiceDescriptorsLength(i));
i++;
}
}

QString ServiceDescriptionTable::toString(void) const
{
return "SDT";
QString str =
QString("SDT: TSID(0x%1) original_network_id(0x%2) services(%3)\n")
.arg(OriginalNetworkID(), 0, 16).arg(TSID(), 0, 16)
.arg(ServiceCount());

for (uint i = 0; i < ServiceCount(); i++)
{
str.append(QString(" Service #%1 SID(0x%2) ")
.arg(i, 2, 10).arg(ServiceID(i), 0, 16));
str.append(QString("eit_schd(%1) eit_pf(%2) free_ca(%3)\n")
.arg(HasEITSchedule(i) ? "t" : "f")
.arg(HasEITPresentFollowing(i) ? "t" : "f")
.arg(HasFreeCA(i) ? "t" : "f"));

if (0 != ServiceDescriptorsLength(i))
{
str.append(QString(" Service descriptors length: %1\n")
.arg(ServiceDescriptorsLength(i)));
vector<const unsigned char*> desc =
MPEGDescriptor::Parse(ServiceDescriptors(i),
ServiceDescriptorsLength(i));
for (uint i = 0; i < desc.size(); i++)
str.append(QString(" %1\n")
.arg(MPEGDescriptor(desc[i]).toString()));
}
}
return str;
}
139 changes: 126 additions & 13 deletions mythtv/libs/libmythtv/mpeg/dvbtables.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,65 +5,178 @@

#include <qstring.h>
#include "mpegtables.h"
#include "dvbdescriptors.h"

/** \class NetworkInformationTable
* \brief This table tells the decoder on which PIDs to find other tables.
* \todo This is just a stub.
*/
class NetworkInformationTable : PSIPTable
class NetworkInformationTable : public PSIPTable
{
public:
NetworkInformationTable(const PSIPTable& table) : PSIPTable(table)
NetworkInformationTable(const PSIPTable& table)
: PSIPTable(table), _cached_network_name(QString::null)
{
// start_code_prefix 8 0.0 0
// table_id 8 1.0 0x40
assert(TableID::NIT == TableID());
// table_id 8 1.0 0x40/0x41
assert(TableID::NIT == TableID() || TableID::NITo == TableID());
Parse();
// section_syntax_indicator 1 2.0 1
// private_indicator 1 2.1 1
// reserved_future_use 1 2.1 1
// reserved 2 2.2 3
// table_id_extension 16 4.0 0x0000
// section_length 12 2.4 0
// reserved 2 6.0 3
// version_number 5 6.2 0
// current_next_indicator 1 6.7 1
// section_number 8 7.0 0x00
// last_section_number 8 8.0 0x00
}
~NetworkInformationTable() { ; }

/// network_id 16 4.0 0x0000
uint NetworkID() const { return TableIDExtension(); }

// reserved_future_use 4 9.0 0xf
/// network_desc_length 12 9.4 0
uint NetworkDescriptorsLength() const
{ return ((pesdata()[9]<<8) | pesdata()[10]) & 0xfff; }

/// for(i=0; i<N; i++) x 11.0
/// { descriptor() }
const unsigned char* NetworkDescriptors() const { return pesdata()+11; }

// reserved_future_use 4 0.0+ndl 0xf
/// trans_stream_loop_len 12 0.4+ndl
uint TransportStreamCount() const
{ return ((_tsc_ptr[0]<<8) | _tsc_ptr[1]) & 0xfff; }
// for(i=0; i<N; i++) {
/// transport_stream_id 16 0.0+p
uint TSID(uint i) const { return (_ptrs[i][0]<<8) | _ptrs[i][1]; }
/// original_network_id 16 2.0+p
uint OriginalNetworkID(uint i) const
{ return (_ptrs[i][2]<<8) | _ptrs[i][3]; }
// reserved_future_use 4 4.0+p
/// trans_desc_length 12 4.4+p
uint TransportDescriptorsLength(uint i) const
{ return ((_ptrs[i][4]<<8) | _ptrs[i][5]) & 0xfff; }
/// for(j=0;j<N;j++) x 6.0+p
/// { descriptor() }
const unsigned char* TransportDescriptors(uint i) const
{ return _ptrs[i]+6; }
// }

void Parse(void) const;
QString toString(void) const;
QString NetworkName() const;

private:
mutable vector<unsigned char*> _ptrs; // used to parse
mutable QString _cached_network_name;
mutable const unsigned char* _tsc_ptr;
mutable vector<const unsigned char*> _ptrs; // used to parse
};

/** \class ServiceDescriptionTable
* \brief This table tells the decoder on which PIDs to find A/V data.
* \todo This is just a stub.
*/
class ServiceDescriptionTable: PSIPTable
class ServiceDescriptionTable: public PSIPTable
{
public:
ServiceDescriptionTable(const PSIPTable& table) : PSIPTable(table)
{
// start_code_prefix 8 0.0 0
// table_id 8 1.0 0x42
assert(TableID::SDT == TableID());
// table_id 8 1.0 0x42/0x46
assert(TableID::SDT == TableID() || TableID::SDTo == TableID());
Parse();
// section_syntax_indicator 1 2.0 1
// private_indicator 1 2.1 1
// reserved_future_use 1 2.1 1
// reserved 2 2.2 3
// table_id_extension 16 4.0 0x0000
// section_length 12 2.4 0
// reserved 2 6.0 3
// version_number 5 6.2 0
// current_next_indicator 1 6.7 1
// section_number 8 7.0 0x00
// last_section_number 8 8.0 0x00
}
~ServiceDescriptionTable() { ; }

/// transport_stream_id 16 4.0 0x0000
uint TSID() const { return TableIDExtension(); }

/// original_network_id 16 9.0
uint OriginalNetworkID() const { return (pesdata()[4]<<8) | pesdata()[5]; }

/// Number of services
uint ServiceCount() const { return _ptrs.size()-1; }

// reserved_future_use 8 11.0
// for (i=0;i<N;i++) {
/// service_id 16 0.0+p
uint ServiceID(uint i) const { return (_ptrs[i][0]<<8) | (_ptrs[i][1]); }
// reserved_future_use 6 2.0+p
// EIT_schedule_flag 1 2.6+p
bool HasEITSchedule(uint i) const { return bool(_ptrs[i][2] & 0x2); }
// EIT_present_following 1 2.7+p
bool HasEITPresentFollowing(uint i) const
{ return bool(_ptrs[i][2] & 0x1); }
// running_status 3 3.0+p
/// free_CA_mode 1 3.3+p
bool HasFreeCA(uint i) const { return bool(_ptrs[i][3] & 0x10); }
/// desc_loop_length 12 3.4+p
uint ServiceDescriptorsLength(uint i) const
{ return ((_ptrs[i][3]<<8) | (_ptrs[i][4])) & 0xfff; }
/// for (j=0;j<N;j++) x 5.0+p
/// { descriptor() }
const unsigned char* ServiceDescriptors(uint i) const
{ return _ptrs[i]+5; }
// }

void Parse(void) const;
QString toString(void) const;
private:
mutable vector<unsigned char*> _ptrs; // used to parse
mutable vector<const unsigned char*> _ptrs; // used to parse
};

class DiscontinuityInformationTable : public PSIPTable
{
DiscontinuityInformationTable(const PSIPTable& table) : PSIPTable(table)
{ ; }
// table_id 8
// section_syntax_indicator 1
// reserved_future_use 1
// reserved 2
// section_length 12
// transition_flag 1
// reserved_future_use 7
};

class SelectionInformationTable : public PSIPTable
{
SelectionInformationTable(const PSIPTable& table) : PSIPTable(table)
{ ; }
// table_id 8
// section_syntax_indicator 1
// DVB_reserved_future_use 1
// ISO_reserved 2
// section_length 12
// DVB_reserved_future_use 16
// ISO_reserved 2
// version_number 5
// current_next_indicator 1
// section_number 8
// last_section_number 8
// DVB_reserved_for_future_use 4
// transmission_info_loop_length 12
// for (i =0;i<N;i++) { descriptor() }
// for (i=0;i<N;i++)
// {
// service_id 16
// DVB_reserved_future_use 1
// running_status 3
// service_loop_length 12
// for(j=0;j<N;j++) { descriptor() }
// }
// CRC_32 32 rpchof
};

#endif // _DVB_TABLES_H_
217 changes: 217 additions & 0 deletions mythtv/libs/libmythtv/mpeg/mpegdescriptors.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
// -*- Mode: c++ -*-
// Copyright (c) 2005, Daniel Thor Kristjansson

#include "atscdescriptors.h"
#include "dvbdescriptors.h"

desc_list_t MPEGDescriptor::Parse(
const unsigned char* data, uint len)
{
desc_list_t tmp;
uint off = 0;
while (off < len)
{
tmp.push_back(data+off);
MPEGDescriptor desc(data+off);
off += desc.DescriptorLength() + 2;
}
return tmp;
}

const unsigned char* MPEGDescriptor::Find(const desc_list_t& parsed,
uint desc_tag)
{
desc_list_t::const_iterator it = parsed.begin();
for (; it != parsed.end(); ++it)
{
if ((*it)[0] == desc_tag)
return *it;
}
return NULL;
}

QString MPEGDescriptor::DescriptorTagString() const
{
switch(DescriptorTag())
{
// MPEG
case DescriptorID::registration:
return QString("Registration");

// DVB
case DescriptorID::network_name:
return QString("Network Name");
case DescriptorID::service_list:
return QString("Service List");
case DescriptorID::dvb_stuffing:
return QString("DVB Stuffing");
case DescriptorID::satellite_delivery_system:
return QString("Satellite Delivery System");
case DescriptorID::cable_delivery_system:
return QString("Cable Delivery System");
case DescriptorID::VBI_data:
return QString("VBI Data");
case DescriptorID::VBI_teletext:
return QString("VBI Teletext");
case DescriptorID::bouquet_name:
return QString("Bouquet Name");
case DescriptorID::service:
return QString("Service");
case DescriptorID::country_availability:
return QString("Country Availability");
case DescriptorID::linkage:
return QString("Linkage");
case DescriptorID::NVOD_reference:
return QString("NVOD Reference");
case DescriptorID::dvb_time_shifted_service:
return QString("DVB Time-shifted Service");
case DescriptorID::short_event:
return QString("Short Event");
case DescriptorID::extended_event:
return QString("Extended Event");
case DescriptorID::time_shifted_event:
return QString("Time-shifted Event");
case DescriptorID::component:
return QString("Component");
case DescriptorID::mosaic:
return QString("Mosaic");
case DescriptorID::stream_identifier:
return QString("Stream Identifier");
case DescriptorID::CA_identifier:
return QString("Conditional Access Identifier");
case DescriptorID::content:
return QString("Content");
case DescriptorID::parental_rating:
return QString("Parental Rating");
case DescriptorID::teletext:
return QString("Teletext");
case DescriptorID::telephone:
return QString("Telephone");
case DescriptorID::local_time_offset:
return QString("Local Time Offset");
case DescriptorID::subtitling:
return QString("Subtitling");
case DescriptorID::terrestrial_delivery_system:
return QString("Terrestrial Delivery System");
case DescriptorID::multilingual_network_name:
return QString("Multilingual Network Name");
case DescriptorID::multilingual_bouquet_name:
return QString("Multilingual Bouquet Name");
case DescriptorID::multilingual_service_name:
return QString("Multilingual Service Name");
case DescriptorID::multilingual_component:
return QString("Multilingual Component");
case DescriptorID::private_data_specifier:
return QString("Private Data Specifier");
case DescriptorID::service_move:
return QString("Service Move");
case DescriptorID::short_smoothing_buffer:
return QString("Short Smoothing Buffer");
case DescriptorID::frequency_list:
return QString("Frequency List");
case DescriptorID::partial_transport_stream:
return QString("Partial Transport Stream");
case DescriptorID::data_broadcast:
return QString("Data Broadcast");
case DescriptorID::scrambling:
return QString("Scrambling");
case DescriptorID::data_broadcast_id:
return QString("Data Broadcast Identifier");
case DescriptorID::transport_stream:
return QString("Transport Stream");
case DescriptorID::DSNG:
return QString("DSNG");
case DescriptorID::PDC:
return QString("PDC");
case DescriptorID::AC3:
return QString("AC-3");
case DescriptorID::ancillary_data:
return QString("Ancillary Data");
case DescriptorID::cell_list:
return QString("Cell List");
case DescriptorID::cell_frequency_link:
return QString("Cell Frequency Link");
case DescriptorID::announcement_support:
return QString("Announcement Support");
case DescriptorID::application_signalling:
return QString("Application Signalling");
case DescriptorID::adaptation_field_data:
return QString("Adaptation Field Data");
case DescriptorID::service_identifier:
return QString("Service Identifier");
case DescriptorID::service_availability:
return QString("Service Availability");
case DescriptorID::default_authority:
return QString("Default Authority");
case DescriptorID::related_content:
return QString("Related Content");
case DescriptorID::TVA_id:
return QString("TVA Id");
case DescriptorID::dvb_content_identifier:
return QString("DVB Content Identifier");
case DescriptorID::time_slice_fec_identifier:
return QString("Time Slice FEC Identifier");
case DescriptorID::ECM_repetition_rate:
return QString("ECM Repetition Rate");

/// ATSC
case DescriptorID::atsc_stuffing:
return QString("ATSC Stuffing");
case DescriptorID::audio_stream:
return QString("Audio");
case DescriptorID::caption_service:
return QString("Caption Service");
case DescriptorID:: content_advisory:
return QString("Content Advisory");
case DescriptorID::extended_channel_name:
return QString("Extended Channel Name");
case DescriptorID::service_location:
return QString("Service Location");
case DescriptorID::atsc_time_shifted_service:
return QString("ATSC Time-shifted Service");
case DescriptorID::component_name:
return QString("Component Name");
case DescriptorID::DCC_departing_request:
return QString("DCC Departing Request");
case DescriptorID::DCC_arriving_request:
return QString("DCC Arriving Request");
case DescriptorID::DRM_control:
return QString("Consumer Restrictions Control");
case DescriptorID::atsc_content_identifier:
return QString("Content Identifier");

default: return QString("Unknown");
}
}

QString MPEGDescriptor::toString() const {
QString str;

if (DescriptorID::registration == DescriptorTag())
{
str = RegistrationDescriptor(_data).toString();
}
else if (DescriptorID::audio_stream == DescriptorTag())
{
str = AudioStreamDescriptor(_data).toString();
}
else if (DescriptorID::caption_service == DescriptorTag())
{
str = CaptionServiceDescriptor(_data).toString();
}
else if (DescriptorID::component_name == DescriptorTag())
{
str = ComponentNameDescriptor(_data).toString();
}
else
{
str.append(QString(" %1 Descriptor (0x%2)")
.arg(DescriptorTagString())
.arg(int(DescriptorTag()), 0, 16));
str.append(QString(" length(%1)").arg(int(DescriptorLength())));
//for (uint i=0; i<DescriptorLength(); i++)
// str.append(QString(" 0x%1").arg(int(_data[i+2]), 0, 16));
}
return str;
}

137 changes: 137 additions & 0 deletions mythtv/libs/libmythtv/mpeg/mpegdescriptors.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// -*- Mode: c++ -*-
// Copyright (c) 2003-2004, Daniel Thor Kristjansson
#ifndef _MPEG_DESCRIPTORS_H_
#define _MPEG_DESCRIPTORS_H_

typedef vector<const unsigned char*> desc_list_t;

class DescriptorID
{
public:
enum
{
// MPEG
registration = 0x05,
conditional_access = 0x09,
ISO_639_language = 0x0A,

// DVB
network_name = 0x40,
service_list = 0x41,
dvb_stuffing = 0x42,
satellite_delivery_system = 0x43,
cable_delivery_system = 0x44,
VBI_data = 0x45,
VBI_teletext = 0x46,
bouquet_name = 0x47,
service = 0x48,
country_availability = 0x49,
linkage = 0x4A,
NVOD_reference = 0x4B,
dvb_time_shifted_service = 0x4C,
short_event = 0x4D,
extended_event = 0x4E,
time_shifted_event = 0x4F,
component = 0x50,
mosaic = 0x51,
stream_identifier = 0x52,
CA_identifier = 0x53,
content = 0x54,
parental_rating = 0x55,
teletext = 0x56,
telephone = 0x57,
local_time_offset = 0x58,
subtitling = 0x59,
terrestrial_delivery_system = 0x5A,
multilingual_network_name = 0x5B,
multilingual_bouquet_name = 0x5C,
multilingual_service_name = 0x5D,
multilingual_component = 0x5E,
private_data_specifier = 0x5F,
service_move = 0x60,
short_smoothing_buffer = 0x61,
frequency_list = 0x62,
partial_transport_stream = 0x63,
data_broadcast = 0x64,
scrambling = 0x65,
data_broadcast_id = 0x66,
transport_stream = 0x67,
DSNG = 0x68,
PDC = 0x69,
AC3 = 0x6A,
ancillary_data = 0x6B,
cell_list = 0x6C,
cell_frequency_link = 0x6D,
announcement_support = 0x6E,
application_signalling = 0x6F,
adaptation_field_data = 0x70,
service_identifier = 0x71,
service_availability = 0x72,
default_authority = 0x73,
related_content = 0x74,
TVA_id = 0x75,
dvb_content_identifier = 0x76,
time_slice_fec_identifier = 0x77,
ECM_repetition_rate = 0x78,

// private
dvb_uk_channel_list = 0x83,

// ATSC
atsc_stuffing = 0x80,
audio_stream = 0x81,
caption_service = 0x86,
content_advisory = 0x87,
extended_channel_name = 0xA0,
service_location = 0xA1,
atsc_time_shifted_service = 0xA2,
component_name = 0xA3,
DCC_departing_request = 0xA8,
DCC_arriving_request = 0xA9,
DRM_control = 0xAA,
atsc_content_identifier = 0xB6,
};
};

class MPEGDescriptor
{
public:
//operator (const unsigned char*)() const { return _data; }

MPEGDescriptor(const unsigned char* data) : _data(data) { ; }
uint DescriptorTag() const { return _data[0]; }
QString DescriptorTagString() const;
uint DescriptorLength() const { return _data[1]; }
static desc_list_t Parse(const unsigned char* data, uint len);
static const unsigned char* Find(const desc_list_t& parsed, uint desc_tag);
QString toString() const;
protected:
const unsigned char* _data;
};

// a_52a.pdf p119, Table A1
class RegistrationDescriptor : public MPEGDescriptor
{
public:
RegistrationDescriptor(const unsigned char* data) : MPEGDescriptor(data)
{
assert(0x05 == DescriptorTag());
if (0x04 != DescriptorLength())
{
cerr<<"Registration Descriptor length != 4 !!!!"<<endl;
}
//assert(0x41432d33==formatIdentifier());
}
uint FormatIdentifier() const
{ return (_data[2]<<24) | (_data[3]<<16) | (_data[4]<<8) | _data[5]; }
QString toString() const
{
if (0x41432d33 == FormatIdentifier())
return QString(" Registration Descriptor OK");
return QString(" Registration Descriptor not OK \n"
" format identifier is 0x%1 but should be 0x41432d33")
.arg(FormatIdentifier(), 0, 16);
}
};

#endif // _MPEG_DESCRIPTORS_H_
250 changes: 215 additions & 35 deletions mythtv/libs/libmythtv/mpeg/mpegstreamdata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,20 @@
* \brief Encapsulates data about MPEG stream and emits events for each table.
*/

/** \fn MPEGStreamData::MPEGStreamData(int, bool)
* \brief Initializes MPEGStreamData.
*
* This adds the PID of the PAT table to "_pids_listening"
*
* \param desiredProgram If you want rewritten PAT and PMTs, for
* a desired program set this to a value > -1
* \param cacheTables If true PAT and PMT tables will be cached
*/
MPEGStreamData::MPEGStreamData(int desiredProgram, bool cacheTables)
: _cache_tables(cacheTables), _desired_program(desiredProgram),
_pat_single_program(0), _pmt_single_program(0)
: QObject(NULL, "MPEGStreamData"),
_pat_version(-1), _cache_tables(cacheTables), _cache_lock(true),
_cached_pat(NULL), _desired_program(desiredProgram),
_pat_single_program(NULL), _pmt_single_program(NULL)
{
AddListeningPID(MPEG_PAT_PID);

Expand Down Expand Up @@ -47,6 +58,20 @@ void MPEGStreamData::Reset(int desiredProgram)

_pid_video_single_program = _pid_pmt_single_program = 0xffffffff;
_pmt_version.clear();

{
_cache_lock.lock();

DeleteCachedTable(_cached_pat);
_cached_pat = NULL;

pmt_cache_t::iterator it = _cached_pmts.begin();
for (; it != _cached_pmts.end(); ++it)
DeleteCachedTable(*it);
_cached_pmts.clear();

_cache_lock.unlock();
}
}

void MPEGStreamData::DeletePartialPES(uint pid)
Expand Down Expand Up @@ -269,39 +294,98 @@ bool MPEGStreamData::IsRedundant(const PSIPTable &psip) const
return false;
}

/** \fn MPEGStreamData::HandleTables(const TSPacket*)
/** \fn MPEGStreamData::HandleTables(uint pid, const PSIPTable &psip)
* \brief Assembles PSIP packets and processes them.
*/
bool MPEGStreamData::HandleTables(uint pid, const PSIPTable &psip)
{
// If we get this far decode table
switch (psip.TableID())
{
case TableID::PAT:
{
SetVersionPAT(psip.Version());
if (_cache_tables)
{
ProgramAssociationTable *pat =
new ProgramAssociationTable(psip);
CachePAT(pat);
emit UpdatePAT(pat);
if (CreatePATSingleProgram(*pat))
emit UpdatePATSingleProgram(PATSingleProgram());
}
else
{
ProgramAssociationTable pat(psip);
emit UpdatePAT(&pat);
if (CreatePATSingleProgram(pat))
emit UpdatePATSingleProgram(PATSingleProgram());
}
return true;
}
case TableID::PMT:
{
SetVersionPMT(pid, psip.Version());
if (_cache_tables)
{
ProgramMapTable *pmt = new ProgramMapTable(psip);
CachePMT(pid, pmt);
emit UpdatePMT(pid, pmt);
if (pid == _pid_pmt_single_program)
{
if (CreatePMTSingleProgram(*pmt))
emit UpdatePMTSingleProgram(PMTSingleProgram());
}
}
else
{
ProgramMapTable pmt(psip);
emit UpdatePMT(pid, &pmt);
if (pid == _pid_pmt_single_program)
{
if (CreatePMTSingleProgram(pmt))
emit UpdatePMTSingleProgram(PMTSingleProgram());
}
}
return true;
}
}
return false;
}

/** \fn MPEGStreamData::HandleTSTables(const TSPacket*)
* \brief Assembles PSIP packets and processes them.
*/
void MPEGStreamData::HandleTables(const TSPacket* tspacket)
bool MPEGStreamData::HandleTSTables(const TSPacket* tspacket)
{
#define HT_RETURN { delete psip; return; }
#define HT_RETURN(HANDLED) { delete psip; return HANDLED; }
// Assemble PSIP
PSIPTable *psip = AssemblePSIP(tspacket);
if (!psip)
return;
return true;

// Validate PSIP
if (!psip->IsGood())
{
VERBOSE(VB_RECORD, QString("PSIP packet failed CRC check"));
HT_RETURN;
HT_RETURN(true);
}

if (!psip->IsCurrent()) // we don't cache the next table, for now
HT_RETURN;
HT_RETURN(true);

if (1!=tspacket->AdaptationFieldControl())
{ // payload only, ATSC req.
VERBOSE(VB_RECORD,
"PSIP packet has Adaptation Field Control, not ATSC compiant");
HT_RETURN;
HT_RETURN(true);
}

if (tspacket->ScramplingControl())
{ // scrambled! ATSC, DVB require tables not to be scrambled
VERBOSE(VB_RECORD,
"PSIP packet is scrambled, not ATSC/DVB compiant");
HT_RETURN;
HT_RETURN(true);
}

// Don't decode redundant packets,
Expand All @@ -313,32 +397,12 @@ void MPEGStreamData::HandleTables(const TSPacket* tspacket)
if (TableID::PMT == psip->TableID() &&
tspacket->PID() == _pid_pmt_single_program)
emit UpdatePMTSingleProgram(PMTSingleProgram());
HT_RETURN; // already parsed this table, toss it.
HT_RETURN(true); // already parsed this table, toss it.
}

// If we get this far decode table
switch (psip->TableID())
{
case TableID::PAT:
{
ProgramAssociationTable pat(*psip);
emit UpdatePAT(&pat);
if (CreatePATSingleProgram(pat))
emit UpdatePATSingleProgram(PATSingleProgram());
HT_RETURN;
}
case TableID::PMT:
{
ProgramMapTable pmt(*psip);
emit UpdatePMT(tspacket->PID(), &pmt);
if (tspacket->PID() == _pid_pmt_single_program)
{
if (CreatePMTSingleProgram(pmt))
emit UpdatePMTSingleProgram(PMTSingleProgram());
}
HT_RETURN;
}
}
bool handled = HandleTables(tspacket->PID(), *psip);
HT_RETURN(handled);
#undef HT_RETURN
}

int MPEGStreamData::ProcessData(unsigned char *buffer, int len)
Expand Down Expand Up @@ -374,7 +438,7 @@ bool MPEGStreamData::ProcessTSPacket(const TSPacket& tspacket)
if (ok && !tspacket.ScramplingControl() && tspacket.HasPayload() &&
IsListeningPID(tspacket.PID()))
{
HandleTables(&tspacket);
HandleTSTables(&tspacket);
}
return ok;
}
Expand Down Expand Up @@ -434,3 +498,119 @@ void MPEGStreamData::SavePartialPES(uint pid, PESPacket* packet)
delete old;
}
}

bool MPEGStreamData::HasCachedPAT(void) const
{
return (bool)(_cached_pat);
}

bool MPEGStreamData::HasCachedPMT(uint pid) const
{
_cache_lock.lock();
pmt_cache_t::const_iterator it = _cached_pmts.find(pid);
bool exists = (it != _cached_pmts.end());
_cache_lock.unlock();
return exists;
}

bool MPEGStreamData::HasAllPMTsCached(void) const
{
if (_cached_pat)
return false;

bool ret = true;
_cache_lock.lock();

if (_cached_pat->ProgramCount() > _cached_pmts.size())
ret = false;
else
{
// Verify we have the right ones
pmt_cache_t::const_iterator it = _cached_pmts.begin();
for (; it != _cached_pmts.end() && ret; ++it)
ret &= (bool)(_cached_pat->FindProgram(it.key()));
}

_cache_lock.unlock();
return ret;
}

const ProgramAssociationTable *MPEGStreamData::GetCachedPAT(void) const
{
_cache_lock.lock();
const ProgramAssociationTable *pat = _cached_pat;
IncrementRefCnt(pat);
_cache_lock.unlock();

return pat;
}

const ProgramMapTable *MPEGStreamData::GetCachedPMT(uint pid) const
{
ProgramMapTable *pmt = NULL;

_cache_lock.lock();
pmt_cache_t::const_iterator it = _cached_pmts.find(pid);
if (it != _cached_pmts.end())
IncrementRefCnt(pmt = *it);
_cache_lock.unlock();

return pmt;
}

vector<const ProgramMapTable*> MPEGStreamData::GetCachedPMTs(void) const
{
vector<const ProgramMapTable*> pmts;

_cache_lock.lock();
pmt_cache_t::const_iterator it = _cached_pmts.begin();
for (; it != _cached_pmts.end(); ++it)
{
ProgramMapTable* pmt = *it;
IncrementRefCnt(pmt);
pmts.push_back(pmt);
}
_cache_lock.unlock();

return pmts;
}

void MPEGStreamData::ReturnCachedTable(const PSIPTable *psip) const
{
// TODO
}

void MPEGStreamData::ReturnCachedTables(pmt_vec_t &pmts) const
{
for (pmt_vec_t::iterator it = pmts.begin(); it != pmts.end(); ++it)
ReturnCachedTable(*it);
pmts.clear();
}

void MPEGStreamData::IncrementRefCnt(const PSIPTable *psip) const
{
// TODO
}

void MPEGStreamData::DeleteCachedTable(PSIPTable *psip) const
{
// TODO
}

void MPEGStreamData::CachePAT(ProgramAssociationTable *pat)
{
_cache_lock.lock();
DeleteCachedTable(_cached_pat);
_cached_pat = pat;
_cache_lock.unlock();
}

void MPEGStreamData::CachePMT(uint pid, ProgramMapTable *pmt)
{
_cache_lock.lock();
pmt_cache_t::iterator it = _cached_pmts.find(pid);
if (it != _cached_pmts.end())
DeleteCachedTable(*it);
_cached_pmts[pid] = pmt;
_cache_lock.unlock();
}
133 changes: 88 additions & 45 deletions mythtv/libs/libmythtv/mpeg/mpegstreamdata.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,79 +14,92 @@ class RingBuffer;
class PESPacket;

typedef QMap<unsigned int, PESPacket*> pid_pes_map_t;
typedef QMap<const PSIPTable*, int> psip_refcnt_map_t;
typedef vector<const ProgramMapTable*> pmt_vec_t;
typedef QMap<uint, ProgramMapTable*> pmt_cache_t;

class MPEGStreamData : public QObject
class MPEGStreamData : virtual public QObject
{
Q_OBJECT
public:
MPEGStreamData(int desiredProgram, bool cacheTables);
virtual ~MPEGStreamData();

void SetCaching(bool cacheTables) { _cache_tables = cacheTables; }
virtual void Reset(int desiredProgram);

// Table processing
virtual bool IsRedundant(const PSIPTable&) const;
virtual void HandleTables(const TSPacket* tspacket);
virtual bool HandleTables(uint pid, const PSIPTable &psip);
virtual bool HandleTSTables(const TSPacket* tspacket);
virtual bool ProcessTSPacket(const TSPacket& tspacket);
virtual int ProcessData(unsigned char *buffer, int len);
inline void HandleAdaptationFieldControl(const TSPacket* tspacket);

PSIPTable* AssemblePSIP(const TSPacket* tspacket);
bool AssemblePSIP(PSIPTable& psip, TSPacket* tspacket);
// Listening
void AddListeningPID(uint pid) { _pids_listening[pid] = true; }
void AddNotListeningPID(uint pid) { _pids_notlistening[pid] = true; }
void AddWritingPID(uint pid) { _pids_writing[pid] = true; }
void AddAudioPID(uint pid) { _pids_audio[pid] = true; }

void AddListeningPID(uint pid) { _pids_listening[pid] = true; }
void AddNotListeningPID(uint pid) { _pids_notlistening[pid] = true; }
void AddWritingPID(uint pid) { _pids_writing[pid] = true; }
void AddAudioPID(uint pid) { _pids_audio[pid] = true; }

void RemoveListeningPID(uint pid) { _pids_listening.erase(pid); }
void RemoveNotListeningPID(uint pid) { _pids_notlistening.erase(pid); }
void RemoveWritingPID(uint pid) { _pids_writing.erase(pid); }
void RemoveAudioPID(uint pid) { _pids_audio.erase(pid); }
void RemoveListeningPID(uint pid) { _pids_listening.erase(pid); }
void RemoveNotListeningPID(uint pid) { _pids_notlistening.erase(pid); }
void RemoveWritingPID(uint pid) { _pids_writing.erase(pid); }
void RemoveAudioPID(uint pid) { _pids_audio.erase(pid); }

bool IsListeningPID(uint pid) const;
bool IsNotListeningPID(uint pid) const;
bool IsWritingPID(uint pid) const;
bool IsAudioPID(uint pid) const;

const QMap<uint, bool>& ListeningPIDs() const { return _pids_listening; }
const QMap<uint, bool>& ListeningPIDs(void) const
{ return _pids_listening; }

void SavePartialPES(uint pid, PESPacket* packet);
PESPacket* GetPartialPES(uint pid)
{ return _partial_pes_packet_cache[pid]; }
void ClearPartialPES(uint pid) { _partial_pes_packet_cache.remove(pid); }
void DeletePartialPES(uint pid);
// Table versions
void SetVersionPAT(int version) { _pat_version = version; }
int VersionPAT(void) const { return _pat_version; }
void SetVersionPMT(uint pid, int ver) { _pmt_version[pid] = ver; }
inline int VersionPMT(uint pid) const;

// Caching
bool HasCachedPAT(void) const;
bool HasCachedPMT(uint pid) const;
bool HasAllPMTsCached(void) const;

void SetVersionPMT(uint pid, int version) { _pmt_version[pid] = version; }
inline int VersionPMT(uint pid) const;
const ProgramAssociationTable *GetCachedPAT(void) const;
const ProgramMapTable *GetCachedPMT(uint pid) const;
pmt_vec_t GetCachedPMTs(void) const;

inline void HandleAdaptationFieldControl(const TSPacket* tspacket);
void ReturnCachedTable(const PSIPTable *psip) const;
void ReturnCachedTables(pmt_vec_t&) const;

signals:
void UpdatePAT(const ProgramAssociationTable*);
void UpdatePMT(uint pid, const ProgramMapTable*);

public:
// Single program stuff, sets
void SetDesiredProgram(int p) { _desired_program = p; }
inline void SetPATSingleProgram(ProgramAssociationTable* pPAT);
inline void SetPMTSingleProgram(ProgramMapTable* pPMT);
void SetDesiredProgram(int p) { _desired_program = p; }
inline void SetPATSingleProgram(ProgramAssociationTable*);
inline void SetPMTSingleProgram(ProgramMapTable*);

// Single program stuff, gets
int DesiredProgram() const { return _desired_program; }
uint VideoPIDSingleProgram() const { return _pid_video_single_program; }
int DesiredProgram(void) const { return _desired_program; }
uint VideoPIDSingleProgram(void) const { return _pid_video_single_program; }

const ProgramAssociationTable* PATSingleProgram() const
const ProgramAssociationTable* PATSingleProgram(void) const
{ return _pat_single_program; }
const ProgramMapTable* PMTSingleProgram() const
const ProgramMapTable* PMTSingleProgram(void) const
{ return _pmt_single_program; }

ProgramAssociationTable* PATSingleProgram()
ProgramAssociationTable* PATSingleProgram(void)
{ return _pat_single_program; }
ProgramMapTable* PMTSingleProgram()
ProgramMapTable* PMTSingleProgram(void)
{ return _pmt_single_program; }

// Single program stuff, mostly used internally
int VersionPATSingleProgram() const;
int VersionPMTSingleProgram() const;
int VersionPATSingleProgram(void) const;
int VersionPMTSingleProgram(void) const;

bool CreatePATSingleProgram(const ProgramAssociationTable&);
bool CreatePMTSingleProgram(const ProgramMapTable&);
Expand All @@ -97,22 +110,52 @@ class MPEGStreamData : public QObject
void UpdatePMTSingleProgram(ProgramMapTable*);

protected:
// Table processing -- for internal use
PSIPTable* AssemblePSIP(const TSPacket* tspacket);
bool AssemblePSIP(PSIPTable& psip, TSPacket* tspacket);
void SavePartialPES(uint pid, PESPacket* packet);
PESPacket* GetPartialPES(uint pid)
{ return _partial_pes_packet_cache[pid]; }
void ClearPartialPES(uint pid)
{ _partial_pes_packet_cache.remove(pid); }
void DeletePartialPES(uint pid);

static int ResyncStream(unsigned char *buffer, int curr_pos, int len);

bool _cache_tables;
QMap<uint, bool> _pids_listening;
QMap<uint, bool> _pids_notlistening;
QMap<uint, bool> _pids_writing;
QMap<uint, bool> _pids_audio;
QMap<uint, int> _pmt_version;
pid_pes_map_t _partial_pes_packet_cache;
// Caching
void IncrementRefCnt(const PSIPTable *psip) const;
void DeleteCachedTable(PSIPTable *psip) const;
void CachePAT(ProgramAssociationTable *pat);
void CachePMT(uint pid, ProgramMapTable *pmt);

protected:
// Listening
QMap<uint, bool> _pids_listening;
QMap<uint, bool> _pids_notlistening;
QMap<uint, bool> _pids_writing;
QMap<uint, bool> _pids_audio;

// Table versions
int _pat_version;
QMap<uint, int> _pmt_version;

// PSIP construction
pid_pes_map_t _partial_pes_packet_cache;

// Caching
bool _cache_tables;
mutable QMutex _cache_lock;
ProgramAssociationTable *_cached_pat;
pmt_cache_t _cached_pmts;
mutable psip_refcnt_map_t _cached_delete_not_yet_returned;
mutable psip_refcnt_map_t _cached_delete_slated_for_deletion;

// Single program variables
int _desired_program;
uint _pid_video_single_program;
uint _pid_pmt_single_program;
ProgramAssociationTable* _pat_single_program;
ProgramMapTable* _pmt_single_program;
int _desired_program;
uint _pid_video_single_program;
uint _pid_pmt_single_program;
ProgramAssociationTable *_pat_single_program;
ProgramMapTable *_pmt_single_program;
};

#include "mpegtables.h"
Expand Down
46 changes: 28 additions & 18 deletions mythtv/libs/libmythtv/mpeg/mpegtables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,14 @@ const QString PSIPTable::toString() const
//for (unsigned int i=0; i<9; i++)
//str.append(QString(" 0x%1").arg(int(pesdata()[i]), 0, 16));
//str.append("\n");
str.append(QString(" PSIP prefix(0x%1) tableID(0x%1) length(%2) extension(0x%3)\n").
arg(StartCodePrefix(), 0, 16).arg(TableID(), 0, 16).
arg(Length()).arg(TableIDExtension(), 0, 16));
str.append(QString(" version(%1) current(%2) section(%3) last_section(%4)\n").
arg(Version()).arg(IsCurrent()).arg(Section()).arg(LastSection()));
str.append(QString(" PSIP prefix(0x%1) tableID(0x%1) "
"length(%2) extension(0x%3)\n")
.arg(StartCodePrefix(), 0, 16).arg(TableID(), 0, 16)
.arg(Length()).arg(TableIDExtension(), 0, 16));
str.append(QString(" version(%1) current(%2) "
"section(%3) last_section(%4)\n")
.arg(Version()).arg(IsCurrent())
.arg(Section()).arg(LastSection()));
//str.append(QString(" protocol ver: "<<protocolVersion()<<endl;
return str;
}
Expand All @@ -138,25 +141,32 @@ const QString ProgramAssociationTable::toString() const
const QString ProgramMapTable::toString() const
{
Parse();
QString str;
str.append(QString("Program Map Table ver(%1) pid(0x%2) pnum(%3)\n").
arg(Version()).arg(tsheader()->PID(), 0, 16).arg(ProgramNumber()));
if (0!=ProgramInfoLength()) {
QString str =
QString("Program Map Table ver(%1) pid(0x%2) pnum(%3)\n")
.arg(Version()).arg(tsheader()->PID(), 0, 16)
.arg(ProgramNumber());

if (0 != ProgramInfoLength())
{
vector<const unsigned char*> desc =
ATSCDescriptor::Parse(ProgramInfo(), ProgramInfoLength());
MPEGDescriptor::Parse(ProgramInfo(), ProgramInfoLength());
for (uint i=0; i<desc.size(); i++)
str.append(QString(" %1\n").arg(ATSCDescriptor(desc[i]).toString()));
str.append(QString(" %1\n")
.arg(MPEGDescriptor(desc[i]).toString()));
}
str.append("\n");
for (unsigned int i=0; i<StreamCount(); i++) {
str.append(QString(" Stream #%1 pid(0x%2) type(%3 0x%4)\n").
arg(i).arg(StreamPID(i), 0, 16).
arg(StreamTypeString(i)).arg(int(StreamType(i))));
if (0!=StreamInfoLength(i)) {
for (unsigned int i=0; i<StreamCount(); i++)
{
str.append(QString(" Stream #%1 pid(0x%2) type(%3 0x%4)\n")
.arg(i).arg(StreamPID(i), 0, 16)
.arg(StreamTypeString(i)).arg(int(StreamType(i))));
if (0 != StreamInfoLength(i))
{
vector<const unsigned char*> desc =
ATSCDescriptor::Parse(StreamInfo(i), StreamInfoLength(i));
MPEGDescriptor::Parse(StreamInfo(i), StreamInfoLength(i));
for (uint i=0; i<desc.size(); i++)
str.append(QString(" %1\n").arg(ATSCDescriptor(desc[i]).toString()));
str.append(QString(" %1\n")
.arg(MPEGDescriptor(desc[i]).toString()));
}
}
return str;
Expand Down
54 changes: 37 additions & 17 deletions mythtv/libs/libmythtv/mpeg/mpegtables.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,43 @@
class StreamID
{
public:
enum {
MPEG1Video = 0x01,
MPEG2Video = 0x02,
MPEG4Video = 0x10,
H264Video = 0x1b,

MPEG1Audio = 0x03,
MPEG2Audio = 0x04,
AACAudio = 0x0f,
AC3Audio = 0x81,
DTSAudio = 0x8a,

PrivSec = 0x05,
PrivData = 0x06,
enum
{
// video
MPEG1Video = 0x01,
MPEG2Video = 0x02,
MPEG4Video = 0x10,
H264Video = 0x1b,
OpenCableVideo = 0x80,

// audio
MPEG1Audio = 0x03,
MPEG2Audio = 0x04,
AACAudio = 0x0f,
AC3Audio = 0x81,
DTSAudio = 0x8a,

// other
PrivSec = 0x05,
PrivData = 0x06,
};
};

enum
{
MPEG_PAT_PID = 0x0000,
MPEG_CAT_PID = 0x0001,
MPEG_TSDT_PID = 0x0002,

DVB_NIT_PID = 0x0010,
DVB_SDT_PID = 0x0011,
DVB_EIT_PID = 0x0012,
DVB_RST_PID = 0x0013,
DVB_TOT_PID = 0x0013,

ATSC_PSIP_PID = 0x1ffb,
};

/** \class TableID
* \brief Contains listing of Table ID's for various tables (PAT=0,PMT=2,etc).
*/
Expand All @@ -65,8 +85,8 @@ class TableID
SDTo = 0x46, // always on pid 0x11
BAT = 0x4a, // always on pid 0x11
PF_EITo = 0x4f, // always on pid 0x12
SC_EITbeg = 0x50, // always on pid 0x12
SC_EITend = 0x5f, // always on pid 0x12
SC_EITbeg = 0x50, // always on pid 0x12
SC_EITend = 0x5f, // always on pid 0x12
SC_EITbego = 0x60, // always on pid 0x12
SC_EITendo = 0x6f, // always on pid 0x12
RST = 0x71, // always on pid 0x13
Expand All @@ -90,7 +110,7 @@ class TableID
TSS = 0xA2,
CMPNAME = 0xA3,

MGT = 0xC7,
MGT = 0xC7, // always on pid 0x1ffb
TVCT = 0xC8,
CVCT = 0xC9,
RRT = 0xCA,
Expand Down
35 changes: 35 additions & 0 deletions mythtv/libs/libmythtv/mpeg/scanstreamdata.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// -*- Mode: c++ -*-
// Copyright (c) 2003-2004, Daniel Thor Kristjansson
#include "scanstreamdata.h"
#include "atsctables.h"
#include "dvbtables.h"

ScanStreamData::~ScanStreamData() { ; }

/** \fn ScanStreamData::IsRedundant(const PSIPTable&) const
* \brief Returns true if table already seen.
* \todo This is just a stub.
*/
bool ScanStreamData::IsRedundant(const PSIPTable &psip) const
{
return ATSCStreamData::IsRedundant(psip) ||
DVBStreamData::IsRedundant(psip);
}

/** \fn ScanStreamData::HandleTables(const PSIPTable&)
* \brief Assembles PSIP packets and processes them.
* \todo This is just a stub.
*/
bool ScanStreamData::HandleTables(uint pid, const PSIPTable &psip)
{
if (ATSCStreamData::HandleTables(pid, psip))
return true;
else
return DVBStreamData::HandleTables(pid, psip);
}

void ScanStreamData::Reset()
{
ATSCStreamData::Reset(-1,-1);
DVBStreamData::Reset();
}
46 changes: 46 additions & 0 deletions mythtv/libs/libmythtv/mpeg/scanstreamdata.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// -*- Mode: c++ -*-
// Copyright (c) 2003-2004, Daniel Thor Kristjansson
#ifndef SCANSTREAMDATA_H_
#define SCANSTREAMDATA_H_

#include "atscstreamdata.h"
#include "dvbstreamdata.h"

class ScanStreamData : virtual public MPEGStreamData,
virtual public ATSCStreamData,
virtual public DVBStreamData
{
Q_OBJECT
public:
ScanStreamData() : QObject(NULL, "ScanStreamData"),
MPEGStreamData(-1, true),
ATSCStreamData(-1,-1, true), DVBStreamData(true) { ; }
virtual ~ScanStreamData();

bool IsRedundant(const PSIPTable&) const;
bool HandleTables(uint pid, const PSIPTable &psip);

void Reset();

signals:
// ATSC
void UpdateMGT( const MasterGuideTable*);
void UpdateSTT( const SystemTimeTable*);
void UpdateRRT( const RatingRegionTable*);
void UpdateDCCT( const DirectedChannelChangeTable*);
void UpdateDCCSCT(const DirectedChannelChangeSelectionCodeTable*);

void UpdateVCT( uint pid, const VirtualChannelTable*);
void UpdateTVCT(uint pid, const TerrestrialVirtualChannelTable*);
void UpdateCVCT(uint pid, const CableVirtualChannelTable*);
void UpdateEIT( uint pid, const EventInformationTable*);
void UpdateETT( uint pid, const ExtendedTextTable*);

// DVB
void UpdateNIT(const NetworkInformationTable*);
void UpdateSDT(uint tsid, const ServiceDescriptionTable*);

private:
};

#endif // SCANSTREAMDATA_H_
2 changes: 0 additions & 2 deletions mythtv/libs/libmythtv/mpeg/tspacket.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ using namespace std;
#define VIDEO_PID(bp) ((bp)+1)
#define AUDIO_PID(bp) ((bp)+4)
#define SYNC_BYTE 0x0047
#define MPEG_PAT_PID 0x0000
#define ATSC_PSIP_PID 0x1ffb

/** \class TSHeader
* \brief Used to access header of a TSPacket.
Expand Down