1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

Added handling shared PMT pids and multiple PMT sections

This commit is contained in:
Klaus Schmidinger 2020-06-19 12:19:15 +02:00
parent 54cf10588d
commit 5d39daa13c
4 changed files with 142 additions and 49 deletions

View File

@ -3601,6 +3601,7 @@ Helmut Binder <cco@aon.at>
for adding cMtdHandler::StopDecrypting()
for adding support for detecting new channels broadcast in HEVC
for adding support for detecting 'advanced codec digital radio sound service'
for adding handling shared PMT pids and multiple PMT sections
Ulrich Eckhardt <uli@uli-eckhardt.de>
for reporting a problem with shutdown after user inactivity in case a plugin is

View File

@ -9453,7 +9453,7 @@ Video Disk Recorder Revision History
The version numbers (both VDRVERSNUM and APIVERSNUM) have been bumped to 2.4.2, so
that plugins can detect the presence of the new cControl::Control().
2020-06-16: Version 2.4.3
2020-06-19: Version 2.4.3
- Added a missing '-D' to the 'plugins' target of the Makefile (thanks to Johann
Friedrichs).
@ -9474,3 +9474,4 @@ Video Disk Recorder Revision History
- Added support for detecting new channels broadcast in HEVC (thanks to Helmut Binder).
- Added support for detecting 'advanced codec digital radio sound service' (thanks to
Helmut Binder).
- Added handling shared PMT pids and multiple PMT sections (thanks to Helmut Binder).

170
pat.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: pat.c 4.5 2019/03/15 10:14:35 kls Exp $
* $Id: pat.c 4.6 2020/06/19 12:19:15 kls Exp $
*/
#include "pat.h"
@ -280,6 +280,54 @@ int GetPmtPid(int Source, int Transponder, int ServiceId)
return CaDescriptorHandler.GetPmtPid(Source, Transponder, ServiceId);
}
// --- cPmtPidEntry ----------------------------------------------------------
class cPmtPidEntry : public cListObject {
private:
int pid;
bool complete;
public:
cPmtPidEntry(int Pid);
int Pid(void) { return pid; }
int Complete(void) { return complete; }
void SetComplete(bool State) { complete = State; }
};
cPmtPidEntry::cPmtPidEntry(int Pid)
{
pid = Pid;
complete = false;
}
// --- cPmtSidEntry ----------------------------------------------------------
class cPmtSidEntry : public cListObject {
private:
int sid;
int pid;
cPmtPidEntry *pidEntry;
int version;
bool received;
public:
cPmtSidEntry(int Sid, int Pid, cPmtPidEntry *PidEntry);
int Sid(void) { return sid; }
int Pid(void) { return pid; }
cPmtPidEntry *PidEntry(void) { return pidEntry; }
int Version(void) { return version; }
int Received(void) { return received; }
void SetVersion(int Version) { version = Version; }
void SetReceived(bool State) { received = State; }
};
cPmtSidEntry::cPmtSidEntry(int Sid, int Pid, cPmtPidEntry *PidEntry)
{
sid = Sid;
pid = Pid;
pidEntry = PidEntry;
version = -1;
received = false;
}
// --- cPatFilter ------------------------------------------------------------
//#define DEBUG_PAT_PMT
@ -307,24 +355,47 @@ void cPatFilter::Trigger(int Sid)
{
cMutexLock MutexLock(&mutex);
patVersion = -1;
pmtIndex = -1;
numPmtEntries = 0;
sectionSyncer.Reset();
if (Sid != 0 && activePmt)
Del(activePmt->Pid(), SI::TableIdPMT);
activePmt = NULL;
if (Sid >= 0) {
sid = Sid;
DBGLOG("PAT filter trigger SID %d", Sid);
}
}
bool cPatFilter::PmtPidComplete(int PmtPid)
{
for (cPmtSidEntry *se = pmtSidList.First(); se; se = pmtSidList.Next(se)) {
if (se->Pid() == PmtPid && !se->Received())
return false;
}
return true;
}
void cPatFilter::PmtPidReset(int PmtPid)
{
for (cPmtSidEntry *se = pmtSidList.First(); se; se = pmtSidList.Next(se)) {
if (se->Pid() == PmtPid)
se->SetReceived(false);
}
}
bool cPatFilter::PmtVersionChanged(int PmtPid, int Sid, int Version, bool SetNewVersion)
{
int Id = MakePmtId(PmtPid, Sid);
for (int i = 0; i < numPmtEntries; i++) {
if (pmtId[i] == Id) {
if (pmtVersion[i] != Version) {
int i = 0;
for (cPmtSidEntry *se = pmtSidList.First(); se; se = pmtSidList.Next(se), i++) {
if (se->Sid() == Sid && se->Pid() == PmtPid) {
if (!se->Received()) {
se->SetReceived(true);
if (PmtPidComplete(PmtPid))
se->PidEntry()->SetComplete(true);
}
if (se->Version() != Version) {
DBGLOG("PMT %d %2d %5d/%d %2d -> %2d", Transponder(), i, PmtPid, Sid, se->Version(), Version);
if (SetNewVersion)
pmtVersion[i] = Version;
else
DBGLOG("PMT %d %2d %5d %2d -> %2d", Transponder(), i, PmtPid, pmtVersion[i], Version);
se->SetVersion(Version);
return true;
}
break;
@ -335,10 +406,12 @@ bool cPatFilter::PmtVersionChanged(int PmtPid, int Sid, int Version, bool SetNew
void cPatFilter::SwitchToNextPmtPid(void)
{
if (pmtIndex >= 0) {
Del(GetPmtPid(pmtIndex), SI::TableIdPMT);
pmtIndex = (pmtIndex + 1) % numPmtEntries;
Add(GetPmtPid(pmtIndex), SI::TableIdPMT);
if (activePmt) {
Del(activePmt->Pid(), SI::TableIdPMT);
if (!(activePmt = pmtPidList.Next(activePmt)))
activePmt = pmtPidList.First();
PmtPidReset(activePmt->Pid());
Add(activePmt->Pid(), SI::TableIdPMT);
}
}
@ -350,32 +423,48 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
SI::PAT pat(Data, false);
if (!pat.CheckCRCAndParse())
return;
if (pat.getVersionNumber() != patVersion) {
DBGLOG("PAT %d %d -> %d", Transponder(), patVersion, pat.getVersionNumber());
if (pmtIndex >= 0) {
Del(GetPmtPid(pmtIndex), SI::TableIdPMT);
pmtIndex = -1;
if (sectionSyncer.Sync(pat.getVersionNumber(), pat.getSectionNumber(), pat.getLastSectionNumber())) {
DBGLOG("PAT %d %d -> %d %d/%d", Transponder(), patVersion, pat.getVersionNumber(), pat.getSectionNumber(), pat.getLastSectionNumber());
if (pat.getVersionNumber() != patVersion) {
if (pat.getLastSectionNumber() > 0)
DBGLOG(" PAT %d: %d sections", Transponder(), pat.getLastSectionNumber() + 1);
if (activePmt) {
Del(activePmt->Pid(), SI::TableIdPMT);
activePmt = NULL;
}
pmtSidList.Clear();
pmtPidList.Clear();
patVersion = pat.getVersionNumber();
}
numPmtEntries = 0;
SI::PAT::Association assoc;
for (SI::Loop::Iterator it; pat.associationLoop.getNext(assoc, it); ) {
if (!assoc.isNITPid() && numPmtEntries < MAXPMTENTRIES) {
DBGLOG(" PMT pid %2d %5d SID %5d", numPmtEntries, assoc.getPid(), assoc.getServiceId());
pmtId[numPmtEntries] = MakePmtId(assoc.getPid(), assoc.getServiceId());
pmtVersion[numPmtEntries] = -1;
if (sid == assoc.getServiceId()) {
pmtIndex = numPmtEntries;
DBGLOG("sid = %d pmtIndex = %d", sid, pmtIndex);
if (!assoc.isNITPid()) {
int PmtPid = assoc.getPid();
cPmtPidEntry *pPid = NULL;
int PidIndex = 0;
for (pPid = pmtPidList.First(); pPid && pPid->Pid() != PmtPid; pPid = pmtPidList.Next(pPid))
PidIndex++;
if (!pPid) { // new PMT Pid
pPid = new cPmtPidEntry(PmtPid);
pmtPidList.Add(pPid);
}
pmtSidList.Add(new cPmtSidEntry(assoc.getServiceId(), PmtPid, pPid));
DBGLOG(" PMT pid %2d/%2d %5d SID %5d", PidIndex, pmtSidList.Count() - 1, PmtPid, assoc.getServiceId());
if (sid == assoc.getServiceId()) {
activePmt = pPid;
DBGLOG("sid = %d pidIndex = %d", sid, PidIndex);
}
numPmtEntries++;
}
}
if (numPmtEntries > 0 && pmtIndex < 0)
pmtIndex = 0;
if (pmtIndex >= 0)
Add(GetPmtPid(pmtIndex), SI::TableIdPMT);
patVersion = pat.getVersionNumber();
timer.Set(PMT_SCAN_TIMEOUT);
if (sectionSyncer.Complete()) { // all PAT sections done
if (pmtPidList.Count() != pmtSidList.Count())
DBGLOG(" PAT %d: shared PMT PIDs", Transponder());
if (pmtSidList.Count() && !activePmt)
activePmt = pmtPidList.First();
if (activePmt)
Add(activePmt->Pid(), SI::TableIdPMT);
timer.Set(PMT_SCAN_TIMEOUT);
}
}
}
}
@ -384,8 +473,9 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
SI::PMT pmt(Data, false);
if (!pmt.CheckCRCAndParse())
return;
if (!PmtVersionChanged(Pid, pmt.getTableIdExtension(), pmt.getVersionNumber())) {
SwitchToNextPmtPid();
if (!PmtVersionChanged(Pid, pmt.getTableIdExtension(), pmt.getVersionNumber(), true)) {
if (activePmt && activePmt->Complete())
SwitchToNextPmtPid();
return;
}
cStateKey StateKey;
@ -393,8 +483,8 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
if (!Channels)
return;
bool ChannelsModified = false;
PmtVersionChanged(Pid, pmt.getTableIdExtension(), pmt.getVersionNumber(), true);
SwitchToNextPmtPid();
if (activePmt && activePmt->Complete())
SwitchToNextPmtPid();
cChannel *Channel = Channels->GetByServiceID(Source(), Transponder(), pmt.getServiceId());
if (Channel) {
SI::CaDescriptor *d;
@ -636,8 +726,8 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
StateKey.Remove(ChannelsModified);
}
if (timer.TimedOut()) {
if (pmtIndex >= 0)
DBGLOG("PMT timeout %d", pmtIndex);
if (activePmt)
DBGLOG("PMT timeout Pid %d", activePmt->Pid());
SwitchToNextPmtPid();
timer.Set(PMT_SCAN_TIMEOUT);
}

17
pat.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: pat.h 4.1 2016/12/23 14:03:24 kls Exp $
* $Id: pat.h 4.2 2020/06/19 12:19:15 kls Exp $
*/
#ifndef __PAT_H
@ -14,20 +14,21 @@
#include "filter.h"
#include "thread.h"
#define MAXPMTENTRIES 64
class cPmtPidEntry;
class cPmtSidEntry;
class cPatFilter : public cFilter {
private:
cMutex mutex;
cTimeMs timer;
int patVersion;
int pmtIndex;
int pmtId[MAXPMTENTRIES];
int pmtVersion[MAXPMTENTRIES];
int numPmtEntries;
int sid;
int GetPmtPid(int Index) { return pmtId[Index] & 0x0000FFFF; }
int MakePmtId(int PmtPid, int Sid) { return PmtPid | (Sid << 16); }
cPmtPidEntry *activePmt;
cList<cPmtPidEntry> pmtPidList;
cList<cPmtSidEntry> pmtSidList;
cSectionSyncer sectionSyncer;
bool PmtPidComplete(int PmtPid);
void PmtPidReset(int PmtPid);
bool PmtVersionChanged(int PmtPid, int Sid, int Version, bool SetNewVersion = false);
void SwitchToNextPmtPid(void);
protected: