From 36a833053b4b1d51ffe51ca75d06478819e92eeb Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Tue, 16 Mar 2021 15:10:54 +0100 Subject: [PATCH] Improved cSectionSyncer --- HISTORY | 8 +++++++- eit.c | 43 ++++++++++++++++++++++++++++++++----------- eit.h | 23 ++++++++++++++++++++--- filter.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++-- filter.h | 37 ++++++++++++++++++++++++++++++++++--- nit.c | 10 ++++------ pat.c | 6 +++--- sdt.c | 10 ++++------ 8 files changed, 155 insertions(+), 35 deletions(-) diff --git a/HISTORY b/HISTORY index c9adec0f..c1b1e5c9 100644 --- a/HISTORY +++ b/HISTORY @@ -9578,7 +9578,7 @@ Video Disk Recorder Revision History given (reported by Manuel Reimer). - Fixed handling $(PKG_CONFIG) in newplugin (thanks to Winfried Köhler). -2021-01-19: +2021-03-16: - Fixed strreplace() to handle NULL strings (reported by Jürgen Schneider). - Somewhere down the road the 'x' bit of Doxyfile.filter got lost, so the @@ -9605,3 +9605,9 @@ Video Disk Recorder Revision History Jürgen Schneider). - Added some missing user command calls for copying, renaming and moving recordings (thanks to Peter Bieringer). +- Improved cSectionSyncer to make sure that no sections are missed, and to allow + handling partially used segments (as in the EIT) and processing sections in random + order. Segment syncing is now done with the two member functions Check() and + Processed(). The old functions Sync() and Repeat() are deprecated and may be + removed in a future version. See the comments in filter.h for a description on + how to use these new function. diff --git a/eit.c b/eit.c index 9c3f6f61..e1f52993 100644 --- a/eit.c +++ b/eit.c @@ -8,7 +8,7 @@ * Robert Schneider and Rolf Hakenes . * Adapted to 'libsi' for VDR 1.3.0 by Marcel Wiesweg . * - * $Id: eit.c 4.11 2020/11/28 21:45:05 kls Exp $ + * $Id: eit.c 5.1 2021/03/16 15:10:54 kls Exp $ */ #include "eit.h" @@ -22,6 +22,28 @@ #define DBGEIT 0 +// --- cEitTables ------------------------------------------------------------ + +bool cEitTables::Check(uchar TableId, uchar Version, int SectionNumber) +{ + int ti = Index(TableId); + return sectionSyncer[ti].Check(Version, SectionNumber); +} + +bool cEitTables::Processed(uchar TableId, uchar LastTableId, int SectionNumber, int LastSectionNumber, int SegmentLastSectionNumber) +{ + int ti = Index(TableId); + int LastIndex = Index(LastTableId); + if (sectionSyncer[ti].Processed(SectionNumber, LastSectionNumber, SegmentLastSectionNumber)) { + for (int i = 0; i <= LastIndex; i++) { + if (!sectionSyncer[i].Complete()) + return false; + } + return true; // all tables have been processed + } + return false; +} + // --- cEIT ------------------------------------------------------------------ class cEIT : public SI::EIT { @@ -34,13 +56,13 @@ cEIT::cEIT(cSectionSyncerHash &SectionSyncerHash, int Source, u_char Tid, const { if (!CheckCRCAndParse()) return; - int HashId = Tid + (getServiceId() << 8); - cSectionSyncerEntry *SectionSyncerEntry = SectionSyncerHash.Get(HashId); - if (!SectionSyncerEntry) { - SectionSyncerEntry = new cSectionSyncerEntry; - SectionSyncerHash.Add(SectionSyncerEntry, HashId); + int HashId = getServiceId(); + cEitTables *EitTables = SectionSyncerHash.Get(HashId); + if (!EitTables) { + EitTables = new cEitTables; + SectionSyncerHash.Add(EitTables, HashId); } - bool Process = SectionSyncerEntry->Sync(getVersionNumber(), getSectionNumber(), getLastSectionNumber()); + bool Process = EitTables->Check(Tid, getVersionNumber(), getSectionNumber()); if (Tid != 0x4E && !Process) // we need to set the 'seen' tag to watch the running status of the present/following event return; @@ -50,10 +72,8 @@ cEIT::cEIT(cSectionSyncerHash &SectionSyncerHash, int Source, u_char Tid, const cStateKey ChannelsStateKey; cChannels *Channels = cChannels::GetChannelsWrite(ChannelsStateKey, 10); - if (!Channels) { - SectionSyncerEntry->Repeat(); // let's not miss any section of the EIT + if (!Channels) return; - } tChannelID channelID(Source, getOriginalNetworkId(), getTransportStreamId(), getServiceId()); cChannel *Channel = Channels->GetByChannelID(channelID, true); if (!Channel || EpgHandlers.IgnoreChannel(Channel)) { @@ -64,7 +84,6 @@ cEIT::cEIT(cSectionSyncerHash &SectionSyncerHash, int Source, u_char Tid, const cStateKey SchedulesStateKey; cSchedules *Schedules = cSchedules::GetSchedulesWrite(SchedulesStateKey, 10); if (!Schedules) { - SectionSyncerEntry->Repeat(); // let's not miss any section of the EIT ChannelsStateKey.Remove(false); return; } @@ -361,6 +380,8 @@ cEIT::cEIT(cSectionSyncerHash &SectionSyncerHash, int Source, u_char Tid, const EpgHandlers.DropOutdated(pSchedule, SegmentStart, SegmentEnd, Tid, getVersionNumber()); pSchedule->SetModified(); } + if (Process) + EitTables->Processed(Tid, getLastTableId(), getSectionNumber(), getLastSectionNumber(), getSegmentLastSectionNumber()); SchedulesStateKey.Remove(Modified); ChannelsStateKey.Remove(ChannelsModified); EpgHandlers.EndSegmentTransfer(Modified); diff --git a/eit.h b/eit.h index c93aabc3..aedf0b56 100644 --- a/eit.h +++ b/eit.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: eit.h 4.2 2017/05/08 21:10:29 kls Exp $ + * $Id: eit.h 5.1 2021/03/16 15:10:54 kls Exp $ */ #ifndef __EIT_H @@ -13,9 +13,26 @@ #include "filter.h" #include "tools.h" -class cSectionSyncerEntry : public cListObject, public cSectionSyncer {}; +#define NUM_EIT_TABLES 17 -class cSectionSyncerHash : public cHash { +// Event information (or EPG) is broadcast in tables 0x4E and 0x4F for "present/following" events on +// "this transponder" (0x4E) and "other transponders" (0x4F), as well as 0x50-0x5F ("all events on this +// transponder) and 0x60-0x6F ("all events on other transponders). Since it's either "this" or "other", +// we only use one section syncer for 0x4E/0x4F and 16 syncers for either 0x5X or 0x6X. + +class cEitTables : public cListObject { +private: + cSectionSyncerRandom sectionSyncer[NUM_EIT_TABLES]; // for tables 0x4E/0x4F and 0x50-0x5F/0x60-0x6F + bool complete; + int Index(uchar TableId) { return (TableId < 0x50) ? 0 : (TableId & 0x0F) + 1; } +public: + cEitTables(void) { complete = false; } + bool Check(uchar TableId, uchar Version, int SectionNumber); + bool Processed(uchar TableId, uchar LastTableId, int SectionNumber, int LastSectionNumber, int SegmentLastSectionNumber = -1); + bool Complete(void) { return complete; } + }; + +class cSectionSyncerHash : public cHash { public: cSectionSyncerHash(void) : cHash(HASHSIZE, true) {}; }; diff --git a/filter.c b/filter.c index 742041a7..e1abd574 100644 --- a/filter.c +++ b/filter.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: filter.c 4.3 2017/05/09 08:37:23 kls Exp $ + * $Id: filter.c 5.1 2021/03/16 15:10:54 kls Exp $ */ #include "filter.h" @@ -12,8 +12,9 @@ // --- cSectionSyncer -------------------------------------------------------- -cSectionSyncer::cSectionSyncer(void) +cSectionSyncer::cSectionSyncer(bool Random) { + random = Random; Reset(); } @@ -23,9 +24,56 @@ void cSectionSyncer::Reset(void) currentSection = -1; synced = false; complete = false; + segments = 0; memset(sections, 0x00, sizeof(sections)); } +bool cSectionSyncer::Check(uchar Version, int SectionNumber) +{ + if (Version != currentVersion) { + Reset(); + currentVersion = Version; + } + if (complete) + return false; + if (!random) { + if (!synced) { + if (SectionNumber == 0) { + currentSection = 0; + synced = true; + } + else + return false; + } + if (SectionNumber != currentSection) + return false; + } + return !GetSectionFlag(SectionNumber); +} + +bool cSectionSyncer::Processed(int SectionNumber, int LastSectionNumber, int SegmentLastSectionNumber) +{ + SetSectionFlag(SectionNumber, true); // the flag for this section + if (!random) + currentSection++; // expect the next section + int Index = SectionNumber / 8; // the segment (byte) in which this section lies + uchar b = 0xFF; // all sections in this segment + if (SegmentLastSectionNumber < 0 && Index == LastSectionNumber / 8) + SegmentLastSectionNumber = LastSectionNumber; + if (SegmentLastSectionNumber >= 0) { + b >>= 7 - (SegmentLastSectionNumber & 0x07); // limits them up to the last section in this segment + if (!random && SectionNumber == SegmentLastSectionNumber) + currentSection = (SectionNumber + 8) & ~0x07; // expect first section of next segment + } + if (sections[Index] == b) // all expected sections in this segment have been received + segments |= 1 << Index; // so we set the respective bit in the segments flags + uint32_t s = 0xFFFFFFFF; // all segments + s >>= 31 - (LastSectionNumber / 8); // limits them up to the last expected segment + complete = segments == s; + return complete; +} + +#if DEPRECATED_SECTIONSYNCER_SYNC_REPEAT void cSectionSyncer::Repeat(void) { SetSectionFlag(currentSection, false); @@ -52,6 +100,7 @@ bool cSectionSyncer::Sync(uchar Version, int Number, int LastNumber) complete = true; return Result; } +#endif // --- cFilterData ----------------------------------------------------------- diff --git a/filter.h b/filter.h index b8198345..deb75162 100644 --- a/filter.h +++ b/filter.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: filter.h 4.3 2017/05/09 08:37:23 kls Exp $ + * $Id: filter.h 5.1 2021/03/16 15:10:54 kls Exp $ */ #ifndef __FILTER_H @@ -13,21 +13,52 @@ #include #include "tools.h" +#define DEPRECATED_SECTIONSYNCER_SYNC_REPEAT 1 + class cSectionSyncer { private: int currentVersion; int currentSection; + bool random; bool synced; bool complete; + uint32_t segments; // bit flags for the 32 segments uchar sections[32]; // holds 32 * 8 = 256 bits, as flags for the sections void SetSectionFlag(uchar Section, bool On) { if (On) sections[Section / 8] |= (1 << (Section % 8)); else sections[Section / 8] &= ~(1 << (Section % 8)); } bool GetSectionFlag(uchar Section) { return sections[Section / 8] & (1 << (Section % 8)); } public: - cSectionSyncer(void); + cSectionSyncer(bool Random = false); + ///< Sets up a new section syncer. + ///< Call Check() to see whether a given section needs processing. Once the section + ///< has been processed, call Processed() to mark it as such. If, for any reason, + ///< processing is not completed after calling Check(), nothing special needs to be + ///< done. Just don't call Processed() and a later call to Check() with the same + ///< SectionNumber will return true again. + ///< If Random is true, sections can be processed in random order, not necessarily + ///< starting with section 0. void Reset(void); - void Repeat(void); + bool Check(uchar Version, int SectionNumber); + ///< Returns true if Version is not the current version, or the given SectionNumber has not + ///< been marked as processed, yet. Sections are handled in ascending order, starting at 0, + ///< unless Random is true in the constructor call. + bool Processed(int SectionNumber, int LastSectionNumber, int SegmentLastSectionNumber = -1); + ///< Marks the given SectionNumber as processed. + ///< LastSectionNumber is used to determine whether all sections have been processed. + ///< SegmentLastSectionNumber can be given to handle partially filled segments (like, + ///< for instance in the EIT). + ///< Returns true if all sections have been processed. bool Complete(void) { return complete; } + ///< Returns true if all sections have been processed. +#if DEPRECATED_SECTIONSYNCER_SYNC_REPEAT + void Repeat(void); bool Sync(uchar Version, int Number, int LastNumber); +#endif + }; + +class cSectionSyncerRandom : public cSectionSyncer { + ///< Helper class for having an array of random section syncers. +public: + cSectionSyncerRandom(void): cSectionSyncer(true) {} }; class cFilterData : public cListObject { diff --git a/nit.c b/nit.c index 785aba52..980a3b2d 100644 --- a/nit.c +++ b/nit.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: nit.c 4.9 2019/05/31 13:25:00 kls Exp $ + * $Id: nit.c 5.1 2021/03/16 15:10:54 kls Exp $ */ #include "nit.h" @@ -43,7 +43,7 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length SI::NIT nit(Data, false); if (!nit.CheckCRCAndParse()) return; - if (!sectionSyncer.Sync(nit.getVersionNumber(), nit.getSectionNumber(), nit.getLastSectionNumber())) + if (!sectionSyncer.Check(nit.getVersionNumber(), nit.getSectionNumber())) return; if (DebugNit) { char NetworkName[MAXNETWORKNAME] = ""; @@ -63,10 +63,8 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length } cStateKey StateKey; cChannels *Channels = cChannels::GetChannelsWrite(StateKey, 10); - if (!Channels) { - sectionSyncer.Repeat(); // let's not miss any section of the NIT + if (!Channels) return; - } bool ChannelsModified = false; SI::NIT::TransportStream ts; for (SI::Loop::Iterator it; nit.transportStreamLoop.getNext(ts, it); ) { @@ -371,7 +369,7 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length delete d; } } - if (nit.getSectionNumber() == nit.getLastSectionNumber()) { + if (sectionSyncer.Processed(nit.getSectionNumber(), nit.getLastSectionNumber())) { dbgnit(" trigger sdtFilter for current tp %d\n", Transponder()); sdtFilter->Trigger(Source()); } diff --git a/pat.c b/pat.c index 72164c49..39009b14 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.9 2020/12/18 14:51:57 kls Exp $ + * $Id: pat.c 5.1 2021/03/16 15:10:54 kls Exp $ */ #include "pat.h" @@ -424,7 +424,7 @@ 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 (sectionSyncer.Sync(pat.getVersionNumber(), pat.getSectionNumber(), pat.getLastSectionNumber())) { + if (sectionSyncer.Check(pat.getVersionNumber(), pat.getSectionNumber())) { DBGLOG("PAT %d %d -> %d %d/%d", Transponder(), patVersion, pat.getVersionNumber(), pat.getSectionNumber(), pat.getLastSectionNumber()); if (pat.getVersionNumber() != patVersion) { if (pat.getLastSectionNumber() > 0) @@ -457,7 +457,7 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length } } } - if (sectionSyncer.Complete()) { // all PAT sections done + 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) diff --git a/sdt.c b/sdt.c index e6a8f40f..7634761e 100644 --- a/sdt.c +++ b/sdt.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: sdt.c 4.8 2020/06/16 14:50:07 kls Exp $ + * $Id: sdt.c 5.1 2021/03/16 15:10:54 kls Exp $ */ #include "sdt.h" @@ -82,14 +82,12 @@ void cSdtFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length } if (!(source && Transponder())) return; - if (!sectionSyncer.Sync(sdt.getVersionNumber(), sdt.getSectionNumber(), sdt.getLastSectionNumber())) + if (!sectionSyncer.Check(sdt.getVersionNumber(), sdt.getSectionNumber())) return; cStateKey StateKey; cChannels *Channels = cChannels::GetChannelsWrite(StateKey, 10); - if (!Channels) { - sectionSyncer.Repeat(); // let's not miss any section of the SDT + if (!Channels) return; - } dbgsdt("SDT: %2d %2d %2d %s %d\n", sdt.getVersionNumber(), sdt.getSectionNumber(), sdt.getLastSectionNumber(), *cSource::ToString(source), Transponder()); bool ChannelsModified = false; SI::SDT::Service SiSdtService; @@ -203,7 +201,7 @@ void cSdtFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length delete LinkChannels; } } - if (sdt.getSectionNumber() == sdt.getLastSectionNumber()) { + if (sectionSyncer.Processed(sdt.getSectionNumber(), sdt.getLastSectionNumber())) { if (Setup.UpdateChannels == 1 || Setup.UpdateChannels >= 3) { ChannelsModified |= Channels->MarkObsoleteChannels(source, sdt.getOriginalNetworkId(), sdt.getTransportStreamId()); if (source != Source())