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
1921static 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
366368void 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
705739eDVBCAService::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}
0 commit comments