diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 6ac848a9..39ac844d 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -2343,6 +2343,7 @@ Andr for reporting a bug in selecting the last replayed recording in the Recordings menu in case there are folders and plain recordings with names that differ only in non-alphanumeric characters + for reporting a problem with permanently looping through PMT PIDs on a SatIP receiver Jürgen Schilling for reporting that color buttons were displayed in the recording info menu if it diff --git a/HISTORY b/HISTORY index dc0bb7bf..2b1a35fa 100644 --- a/HISTORY +++ b/HISTORY @@ -9706,7 +9706,7 @@ Video Disk Recorder Revision History order to restore this functionality. However, it is recommended to use the function with the TimerActive parameter instead. -2021-05-26: +2021-06-08: - cRecordingInfo::Errors() now returns -1 for old recordings; added a missing 'const' (suggested by Christoph Haubrich). @@ -9714,3 +9714,5 @@ Video Disk Recorder Revision History - Added missing initialization of cRecorder::lastErrors. - Now using __cplusplus instead of DISABLE_TEMPLATES_COLLIDING_WITH_STL, and using std::min(), std::max() and std::swap() if available (thanks to Winfried Köhler). +- No longer permanently looping through PMT PIDs, which caused problems with some + SatIP receivers (reported by André Weidemann; with help from Helmut Binder). diff --git a/device.c b/device.c index b2a85944..7a5eb3ce 100644 --- a/device.c +++ b/device.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: device.c 5.2 2021/03/17 10:59:36 kls Exp $ + * $Id: device.c 5.3 2021/06/08 14:57:26 kls Exp $ */ #include "device.h" @@ -858,6 +858,13 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView) cStatus::MsgChannelSwitch(this, 0, LiveView); if (LiveView) { + if (IsPrimaryDevice() && !Replaying() && !Transferring()) { // this is only for FF DVB cards! + LOCK_CHANNELS_READ; + if (const cChannel *ch = Channels->GetByNumber(currentChannel)) { + if (patFilter) + patFilter->Release(ch->Sid()); + } + } StopReplay(); DELETENULL(liveSubtitle); DELETENULL(dvbSubtitleConverter); @@ -898,8 +905,6 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView) if (SetChannelDevice(Channel, LiveView)) { // Start section handling: if (sectionHandler) { - if (patFilter) - patFilter->Trigger(Channel->Sid()); sectionHandler->SetChannel(Channel); sectionHandler->SetStatus(true); } @@ -913,6 +918,8 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView) if (Result == scrOk) { if (LiveView && IsPrimaryDevice()) { + if (patFilter) // this is only for FF DVB cards! + patFilter->Request(Channel->Sid()); currentChannel = Channel->Number(); // Set the available audio tracks: ClrAvailableTracks(); @@ -1807,6 +1814,8 @@ bool cDevice::AttachReceiver(cReceiver *Receiver) dsyslog("CAM %d: %sknown to decrypt channel %s (scramblingTimeout = %ds)", camSlot->MasterSlotNumber(), KnownToDecrypt ? "" : "not ", *Receiver->ChannelID().ToString(), Receiver->scramblingTimeout); } } + if (patFilter && Receiver->ChannelID().Valid()) + patFilter->Request(Receiver->ChannelID().Sid()); Start(); return true; } @@ -1827,6 +1836,8 @@ void cDevice::Detach(cReceiver *Receiver) else if (receiver[i]) receiversLeft = true; } + if (patFilter && Receiver->ChannelID().Valid()) + patFilter->Release(Receiver->ChannelID().Sid()); mutexReceiver.Unlock(); Receiver->device = NULL; Receiver->Activate(false); diff --git a/pat.c b/pat.c index 39009b14..30424866 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 5.1 2021/03/16 15:10:54 kls Exp $ + * $Id: pat.c 5.2 2021/06/08 14:57:26 kls Exp $ */ #include "pat.h" @@ -285,10 +285,20 @@ int GetPmtPid(int Source, int Transponder, int ServiceId) class cPmtPidEntry : public cListObject { private: int pid; - bool complete; + int count; // the number of SIDs currently requested from this PID + int state; // adding/deleting PIDs to/from the filter may only be done from within the Process() function, + // otherwise there could be a deadlock between cPatFilter::mutex and cSectionHandler::mutex; + // this member tells whether this PID needs to be added to (>0) or deleted from (<0) the filter + bool complete; // true if all SIDs on this PID have been received public: cPmtPidEntry(int Pid); int Pid(void) { return pid; } + int Count(void) { return count; } + int State(void) { int s = state; state = 0; return s; } // returns the current state and resets it + void SetState(void) { state = 1; } + void ClrState(void) { state = -1; } + void Inc(void) { if (++count == 1) state = 1; } + void Dec(void) { if (--count == 0) state = -1; } int Complete(void) { return complete; } void SetComplete(bool State) { complete = State; } }; @@ -296,6 +306,8 @@ public: cPmtPidEntry::cPmtPidEntry(int Pid) { pid = Pid; + count = 0; + state = 0; complete = false; } @@ -309,7 +321,7 @@ private: int version; bool received; public: - cPmtSidEntry(int Sid, int Pid, cPmtPidEntry *PidEntry); + cPmtSidEntry(int Sid, cPmtPidEntry *PidEntry); int Sid(void) { return sid; } int Pid(void) { return pid; } cPmtPidEntry *PidEntry(void) { return pidEntry; } @@ -319,15 +331,29 @@ public: void SetReceived(bool State) { received = State; } }; -cPmtSidEntry::cPmtSidEntry(int Sid, int Pid, cPmtPidEntry *PidEntry) +cPmtSidEntry::cPmtSidEntry(int Sid, cPmtPidEntry *PidEntry) { sid = Sid; - pid = Pid; + pid = PidEntry->Pid(); pidEntry = PidEntry; version = -1; received = false; } +// --- cPmtSidRequest -------------------------------------------------------- + +class cPmtSidRequest : public cListObject { +private: + int sid; + int count; // the number of requests for this SID +public: + cPmtSidRequest(int Sid) { sid = Sid; count = 1; } + int Sid(void) { return sid; } + int Count(void) { return count; } + void Inc(void) { count++; } + void Dec(void) { count--; } + }; + // --- cPatFilter ------------------------------------------------------------ //#define DEBUG_PAT_PMT @@ -339,32 +365,95 @@ cPmtSidEntry::cPmtSidEntry(int Sid, int Pid, cPmtPidEntry *PidEntry) cPatFilter::cPatFilter(void) { - Trigger(0); + patVersion = -1; + activePmt = NULL; + transponder = 0; + source = 0; Set(0x00, 0x00); // PAT } -void cPatFilter::SetStatus(bool On) +bool cPatFilter::TransponderChanged(void) { - cMutexLock MutexLock(&mutex); - DBGLOG("PAT filter set status %d", On); - cFilter::SetStatus(On); - Trigger(); + if (source != Source() || transponder != Transponder()) { + DBGLOG("PAT filter transponder changed from %d/%d to %d/%d", source, transponder, Source(), Transponder()); + source = Source(); + transponder = Transponder(); + return true; + } + return false; } -void cPatFilter::Trigger(int Sid) +void cPatFilter::Trigger(int) { cMutexLock MutexLock(&mutex); - patVersion = -1; - 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); + DBGLOG("PAT filter trigger"); + if (activePmt != pmtPidList.First()) { + if (activePmt && activePmt->Count() == 0) + activePmt->ClrState(); + activePmt = pmtPidList.First(); + if (activePmt && activePmt->Count() == 0) { + activePmt->SetState(); + timer.Set(PMT_SCAN_TIMEOUT); + } } } +void cPatFilter::Request(int Sid) +{ + cMutexLock MutexLock(&mutex); + DBGLOG("PAT filter request SID %d", Sid); + for (cPmtSidRequest *sr = pmtSidRequestList.First(); sr; sr = pmtSidRequestList.Next(sr)) { + if (sr->Sid() == Sid) { + sr->Inc(); + DBGLOG("PAT filter add SID request %d (%d)", Sid, sr->Count()); + return; + } + } + DBGLOG("PAT filter new SID request %d", Sid); + pmtSidRequestList.Add(new cPmtSidRequest(Sid)); + for (cPmtSidEntry *se = pmtSidList.First(); se; se = pmtSidList.Next(se)) { + if (se->Sid() == Sid) { + cPmtPidEntry *pPid = se->PidEntry(); + pPid->Inc(); + DBGLOG(" PMT pid %5d SID %5d (%d)", pPid->Pid(), se->Sid(), pPid->Count()); + break; + } + } +} + +void cPatFilter::Release(int Sid) +{ + cMutexLock MutexLock(&mutex); + DBGLOG("PAT filter release SID %d", Sid); + for (cPmtSidRequest *sr = pmtSidRequestList.First(); sr; sr = pmtSidRequestList.Next(sr)) { + if (sr->Sid() == Sid) { + sr->Dec(); + DBGLOG("PAT filter del SID request %d (%d)", Sid, sr->Count()); + if (sr->Count() == 0) { + pmtSidRequestList.Del(sr); + for (cPmtSidEntry *se = pmtSidList.First(); se; se = pmtSidList.Next(se)) { + if (se->Sid() == Sid) { + cPmtPidEntry *pPid = se->PidEntry(); + pPid->Dec(); + DBGLOG(" PMT pid %5d SID %5d (%d)", pPid->Pid(), se->Sid(), pPid->Count()); + break; + } + } + } + break; + } + } +} + +int cPatFilter::NumSidRequests(int Sid) +{ + for (cPmtSidRequest *sr = pmtSidRequestList.First(); sr; sr = pmtSidRequestList.Next(sr)) { + if (sr->Sid() == Sid) + return sr->Count(); + } + return 0; +} + bool cPatFilter::PmtPidComplete(int PmtPid) { for (cPmtSidEntry *se = pmtSidList.First(); se; se = pmtSidList.Next(se)) { @@ -377,15 +466,16 @@ bool cPatFilter::PmtPidComplete(int PmtPid) void cPatFilter::PmtPidReset(int PmtPid) { for (cPmtSidEntry *se = pmtSidList.First(); se; se = pmtSidList.Next(se)) { - if (se->Pid() == PmtPid) + if (se->Pid() == PmtPid) { se->SetReceived(false); + se->PidEntry()->SetComplete(false); + } } } bool cPatFilter::PmtVersionChanged(int PmtPid, int Sid, int Version, bool SetNewVersion) { - int i = 0; - for (cPmtSidEntry *se = pmtSidList.First(); se; se = pmtSidList.Next(se), i++) { + for (cPmtSidEntry *se = pmtSidList.First(); se; se = pmtSidList.Next(se)) { if (se->Sid() == Sid && se->Pid() == PmtPid) { if (!se->Received()) { se->SetReceived(true); @@ -395,7 +485,7 @@ bool cPatFilter::PmtVersionChanged(int PmtPid, int Sid, int Version, bool SetNew if (SetNewVersion) se->SetVersion(Version); else - DBGLOG("PMT %d %2d %5d/%d %2d -> %2d", Transponder(), i, PmtPid, Sid, se->Version(), Version); + DBGLOG("PMT %d %5d/%5d %2d -> %2d %d", Transponder(), PmtPid, Sid, se->Version(), Version, NumSidRequests(Sid)); return true; } break; @@ -407,18 +497,39 @@ bool cPatFilter::PmtVersionChanged(int PmtPid, int Sid, int Version, bool SetNew void cPatFilter::SwitchToNextPmtPid(void) { if (activePmt) { - Del(activePmt->Pid(), SI::TableIdPMT); - if (!(activePmt = pmtPidList.Next(activePmt))) - activePmt = pmtPidList.First(); - PmtPidReset(activePmt->Pid()); - activePmt->SetComplete(false); - Add(activePmt->Pid(), SI::TableIdPMT); + if (activePmt->Count() == 0) + Del(activePmt->Pid(), SI::TableIdPMT); + for (;;) { + activePmt = pmtPidList.Next(activePmt); + if (!activePmt || activePmt->Count() == 0) + break; + } + if (activePmt) { + PmtPidReset(activePmt->Pid()); + Add(activePmt->Pid(), SI::TableIdPMT); + timer.Set(PMT_SCAN_TIMEOUT); + } } } void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length) { cMutexLock MutexLock(&mutex); + if (TransponderChanged()) { + patVersion = -1; + sectionSyncer.Reset(); + } + if (patVersion >= 0) { + for (cPmtPidEntry *pPid = pmtPidList.First(); pPid; pPid = pmtPidList.Next(pPid)) { + int State = pPid->State(); + if (State > 0) + Add(pPid->Pid(), SI::TableIdPMT); + else if (State < 0) + Del(pPid->Pid(), SI::TableIdPMT); + } + } + else if (Pid != 0x00) + return; if (Pid == 0x00) { if (Tid == SI::TableIdPAT) { SI::PAT pat(Data, false); @@ -426,13 +537,11 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length return; if (sectionSyncer.Check(pat.getVersionNumber(), pat.getSectionNumber())) { DBGLOG("PAT %d %d -> %d %d/%d", Transponder(), patVersion, pat.getVersionNumber(), pat.getSectionNumber(), pat.getLastSectionNumber()); + bool NeedsSetStatus = patVersion >= 0; 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; - } + if (NeedsSetStatus) + SetStatus(false); // deletes all PIDs from the filter + activePmt = NULL; pmtSidList.Clear(); pmtPidList.Clear(); patVersion = pat.getVersionNumber(); @@ -441,36 +550,39 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length for (SI::Loop::Iterator it; pat.associationLoop.getNext(assoc, it); ) { if (!assoc.isNITPid()) { int PmtPid = assoc.getPid(); + int PmtSid = assoc.getServiceId(); cPmtPidEntry *pPid = NULL; - int PidIndex = 0; - for (pPid = pmtPidList.First(); pPid && pPid->Pid() != PmtPid; pPid = pmtPidList.Next(pPid)) - PidIndex++; + for (pPid = pmtPidList.First(); pPid; pPid = pmtPidList.Next(pPid)) { + if (pPid->Pid() == PmtPid) + break; + } + int SidRequest = NumSidRequests(PmtSid); + DBGLOG(" PMT pid %5d SID %5d%s%s", PmtPid, PmtSid, SidRequest ? " R" : "", pPid ? " S" : ""); 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); - } + pmtSidList.Add(new cPmtSidEntry(PmtSid, pPid)); + if (SidRequest > 0) + pPid->Inc(); } } if (sectionSyncer.Processed(pat.getSectionNumber(), pat.getLastSectionNumber())) { // 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); + for (cPmtPidEntry *pPid = pmtPidList.First(); pPid; pPid = pmtPidList.Next(pPid)) { + if (pPid->Count() == 0) { + pPid->SetState(); + activePmt = pPid; + timer.Set(PMT_SCAN_TIMEOUT); + break; + } + } + if (NeedsSetStatus) + SetStatus(true); } } } } else if (Tid == SI::TableIdPMT && Source() && Transponder()) { - timer.Set(PMT_SCAN_TIMEOUT); SI::PMT pmt(Data, false); if (!pmt.CheckCRCAndParse()) return; @@ -731,6 +843,5 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length if (activePmt) DBGLOG("PMT timeout Pid %d", activePmt->Pid()); SwitchToNextPmtPid(); - timer.Set(PMT_SCAN_TIMEOUT); } } diff --git a/pat.h b/pat.h index 09efe4bb..84f263ea 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.2 2020/06/19 12:19:15 kls Exp $ + * $Id: pat.h 5.1 2021/06/08 14:57:26 kls Exp $ */ #ifndef __PAT_H @@ -16,27 +16,33 @@ class cPmtPidEntry; class cPmtSidEntry; +class cPmtSidRequest; class cPatFilter : public cFilter { private: cMutex mutex; cTimeMs timer; int patVersion; - int sid; cPmtPidEntry *activePmt; cList pmtPidList; cList pmtSidList; + cList pmtSidRequestList; + int source; + int transponder; cSectionSyncer sectionSyncer; + bool TransponderChanged(void); bool PmtPidComplete(int PmtPid); void PmtPidReset(int PmtPid); bool PmtVersionChanged(int PmtPid, int Sid, int Version, bool SetNewVersion = false); + int NumSidRequests(int Sid); void SwitchToNextPmtPid(void); protected: virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length); public: cPatFilter(void); - virtual void SetStatus(bool On); - void Trigger(int Sid = -1); + void Trigger(int); // triggers reading the PMT PIDs that are currently not requested (dummy parameter for backwards compatibility, value is ignored) + void Request(int Sid); // requests permanent reading of the PMT PID for this SID + void Release(int Sid); // releases permanent reading of the PMT PID for this SID }; void GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, cDynamicBuffer &Buffer, int EsPid);