Improved PAT/PMT scanning to speed up initial tuning to encrypted channels on transponders with many PAT entries

This commit is contained in:
Klaus Schmidinger 2014-02-18 14:14:33 +01:00
parent 3736508d95
commit 4709eb94fa
6 changed files with 103 additions and 65 deletions

View File

@ -3203,3 +3203,7 @@ Eike Sauer <EikeSauer@t-online.de>
Christian Paulick <cpaulick@xeatre.tv> Christian Paulick <cpaulick@xeatre.tv>
for reporting a problem with frame detection in MPEG-2 streams that have "bottom fields" for reporting a problem with frame detection in MPEG-2 streams that have "bottom fields"
or varying GOP structures or varying GOP structures
Mariusz Bialonczyk <manio@skyboo.net>
for reporting that acquiring the CA descriptors takes way too long on transponders
with many PAT entries, and his help in debugging this

View File

@ -7892,3 +7892,5 @@ Video Disk Recorder Revision History
- Fixed a possible crash in the OSD demo (reported by Christopher Reimer). - Fixed a possible crash in the OSD demo (reported by Christopher Reimer).
- Fixed learning keyboard remote control codes (thanks to Lars Hanisch). - Fixed learning keyboard remote control codes (thanks to Lars Hanisch).
- Fixed the replay progress display for very long recordings. - Fixed the replay progress display for very long recordings.
- Improved PAT/PMT scanning to speed up initial tuning to encrypted channels on
transponders with many PAT entries (reported by Mariusz Bialonczyk).

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: device.c 2.74.1.2 2013/08/22 10:35:30 kls Exp $ * $Id: device.c 2.74.1.3 2014/02/18 14:12:08 kls Exp $
*/ */
#include "device.h" #include "device.h"
@ -785,6 +785,7 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
if (SetChannelDevice(Channel, LiveView)) { if (SetChannelDevice(Channel, LiveView)) {
// Start section handling: // Start section handling:
if (sectionHandler) { if (sectionHandler) {
patFilter->Trigger(Channel->Sid());
sectionHandler->SetChannel(Channel); sectionHandler->SetChannel(Channel);
sectionHandler->SetStatus(true); sectionHandler->SetStatus(true);
} }

133
pat.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: pat.c 2.19 2012/11/25 14:12:21 kls Exp $ * $Id: pat.c 2.19.1.1 2014/02/18 14:12:17 kls Exp $
*/ */
#include "pat.h" #include "pat.h"
@ -12,9 +12,8 @@
#include "channels.h" #include "channels.h"
#include "libsi/section.h" #include "libsi/section.h"
#include "libsi/descriptor.h" #include "libsi/descriptor.h"
#include "thread.h"
#define PMT_SCAN_TIMEOUT 10 // seconds #define PMT_SCAN_TIMEOUT 1000 // ms
// --- cCaDescriptor --------------------------------------------------------- // --- cCaDescriptor ---------------------------------------------------------
@ -229,94 +228,115 @@ int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSy
// --- cPatFilter ------------------------------------------------------------ // --- cPatFilter ------------------------------------------------------------
//#define DEBUG_PAT_PMT
#ifdef DEBUG_PAT_PMT
#define DBGLOG(a...) { cString s = cString::sprintf(a); fprintf(stderr, "%s\n", *s); dsyslog("%s", *s); }
#else
#define DBGLOG(a...)
#endif
cPatFilter::cPatFilter(void) cPatFilter::cPatFilter(void)
{ {
pmtIndex = 0; Trigger(0);
pmtPid = 0;
pmtSid = 0;
lastPmtScan = 0;
numPmtEntries = 0;
Set(0x00, 0x00); // PAT Set(0x00, 0x00); // PAT
} }
void cPatFilter::SetStatus(bool On) void cPatFilter::SetStatus(bool On)
{ {
cMutexLock MutexLock(&mutex);
DBGLOG("PAT filter set status %d", On);
cFilter::SetStatus(On); cFilter::SetStatus(On);
pmtIndex = 0; Trigger();
pmtPid = 0;
pmtSid = 0;
lastPmtScan = 0;
numPmtEntries = 0;
} }
void cPatFilter::Trigger(void) void cPatFilter::Trigger(int Sid)
{ {
cMutexLock MutexLock(&mutex);
patVersion = -1;
pmtIndex = -1;
numPmtEntries = 0; numPmtEntries = 0;
if (Sid >= 0) {
sid = Sid;
DBGLOG("PAT filter trigger SID %d", Sid);
}
} }
bool cPatFilter::PmtVersionChanged(int PmtPid, int Sid, int Version) bool cPatFilter::PmtVersionChanged(int PmtPid, int Sid, int Version, bool SetNewVersion)
{ {
uint64_t v = Version; int Id = MakePmtId(PmtPid, Sid);
v <<= 32;
uint64_t id = (PmtPid | (Sid << 16)) & 0x00000000FFFFFFFFLL;
for (int i = 0; i < numPmtEntries; i++) { for (int i = 0; i < numPmtEntries; i++) {
if ((pmtVersion[i] & 0x00000000FFFFFFFFLL) == id) { if (pmtId[i] == Id) {
bool Changed = (pmtVersion[i] & 0x000000FF00000000LL) != v; if (pmtVersion[i] != Version) {
if (Changed) if (SetNewVersion)
pmtVersion[i] = id | v; pmtVersion[i] = Version;
return Changed; else
DBGLOG("PMT %d %2d %5d %2d -> %2d", Transponder(), i, PmtPid, pmtVersion[i], Version);
return true;
}
break;
} }
} }
if (numPmtEntries < MAXPMTENTRIES) return false;
pmtVersion[numPmtEntries++] = id | v; }
return true;
void cPatFilter::SwitchToNextPmtPid(void)
{
if (pmtIndex >= 0) {
Del(GetPmtPid(pmtIndex), SI::TableIdPMT);
pmtIndex = (pmtIndex + 1) % numPmtEntries;
Add(GetPmtPid(pmtIndex), SI::TableIdPMT);
}
} }
void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length) void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
{ {
cMutexLock MutexLock(&mutex);
if (Pid == 0x00) { if (Pid == 0x00) {
if (Tid == 0x00) { if (Tid == SI::TableIdPAT) {
if (pmtPid && time(NULL) - lastPmtScan > PMT_SCAN_TIMEOUT) { SI::PAT pat(Data, false);
Del(pmtPid, 0x02); if (!pat.CheckCRCAndParse())
pmtPid = 0; return;
pmtIndex++; if (pat.getVersionNumber() != patVersion) {
lastPmtScan = time(NULL); DBGLOG("PAT %d/%d %d %d -> %d", pat.getSectionNumber(), pat.getLastSectionNumber(), Transponder(), patVersion, pat.getVersionNumber());
} if (pmtIndex >= 0) {
if (!pmtPid) { Del(GetPmtPid(pmtIndex), SI::TableIdPMT);
SI::PAT pat(Data, false); pmtIndex = -1;
if (!pat.CheckCRCAndParse()) }
return; numPmtEntries = 0;
SI::PAT::Association assoc; SI::PAT::Association assoc;
int Index = 0;
for (SI::Loop::Iterator it; pat.associationLoop.getNext(assoc, it); ) { for (SI::Loop::Iterator it; pat.associationLoop.getNext(assoc, it); ) {
if (!assoc.isNITPid()) { if (!assoc.isNITPid() && numPmtEntries < MAXPMTENTRIES) {
if (Index++ >= pmtIndex && Channels.GetByServiceID(Source(), Transponder(), assoc.getServiceId())) { DBGLOG(" PMT pid %2d %5d SID %5d", numPmtEntries, assoc.getPid(), assoc.getServiceId());
pmtPid = assoc.getPid(); pmtId[numPmtEntries] = MakePmtId(assoc.getPid(), assoc.getServiceId());
pmtSid = assoc.getServiceId(); pmtVersion[numPmtEntries] = -1;
Add(pmtPid, 0x02); if (sid == assoc.getServiceId()) {
break; pmtIndex = numPmtEntries;
DBGLOG("sid = %d pmtIndex = %d", sid, pmtIndex);
} }
numPmtEntries++;
} }
} }
if (!pmtPid) if (numPmtEntries > 0 && pmtIndex < 0)
pmtIndex = 0; pmtIndex = 0;
Add(GetPmtPid(pmtIndex), SI::TableIdPMT);
patVersion = pat.getVersionNumber();
timer.Set(PMT_SCAN_TIMEOUT);
} }
} }
} }
else if (Pid == pmtPid && Tid == SI::TableIdPMT && Source() && Transponder()) { else if (Tid == SI::TableIdPMT && Source() && Transponder()) {
timer.Set(PMT_SCAN_TIMEOUT);
SI::PMT pmt(Data, false); SI::PMT pmt(Data, false);
if (!pmt.CheckCRCAndParse()) if (!pmt.CheckCRCAndParse())
return; return;
if (pmt.getServiceId() != pmtSid) if (!PmtVersionChanged(Pid, pmt.getTableIdExtension(), pmt.getVersionNumber())) {
return; // skip broken PMT records SwitchToNextPmtPid();
if (!PmtVersionChanged(pmtPid, pmt.getTableIdExtension(), pmt.getVersionNumber())) {
lastPmtScan = 0; // this triggers the next scan
return; return;
} }
if (!Channels.Lock(true, 10)) { if (!Channels.Lock(true, 10))
numPmtEntries = 0; // to make sure we try again
return; return;
} PmtVersionChanged(Pid, pmt.getTableIdExtension(), pmt.getVersionNumber(), true);
SwitchToNextPmtPid();
cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), pmt.getServiceId()); cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), pmt.getServiceId());
if (Channel) { if (Channel) {
SI::CaDescriptor *d; SI::CaDescriptor *d;
@ -552,7 +572,12 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
} }
Channel->SetCaDescriptors(CaDescriptorHandler.AddCaDescriptors(CaDescriptors)); Channel->SetCaDescriptors(CaDescriptorHandler.AddCaDescriptors(CaDescriptors));
} }
lastPmtScan = 0; // this triggers the next scan
Channels.Unlock(); Channels.Unlock();
} }
if (timer.TimedOut()) {
if (pmtIndex >= 0)
DBGLOG("PMT timeout %d", pmtIndex);
SwitchToNextPmtPid();
timer.Set(PMT_SCAN_TIMEOUT);
}
} }

20
pat.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: pat.h 2.3 2013/02/16 15:20:24 kls Exp $ * $Id: pat.h 2.3.1.1 2014/02/18 14:12:24 kls Exp $
*/ */
#ifndef __PAT_H #ifndef __PAT_H
@ -12,24 +12,30 @@
#include <stdint.h> #include <stdint.h>
#include "filter.h" #include "filter.h"
#include "thread.h"
#define MAXPMTENTRIES 64 #define MAXPMTENTRIES 64
class cPatFilter : public cFilter { class cPatFilter : public cFilter {
private: private:
time_t lastPmtScan; cMutex mutex;
cTimeMs timer;
int patVersion;
int pmtIndex; int pmtIndex;
int pmtPid; int pmtId[MAXPMTENTRIES];
int pmtSid; int pmtVersion[MAXPMTENTRIES];
uint64_t pmtVersion[MAXPMTENTRIES];
int numPmtEntries; int numPmtEntries;
bool PmtVersionChanged(int PmtPid, int Sid, int Version); int sid;
int GetPmtPid(int Index) { return pmtId[Index] & 0x0000FFFF; }
int MakePmtId(int PmtPid, int Sid) { return PmtPid | (Sid << 16); }
bool PmtVersionChanged(int PmtPid, int Sid, int Version, bool SetNewVersion = false);
void SwitchToNextPmtPid(void);
protected: protected:
virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length); virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
public: public:
cPatFilter(void); cPatFilter(void);
virtual void SetStatus(bool On); virtual void SetStatus(bool On);
void Trigger(void); void Trigger(int Sid = -1);
}; };
int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, int EsPid); int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, int EsPid);

6
sdt.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: sdt.c 2.5 2010/05/16 14:23:21 kls Exp $ * $Id: sdt.c 2.5.1.1 2014/02/18 14:12:33 kls Exp $
*/ */
#include "sdt.h" #include "sdt.h"
@ -92,7 +92,7 @@ void cSdtFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
} }
else if (*pn && Setup.UpdateChannels >= 4) { else if (*pn && Setup.UpdateChannels >= 4) {
channel = Channels.NewChannel(Channel(), pn, ps, pp, sdt.getOriginalNetworkId(), sdt.getTransportStreamId(), SiSdtService.getServiceId()); channel = Channels.NewChannel(Channel(), pn, ps, pp, sdt.getOriginalNetworkId(), sdt.getTransportStreamId(), SiSdtService.getServiceId());
patFilter->Trigger(); patFilter->Trigger(SiSdtService.getServiceId());
} }
} }
default: ; default: ;
@ -118,7 +118,7 @@ void cSdtFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
cChannel *link = Channels.GetByChannelID(tChannelID(Source(), Service.getOriginalNetworkId(), Service.getTransportStream(), Service.getServiceId())); cChannel *link = Channels.GetByChannelID(tChannelID(Source(), Service.getOriginalNetworkId(), Service.getTransportStream(), Service.getServiceId()));
if (!link && Setup.UpdateChannels >= 4) { if (!link && Setup.UpdateChannels >= 4) {
link = Channels.NewChannel(Channel(), "NVOD", "", "", Service.getOriginalNetworkId(), Service.getTransportStream(), Service.getServiceId()); link = Channels.NewChannel(Channel(), "NVOD", "", "", Service.getOriginalNetworkId(), Service.getTransportStream(), Service.getServiceId());
patFilter->Trigger(); patFilter->Trigger(Service.getServiceId());
} }
if (link) { if (link) {
if (!LinkChannels) if (!LinkChannels)