Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More robust parsing for ATSC PSIP tables (proposal) #624

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
200 changes: 145 additions & 55 deletions mythtv/libs/libmythtv/mpeg/atscstreamdata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,61 +219,91 @@ bool ATSCStreamData::HandleTables(uint pid, const PSIPTable &psip)
case TableID::MGT:
{
SetVersionMGT(version);
if (m_cacheTables)
try
{
auto *mgt = new MasterGuideTable(psip);
CacheMGT(mgt);
ProcessMGT(mgt);
if (m_cacheTables)
{
auto *mgt = new MasterGuideTable(psip);
CacheMGT(mgt);
ProcessMGT(mgt);
}
else
{
MasterGuideTable mgt(psip);
ProcessMGT(&mgt);
}
}
else
catch (const PsipParseException& e)
{
MasterGuideTable mgt(psip);
ProcessMGT(&mgt);
m_parseErrors[e.m_error]++;
}
return true;
}
case TableID::TVCT:
{
uint tsid = psip.TableIDExtension();
SetVersionTVCT(tsid, version);
if (m_cacheTables)
try
{
auto *vct = new TerrestrialVirtualChannelTable(psip);
CacheTVCT(pid, vct);
ProcessTVCT(tsid, vct);
if (m_cacheTables)
{
auto *vct = new TerrestrialVirtualChannelTable(psip);
CacheTVCT(pid, vct);
ProcessTVCT(tsid, vct);
}
else
{
TerrestrialVirtualChannelTable vct(psip);
ProcessTVCT(tsid, &vct);
}
}
else
catch (const PsipParseException& e)
{
TerrestrialVirtualChannelTable vct(psip);
ProcessTVCT(tsid, &vct);
m_parseErrors[e.m_error]++;
}
return true;
}
case TableID::CVCT:
{
uint tsid = psip.TableIDExtension();
SetVersionCVCT(tsid, version);
if (m_cacheTables)
try
{
auto *vct = new CableVirtualChannelTable(psip);
CacheCVCT(pid, vct);
ProcessCVCT(tsid, vct);
if (m_cacheTables)
{
auto *vct = new CableVirtualChannelTable(psip);
CacheCVCT(pid, vct);
ProcessCVCT(tsid, vct);
}
else
{
CableVirtualChannelTable vct(psip);
ProcessCVCT(tsid, &vct);
}
}
else
catch (const PsipParseException& e)
{
CableVirtualChannelTable vct(psip);
ProcessCVCT(tsid, &vct);
m_parseErrors[e.m_error]++;
}
return true;
}
case TableID::RRT:
{
uint region = psip.TableIDExtension();
SetVersionRRT(region, version);
RatingRegionTable rrt(psip);
QMutexLocker locker(&m_listenerLock);
for (auto & listener : m_atscAuxListeners)
listener->HandleRRT(&rrt);
try
{
RatingRegionTable rrt(psip);
QMutexLocker locker(&m_listenerLock);
for (auto & listener : m_atscAuxListeners)
listener->HandleRRT(&rrt);
}
catch (const PsipParseException& e)
{
LOG(VB_GENERAL, LOG_ERR, LOC +
QString("RatingRegionTable constructor failed (%1): %2")
.arg(1).arg(e.what()));
}
return true;
}
case TableID::EIT:
Expand All @@ -285,51 +315,69 @@ bool ATSCStreamData::HandleTables(uint pid, const PSIPTable &psip)
uint key = (pid<<16) | psip.TableIDExtension();
m_eitStatus.SetSectionSeen(key, version, psip.Section(), psip.LastSection());

EventInformationTable eit(psip);
for (auto & listener : m_atscEitListeners)
listener->HandleEIT(pid, &eit);

const uint mm = GetATSCMajorMinor(eit.SourceID());
if (mm && m_eitHelper)
m_eitHelper->AddEIT(mm >> 16, mm & 0xffff, &eit);
try
{
EventInformationTable eit(psip);
for (auto & listener : m_atscEitListeners)
listener->HandleEIT(pid, &eit);

const uint mm = GetATSCMajorMinor(eit.SourceID());
if (mm && m_eitHelper)
m_eitHelper->AddEIT(mm >> 16, mm & 0xffff, &eit);
}
catch (const PsipParseException& e)
{
m_parseErrors[e.m_error]++;
}
return true;
}
case TableID::ETT:
{
ExtendedTextTable ett(psip);

QMutexLocker locker(&m_listenerLock);
for (auto & listener : m_atscEitListeners)
listener->HandleETT(pid, &ett);

if (ett.IsEventETM() && m_eitHelper) // Guide ETTs
try
{
const uint mm = GetATSCMajorMinor(ett.SourceID());
if (mm)
m_eitHelper->AddETT(mm >> 16, mm & 0xffff, &ett);
ExtendedTextTable ett(psip);

QMutexLocker locker(&m_listenerLock);
for (auto & listener : m_atscEitListeners)
listener->HandleETT(pid, &ett);

if (ett.IsEventETM() && m_eitHelper) // Guide ETTs
{
const uint mm = GetATSCMajorMinor(ett.SourceID());
if (mm)
m_eitHelper->AddETT(mm >> 16, mm & 0xffff, &ett);
}
}
catch (const PsipParseException& e)
{
m_parseErrors[e.m_error]++;
}

return true;
}
case TableID::STT:
{
SystemTimeTable stt(psip);

UpdateTimeOffset(stt.UTCUnix());
try
{
SystemTimeTable stt(psip);

// only update internal offset if it changes
if (stt.GPSOffset() != m_gpsUtcOffset)
m_gpsUtcOffset = stt.GPSOffset();
UpdateTimeOffset(stt.UTCUnix());

m_listenerLock.lock();
for (auto & listener : m_atscMainListeners)
listener->HandleSTT(&stt);
m_listenerLock.unlock();
// only update internal offset if it changes
if (stt.GPSOffset() != m_gpsUtcOffset)
m_gpsUtcOffset = stt.GPSOffset();

if (m_eitHelper && GPSOffset() != m_eitHelper->GetGPSOffset())
m_eitHelper->SetGPSOffset(GPSOffset());
m_listenerLock.lock();
for (auto & listener : m_atscMainListeners)
listener->HandleSTT(&stt);
m_listenerLock.unlock();

if (m_eitHelper && GPSOffset() != m_eitHelper->GetGPSOffset())
m_eitHelper->SetGPSOffset(GPSOffset());
}
catch (const PsipParseException& e)
{
m_parseErrors[e.m_error]++;
}
return true;
}
case TableID::DCCT:
Expand Down Expand Up @@ -1058,3 +1106,45 @@ void ATSCStreamData::RemoveATSC81EITListener(ATSC81EITStreamListener *val)
}
}
}

void ATSCStreamData::DumpErrors() const
{
if (m_parseErrors[PsipParseException::MgtLength] ||
m_parseErrors[PsipParseException::MgtTableCount] ||
m_parseErrors[PsipParseException::MgtTableDescriptors] ||
m_parseErrors[PsipParseException::MgtGlobalDescriptors] ||
m_parseErrors[PsipParseException::MgtBadParse])
{
LOG(VB_GENERAL, LOG_INFO, LOC + QString("MGT parsing error: %1/%2/%3/%4/%5")
.arg(m_parseErrors[PsipParseException::MgtLength])
.arg(m_parseErrors[PsipParseException::MgtTableCount])
.arg(m_parseErrors[PsipParseException::MgtTableDescriptors])
.arg(m_parseErrors[PsipParseException::MgtGlobalDescriptors])
.arg(m_parseErrors[PsipParseException::MgtBadParse]));
}
if (m_parseErrors[PsipParseException::VctLength] ||
m_parseErrors[PsipParseException::VctChannelCount] ||
m_parseErrors[PsipParseException::VctChannelDescriptors] ||
m_parseErrors[PsipParseException::VctGlobalDescriptors] ||
m_parseErrors[PsipParseException::VctBadParse])
{
LOG(VB_GENERAL, LOG_INFO, LOC + QString("xVCT parsing error: %1/%2/%3/%4/%5")
.arg(m_parseErrors[PsipParseException::VctLength])
.arg(m_parseErrors[PsipParseException::VctChannelCount])
.arg(m_parseErrors[PsipParseException::VctChannelDescriptors])
.arg(m_parseErrors[PsipParseException::VctGlobalDescriptors])
.arg(m_parseErrors[PsipParseException::VctBadParse]));
}
if (m_parseErrors[PsipParseException::EitEventCount] ||
m_parseErrors[PsipParseException::EitEventDescriptors] ||
m_parseErrors[PsipParseException::EitBadParse])
{
LOG(VB_GENERAL, LOG_INFO, LOC + QString("EIT parsing error: %1/%2/%3/%4")
.arg(m_parseErrors[PsipParseException::EitLength])
.arg(m_parseErrors[PsipParseException::EitEventCount])
.arg(m_parseErrors[PsipParseException::EitEventDescriptors])
.arg(m_parseErrors[PsipParseException::EitBadParse]));
}

MPEGStreamData::DumpErrors();
}
1 change: 1 addition & 0 deletions mythtv/libs/libmythtv/mpeg/atscstreamdata.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class MTV_PUBLIC ATSCStreamData : virtual public MPEGStreamData
// Table processing
bool HandleTables(uint pid, const PSIPTable &psip) override; // MPEGStreamData
bool IsRedundant(uint pid, const PSIPTable &psip) const override; // MPEGStreamData
void DumpErrors() const override; // MPEGStreamData

/// Current UTC to GPS time offset in seconds
uint GPSOffset(void) const { return m_gpsUtcOffset; }
Expand Down
96 changes: 87 additions & 9 deletions mythtv/libs/libmythtv/mpeg/atsctables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,29 +41,107 @@ int MasterGuideTable::TableClass(uint i) const
return TableClass::UNKNOWN;
}

void MasterGuideTable::Parse(void) const
void MasterGuideTable::Parse(void)
{
if (SectionLength() > TSSizeInBuffer())
throw AtscParseException(PsipParseException::MgtLength);

// If no descriptors are present, what is the maximum possible number
// of table entries?
uint maxEntriesPossible =
(SectionLength() - kMGTHeaderSize - kMGTMinTrailerSize) / kMGTTableEntrySize;
m_tableCount = TableCountRaw();
if ((m_tableCount == 0) || (m_tableCount > maxEntriesPossible))
throw AtscParseException(PsipParseException::MgtTableCount);

// Pre-allocate the entire vector
m_ptrs.clear();
m_ptrs.reserve(m_tableCount+1);
m_ptrs.push_back(const_cast<unsigned char*>(psipdata()) + 3);
for (uint i = 0; i < TableCount(); i++)
m_ptrs.push_back(m_ptrs[i] + 11 + TableDescriptorsLength(i));

// Process tables
const uint8_t *limit = pesdata() + SectionLength() - kMpegCRCSize;
for (uint i = 0; i < m_tableCount; i++)
{
uint8_t *next = m_ptrs[i] + kMGTTableEntrySize + TableDescriptorsLength(i);
if (next > limit)
throw AtscParseException(PsipParseException::MgtTableDescriptors);
m_ptrs.push_back(next);
}

// Validate global descriptor count
const uint8_t *gd_end = m_ptrs[m_tableCount] + 2 + GlobalDescriptorsLength();
if (gd_end > limit)
throw AtscParseException(PsipParseException::MgtGlobalDescriptors);
if (gd_end < limit)
throw AtscParseException(PsipParseException::MgtBadParse);
}


void VirtualChannelTable::Parse(void) const
void VirtualChannelTable::Parse(void)
{
if (SectionLength() > TSSizeInBuffer())
throw AtscParseException(PsipParseException::VctLength);

// If no descriptors are present, what is the maximum possible number
// of channel entries?
uint maxEntriesPossible =
(SectionLength() - kVCTHeaderSize - kVCTMinTrailerSize) / kVCTTableEntrySize;
m_channelCount = ChannelCountRaw();
if ((m_channelCount == 0) || (m_channelCount > maxEntriesPossible))
throw AtscParseException(PsipParseException::VctChannelCount);

// Pre-allocate the entire vector
m_ptrs.clear();
m_ptrs.reserve(m_channelCount+1);
m_ptrs.push_back(const_cast<unsigned char*>(psipdata()) + 2);
for (uint i = 0; i < ChannelCount(); i++)
m_ptrs.push_back(m_ptrs[i] + 32 + DescriptorsLength(i));

// Process tables
const uint8_t *limit = pesdata() + SectionLength() - kMpegCRCSize;
for (uint i = 0; i < m_channelCount; i++)
{
uint8_t *next = m_ptrs[i] + kVCTTableEntrySize + DescriptorsLength(i);
if (next > limit)
throw AtscParseException(PsipParseException::VctChannelDescriptors);
m_ptrs.push_back(next);
}

// Validate additional descriptor count
const uint8_t *gd_end = m_ptrs[m_channelCount] + 2 + GlobalDescriptorsLength();
if (gd_end > limit)
throw AtscParseException(PsipParseException::VctGlobalDescriptors);
if (gd_end < limit)
throw AtscParseException(PsipParseException::VctBadParse);
}

void EventInformationTable::Parse(void) const
void EventInformationTable::Parse(void)
{
if (SectionLength() > TSSizeInBuffer())
throw AtscParseException(PsipParseException::EitLength);

// If no descriptors are present, what is the maximum possible number
// of event entries?
uint maxEntriesPossible =
(SectionLength() - kEITHeaderSize - kEITMinTrailerSize) / kEITTableEntrySize;
m_eventCount = EventCountRaw();
if ((m_eventCount == 0) || (m_eventCount > maxEntriesPossible))
throw AtscParseException(PsipParseException::EitEventCount);

// Pre-allocate the entire vector
m_ptrs.clear();
m_ptrs.reserve(m_eventCount+1);
m_ptrs.push_back(const_cast<unsigned char*>(psipdata()) + 2);
for (uint i = 0; i < EventCount(); i++)
m_ptrs.push_back(m_ptrs[i] + 12 + TitleLength(i) + DescriptorsLength(i));

// Process tables
const uint8_t *limit = pesdata() + SectionLength() - kMpegCRCSize;
uint8_t *next { nullptr};
for (uint i = 0; i < m_eventCount; i++)
{
next = m_ptrs[i] + kEITTableEntrySize + TitleLength(i) + DescriptorsLength(i);
if (next > limit)
throw AtscParseException(PsipParseException::EitEventDescriptors);
m_ptrs.push_back(next);
}
}

QString MasterGuideTable::toString(void) const
Expand Down
Loading