mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
Improved cSectionSyncer
This commit is contained in:
parent
ad35c9c2d3
commit
36a833053b
8
HISTORY
8
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.
|
||||
|
43
eit.c
43
eit.c
@ -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
23
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<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) {};
|
||||
};
|
||||
|
53
filter.c
53
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 -----------------------------------------------------------
|
||||
|
||||
|
37
filter.h
37
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 <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
10
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());
|
||||
}
|
||||
|
6
pat.c
6
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)
|
||||
|
10
sdt.c
10
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())
|
||||
|
Loading…
Reference in New Issue
Block a user