diff --git a/CONTRIBUTORS b/CONTRIBUTORS index e08d1685..9934dbdb 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -3601,6 +3601,7 @@ Helmut Binder 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 for reporting a problem with shutdown after user inactivity in case a plugin is diff --git a/HISTORY b/HISTORY index 3bb36082..0965d5ab 100644 --- a/HISTORY +++ b/HISTORY @@ -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). diff --git a/pat.c b/pat.c index 00eed49f..2f2e286c 100644 --- a/pat.c +++ b/pat.c @@ -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); } diff --git a/pat.h b/pat.h index b9929754..09efe4bb 100644 --- a/pat.h +++ b/pat.h @@ -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 pmtPidList; + cList 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: