Improved cSectionSyncer

This commit is contained in:
Klaus Schmidinger 2021-03-16 15:10:54 +01:00
parent ad35c9c2d3
commit 36a833053b
8 changed files with 155 additions and 35 deletions

View File

@ -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.

43
eit.c
View File

@ -8,7 +8,7 @@
* Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
* Adapted to 'libsi' for VDR 1.3.0 by Marcel Wiesweg <marcel.wiesweg@gmx.de>.
*
* $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);

23
eit.h
View File

@ -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<cSectionSyncerEntry> {
// 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<cEitTables> {
public:
cSectionSyncerHash(void) : cHash(HASHSIZE, true) {};
};

View File

@ -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 -----------------------------------------------------------

View File

@ -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 <sys/types.h>
#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 {

10
nit.c
View File

@ -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());
}

6
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.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)

10
sdt.c
View File

@ -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())