diff --git a/lib/dvb/cahandler.cpp b/lib/dvb/cahandler.cpp index edd54f0bc00..1c3c69c8479 100644 --- a/lib/dvb/cahandler.cpp +++ b/lib/dvb/cahandler.cpp @@ -546,6 +546,25 @@ int eDVBCAHandler::unregisterService(const eServiceReferenceDVB &ref, int adapte { if (!used_demux_slots) // no more used.. so we remove it { + /* + * Send CMD_NOT_SELECTED to tell the softcam to stop + * descrambling this service before we delete it. + * Without this, switching from an encrypted channel + * to FTA/IPTV would leave the softcam in descrambling + * state (e.g. ecm.info not removed). + */ + if (m_protocol3_established && caservice->getCAPMTVersion() >= 0) + { + for (ePtrList::iterator client_it = clients.begin(); client_it != clients.end(); ++client_it) + { + if (client_it->state() == eSocket::Connection) + { + eDebug("[eDVBCAHandler] sending CMD_NOT_SELECTED for service %s", caservice->toString().c_str()); + caservice->writeCAPMTObject(*client_it, LIST_UPDATE, CMD_NOT_SELECTED); + } + } + } + delete it->second; services.erase(it); diff --git a/lib/dvb/csasession.cpp b/lib/dvb/csasession.cpp index 69d5159f17f..1314c0a2fce 100644 --- a/lib/dvb/csasession.cpp +++ b/lib/dvb/csasession.cpp @@ -141,10 +141,18 @@ void eDVBCSASession::startECMMonitor(iDVBDemux *demux, uint16_t ecm_pid, uint16_ if (info.is_csa_alt && !m_active) { - eDebug("[eDVBCSASession] ECM Monitor: Activating from cache (CSA-ALT)"); m_ecm_analyzed = true; m_csa_alt = true; - setActive(true); + if (shouldSuppressActivation && shouldSuppressActivation()) + { + eDebug("[eDVBCSASession] ECM Monitor: CSA-ALT cached but activation suppressed (CI module)"); + stopECMMonitor(); + } + else + { + eDebug("[eDVBCSASession] ECM Monitor: Activating from cache (CSA-ALT)"); + setActive(true); + } } } @@ -235,7 +243,15 @@ void eDVBCSASession::ecmDataReceived(const uint8_t *data) eDebug("[eDVBCSASession] CSA-ALT detected from ECM! Activating software descrambling"); if (!m_active) { - setActive(true); + if (shouldSuppressActivation && shouldSuppressActivation()) + { + eDebug("[eDVBCSASession] Activation suppressed (CI module handles decryption)"); + stopECMMonitor(); + } + else + { + setActive(true); + } } } else diff --git a/lib/dvb/csasession.h b/lib/dvb/csasession.h index 00bed6144a0..ce006aee7a8 100644 --- a/lib/dvb/csasession.h +++ b/lib/dvb/csasession.h @@ -5,6 +5,7 @@ #include #include #include +#include class eDVBCSAEngine; class eDVBCAHandler; @@ -91,6 +92,10 @@ class eDVBCSASession : public iServiceScrambled, public sigc::trackable // Signal when first CW is received (for decoder start timing) sigc::signal firstCwReceived; + // Optional callback to check if activation should be suppressed + // (e.g. CI module handles decryption). Return true to suppress. + std::function shouldSuppressActivation; + private: eServiceReferenceDVB m_service_ref; ePtr m_engine; diff --git a/lib/dvb/pmt.cpp b/lib/dvb/pmt.cpp index 1190dfd41a9..7357f19ada5 100644 --- a/lib/dvb/pmt.cpp +++ b/lib/dvb/pmt.cpp @@ -203,8 +203,6 @@ void eDVBServicePMTHandler::PMTready(int error) { eDVBCIInterfaces::getInstance()->recheckPMTHandlers(); eDVBCIInterfaces::getInstance()->gotPMT(this); - if (isCiConnected()) - serviceEvent(eventCIConnected); } } if (m_ca_servicePtr) diff --git a/lib/dvb/pmt.h b/lib/dvb/pmt.h index 274c498e677..56a9ff39140 100644 --- a/lib/dvb/pmt.h +++ b/lib/dvb/pmt.h @@ -145,7 +145,6 @@ class eDVBServicePMTHandler: public eDVBPMTParser eventStartPvrDescramble, // start PVR Descramble Convert eventChannelAllocated, eventStreamCorrupt, - eventCIConnected, // a CI slot was assigned to this service after recheckPMTHandlers }; #ifndef SWIG sigc::signal serviceEvent; diff --git a/lib/service/servicedvb.cpp b/lib/service/servicedvb.cpp index 9d0899ecae5..e259ed36845 100644 --- a/lib/service/servicedvb.cpp +++ b/lib/service/servicedvb.cpp @@ -1384,14 +1384,6 @@ void eDVBServicePlay::serviceEvent(int event) case eDVBServicePMTHandler::eventHBBTVInfo: m_event((iPlayableService*)this, evHBBTVInfo); break; - case eDVBServicePMTHandler::eventCIConnected: - if (m_csa_session && m_csa_session->isActive()) - { - eDebug("[eDVBServicePlay] CI module connected - deactivating SoftCSA to save resources"); - m_csa_session->stopECMMonitor(); - m_csa_session->forceDeactivate(); - } - break; } } @@ -4379,6 +4371,11 @@ void eDVBServicePlay::setupSpeculativeDescrambling() m_soft_decoder->m_audio_pid_selected.connect( sigc::mem_fun(*this, &eDVBServicePlay::onSoftDecoderAudioPidSelected)); + // Suppress SoftCSA activation when CI module handles decryption + m_csa_session->shouldSuppressActivation = [this]() { + return m_service_handler.isCiConnected(); + }; + // Connect to session's activated signal for decoder handover m_csa_session->activated.connect( sigc::mem_fun(*this, &eDVBServicePlay::onSessionActivated)); diff --git a/lib/service/servicedvbfcc.cpp b/lib/service/servicedvbfcc.cpp index d2a33720ad4..adf1bd34ca6 100644 --- a/lib/service/servicedvbfcc.cpp +++ b/lib/service/servicedvbfcc.cpp @@ -592,6 +592,11 @@ void eDVBServiceFCCPlay::setupSpeculativeDescrambling() m_soft_decoder->m_audio_pid_selected.connect( [this](int pid) { this->onSoftDecoderAudioPidSelected(pid); }); + // Suppress SoftCSA activation when CI module handles decryption + m_csa_session->shouldSuppressActivation = [this]() { + return m_service_handler.isCiConnected(); + }; + // Connect to session's activated signal - use FCC-specific callback! // This is the key difference from base class: we use onFCCSessionActivated // which handles FCC decoder stop/start properly