diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 457439a9..e1b098a5 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -132,7 +132,31 @@ if(CMAKE_IARM_MGR) set(EXT_SOURCES "${EXT_SOURCES}" rdk/IIarm/DeviceIARMInterface.cpp) set(EXT_SOURCES "${EXT_SOURCES}" rdk/IFirebolt/DeviceFireboltInterface.cpp) set(EXT_SOURCES "${EXT_SOURCES}" IFirebolt/FireboltInterface.cpp) - list(APPEND LIB_EXT_DEPENDS -lIARMBus -lds -ldshalcli) + if(CMAKE_DS_THUNDER_PLUGIN) + # Thunder path: libds (-lds -ldshalcli) replaced by Thunder plugins; keep only IARM bus + list(APPEND LIB_EXT_DEPENDS -lIARMBus) + else() + list(APPEND LIB_EXT_DEPENDS -lIARMBus -lds -ldshalcli) + endif() +endif() + +if(CMAKE_DS_THUNDER_PLUGIN) + message("CMAKE_DS_THUNDER_PLUGIN set: libds replaced by Thunder HdcpProfile.1 / DisplaySettings.1") + set(LIB_EXT_DEFINES "${LIB_EXT_DEFINES} -DUSE_DS_THUNDER_PLUGIN=1") + # PlayerThunderAccess.cpp is required; ensure it is compiled even if CMAKE_WPEFRAMEWORK_REQUIRED was not set + if(NOT CMAKE_WPEFRAMEWORK_REQUIRED) + message("CMAKE_DS_THUNDER_PLUGIN: auto-enabling WPEFramework for PlayerThunderAccess") + list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/") + set(LIB_EXT_DEFINES "${LIB_EXT_DEFINES} -DUSE_CPP_THUNDER_PLUGIN_ACCESS") + set(THUNDER_INTERFACE_SRCS ${THUNDER_INTERFACE_SRCS} rdk/PlayerThunderAccess.cpp Module.cpp) + find_package(WPEFramework REQUIRED) + if(WPEFRAMEWORK_FOUND) + include_directories(SYSTEM ${WPEFRAMEWORK_INCLUDE_DIRS}) + list(APPEND LIB_EXT_DEPENDS ${WPEFRAMEWORK_LIBRARIES}) + else() + message(FATAL_ERROR "CMAKE_DS_THUNDER_PLUGIN requires WPEFramework but it was not found") + endif() + endif() endif() if(CMAKE_PLAYER_RFC_REQUIRED) diff --git a/externals/PlayerThunderAccessBase.h b/externals/PlayerThunderAccessBase.h index 8916f1de..090b0ac0 100644 --- a/externals/PlayerThunderAccessBase.h +++ b/externals/PlayerThunderAccessBase.h @@ -43,7 +43,8 @@ enum PlayerThunderAccessPlugin SECMANAGER, WATERMARK, HDMIINPUT, - COMPOSITEINPUT + COMPOSITEINPUT, + HDCPPROFILE }; /* diff --git a/externals/contentsecuritymanager/ThunderAccessPlayer.cpp b/externals/contentsecuritymanager/ThunderAccessPlayer.cpp index 6ab170f8..12253dc4 100755 --- a/externals/contentsecuritymanager/ThunderAccessPlayer.cpp +++ b/externals/contentsecuritymanager/ThunderAccessPlayer.cpp @@ -64,7 +64,7 @@ ThunderAccessPlayer::ThunderAccessPlayer(std::string callsign) ,controllerObject(NULL) ,pluginCallsign(callsign) { - MW_LOG_INFO( "[ThunderAccessPlayer]Inside"); + MW_LOG_WARN( "[GSK-TAP-CTOR] callsign=%s server=%s", callsign.c_str(), SERVER_DETAILS); uint32_t status = Core::ERROR_NONE; Core::SystemInfo::SetEnvironment(_T("THUNDER_ACCESS"), (_T(SERVER_DETAILS))); @@ -72,19 +72,22 @@ ThunderAccessPlayer::ThunderAccessPlayer(std::string callsign) #ifdef DISABLE_SECURITY_TOKEN gSecurityPlayerData.securityToken = "token=" + sToken; gSecurityPlayerData.tokenQueried = true; + MW_LOG_WARN( "[GSK-TAP-TOKEN] security token DISABLED"); #else if(!gSecurityPlayerData.tokenQueried) { unsigned char buffer[MAX_LENGTH] = {0}; gSecurityPlayerData.tokenStatus = GetSecurityToken(MAX_LENGTH,buffer); if(gSecurityPlayerData.tokenStatus > 0){ - MW_LOG_INFO( "[ThunderAccessPlayer] : GetSecurityToken success"); sToken = (char*)buffer; gSecurityPlayerData.securityToken = "token=" + sToken; + MW_LOG_WARN( "[GSK-TAP-TOKEN] GetSecurityToken success len=%d", gSecurityPlayerData.tokenStatus); + } else { + MW_LOG_WARN( "[GSK-TAP-TOKEN] GetSecurityToken failed status=%d proceeding without token", gSecurityPlayerData.tokenStatus); } gSecurityPlayerData.tokenQueried = true; - - //MW_LOG_WARN( "[ThunderAccessPlayer] securityToken : %s tokenStatus : %d tokenQueried : %s", gSecurityPlayerData.securityToken.c_str(), gSecurityPlayerData.tokenStatus, ((gSecurityPlayerData.tokenQueried)?"true":"false")); + } else { + MW_LOG_INFO( "[GSK-TAP-TOKEN] reusing cached token tokenStatus=%d", gSecurityPlayerData.tokenStatus); } #endif if (NULL == controllerObject) { @@ -97,9 +100,9 @@ ThunderAccessPlayer::ThunderAccessPlayer(std::string callsign) } if (NULL == controllerObject) { - MW_LOG_WARN( "[ThunderAccessPlayer] Controller object creation failed"); + MW_LOG_WARN( "[GSK-TAP-CTRL] controller object creation FAILED"); } else { - MW_LOG_INFO( "[ThunderAccessPlayer] Controller object creation success"); + MW_LOG_WARN( "[GSK-TAP-CTRL] controller object created OK server=%s", SERVER_DETAILS); } } @@ -110,9 +113,9 @@ ThunderAccessPlayer::ThunderAccessPlayer(std::string callsign) remoteObject = new JSONRPC::LinkType(_T(pluginCallsign), _T("")); } if (NULL == remoteObject) { - MW_LOG_WARN( "[ThunderAccessPlayer] %s Client initialization failed", pluginCallsign.c_str()); + MW_LOG_WARN( "[GSK-TAP-REMOTE] %s remote object creation FAILED", pluginCallsign.c_str()); } else { - MW_LOG_INFO( "[ThunderAccessPlayer] %s Client initialization success", pluginCallsign.c_str()); + MW_LOG_WARN( "[GSK-TAP-REMOTE] %s remote object created OK server=%s", pluginCallsign.c_str(), SERVER_DETAILS); } } @@ -138,18 +141,19 @@ bool ThunderAccessPlayer::ActivatePlugin() if (NULL != controllerObject) { controlParam["callsign"] = pluginCallsign; + MW_LOG_WARN( "[GSK-TAP-ACTIVATE] sending activate callsign=%s", pluginCallsign.c_str()); status = controllerObject->Invoke(THUNDER_RPC_TIMEOUT, _T("activate"), controlParam, result); if (Core::ERROR_NONE == status){ result.ToString(response); - MW_LOG_INFO( "[ThunderAccessPlayer] %s plugin Activated. Response : %s ", pluginCallsign.c_str(), response.c_str()); + MW_LOG_WARN( "[GSK-TAP-ACTIVATE] %s activated OK response=%s", pluginCallsign.c_str(), response.c_str()); } else { - MW_LOG_WARN( "[ThunderAccessPlayer] %s plugin Activation failed with error status : %u ", pluginCallsign.c_str(), status); + MW_LOG_WARN( "[GSK-TAP-ACTIVATE] %s activation FAILED errorStatus=%u", pluginCallsign.c_str(), status); ret = false; } } else { - MW_LOG_WARN( "[ThunderAccessPlayer] Controller Object NULL "); + MW_LOG_WARN( "[GSK-TAP-ACTIVATE] controller object is NULL cannot activate %s", pluginCallsign.c_str()); ret = false; } return ret; @@ -163,16 +167,17 @@ bool ThunderAccessPlayer::SubscribeEvent (string eventName, std::functionSubscribe(THUNDER_RPC_TIMEOUT, _T(eventName), functionHandler); if (Core::ERROR_NONE == status) { - MW_LOG_INFO( "[ThunderAccessPlayer] Subscribed to : %s", eventName.c_str()); + MW_LOG_WARN( "[GSK-TAP-SUBSCRIBE] %s::%s subscribed OK", pluginCallsign.c_str(), eventName.c_str()); } else { - MW_LOG_WARN( "[ThunderAccessPlayer] Subscription failed for : %s with error status %u", eventName.c_str(), status); + MW_LOG_WARN( "[GSK-TAP-SUBSCRIBE] %s::%s subscription FAILED errorStatus=%u", pluginCallsign.c_str(), eventName.c_str(), status); ret = false; } } else { - MW_LOG_WARN( "[ThunderAccessPlayer] remoteObject not created for the plugin!"); + MW_LOG_WARN( "[GSK-TAP-SUBSCRIBE] remoteObject is NULL for %s cannot subscribe to %s", pluginCallsign.c_str(), eventName.c_str()); ret = false; } return ret; @@ -186,11 +191,12 @@ bool ThunderAccessPlayer::SubscribeEvent (string eventName, std::functionUnsubscribe(THUNDER_RPC_TIMEOUT, _T(eventName)); - MW_LOG_INFO( "[ThunderAccessPlayer] UnSubscribed : %s event", eventName.c_str()); + MW_LOG_WARN( "[GSK-TAP-UNSUBSCRIBE] %s::%s unsubscribed OK", pluginCallsign.c_str(), eventName.c_str()); } else { - MW_LOG_WARN( "[ThunderAccessPlayer] remoteObject not created for the plugin!"); + MW_LOG_WARN( "[GSK-TAP-UNSUBSCRIBE] remoteObject is NULL for %s cannot unsubscribe %s", pluginCallsign.c_str(), eventName.c_str()); ret = false; } return ret; @@ -206,26 +212,26 @@ bool ThunderAccessPlayer::InvokeJSONRPC(std::string method, const JsonObject &pa std::string response; uint32_t status = Core::ERROR_NONE; + MW_LOG_WARN( "[GSK-TAP-INVOKE] callsign=%s method=%s", pluginCallsign.c_str(), method.c_str()); if(NULL == remoteObject) { - MW_LOG_WARN( "[ThunderAccessPlayer] client not initialized! "); + MW_LOG_WARN( "[GSK-TAP-INVOKE] remoteObject is NULL for %s cannot invoke %s", pluginCallsign.c_str(), method.c_str()); return false; } JsonObject result_internal; status = remoteObject->Invoke(waitTime, _T(method), param, result_internal); + result_internal.ToString(response); if (Core::ERROR_NONE == status) { if (result_internal["success"].Boolean()) { - result_internal.ToString(response); - MW_LOG_TRACE( "[ThunderAccessPlayer] %s success! Response : %s", method.c_str() , response.c_str()); + MW_LOG_WARN( "[GSK-TAP-INVOKE] %s::%s SUCCESS response=%s", pluginCallsign.c_str(), method.c_str(), response.c_str()); } else { - result_internal.ToString(response); - MW_LOG_WARN( "[ThunderAccessPlayer] %s call failed! Response : %s", method.c_str() , response.c_str()); + MW_LOG_WARN( "[GSK-TAP-INVOKE] %s::%s FAILED (no success field) response=%s", pluginCallsign.c_str(), method.c_str(), response.c_str()); ret = false; } } else { - MW_LOG_WARN( "[ThunderAccessPlayer] %s : invoke failed with error status %u", method.c_str(), status); + MW_LOG_WARN( "[GSK-TAP-INVOKE] %s::%s invoke FAILED errorStatus=%u response=%s", pluginCallsign.c_str(), method.c_str(), status, response.c_str()); ret = false; } diff --git a/externals/rdk/IFirebolt/DeviceFireboltInterface.cpp b/externals/rdk/IFirebolt/DeviceFireboltInterface.cpp index 2f08631b..c3ee01bd 100644 --- a/externals/rdk/IFirebolt/DeviceFireboltInterface.cpp +++ b/externals/rdk/IFirebolt/DeviceFireboltInterface.cpp @@ -229,6 +229,14 @@ static void HDCPEventHandlerFirebolt(const Firebolt::Device::HDCPVersionMap& t_H { std::shared_ptr pInstance = PlayerExternalsRdkInterface::GetPlayerExternalsRdkInterfaceInstance(); +#ifdef USE_DS_THUNDER_PLUGIN + /* When the Thunder plugin path is active, HDCP version and connection state + * are read authoritatively from HdcpProfile.1::getHDCPStatus inside + * SetHDMIStatus(). Route through the coalescing worker thread so that + * concurrent Firebolt callbacks don't race on the Thunder RPC object. */ + MW_LOG_WARN("[DS-Thunder] HDCPEventHandlerFirebolt: routing to PostHDMIStatusUpdate\n"); + pInstance->PostHDMIStatusUpdate(); +#else if(t_HDCPVersionMap.hdcp2_2) { pInstance->setHdcpProtocol(dsHDCP_VERSION_2X); @@ -245,7 +253,7 @@ static void HDCPEventHandlerFirebolt(const Firebolt::Device::HDCPVersionMap& t_H } pInstance->SetHDMIStatus(); - +#endif } /** @@ -253,6 +261,15 @@ static void HDCPEventHandlerFirebolt(const Firebolt::Device::HDCPVersionMap& t_H */ static void ResolutionHandlerFirebolt(const std::string& t_res) { +#ifdef USE_DS_THUNDER_PLUGIN + /* When the Thunder plugin path is active, resolution is fetched from + * DisplaySettings.1::getCurrentResolution inside SetHDMIStatus(). Route + * through the coalescing worker thread so this event is handled in order + * with HDCP events. */ + MW_LOG_WARN("[DS-Thunder] ResolutionHandlerFirebolt: routing to PostHDMIStatusUpdate\n"); + std::shared_ptr pInstance = PlayerExternalsRdkInterface::GetPlayerExternalsRdkInterfaceInstance(); + pInstance->PostHDMIStatusUpdate(); +#else int width = 1280; int height = 720; @@ -272,5 +289,5 @@ static void ResolutionHandlerFirebolt(const std::string& t_res) { MW_LOG_ERR("Failed to get current resolution"); } - +#endif } diff --git a/externals/rdk/IIarm/DeviceIARMInterface.cpp b/externals/rdk/IIarm/DeviceIARMInterface.cpp index 234e6a06..8a423712 100644 --- a/externals/rdk/IIarm/DeviceIARMInterface.cpp +++ b/externals/rdk/IIarm/DeviceIARMInterface.cpp @@ -89,7 +89,7 @@ typedef struct _IARM_BUS_NetSrvMgr_Iface_EventData_t { std::shared_ptr s_pDeviceIARMInterface = nullptr; -#ifndef USE_DS_EVENT_SUPPORTED +#if !defined(USE_DS_EVENT_SUPPORTED) && !defined(USE_DS_THUNDER_PLUGIN) static void HDMIEventHandler(const char *owner, IARM_EventId_t eventId, void *data, size_t len); static void ResolutionHandler(const char *owner, IARM_EventId_t eventId, void *data, size_t len); #endif @@ -259,6 +259,7 @@ static void IARM_PowerChangeHandler (const PowerController_PowerState_t currentS void DeviceIARMInterface::IARMInit() { +#ifndef USE_DS_THUNDER_PLUGIN //char processName[20] = {0}; IARM_Result_t result; MW_PRE_LOGGER_LOG("IARM Interface Init started in Player\n"); @@ -292,12 +293,12 @@ void DeviceIARMInterface::IARMInit() } MW_PRE_LOGGER_LOG("IARM Interface Init completed in Player\n"); - +#endif /* USE_DS_THUNDER_PLUGIN */ } void DeviceIARMInterface::RegisterDsMgrEventHandler() { -#ifndef USE_DS_EVENT_SUPPORTED +#if !defined(USE_DS_EVENT_SUPPORTED) && !defined(USE_DS_THUNDER_PLUGIN) IARM_Bus_RegisterEventHandler(IARM_BUS_DSMGR_NAME,IARM_BUS_DSMGR_EVENT_HDMI_HOTPLUG, HDMIEventHandler); IARM_Bus_RegisterEventHandler(IARM_BUS_DSMGR_NAME,IARM_BUS_DSMGR_EVENT_HDCP_STATUS, HDMIEventHandler); IARM_Bus_RegisterEventHandler(IARM_BUS_DSMGR_NAME,IARM_BUS_DSMGR_EVENT_RES_POSTCHANGE, ResolutionHandler); @@ -306,16 +307,19 @@ void DeviceIARMInterface::RegisterDsMgrEventHandler() void DeviceIARMInterface::RemoveEventHandlers() { -#ifndef USE_DS_EVENT_SUPPORTED +#if !defined(USE_DS_EVENT_SUPPORTED) && !defined(USE_DS_THUNDER_PLUGIN) IARM_Bus_RemoveEventHandler(IARM_BUS_DSMGR_NAME,IARM_BUS_DSMGR_EVENT_HDMI_HOTPLUG, HDMIEventHandler); IARM_Bus_RemoveEventHandler(IARM_BUS_DSMGR_NAME,IARM_BUS_DSMGR_EVENT_HDCP_STATUS, HDMIEventHandler); IARM_Bus_RemoveEventHandler(IARM_BUS_DSMGR_NAME,IARM_BUS_DSMGR_EVENT_RES_POSTCHANGE, ResolutionHandler); #endif +#ifndef USE_DS_THUNDER_PLUGIN IARM_Bus_RemoveEventHandler("NET_SRV_MGR", IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_IPADDRESS, getActiveInterfaceEventHandler); +#endif } void DeviceIARMInterface::RegisterNtwMgrEventHandler() { +#ifndef USE_DS_THUNDER_PLUGIN std::shared_ptr pInstance = PlayerExternalsRdkInterface::GetPlayerExternalsRdkInterfaceInstance(); bool wifiStatus = false; @@ -336,6 +340,7 @@ void DeviceIARMInterface::RegisterNtwMgrEventHandler() } IARM_Bus_RegisterEventHandler("NET_SRV_MGR", IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_IPADDRESS, getActiveInterfaceEventHandler); pInstance->SetActiveInterface(wifiStatus); +#endif /* USE_DS_THUNDER_PLUGIN */ } char * DeviceIARMInterface::GetTR181Config(const char * paramName, size_t & iConfigLen) @@ -407,7 +412,7 @@ static void getActiveInterfaceEventHandler (const char *owner, IARM_EventId_t ev } -#ifndef USE_DS_EVENT_SUPPORTED +#if !defined(USE_DS_EVENT_SUPPORTED) && !defined(USE_DS_THUNDER_PLUGIN) /** * @brief IARM event handler for HDCP and HDMI hot plug events */ diff --git a/externals/rdk/PlayerExternalsRdkInterface.cpp b/externals/rdk/PlayerExternalsRdkInterface.cpp index 107f202b..3172bddb 100644 --- a/externals/rdk/PlayerExternalsRdkInterface.cpp +++ b/externals/rdk/PlayerExternalsRdkInterface.cpp @@ -29,6 +29,10 @@ #include "PlayerExternalsInterface.h" #include +#ifdef USE_DS_THUNDER_PLUGIN +#include "PlayerThunderAccess.h" +#endif + #include #define DISPLAY_WIDTH_UNKNOWN -1 /**< Parsing failed for getResolution().getName(); */ @@ -125,9 +129,16 @@ void PlayerExternalsRdkInterface::Initialize() MW_PRE_LOGGER_LOG("Done getting interface \n"); +#ifdef USE_DS_THUNDER_PLUGIN + /* Register event handlers first so Thunder objects exist before SetHDMIStatus() is called */ + RegisterThunderEventHandlers(); + /* Post the initial HDMI status query through the worker thread */ + PostHDMIStatusUpdate(); +#else SetHDMIStatus(); -#ifdef USE_DS_EVENT_SUPPORTED +# ifdef USE_DS_EVENT_SUPPORTED RegisterDsClientEventHandler(); +# endif #endif MW_PRE_LOGGER_LOG("Initializing completed \n"); @@ -203,7 +214,9 @@ void PlayerExternalsRdkInterface::OnResolutionPreChange(int width, int height) PlayerExternalsRdkInterface::~PlayerExternalsRdkInterface() { -#ifdef USE_DS_EVENT_SUPPORTED +#ifdef USE_DS_THUNDER_PLUGIN + RemoveThunderEventHandlers(); +#elif defined(USE_DS_EVENT_SUPPORTED) RemoveDsClientEventHandlers(); #endif m_pDeviceInterfaceBase = nullptr; @@ -228,6 +241,102 @@ void PlayerExternalsRdkInterface::SetResolution(int width, int height) */ void PlayerExternalsRdkInterface::SetHDMIStatus() { +#ifdef USE_DS_THUNDER_PLUGIN + /* + * Thunder path: replaces all libds (device::Manager / VideoOutputPort) calls. + * CSV mapping: + * getHDCPCurrentProtocol -> HdcpProfile.1 getHDCPStatus -> currentHDCPVersion + * getHDCPReceiverProtocol -> HdcpProfile.1 getHDCPStatus -> receiverHDCPVersion + * isContentProtected -> HdcpProfile.1 getHDCPStatus -> isHDCPEnabled + * isDisplayConnected -> HdcpProfile.1 getHDCPStatus -> isConnected + * getPixelResolution -> DisplaySettings.1 getCurrentResolution -> w / h + */ + bool isConnected = false; + bool isHDCPEnabled = false; + dsHdcpProtocolVersion_t hdcpCurrentProtocol = dsHDCP_VERSION_1X; + + MW_LOG_WARN("[DS-Thunder] SetHDMIStatus() called\n"); + + /* --- Query HDCP status via HdcpProfile.1 --- */ + if (m_hdcpProfileThunder) { + JsonObject param, result; + std::string rawResponse; + MW_LOG_WARN("[DS-Thunder] Calling HdcpProfile.1::getHDCPStatus\n"); + bool rpcRet = m_hdcpProfileThunder->InvokeJSONRPC("getHDCPStatus", param, result); + result.ToString(rawResponse); + MW_LOG_WARN("[DS-Thunder] getHDCPStatus rpcRet=%d rawResponse=%s\n", rpcRet, rawResponse.c_str()); + if (rpcRet) { + /* Response is nested: { "HDCPStatus": { "isConnected":..., ... }, "success":true } */ + if (result.HasLabel("HDCPStatus")) { + JsonObject hdcpStatus = result["HDCPStatus"].Object(); + std::string hdcpStatusStr; + hdcpStatus.ToString(hdcpStatusStr); + MW_LOG_WARN("[DS-Thunder] HDCPStatus object=%s\n", hdcpStatusStr.c_str()); + isConnected = hdcpStatus["isConnected"].Boolean(); + isHDCPEnabled = hdcpStatus["isHDCPEnabled"].Boolean(); + std::string currentVer = hdcpStatus["currentHDCPVersion"].String(); + hdcpCurrentProtocol = (currentVer == "2.2") ? dsHDCP_VERSION_2X : dsHDCP_VERSION_1X; + MW_LOG_WARN("[DS-Thunder] getHDCPStatus: isConnected=%d isHDCPEnabled=%d currentHDCPVersion=%s\n", + isConnected, isHDCPEnabled, currentVer.c_str()); + } else { + MW_LOG_WARN("[DS-Thunder] getHDCPStatus: 'HDCPStatus' key missing in response — falling back to flat read\n"); + /* Fallback: try flat fields (old API) */ + isConnected = result["isConnected"].Boolean(); + isHDCPEnabled = result["isHDCPEnabled"].Boolean(); + std::string currentVer = result["currentHDCPVersion"].String(); + hdcpCurrentProtocol = (currentVer == "2.2") ? dsHDCP_VERSION_2X : dsHDCP_VERSION_1X; + MW_LOG_WARN("[DS-Thunder] fallback flat: isConnected=%d isHDCPEnabled=%d currentHDCPVersion=%s\n", + isConnected, isHDCPEnabled, currentVer.c_str()); + } + } else { + MW_LOG_WARN("[DS-Thunder] getHDCPStatus JSONRPC call failed (rpcRet=false)\n"); + } + } else { + MW_LOG_WARN("[DS-Thunder] m_hdcpProfileThunder is NULL — was RegisterThunderEventHandlers() called?\n"); + } + + /* --- Query pixel resolution via DisplaySettings.1::getCurrentResolution --- */ + if (isConnected && m_dsThunder) { + JsonObject param, result; + std::string rawResponse; + MW_LOG_WARN("[DS-Thunder] Calling DisplaySettings.1::getCurrentResolution\n"); + bool rpcRet = m_dsThunder->InvokeJSONRPC("getCurrentResolution", param, result); + result.ToString(rawResponse); + MW_LOG_WARN("[DS-Thunder] getCurrentResolution rpcRet=%d rawResponse=%s\n", rpcRet, rawResponse.c_str()); + if (rpcRet && result["success"].Boolean()) { + int w = static_cast(result["w"].Number()); + int h = static_cast(result["h"].Number()); + MW_LOG_WARN("[DS-Thunder] getCurrentResolution: resolution=%s w=%d h=%d\n", + result["resolution"].String().c_str(), w, h); + if (w > 0 && h > 0) { + SetResolution(w, h); + } else { + MW_LOG_WARN("[DS-Thunder] getCurrentResolution: invalid resolution w=%d h=%d — keeping current\n", w, h); + } + } else { + MW_LOG_WARN("[DS-Thunder] getCurrentResolution failed rpcRet=%d success=%d\n", + rpcRet, result["success"].Boolean()); + } + } else if (!isConnected) { + MW_LOG_WARN("[DS-Thunder] Display not connected — setting resolution to NA\n"); + SetResolution(DISPLAY_RESOLUTION_NA, DISPLAY_RESOLUTION_NA); + } else { + MW_LOG_WARN("[DS-Thunder] m_dsThunder is NULL\n"); + } + + m_isHDCPEnabled = isHDCPEnabled; + if (m_isHDCPEnabled) { + m_hdcpCurrentProtocol = hdcpCurrentProtocol; + MW_LOG_WARN("[DS-Thunder] HDCP version detected: %s\n", m_hdcpCurrentProtocol == dsHDCP_VERSION_2X ? "2.x" : "1.4"); + } else { + MW_LOG_WARN("[DS-Thunder] HDCP is not enabled\n"); + } + if (!isConnected) { + m_hdcpCurrentProtocol = dsHDCP_VERSION_1X; + MW_LOG_WARN("[DS-Thunder] GetHDCPVersion: Display not connected — defaulting to HDCP 1.4 (%d)\n", m_hdcpCurrentProtocol); + } + +#else bool isConnected = false; bool isHDCPCompliant = false; bool isHDCPEnabled = true; @@ -235,12 +344,9 @@ void PlayerExternalsRdkInterface::SetHDMIStatus() dsHdcpProtocolVersion_t hdcpReceiverProtocol = dsHDCP_VERSION_MAX; dsHdcpProtocolVersion_t hdcpCurrentProtocol = dsHDCP_VERSION_MAX; - - - try { //Get the HDMI port - device::Manager::Initialize(); + device::Manager::Initialize(); std::string strVideoPort = device::Host::getInstance().getDefaultVideoPortName(); ::device::VideoOutputPort &vPort = ::device::Host::getInstance().getVideoOutputPort(strVideoPort); isConnected = vPort.isDisplayConnected(); @@ -299,7 +405,7 @@ void PlayerExternalsRdkInterface::SetHDMIStatus() SetResolution(DISPLAY_RESOLUTION_NA,DISPLAY_RESOLUTION_NA); } - device::Manager::DeInitialize(); + device::Manager::DeInitialize(); } catch (const std::exception& e) { MW_LOG_WARN("DeviceSettings exception caught: %s\n", e.what()); @@ -327,11 +433,192 @@ void PlayerExternalsRdkInterface::SetHDMIStatus() m_hdcpCurrentProtocol = dsHDCP_VERSION_1X; MW_LOG_WARN(" GetHDCPVersion: Did not detect HDCP version defaulting to 1.4 (%d)\n", m_hdcpCurrentProtocol); } - +#endif return; } +#ifdef USE_DS_THUNDER_PLUGIN +/** + * @brief Signal the worker thread that an HDMI status update is needed. + * Safe to call from Thunder event callbacks (returns immediately). + * Multiple concurrent calls are coalesced — the worker runs SetHDMIStatus() + * exactly once per burst of events. + */ +void PlayerExternalsRdkInterface::PostHDMIStatusUpdate() +{ + { + std::lock_guard lk(m_eventMutex); + m_eventPending = true; + } + m_eventCv.notify_one(); + MW_LOG_WARN("[DS-Thunder] PostHDMIStatusUpdate: event queued\n"); +} + +/** + * @brief Worker thread body. + * Waits for a pending update, then calls SetHDMIStatus() once. + * Coalesces rapid back-to-back events into a single call. + */ +void PlayerExternalsRdkInterface::EventWorkerLoop() +{ + MW_LOG_WARN("[DS-Thunder] EventWorkerLoop: thread started\n"); + while (true) { + std::unique_lock lk(m_eventMutex); + m_eventCv.wait(lk, [this] { + return m_eventPending.load() || m_eventWorkerStop.load(); + }); + if (m_eventWorkerStop.load()) { + MW_LOG_WARN("[DS-Thunder] EventWorkerLoop: stop requested — exiting\n"); + break; + } + m_eventPending = false; + lk.unlock(); /* unlock before JSONRPC calls */ + MW_LOG_WARN("[DS-Thunder] EventWorkerLoop: calling SetHDMIStatus()\n"); + SetHDMIStatus(); + } +} + +/** + * @brief Create Thunder plugin objects and, in IARM mode, subscribe to Thunder + * HDMI/HDCP/resolution events. + * + * Event-source split: + * IARM mode : Thunder events are the source. + * Callbacks route through PostHDMIStatusUpdate() so that + * InvokeJSONRPC is never called from inside a Thunder callback. + * CSV mapping: + * IARM_BUS_DSMGR_EVENT_HDCP_STATUS -> HdcpProfile.1 onDisplayConnectionChanged + * IARM_BUS_DSMGR_EVENT_HDMI_HOTPLUG -> DisplaySettings.1 connectedVideoDisplaysUpdated + * IARM_BUS_DSMGR_EVENT_RES_POSTCHANGE -> DisplaySettings.1 resolutionChanged + * IARM_BUS_DSMGR_EVENT_RES_PRECHANGE -> DisplaySettings.1 resolutionPreChange + * Firebolt mode: Firebolt SDK delivers events via subscribeOnHdcpChanged / + * subscribeOnVideoResolutionChanged in DeviceFireboltInterface. + * Those handlers call PostHDMIStatusUpdate() directly. + * No Thunder subscriptions needed here. + * + * The Thunder plugin objects (m_hdcpProfileThunder, m_dsThunder) are created in + * both modes and used solely for the data queries inside SetHDMIStatus() + * (getHDCPStatus and getCurrentResolution). + */ +void PlayerExternalsRdkInterface::RegisterThunderEventHandlers() +{ + MW_LOG_WARN("[DS-Thunder] RegisterThunderEventHandlers() start\n"); + + /* Start the worker thread that serialises SetHDMIStatus() calls. + * Needed in both modes: Thunder callbacks must not call InvokeJSONRPC + * directly, and Firebolt callbacks may arrive concurrently. */ + m_eventWorkerStop = false; + m_eventPending = false; + m_eventWorkerThread = std::thread(&PlayerExternalsRdkInterface::EventWorkerLoop, this); + MW_LOG_WARN("[DS-Thunder] EventWorkerLoop thread started\n"); + + /* ---- HdcpProfile.1 ---- */ + MW_LOG_WARN("[DS-Thunder] Creating PlayerThunderAccess for HDCPPROFILE (org.rdk.HdcpProfile.1)\n"); + m_hdcpProfileThunder = std::make_unique(PlayerThunderAccessPlugin::HDCPPROFILE); + bool activateRet = m_hdcpProfileThunder->ActivatePlugin(); + MW_LOG_WARN("[DS-Thunder] HdcpProfile.1 ActivatePlugin() ret=%d\n", activateRet); + + /* ---- DisplaySettings.1 ---- */ + MW_LOG_WARN("[DS-Thunder] Creating PlayerThunderAccess for DS (org.rdk.DisplaySettings.1)\n"); + m_dsThunder = std::make_unique(PlayerThunderAccessPlugin::DS); + activateRet = m_dsThunder->ActivatePlugin(); + MW_LOG_WARN("[DS-Thunder] DisplaySettings.1 ActivatePlugin() ret=%d\n", activateRet); + + /* ---- Thunder event subscriptions (IARM mode only) ---- */ + if (m_initialized == InitState::IARM) + { + MW_LOG_WARN("[DS-Thunder] IARM mode: subscribing to Thunder events\n"); + + /* onDisplayConnectionChanged: replaces IARM_BUS_DSMGR_EVENT_HDCP_STATUS */ + bool subRet = m_hdcpProfileThunder->SubscribeEvent( + "onDisplayConnectionChanged", + [this](const WPEFramework::Core::JSON::VariantContainer& params) { + std::string paramsStr; + params.ToString(paramsStr); + MW_LOG_WARN("[DS-Thunder] onDisplayConnectionChanged received params=%s\n", paramsStr.c_str()); + PostHDMIStatusUpdate(); + }); + MW_LOG_WARN("[DS-Thunder] HdcpProfile.1 SubscribeEvent(onDisplayConnectionChanged) ret=%d\n", subRet); + + /* connectedVideoDisplaysUpdated: replaces IARM_BUS_DSMGR_EVENT_HDMI_HOTPLUG */ + subRet = m_dsThunder->SubscribeEvent( + "connectedVideoDisplaysUpdated", + [this](const WPEFramework::Core::JSON::VariantContainer& params) { + std::string paramsStr; + params.ToString(paramsStr); + MW_LOG_WARN("[DS-Thunder] connectedVideoDisplaysUpdated received params=%s\n", paramsStr.c_str()); + PostHDMIStatusUpdate(); + }); + MW_LOG_WARN("[DS-Thunder] DisplaySettings.1 SubscribeEvent(connectedVideoDisplaysUpdated) ret=%d\n", subRet); + + /* resolutionChanged: replaces IARM_BUS_DSMGR_EVENT_RES_POSTCHANGE */ + subRet = m_dsThunder->SubscribeEvent( + "resolutionChanged", + [this](const WPEFramework::Core::JSON::VariantContainer& params) { + std::string paramsStr; + params.ToString(paramsStr); + MW_LOG_WARN("[DS-Thunder] resolutionChanged received params=%s\n", paramsStr.c_str()); + PostHDMIStatusUpdate(); + }); + MW_LOG_WARN("[DS-Thunder] DisplaySettings.1 SubscribeEvent(resolutionChanged) ret=%d\n", subRet); + + /* resolutionPreChange: replaces IARM_BUS_DSMGR_EVENT_RES_PRECHANGE (log only) */ + subRet = m_dsThunder->SubscribeEvent( + "resolutionPreChange", + [](const WPEFramework::Core::JSON::VariantContainer& params) { + MW_LOG_WARN("[DS-Thunder] resolutionPreChange event received\n"); + }); + MW_LOG_WARN("[DS-Thunder] DisplaySettings.1 SubscribeEvent(resolutionPreChange) ret=%d\n", subRet); + } + else + { + MW_LOG_WARN("[DS-Thunder] Firebolt mode: Thunder event subscriptions skipped " + "(events delivered by Firebolt SDK -> PostHDMIStatusUpdate)\n"); + } + + MW_LOG_WARN("[DS-Thunder] RegisterThunderEventHandlers() done\n"); +} + +/** + * @brief Stop the worker thread, unsubscribe Thunder events (IARM mode only), + * and release plugin objects. + */ +void PlayerExternalsRdkInterface::RemoveThunderEventHandlers() +{ + /* Stop worker thread first so no further SetHDMIStatus() calls are made */ + { + std::lock_guard lk(m_eventMutex); + m_eventWorkerStop = true; + } + m_eventCv.notify_one(); + if (m_eventWorkerThread.joinable()) { + m_eventWorkerThread.join(); + MW_LOG_WARN("[DS-Thunder] EventWorkerLoop thread joined\n"); + } + + /* Unsubscribe Thunder events — only if they were registered (IARM mode) */ + if (m_initialized == InitState::IARM) + { + if (m_hdcpProfileThunder) { + m_hdcpProfileThunder->UnSubscribeEvent("onDisplayConnectionChanged"); + } + if (m_dsThunder) { + m_dsThunder->UnSubscribeEvent("connectedVideoDisplaysUpdated"); + m_dsThunder->UnSubscribeEvent("resolutionChanged"); + m_dsThunder->UnSubscribeEvent("resolutionPreChange"); + } + } + + if (m_hdcpProfileThunder) { + m_hdcpProfileThunder.reset(); + } + if (m_dsThunder) { + m_dsThunder.reset(); + } +} +#endif /* USE_DS_THUNDER_PLUGIN */ + void PlayerExternalsRdkInterface::setHdcpProtocol(dsHdcpProtocolVersion_t t_protocol) { m_hdcpCurrentProtocol = t_protocol; diff --git a/externals/rdk/PlayerExternalsRdkInterface.h b/externals/rdk/PlayerExternalsRdkInterface.h index 2f35e7bc..7b99e33e 100644 --- a/externals/rdk/PlayerExternalsRdkInterface.h +++ b/externals/rdk/PlayerExternalsRdkInterface.h @@ -24,6 +24,8 @@ #ifndef PLAYER_IARM_RDK_INTERFACE_H #define PLAYER_IARM_RDK_INTERFACE_H + +#ifndef USE_DS_THUNDER_PLUGIN #include "manager.hpp" #include "host.hpp" #include "videoResolution.hpp" @@ -33,6 +35,18 @@ #include "dsDisplay.h" #include "audioOutputPort.hpp" #include "dsAudio.h" +#else +#include "PlayerThunderAccess.h" +#include +#include +#include +#include +#include +// Compatibility aliases: libds types not available when using Thunder +typedef int dsHdcpProtocolVersion_t; +#define dsHDCP_VERSION_1X 14 +#define dsHDCP_VERSION_2X 22 +#endif #include #include "PlayerExternalsInterfaceBase.h" @@ -88,6 +102,20 @@ class PlayerExternalsRdkInterface : public PlayerExternalsInterfaceBase /**< Callback function for fake tune operations */ std::function m_doFakeTuneCallback = nullptr; +#ifdef USE_DS_THUNDER_PLUGIN + std::unique_ptr m_hdcpProfileThunder; + std::unique_ptr m_dsThunder; + void RegisterThunderEventHandlers(); + void RemoveThunderEventHandlers(); + /* Worker thread: serialises and coalesces HDMI-status update requests */ + void EventWorkerLoop(); + std::thread m_eventWorkerThread; + std::mutex m_eventMutex; + std::condition_variable m_eventCv; + std::atomic m_eventPending{false}; + std::atomic m_eventWorkerStop{false}; +#endif + PlayerExternalsRdkInterface(); public: @@ -123,6 +151,12 @@ class PlayerExternalsRdkInterface : public PlayerExternalsInterfaceBase */ void SetResolution(int width, int height); +#ifdef USE_DS_THUNDER_PLUGIN + /* Post a pending HDMI-status update to the coalescing worker thread. + * Safe to call from any Firebolt event callback thread. */ + void PostHDMIStatusUpdate(); +#endif + // Singleton for object creation /** diff --git a/externals/rdk/PlayerThunderAccess.cpp b/externals/rdk/PlayerThunderAccess.cpp index 286ac37c..c14e63b8 100644 --- a/externals/rdk/PlayerThunderAccess.cpp +++ b/externals/rdk/PlayerThunderAccess.cpp @@ -35,7 +35,7 @@ using namespace std; using namespace WPEFramework; -#define SERVER_DETAILS "127.0.0.1:9998" +#define SERVER_DETAILS "100.64.11.1:9998" #define MAX_LENGTH 1024 #define APP_ID "MainPlayer" @@ -52,6 +52,7 @@ using namespace WPEFramework; #define WATERMARK_PLUGIN_CALLSIGN "org.rdk.Watermark.1" #define HDMIINPUT_CALLSIGN "org.rdk.HdmiInput.1" #define COMPOSITEINPUT_CALLSIGN "org.rdk.CompositeInput.1" +#define HDCPPROFILE_CALLSIGN "org.rdk.HdcpProfile.1" /** * @brief Structure to save the Thunder security token details @@ -110,6 +111,9 @@ PlayerThunderAccess::PlayerThunderAccess(PlayerThunderAccessPlugin callsign) case PlayerThunderAccessPlugin::COMPOSITEINPUT : pluginCallsign = COMPOSITEINPUT_CALLSIGN; break; + case PlayerThunderAccessPlugin::HDCPPROFILE : + pluginCallsign = HDCPPROFILE_CALLSIGN; + break; default: MW_LOG_ERR("Undefined plugin tried to initialize: %d", (int)callsign); pluginCallsign = ""; @@ -118,20 +122,26 @@ PlayerThunderAccess::PlayerThunderAccess(PlayerThunderAccessPlugin callsign) uint32_t status = Core::ERROR_NONE; + MW_LOG_WARN("[ThunderAccess] Connecting to Thunder server=%s callsign=%s", SERVER_DETAILS, pluginCallsign.c_str()); + Core::SystemInfo::SetEnvironment(_T("THUNDER_ACCESS"), (_T(SERVER_DETAILS))); string sToken = ""; #ifdef DISABLE_SECURITY_TOKEN gPlayerSecurityData.securityToken = "token=" + sToken; gPlayerSecurityData.tokenQueried = true; + MW_LOG_WARN("[ThunderAccess] Security token DISABLED"); #else if(!gPlayerSecurityData.tokenQueried) { unsigned char buffer[MAX_LENGTH] = {0}; gPlayerSecurityData.tokenStatus = GetSecurityToken(MAX_LENGTH,buffer); if(gPlayerSecurityData.tokenStatus > 0){ - // LOG_INFO( "[ThunderAccess] : GetSecurityToken success"); sToken = (char*)buffer; gPlayerSecurityData.securityToken = "token=" + sToken; + MW_LOG_WARN("[ThunderAccess] Security token obtained (len=%d)", gPlayerSecurityData.tokenStatus); + } + else { + MW_LOG_WARN("[ThunderAccess] GetSecurityToken failed status=%d — proceeding without token", gPlayerSecurityData.tokenStatus); } gPlayerSecurityData.tokenQueried = true; } @@ -149,7 +159,7 @@ PlayerThunderAccess::PlayerThunderAccess(PlayerThunderAccessPlugin callsign) if (NULL == controllerObject) { MW_LOG_WARN( "[ThunderAccess] Controller object creation failed"); } else { - MW_LOG_INFO( "[ThunderAccess] Controller object creation success"); + MW_LOG_INFO( "[ThunderAccess] Controller object creation success (server=%s)", SERVER_DETAILS); } } @@ -162,7 +172,7 @@ PlayerThunderAccess::PlayerThunderAccess(PlayerThunderAccessPlugin callsign) if (NULL == remoteObject) { MW_LOG_WARN( "[ThunderAccess] %s Client initialization failed", pluginCallsign.c_str()); } else { - MW_LOG_INFO( "[ThunderAccess] %s Client initialization success", pluginCallsign.c_str()); + MW_LOG_INFO( "[ThunderAccess] %s Client initialization success (server=%s)", pluginCallsign.c_str(), SERVER_DETAILS); } } diff --git a/externals/rdk/PlayerThunderAccess.h b/externals/rdk/PlayerThunderAccess.h index 427d2f5c..cba5c4c5 100644 --- a/externals/rdk/PlayerThunderAccess.h +++ b/externals/rdk/PlayerThunderAccess.h @@ -79,6 +79,15 @@ class PlayerThunderAccess : public PlayerThunderAccessBase */ bool ActivatePlugin() override; + /** + * @fn SubscribeEvent + * @note Subscribe event data for the specific plugin + * @param eventName,functionHandler Event name, Event handler + * @retval true on success + * @retval false on failure + */ + bool SubscribeEvent (string eventName, std::function functionHandler); + /** * @fn UnSubscribeEvent * @note unSubscribe event data for the specific plugin @@ -397,15 +406,6 @@ class PlayerThunderAccess : public PlayerThunderAccessBase */ bool GetResolutionFromDS_VIDEOIN(int & widthFromDS, int & heightFromDS); - /** - * @fn SubscribeEvent - * @note Subscribe event data for the specific plugin - * @param eventName,functionHandler Event name, Event handler - * @retval true on success - * @retval false on failure - */ - bool SubscribeEvent (string eventName, std::function functionHandler); - /** * @fn GetAudioTrackInternal *