Skip to content

Commit f4de2c2

Browse files
authored
Merge pull request #3698 from WXbet/softcsa-threading
[SoftCSA] Threaded CW delivery and softcam reliability fixes
2 parents fca4fe6 + ccdce60 commit f4de2c2

File tree

14 files changed

+845
-158
lines changed

14 files changed

+845
-158
lines changed

lib/dvb/Makefile.inc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ dvb_libenigma_dvb_a_SOURCES = \
66
dvb/crc32.cpp \
77
dvb/csaengine.cpp \
88
dvb/csasession.cpp \
9+
dvb/cwhandler.cpp \
910
dvb/db.cpp \
1011
dvb/decoder.cpp \
1112
dvb/demux.cpp \
@@ -49,6 +50,7 @@ dvbinclude_HEADERS = \
4950
dvb/crc32.h \
5051
dvb/csaengine.h \
5152
dvb/csasession.h \
53+
dvb/cwhandler.h \
5254
dvb/db.h \
5355
dvb/decoder.h \
5456
dvb/demux.h \

lib/dvb/cahandler.cpp

Lines changed: 83 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
#include <lib/base/init_num.h>
1313

1414
#include <linux/dvb/ca.h>
15+
#include <lib/dvb/cwhandler.h>
1516
#include <map>
17+
#include <vector>
1618

17-
// Cache serviceId per DVB service reference to ensure OSCam always sees the same ID
19+
// Cache serviceId per DVB service reference to ensure softcam always sees the same ID
1820
// This prevents CW delivery issues when switching between StreamRelay and Live-TV
1921
static std::map<eServiceReferenceDVB, uint32_t> s_serviceId_cache;
2022

@@ -46,10 +48,10 @@ void ePMTClient::dataAvailable()
4648
{
4749
if (bytesAvailable() < 6) return;
4850
receivedLength = readBlock((char*)receivedHeader, 1);
49-
// check OSCam protocol version -> version 3 starts with 0xA5
51+
// check softcam protocol version -> version 3 starts with 0xA5
5052
if ((m_protocolVersion == 3 || m_protocolVersion == -1) && receivedHeader[0] == 0xA5)
5153
{
52-
// OSCam protocol 3: read 4 byte msgid + first byte of tag
54+
// Softcam protocol 3: read 4 byte msgid + first byte of tag
5355
readBlock((char*)receivedHeader, 5);
5456
receivedTag[0] = receivedHeader[4];
5557
}
@@ -164,7 +166,7 @@ bool ePMTClient::processCaSetDescrPacket()
164166
auto it = parent->m_service_caid.find(serviceId);
165167
if (it != parent->m_service_caid.end())
166168
caid = it->second;
167-
parent->receivedCw(service, descr.parity, (const char*)descr.cw, caid);
169+
parent->receivedCw(service, descr.parity, (const char*)descr.cw, caid, serviceId);
168170
}
169171
return true;
170172
}
@@ -365,15 +367,23 @@ eDVBCAHandler::~eDVBCAHandler()
365367

366368
void eDVBCAHandler::newConnection(int socket)
367369
{
368-
ePMTClient *client = new ePMTClient(this, socket);
370+
// Route through eDVBCWHandler proxy for MainLoop-independent CW delivery
371+
int client_fd = eDVBCWHandler::getInstance()->addConnection(socket);
372+
if (client_fd < 0)
373+
{
374+
eWarning("[eDVBCAHandler] eDVBCWHandler proxy failed, rejecting connection");
375+
::close(socket);
376+
return;
377+
}
378+
ePMTClient *client = new ePMTClient(this, client_fd);
369379
clients.push_back(client);
370380

371381
/* First distribute current CAPMTs in legacy format (works for all clients),
372382
* then send CLIENT_INFO to initiate Protocol-3 handshake.
373-
* - OSCam: receives legacy CAPMTs, then CLIENT_INFO, responds with
374-
* SERVER_INFO -> Protocol 3 for all subsequent CAPMTs.
375-
* - CCcam: receives legacy CAPMTs (works!), then CLIENT_INFO causes
376-
* disconnect, but CAPMTs were already delivered. */
383+
* - Protocol-3 softcams: receive legacy CAPMTs, then CLIENT_INFO,
384+
* respond with SERVER_INFO -> Protocol 3 for all subsequent CAPMTs.
385+
* - Legacy softcams: receive legacy CAPMTs (works!), then CLIENT_INFO
386+
* causes disconnect, but CAPMTs were already delivered. */
377387
distributeCAPMT();
378388
client->sendClientInfo();
379389
}
@@ -462,21 +472,15 @@ int eDVBCAHandler::registerService(const eServiceReferenceDVB &ref, int adapter,
462472
if (cacheit != pmtCache.end() && cacheit->second)
463473
{
464474
// If streamserver was active and we're adding a different type (e.g. Live-TV),
465-
// send CA PMT update immediately so OSCam knows about the new demux config
475+
// we need to force the softcam to restart descrambling so it resends the CW.
476+
// A simple LIST_UPDATE is not enough because the softcam's demux is already
477+
// running and would just "continue processing" without resending the CW.
478+
// We DEFER the restart to handlePMT() so the new CSA session is already
479+
// activated and its engine registered with CWHandler when the CW arrives.
466480
if (had_streamserver && servicetype != 7 && servicetype != 8)
467481
{
468-
caservice->resetBuildHash();
469-
if (caservice->buildCAPMT(cacheit->second) >= 0)
470-
{
471-
for (ePtrList<ePMTClient>::iterator client_it = clients.begin(); client_it != clients.end(); ++client_it)
472-
{
473-
if (client_it->state() == eSocket::Connection)
474-
{
475-
caservice->writeCAPMTObject(*client_it, LIST_UPDATE);
476-
}
477-
}
478-
eDebug("[eDVBCAService] sent early CA PMT update (streamserver active, new type %d registering)", servicetype);
479-
}
482+
caservice->m_force_cw_send = true;
483+
eDebug("[eDVBCAService] deferred softcam restart (streamserver->live, type %d)", servicetype);
480484
}
481485
else
482486
{
@@ -576,12 +580,7 @@ void eDVBCAHandler::serviceGone()
576580
{
577581
if (!services.size())
578582
{
579-
eDebug("[DVBCAHandler] no more services");
580-
for (ePtrList<ePMTClient>::iterator it = clients.begin(); it != clients.end(); )
581-
{
582-
delete *it;
583-
it = clients.erase(it);
584-
}
583+
eDebug("[DVBCAHandler] no more services (keeping %zu client connections)", clients.size());
585584
if (pmtCache.size() > 500)
586585
{
587586
pmtCache.clear();
@@ -600,14 +599,28 @@ void eDVBCAHandler::distributeCAPMT()
600599
{
601600
if (client_it->state() == eSocket::Connection)
602601
{
603-
unsigned char list_management = LIST_FIRST;
604-
for (CAServiceMap::iterator it = services.begin(); it != services.end(); )
602+
/*
603+
* Collect services that have a valid CAPMT (buildCAPMT was called).
604+
* Services with m_version == -1 have never had their PMT processed,
605+
* so their m_capmt buffer contains uninitialized heap data.
606+
* Sending that would corrupt the protocol stream.
607+
*/
608+
std::vector<eDVBCAService*> ready_services;
609+
for (CAServiceMap::iterator it = services.begin(); it != services.end(); ++it)
605610
{
606-
eDVBCAService *current = it->second;
607-
++it;
608-
if (it == services.end()) list_management |= LIST_LAST;
609-
current->writeCAPMTObject(*client_it, list_management);
610-
list_management = LIST_MORE;
611+
if (it->second->getCAPMTVersion() >= 0)
612+
{
613+
ready_services.push_back(it->second);
614+
}
615+
}
616+
617+
if (ready_services.empty()) continue;
618+
619+
for (size_t idx = 0; idx < ready_services.size(); ++idx)
620+
{
621+
unsigned char list_management = (idx == 0) ? LIST_FIRST : LIST_MORE;
622+
if (idx == ready_services.size() - 1) list_management |= LIST_LAST;
623+
ready_services[idx]->writeCAPMTObject(*client_it, list_management);
611624
}
612625
}
613626
}
@@ -623,7 +636,28 @@ void eDVBCAHandler::processPMTForService(eDVBCAService *service, eTable<ProgramM
623636
/* send the data to the listening client */
624637
service->sendCAPMT();
625638

626-
if (isUpdate)
639+
if (service->m_force_cw_send)
640+
{
641+
/*
642+
* SR→Live transition: force the softcam to restart descrambling.
643+
* Send CMD_NOT_SELECTED to stop the running demux, then LIST_ADD
644+
* with CMD_OK_DESCRAMBLING to re-add. This makes the softcam treat
645+
* it as a new service and immediately resend the CW from cache.
646+
* At this point the new CSA session is already activated and its
647+
* engine registered with CWHandler, so the CW won't be lost.
648+
*/
649+
service->m_force_cw_send = false;
650+
for (ePtrList<ePMTClient>::iterator client_it = clients.begin(); client_it != clients.end(); ++client_it)
651+
{
652+
if (client_it->state() == eSocket::Connection)
653+
{
654+
service->writeCAPMTObject(*client_it, LIST_UPDATE, CMD_NOT_SELECTED);
655+
service->writeCAPMTObject(*client_it, LIST_ADD, CMD_OK_DESCRAMBLING);
656+
}
657+
}
658+
eDebug("[eDVBCAService] forced softcam restart for SR->Live transition");
659+
}
660+
else if (isUpdate)
627661
{
628662
/*
629663
* this is a PMT update for an existing service, so we should
@@ -703,9 +737,10 @@ int eDVBCAHandler::getServiceReference(eServiceReferenceDVB &service, uint32_t s
703737
}
704738

705739
eDVBCAService::eDVBCAService(const eServiceReferenceDVB &service, uint32_t id)
706-
: eUnixDomainSocket(eApp), m_service(service), m_adapter(0), m_service_type_mask(0), m_prev_build_hash(0), m_crc32(0), m_id(id), m_version(-1), m_retryTimer(eTimer::create(eApp))
740+
: eUnixDomainSocket(eApp), m_service(service), m_adapter(0), m_service_type_mask(0), m_prev_build_hash(0), m_crc32(0), m_id(id), m_version(-1), m_retryTimer(eTimer::create(eApp)), m_force_cw_send(false)
707741
{
708742
memset(m_used_demux, 0xFF, sizeof(m_used_demux));
743+
memset(m_capmt, 0, sizeof(m_capmt));
709744
CONNECT(connectionClosed_, eDVBCAService::connectionLost);
710745
CONNECT(m_retryTimer->timeout, eDVBCAService::sendCAPMT);
711746
}
@@ -847,7 +882,7 @@ int eDVBCAService::buildCAPMT(eTable<ProgramMapSection> *ptr)
847882
if ( i != ptr->getSections().end() )
848883
{
849884
crc = (*i)->getCrc32();
850-
if (build_hash == m_prev_build_hash && crc == m_crc32)
885+
if (build_hash == m_prev_build_hash && crc == m_crc32 && !m_force_cw_send)
851886
{
852887
eDebug("[eDVBCAService] don't build/send the same CA PMT twice");
853888
return -1;
@@ -1148,13 +1183,14 @@ void eDVBCAService::sendCAPMT()
11481183
}
11491184
}
11501185

1151-
int eDVBCAService::writeCAPMTObject(eSocket *socket, int list_management)
1186+
int eDVBCAService::writeCAPMTObject(eSocket *socket, int list_management, int cmd_id)
11521187
{
11531188
int wp = 0;
1189+
int lenbytes = 0;
11541190
if (m_capmt[8] & 0x80)
11551191
{
11561192
int i=0;
1157-
int lenbytes = m_capmt[8] & ~0x80;
1193+
lenbytes = m_capmt[8] & ~0x80;
11581194
while(i < lenbytes)
11591195
wp = (wp << 8) | m_capmt[9 + i++];
11601196
wp += 4;
@@ -1167,17 +1203,20 @@ int eDVBCAService::writeCAPMTObject(eSocket *socket, int list_management)
11671203
wp += 4;
11681204
if (list_management >= 0) m_capmt[9] = (unsigned char)list_management;
11691205
}
1206+
// cmd_id is 6 bytes after list_management: list_mgmt(1) + program_number(2) + version(1) + prog_info_len(2)
1207+
if (cmd_id >= 0) m_capmt[9 + lenbytes + 6] = (unsigned char)cmd_id;
11701208

11711209
return socket->writeBlock((const char*)(m_capmt + 5), wp); // skip extra header
11721210
}
11731211

1174-
int eDVBCAService::writeCAPMTObject(ePMTClient *client, int list_management)
1212+
int eDVBCAService::writeCAPMTObject(ePMTClient *client, int list_management, int cmd_id)
11751213
{
11761214
int wp = 0;
1215+
int lenbytes = 0;
11771216
if (m_capmt[8] & 0x80)
11781217
{
11791218
int i=0;
1180-
int lenbytes = m_capmt[8] & ~0x80;
1219+
lenbytes = m_capmt[8] & ~0x80;
11811220
while(i < lenbytes)
11821221
wp = (wp << 8) | m_capmt[9 + i++];
11831222
wp += 4;
@@ -1190,6 +1229,8 @@ int eDVBCAService::writeCAPMTObject(ePMTClient *client, int list_management)
11901229
wp += 4;
11911230
if (list_management >= 0) m_capmt[9] = (unsigned char)list_management;
11921231
}
1232+
// cmd_id is 6 bytes after list_management: list_mgmt(1) + program_number(2) + version(1) + prog_info_len(2)
1233+
if (cmd_id >= 0) m_capmt[9 + lenbytes + 6] = (unsigned char)cmd_id;
11931234

11941235
return client->writeCAPMTObject((const char*)m_capmt, wp);
11951236
}

lib/dvb/cahandler.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ class ePMTClient : public eUnixDomainSocket
9797
eDVBCAHandler *parent;
9898
void connectionLost();
9999
void dataAvailable();
100-
// OSCam Protocol 3 handlers
100+
// Softcam Protocol 3 handlers
101101
bool processCaSetDescrPacket();
102102
bool processServerInfoPacket();
103103
bool processEcmInfoPacket();
@@ -109,6 +109,7 @@ class ePMTClient : public eUnixDomainSocket
109109

110110
class eDVBCAService: public eUnixDomainSocket
111111
{
112+
friend class eDVBCAHandler;
112113
eServiceReferenceDVB m_service;
113114
uint8_t m_used_demux[32];
114115
uint8_t m_adapter;
@@ -119,6 +120,7 @@ class eDVBCAService: public eUnixDomainSocket
119120
int m_version;
120121
unsigned char m_capmt[2048];
121122
ePtr<eTimer> m_retryTimer;
123+
bool m_force_cw_send; // force softcam CW resend on next handlePMT (SR→Live)
122124
public:
123125
eDVBCAService(const eServiceReferenceDVB &service, uint32_t id);
124126
~eDVBCAService();
@@ -136,8 +138,8 @@ class eDVBCAService: public eUnixDomainSocket
136138
uint32_t getServiceTypeMask() const;
137139
void resetBuildHash() { m_prev_build_hash = 0; m_crc32 = 0; }
138140
void sendCAPMT();
139-
int writeCAPMTObject(eSocket *socket, int list_management = -1);
140-
int writeCAPMTObject(ePMTClient *client, int list_management = -1);
141+
int writeCAPMTObject(eSocket *socket, int list_management = -1, int cmd_id = -1);
142+
int writeCAPMTObject(ePMTClient *client, int list_management = -1, int cmd_id = -1);
141143
int buildCAPMT(eTable<ProgramMapSection> *ptr);
142144
int buildCAPMT(ePtr<eDVBService> &dvbservice);
143145
void connectionLost();
@@ -155,7 +157,7 @@ class iCryptoInfo : public iObject
155157
iCryptoInfo();
156158
~iCryptoInfo();
157159
#endif
158-
sigc::signal<void(eServiceReferenceDVB, int, const char*, uint16_t)> receivedCw; // service, parity, cw, caid
160+
sigc::signal<void(eServiceReferenceDVB, int, const char*, uint16_t, uint32_t)> receivedCw; // service, parity, cw, caid, serviceId
159161
};
160162
SWIG_TEMPLATE_TYPEDEF(ePtr<iCryptoInfo>, iCryptoInfoPtr);
161163

@@ -172,7 +174,7 @@ DECLARE_REF(eDVBCAHandler);
172174
ePtrList<ePMTClient> clients;
173175
ePtr<eTimer> serviceLeft;
174176
std::map<eServiceReferenceDVB, ePtr<eTable<ProgramMapSection> > > pmtCache;
175-
std::map<uint32_t, uint16_t> m_service_caid; // serviceId -> CAID (from OSCam ECM_INFO)
177+
std::map<uint32_t, uint16_t> m_service_caid; // serviceId -> CAID (from softcam ECM_INFO)
176178
uint32_t serviceIdCounter;
177179

178180
void newConnection(int socket);

0 commit comments

Comments
 (0)