1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

No longer permanently looping through PMT PIDs, which caused problems with some SatIP receivers

This commit is contained in:
Klaus Schmidinger 2021-06-08 14:57:26 +02:00
parent 58e21d8e37
commit 7b1c097958
5 changed files with 193 additions and 62 deletions

View File

@ -2343,6 +2343,7 @@ Andr
for reporting a bug in selecting the last replayed recording in the Recordings menu 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 in case there are folders and plain recordings with names that differ only in
non-alphanumeric characters non-alphanumeric characters
for reporting a problem with permanently looping through PMT PIDs on a SatIP receiver
Jürgen Schilling <juergen_schilling@web.de> Jürgen Schilling <juergen_schilling@web.de>
for reporting that color buttons were displayed in the recording info menu if it for reporting that color buttons were displayed in the recording info menu if it

View File

@ -9706,7 +9706,7 @@ Video Disk Recorder Revision History
order to restore this functionality. However, it is recommended to use the function order to restore this functionality. However, it is recommended to use the function
with the TimerActive parameter instead. with the TimerActive parameter instead.
2021-05-26: 2021-06-08:
- cRecordingInfo::Errors() now returns -1 for old recordings; added a missing 'const' - cRecordingInfo::Errors() now returns -1 for old recordings; added a missing 'const'
(suggested by Christoph Haubrich). (suggested by Christoph Haubrich).
@ -9714,3 +9714,5 @@ Video Disk Recorder Revision History
- Added missing initialization of cRecorder::lastErrors. - Added missing initialization of cRecorder::lastErrors.
- Now using __cplusplus instead of DISABLE_TEMPLATES_COLLIDING_WITH_STL, and using - 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). 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).

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 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" #include "device.h"
@ -858,6 +858,13 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
cStatus::MsgChannelSwitch(this, 0, LiveView); cStatus::MsgChannelSwitch(this, 0, LiveView);
if (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(); StopReplay();
DELETENULL(liveSubtitle); DELETENULL(liveSubtitle);
DELETENULL(dvbSubtitleConverter); DELETENULL(dvbSubtitleConverter);
@ -898,8 +905,6 @@ 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) {
if (patFilter)
patFilter->Trigger(Channel->Sid());
sectionHandler->SetChannel(Channel); sectionHandler->SetChannel(Channel);
sectionHandler->SetStatus(true); sectionHandler->SetStatus(true);
} }
@ -913,6 +918,8 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
if (Result == scrOk) { if (Result == scrOk) {
if (LiveView && IsPrimaryDevice()) { if (LiveView && IsPrimaryDevice()) {
if (patFilter) // this is only for FF DVB cards!
patFilter->Request(Channel->Sid());
currentChannel = Channel->Number(); currentChannel = Channel->Number();
// Set the available audio tracks: // Set the available audio tracks:
ClrAvailableTracks(); 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); 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(); Start();
return true; return true;
} }
@ -1827,6 +1836,8 @@ void cDevice::Detach(cReceiver *Receiver)
else if (receiver[i]) else if (receiver[i])
receiversLeft = true; receiversLeft = true;
} }
if (patFilter && Receiver->ChannelID().Valid())
patFilter->Release(Receiver->ChannelID().Sid());
mutexReceiver.Unlock(); mutexReceiver.Unlock();
Receiver->device = NULL; Receiver->device = NULL;
Receiver->Activate(false); Receiver->Activate(false);

219
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 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" #include "pat.h"
@ -285,10 +285,20 @@ int GetPmtPid(int Source, int Transponder, int ServiceId)
class cPmtPidEntry : public cListObject { class cPmtPidEntry : public cListObject {
private: private:
int pid; 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: public:
cPmtPidEntry(int Pid); cPmtPidEntry(int Pid);
int Pid(void) { return 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; } int Complete(void) { return complete; }
void SetComplete(bool State) { complete = State; } void SetComplete(bool State) { complete = State; }
}; };
@ -296,6 +306,8 @@ public:
cPmtPidEntry::cPmtPidEntry(int Pid) cPmtPidEntry::cPmtPidEntry(int Pid)
{ {
pid = Pid; pid = Pid;
count = 0;
state = 0;
complete = false; complete = false;
} }
@ -309,7 +321,7 @@ private:
int version; int version;
bool received; bool received;
public: public:
cPmtSidEntry(int Sid, int Pid, cPmtPidEntry *PidEntry); cPmtSidEntry(int Sid, cPmtPidEntry *PidEntry);
int Sid(void) { return sid; } int Sid(void) { return sid; }
int Pid(void) { return pid; } int Pid(void) { return pid; }
cPmtPidEntry *PidEntry(void) { return pidEntry; } cPmtPidEntry *PidEntry(void) { return pidEntry; }
@ -319,15 +331,29 @@ public:
void SetReceived(bool State) { received = State; } void SetReceived(bool State) { received = State; }
}; };
cPmtSidEntry::cPmtSidEntry(int Sid, int Pid, cPmtPidEntry *PidEntry) cPmtSidEntry::cPmtSidEntry(int Sid, cPmtPidEntry *PidEntry)
{ {
sid = Sid; sid = Sid;
pid = Pid; pid = PidEntry->Pid();
pidEntry = PidEntry; pidEntry = PidEntry;
version = -1; version = -1;
received = false; 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 ------------------------------------------------------------ // --- cPatFilter ------------------------------------------------------------
//#define DEBUG_PAT_PMT //#define DEBUG_PAT_PMT
@ -339,32 +365,95 @@ cPmtSidEntry::cPmtSidEntry(int Sid, int Pid, cPmtPidEntry *PidEntry)
cPatFilter::cPatFilter(void) cPatFilter::cPatFilter(void)
{ {
Trigger(0); patVersion = -1;
activePmt = NULL;
transponder = 0;
source = 0;
Set(0x00, 0x00); // PAT Set(0x00, 0x00); // PAT
} }
void cPatFilter::SetStatus(bool On) bool cPatFilter::TransponderChanged(void)
{ {
cMutexLock MutexLock(&mutex); if (source != Source() || transponder != Transponder()) {
DBGLOG("PAT filter set status %d", On); DBGLOG("PAT filter transponder changed from %d/%d to %d/%d", source, transponder, Source(), Transponder());
cFilter::SetStatus(On); source = Source();
Trigger(); transponder = Transponder();
return true;
}
return false;
} }
void cPatFilter::Trigger(int Sid) void cPatFilter::Trigger(int)
{ {
cMutexLock MutexLock(&mutex); cMutexLock MutexLock(&mutex);
patVersion = -1; DBGLOG("PAT filter trigger");
sectionSyncer.Reset(); if (activePmt != pmtPidList.First()) {
if (Sid != 0 && activePmt) if (activePmt && activePmt->Count() == 0)
Del(activePmt->Pid(), SI::TableIdPMT); activePmt->ClrState();
activePmt = NULL; activePmt = pmtPidList.First();
if (Sid >= 0) { if (activePmt && activePmt->Count() == 0) {
sid = Sid; activePmt->SetState();
DBGLOG("PAT filter trigger SID %d", Sid); 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) bool cPatFilter::PmtPidComplete(int PmtPid)
{ {
for (cPmtSidEntry *se = pmtSidList.First(); se; se = pmtSidList.Next(se)) { for (cPmtSidEntry *se = pmtSidList.First(); se; se = pmtSidList.Next(se)) {
@ -377,15 +466,16 @@ bool cPatFilter::PmtPidComplete(int PmtPid)
void cPatFilter::PmtPidReset(int PmtPid) void cPatFilter::PmtPidReset(int PmtPid)
{ {
for (cPmtSidEntry *se = pmtSidList.First(); se; se = pmtSidList.Next(se)) { for (cPmtSidEntry *se = pmtSidList.First(); se; se = pmtSidList.Next(se)) {
if (se->Pid() == PmtPid) if (se->Pid() == PmtPid) {
se->SetReceived(false); se->SetReceived(false);
se->PidEntry()->SetComplete(false);
}
} }
} }
bool cPatFilter::PmtVersionChanged(int PmtPid, int Sid, int Version, bool SetNewVersion) bool cPatFilter::PmtVersionChanged(int PmtPid, int Sid, int Version, bool SetNewVersion)
{ {
int i = 0; for (cPmtSidEntry *se = pmtSidList.First(); se; se = pmtSidList.Next(se)) {
for (cPmtSidEntry *se = pmtSidList.First(); se; se = pmtSidList.Next(se), i++) {
if (se->Sid() == Sid && se->Pid() == PmtPid) { if (se->Sid() == Sid && se->Pid() == PmtPid) {
if (!se->Received()) { if (!se->Received()) {
se->SetReceived(true); se->SetReceived(true);
@ -395,7 +485,7 @@ bool cPatFilter::PmtVersionChanged(int PmtPid, int Sid, int Version, bool SetNew
if (SetNewVersion) if (SetNewVersion)
se->SetVersion(Version); se->SetVersion(Version);
else 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; return true;
} }
break; break;
@ -407,18 +497,39 @@ bool cPatFilter::PmtVersionChanged(int PmtPid, int Sid, int Version, bool SetNew
void cPatFilter::SwitchToNextPmtPid(void) void cPatFilter::SwitchToNextPmtPid(void)
{ {
if (activePmt) { if (activePmt) {
Del(activePmt->Pid(), SI::TableIdPMT); if (activePmt->Count() == 0)
if (!(activePmt = pmtPidList.Next(activePmt))) Del(activePmt->Pid(), SI::TableIdPMT);
activePmt = pmtPidList.First(); for (;;) {
PmtPidReset(activePmt->Pid()); activePmt = pmtPidList.Next(activePmt);
activePmt->SetComplete(false); if (!activePmt || activePmt->Count() == 0)
Add(activePmt->Pid(), SI::TableIdPMT); 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) void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
{ {
cMutexLock MutexLock(&mutex); 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 (Pid == 0x00) {
if (Tid == SI::TableIdPAT) { if (Tid == SI::TableIdPAT) {
SI::PAT pat(Data, false); 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; return;
if (sectionSyncer.Check(pat.getVersionNumber(), pat.getSectionNumber())) { if (sectionSyncer.Check(pat.getVersionNumber(), pat.getSectionNumber())) {
DBGLOG("PAT %d %d -> %d %d/%d", Transponder(), patVersion, pat.getVersionNumber(), pat.getSectionNumber(), pat.getLastSectionNumber()); 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.getVersionNumber() != patVersion) {
if (pat.getLastSectionNumber() > 0) if (NeedsSetStatus)
DBGLOG(" PAT %d: %d sections", Transponder(), pat.getLastSectionNumber() + 1); SetStatus(false); // deletes all PIDs from the filter
if (activePmt) { activePmt = NULL;
Del(activePmt->Pid(), SI::TableIdPMT);
activePmt = NULL;
}
pmtSidList.Clear(); pmtSidList.Clear();
pmtPidList.Clear(); pmtPidList.Clear();
patVersion = pat.getVersionNumber(); 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); ) { for (SI::Loop::Iterator it; pat.associationLoop.getNext(assoc, it); ) {
if (!assoc.isNITPid()) { if (!assoc.isNITPid()) {
int PmtPid = assoc.getPid(); int PmtPid = assoc.getPid();
int PmtSid = assoc.getServiceId();
cPmtPidEntry *pPid = NULL; cPmtPidEntry *pPid = NULL;
int PidIndex = 0; for (pPid = pmtPidList.First(); pPid; pPid = pmtPidList.Next(pPid)) {
for (pPid = pmtPidList.First(); pPid && pPid->Pid() != PmtPid; pPid = pmtPidList.Next(pPid)) if (pPid->Pid() == PmtPid)
PidIndex++; break;
}
int SidRequest = NumSidRequests(PmtSid);
DBGLOG(" PMT pid %5d SID %5d%s%s", PmtPid, PmtSid, SidRequest ? " R" : "", pPid ? " S" : "");
if (!pPid) { // new PMT Pid if (!pPid) { // new PMT Pid
pPid = new cPmtPidEntry(PmtPid); pPid = new cPmtPidEntry(PmtPid);
pmtPidList.Add(pPid); pmtPidList.Add(pPid);
} }
pmtSidList.Add(new cPmtSidEntry(assoc.getServiceId(), PmtPid, pPid)); pmtSidList.Add(new cPmtSidEntry(PmtSid, pPid));
DBGLOG(" PMT pid %2d/%2d %5d SID %5d", PidIndex, pmtSidList.Count() - 1, PmtPid, assoc.getServiceId()); if (SidRequest > 0)
if (sid == assoc.getServiceId()) { pPid->Inc();
activePmt = pPid;
DBGLOG("sid = %d pidIndex = %d", sid, PidIndex);
}
} }
} }
if (sectionSyncer.Processed(pat.getSectionNumber(), pat.getLastSectionNumber())) { // all PAT sections done if (sectionSyncer.Processed(pat.getSectionNumber(), pat.getLastSectionNumber())) { // all PAT sections done
if (pmtPidList.Count() != pmtSidList.Count()) for (cPmtPidEntry *pPid = pmtPidList.First(); pPid; pPid = pmtPidList.Next(pPid)) {
DBGLOG(" PAT %d: shared PMT PIDs", Transponder()); if (pPid->Count() == 0) {
if (pmtSidList.Count() && !activePmt) pPid->SetState();
activePmt = pmtPidList.First(); activePmt = pPid;
if (activePmt) timer.Set(PMT_SCAN_TIMEOUT);
Add(activePmt->Pid(), SI::TableIdPMT); break;
timer.Set(PMT_SCAN_TIMEOUT); }
}
if (NeedsSetStatus)
SetStatus(true);
} }
} }
} }
} }
else if (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;
@ -731,6 +843,5 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
if (activePmt) if (activePmt)
DBGLOG("PMT timeout Pid %d", activePmt->Pid()); DBGLOG("PMT timeout Pid %d", activePmt->Pid());
SwitchToNextPmtPid(); SwitchToNextPmtPid();
timer.Set(PMT_SCAN_TIMEOUT);
} }
} }

14
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 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 #ifndef __PAT_H
@ -16,27 +16,33 @@
class cPmtPidEntry; class cPmtPidEntry;
class cPmtSidEntry; class cPmtSidEntry;
class cPmtSidRequest;
class cPatFilter : public cFilter { class cPatFilter : public cFilter {
private: private:
cMutex mutex; cMutex mutex;
cTimeMs timer; cTimeMs timer;
int patVersion; int patVersion;
int sid;
cPmtPidEntry *activePmt; cPmtPidEntry *activePmt;
cList<cPmtPidEntry> pmtPidList; cList<cPmtPidEntry> pmtPidList;
cList<cPmtSidEntry> pmtSidList; cList<cPmtSidEntry> pmtSidList;
cList<cPmtSidRequest> pmtSidRequestList;
int source;
int transponder;
cSectionSyncer sectionSyncer; cSectionSyncer sectionSyncer;
bool TransponderChanged(void);
bool PmtPidComplete(int PmtPid); bool PmtPidComplete(int PmtPid);
void PmtPidReset(int PmtPid); void PmtPidReset(int PmtPid);
bool PmtVersionChanged(int PmtPid, int Sid, int Version, bool SetNewVersion = false); bool PmtVersionChanged(int PmtPid, int Sid, int Version, bool SetNewVersion = false);
int NumSidRequests(int Sid);
void SwitchToNextPmtPid(void); 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); void Trigger(int); // triggers reading the PMT PIDs that are currently not requested (dummy parameter for backwards compatibility, value is ignored)
void Trigger(int Sid = -1); 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); void GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, cDynamicBuffer &Buffer, int EsPid);