diff --git a/media/client/ipc/source/MediaKeysIpc.cpp b/media/client/ipc/source/MediaKeysIpc.cpp index 24909664e..aa664fc55 100644 --- a/media/client/ipc/source/MediaKeysIpc.cpp +++ b/media/client/ipc/source/MediaKeysIpc.cpp @@ -123,6 +123,10 @@ const char *toString(const firebolt::rialto::MediaKeyErrorStatus &errorStatus) { return "FAIL"; } + case firebolt::rialto::MediaKeyErrorStatus::OUTPUT_RESTRICTED: + { + return "OUTPUT_RESTRICTED"; + } } return "UNKNOWN"; } @@ -334,7 +338,7 @@ bool MediaKeysIpc::containsKey(int32_t keySessionId, const std::vector } MediaKeyErrorStatus MediaKeysIpc::createKeySession(KeySessionType sessionType, std::weak_ptr client, - bool isLDL, int32_t &keySessionId) + bool isLDL, int32_t &keySessionId) { if (!reattachChannelIfRequired()) { @@ -415,6 +419,7 @@ MediaKeyErrorStatus MediaKeysIpc::generateRequest(int32_t keySessionId, InitData break; } + firebolt::rialto::GenerateRequestRequest request; request.set_media_keys_handle(m_mediaKeysHandle); request.set_key_session_id(keySessionId); diff --git a/media/public/include/MediaCommon.h b/media/public/include/MediaCommon.h index 92b544b8c..50252b01b 100644 --- a/media/public/include/MediaCommon.h +++ b/media/public/include/MediaCommon.h @@ -292,7 +292,8 @@ enum class MediaKeyErrorStatus NOT_SUPPORTED, /**< The request parameters are not supported. */ INVALID_STATE, /**< The object is in an invalid state for the operation. */ INTERFACE_NOT_IMPLEMENTED, /**< The interface is not implemented. */ - BUFFER_TOO_SMALL /**< The size of the buffer is too small. */ + BUFFER_TOO_SMALL, /**< The size of the buffer is too small. */ + OUTPUT_RESTRICTED }; /** @@ -473,6 +474,7 @@ struct PlaybackInfo int64_t currentPosition{-1}; /**< The current playback position */ double volume{1.0}; /**< The current volume */ }; + } // namespace firebolt::rialto #endif // FIREBOLT_RIALTO_MEDIA_COMMON_H_ diff --git a/media/server/ipc/source/MediaKeysModuleService.cpp b/media/server/ipc/source/MediaKeysModuleService.cpp index 4cb5f6293..68cc5772e 100644 --- a/media/server/ipc/source/MediaKeysModuleService.cpp +++ b/media/server/ipc/source/MediaKeysModuleService.cpp @@ -66,6 +66,10 @@ convertMediaKeyErrorStatus(const firebolt::rialto::MediaKeyErrorStatus &errorSta { return firebolt::rialto::ProtoMediaKeyErrorStatus::FAIL; } + case firebolt::rialto::MediaKeyErrorStatus::OUTPUT_RESTRICTED: + { + // TODO + } } return firebolt::rialto::ProtoMediaKeyErrorStatus::FAIL; } @@ -100,6 +104,7 @@ firebolt::rialto::InitDataType covertInitDataType(firebolt::rialto::GenerateRequ return firebolt::rialto::InitDataType::UNKNOWN; } } + } // namespace namespace firebolt::rialto::server::ipc @@ -277,11 +282,12 @@ void MediaKeysModuleService::generateRequest(::google::protobuf::RpcController * ::google::protobuf::Closure *done) { RIALTO_SERVER_LOG_DEBUG("entry:"); - MediaKeyErrorStatus status = m_cdmService.generateRequest(request->media_keys_handle(), request->key_session_id(), covertInitDataType(request->init_data_type()), std::vector{request->init_data().begin(), request->init_data().end()}); + + response->set_error_status(convertMediaKeyErrorStatus(status)); done->Run(); } diff --git a/media/server/main/source/ControlServerInternal.cpp b/media/server/main/source/ControlServerInternal.cpp index 49ae2e78a..012e7bc4b 100644 --- a/media/server/main/source/ControlServerInternal.cpp +++ b/media/server/main/source/ControlServerInternal.cpp @@ -98,7 +98,7 @@ ControlServerInternal::~ControlServerInternal() void ControlServerInternal::ack(int32_t ackId) { - RIALTO_SERVER_LOG_DEBUG("Control with id: %d received ack for ping: %d", m_controlId, ackId); + RIALTO_SERVER_LOG_ERROR("Control with id: %d received ack for ping: %d", m_controlId, ackId); auto task = [&]() { if (!m_heartbeatHandler) @@ -114,6 +114,7 @@ void ControlServerInternal::ack(int32_t ackId) m_heartbeatHandler.reset(); }; m_mainThread->enqueueTaskAndWait(m_mainThreadClientId, task); + RIALTO_SERVER_LOG_ERROR("Control with id: %d received ack for ping: %d task exit", m_controlId, ackId); } void ControlServerInternal::setApplicationState(const ApplicationState &state) diff --git a/media/server/main/source/MediaKeysCapabilities.cpp b/media/server/main/source/MediaKeysCapabilities.cpp index f0ad87a81..9988d61eb 100644 --- a/media/server/main/source/MediaKeysCapabilities.cpp +++ b/media/server/main/source/MediaKeysCapabilities.cpp @@ -47,6 +47,8 @@ const char *toString(const firebolt::rialto::MediaKeyErrorStatus &status) return "NOT_SUPPORTED"; case firebolt::rialto::MediaKeyErrorStatus::INVALID_STATE: return "INVALID_STATE"; + case firebolt::rialto::MediaKeyErrorStatus::OUTPUT_RESTRICTED: + return "OUTPUT_RESTRICTED"; } return "Unknown"; } diff --git a/media/server/main/source/MediaKeysServerInternal.cpp b/media/server/main/source/MediaKeysServerInternal.cpp index 7fd1b58bf..7c0ed79ff 100644 --- a/media/server/main/source/MediaKeysServerInternal.cpp +++ b/media/server/main/source/MediaKeysServerInternal.cpp @@ -18,10 +18,40 @@ */ #include +#include +#include #include "MediaKeysServerInternal.h" #include "RialtoServerLogging.h" +/*namespace +{ +const char *toString(const firebolt::rialto::MediaKeyErrorStatus &status) +{ + switch (status) + { + case firebolt::rialto::MediaKeyErrorStatus::OK: + return "OK"; + case firebolt::rialto::MediaKeyErrorStatus::FAIL: + return "FAIL"; + case firebolt::rialto::MediaKeyErrorStatus::BAD_SESSION_ID: + return "BAD_SESSION_ID"; + case firebolt::rialto::MediaKeyErrorStatus::INTERFACE_NOT_IMPLEMENTED: + return "INTERFACE_NOT_IMPLEMENTED"; + case firebolt::rialto::MediaKeyErrorStatus::BUFFER_TOO_SMALL: + return "BUFFER_TOO_SMALL"; + case firebolt::rialto::MediaKeyErrorStatus::NOT_SUPPORTED: + return "NOT_SUPPORTED"; + case firebolt::rialto::MediaKeyErrorStatus::INVALID_STATE: + return "INVALID_STATE"; + case firebolt::rialto::MediaKeyErrorStatus::OUTPUT_RESTRICTED: + return "OUTPUT_RESTRICTED"; + } + return "Unknown"; +} +} */// namespace + + namespace firebolt::rialto { const char *mediaKeyErrorStatusToString(const MediaKeyErrorStatus &status) @@ -51,6 +81,9 @@ std::shared_ptr IMediaKeysFactory::createFactory() namespace firebolt::rialto::server { +constexpr std::chrono::milliseconds kOutputRestrictedRetryInterval{100}; +//constexpr std::chrono::seconds kOutputRestrictedRetryTimeout{6}; + int32_t generateSessionId() { static int32_t keySessionId{0}; @@ -569,12 +602,48 @@ MediaKeyErrorStatus MediaKeysServerInternal::getCdmKeySessionIdInternal(int32_t MediaKeyErrorStatus MediaKeysServerInternal::decrypt(int32_t keySessionId, GstBuffer *encrypted, GstCaps *caps) { - RIALTO_SERVER_LOG_DEBUG("entry:"); + RIALTO_SERVER_LOG_ERROR("DEBUG PURPOSE: entry:decrypt"); - MediaKeyErrorStatus status; + MediaKeyErrorStatus status{MediaKeyErrorStatus::FAIL}; + auto task = [&]() { status = decryptInternal(keySessionId, encrypted, caps); }; - m_mainThread->enqueueTaskAndWait(m_mainThreadClientId, task); + RIALTO_SERVER_LOG_ERROR("DEBUG PURPOSE : Key session id :%d", keySessionId); + switch (status) + { + case firebolt::rialto::MediaKeyErrorStatus::OK: + RIALTO_SERVER_LOG_ERROR("DEBUG PURPOSE : Key session status : OK"); + break; + case firebolt::rialto::MediaKeyErrorStatus::FAIL: + RIALTO_SERVER_LOG_ERROR("DEBUG PURPOSE : Key session status : FAIL"); + break; + case firebolt::rialto::MediaKeyErrorStatus::BAD_SESSION_ID: + RIALTO_SERVER_LOG_ERROR("DEBUG PURPOSE : Key session status : BAD_SESSION_ID"); + break; + case firebolt::rialto::MediaKeyErrorStatus::INTERFACE_NOT_IMPLEMENTED: + RIALTO_SERVER_LOG_ERROR("DEBUG PURPOSE : Key session status : INTERFACE_NOT_IMPLEMENTED"); + break; + case firebolt::rialto::MediaKeyErrorStatus::BUFFER_TOO_SMALL: + RIALTO_SERVER_LOG_ERROR("DEBUG PURPOSE : Key session status : BUFFER_TOO_SMALL"); + break; + case firebolt::rialto::MediaKeyErrorStatus::NOT_SUPPORTED: + RIALTO_SERVER_LOG_ERROR("DEBUG PURPOSE : Key session status : NOT_SUPPORTED"); + break; + case firebolt::rialto::MediaKeyErrorStatus::INVALID_STATE: + RIALTO_SERVER_LOG_ERROR("DEBUG PURPOSE : Key session status : INVALID_STATE"); + break; + case firebolt::rialto::MediaKeyErrorStatus::OUTPUT_RESTRICTED: + RIALTO_SERVER_LOG_ERROR("DEBUG PURPOSE : Key session status : OUTPUT_RESTRICTED"); + break; + } + + if (status == MediaKeyErrorStatus::OUTPUT_RESTRICTED) + { + std::this_thread::sleep_for(kOutputRestrictedRetryInterval); + auto task = [&]() { status = decryptInternal(keySessionId, encrypted, caps); }; + m_mainThread->enqueueTaskAndWait(m_mainThreadClientId, task); + RIALTO_SERVER_LOG_WARN("Decrypt returned OUTPUT_RESTRICTED, retrying once after delay"); + } return status; } @@ -604,12 +673,14 @@ bool MediaKeysServerInternal::isNetflixPlayreadyKeySystem() const return m_kKeySystem.find("netflix") != std::string::npos; } + void MediaKeysServerInternal::ping(std::unique_ptr &&heartbeatHandler) { - RIALTO_SERVER_LOG_DEBUG("entry:"); + RIALTO_SERVER_LOG_ERROR("entry:"); auto task = [&]() { heartbeatHandler.reset(); }; m_mainThread->enqueueTaskAndWait(m_mainThreadClientId, task); + RIALTO_SERVER_LOG_ERROR("exit:"); } MediaKeyErrorStatus MediaKeysServerInternal::getMetricSystemData(std::vector &buffer) diff --git a/media/server/service/source/CdmService.cpp b/media/server/service/source/CdmService.cpp index 8ea716f61..aaad2fde2 100644 --- a/media/server/service/source/CdmService.cpp +++ b/media/server/service/source/CdmService.cpp @@ -501,7 +501,9 @@ MediaKeyErrorStatus CdmService::decrypt(int32_t keySessionId, GstBuffer *encrypt RIALTO_SERVER_LOG_ERROR("Media keys handle for mksId: %d does not exists", keySessionId); return MediaKeyErrorStatus::FAIL; } + RIALTO_SERVER_LOG_ERROR("ping_cmd_service_is_entry"); return m_mediaKeys[mediaKeysHandleIter->second.mediaKeysHandle]->decrypt(keySessionId, encrypted, caps); + RIALTO_SERVER_LOG_ERROR("ping_cmd_service_is_exit"); } bool CdmService::isNetflixPlayreadyKeySystem(int32_t keySessionId) @@ -581,11 +583,13 @@ void CdmService::decrementSessionIdUsageCounter(int32_t keySessionId) void CdmService::ping(const std::shared_ptr &heartbeatProcedure) { std::lock_guard lock{m_mediaKeysMutex}; + RIALTO_SERVER_LOG_ERROR("ping_cmd_service_is_entry"); for (const auto &mediaKeyPair : m_mediaKeys) { auto &mediaKeys = mediaKeyPair.second; mediaKeys->ping(heartbeatProcedure->createHandler()); } + RIALTO_SERVER_LOG_ERROR("ping_cmd_service_is_exited"); } MediaKeyErrorStatus CdmService::getMetricSystemData(int mediaKeysHandle, std::vector &buffer) diff --git a/wrappers/CMakeLists.txt b/wrappers/CMakeLists.txt index 3be841fd1..d70a28037 100644 --- a/wrappers/CMakeLists.txt +++ b/wrappers/CMakeLists.txt @@ -110,6 +110,8 @@ target_include_directories( PRIVATE include + ../common/interface/ + ../logging/include/ ${GStreamerApp_INCLUDE_DIRS} $ ${WRAPPER_INCLUDES} diff --git a/wrappers/include/OcdmSession.h b/wrappers/include/OcdmSession.h index 51841f701..c76716106 100644 --- a/wrappers/include/OcdmSession.h +++ b/wrappers/include/OcdmSession.h @@ -94,6 +94,7 @@ class OcdmSession : public IOcdmSession private: using OcdmGstSessionDecryptExFn = OpenCDMError (*)(struct OpenCDMSession *, GstBuffer *, GstBuffer *, const uint32_t, GstBuffer *, GstBuffer *, uint32_t, GstCaps *); + using OcdmGstSessionDecryptBufferOnceFn = OpenCDMError (*)(struct OpenCDMSession *, GstBuffer *, GstCaps *); /** * @brief The System handle. */ @@ -115,6 +116,7 @@ class OcdmSession : public IOcdmSession struct OpenCDMSession *m_session; static OcdmGstSessionDecryptExFn m_ocdmGstSessionDecryptEx; + static OcdmGstSessionDecryptBufferOnceFn m_ocdmGstSessionDecryptBufferOnce; /** * @brief Requests the processing of the challenge data. diff --git a/wrappers/source/OcdmSession.cpp b/wrappers/source/OcdmSession.cpp index a236826a9..1c1b33638 100644 --- a/wrappers/source/OcdmSession.cpp +++ b/wrappers/source/OcdmSession.cpp @@ -23,6 +23,8 @@ #include "opencdm/open_cdm_ext.h" #include #include +#include +#include "RialtoCommonLogging.h" namespace { @@ -109,6 +111,7 @@ const firebolt::rialto::KeyStatus convertKeyStatus(const KeyStatus &ocdmKeyStatu namespace firebolt::rialto::wrappers { OcdmSession::OcdmGstSessionDecryptExFn OcdmSession::m_ocdmGstSessionDecryptEx{nullptr}; +OcdmSession::OcdmGstSessionDecryptBufferOnceFn OcdmSession::m_ocdmGstSessionDecryptBufferOnce{nullptr}; OcdmSession::OcdmSession(struct OpenCDMSystem *systemHandle, IOcdmSessionClient *client) : m_systemHandle(systemHandle), m_ocdmSessionClient(client), m_session(nullptr) @@ -119,8 +122,13 @@ OcdmSession::OcdmSession(struct OpenCDMSystem *systemHandle, IOcdmSessionClient std::call_once(flag, []() { + void* handle = dlopen("libocdm.so", RTLD_LAZY); m_ocdmGstSessionDecryptEx = (OcdmGstSessionDecryptExFn)dlsym(RTLD_DEFAULT, "opencdm_gstreamer_session_decrypt_ex"); + m_ocdmGstSessionDecryptBufferOnce = (OcdmGstSessionDecryptBufferOnceFn)dlsym(handle,"opencdm_gstreamer_session_decrypt_buffer_once"); + if(m_ocdmGstSessionDecryptBufferOnce != NULL){ + RIALTO_COMMON_LOG_ERROR("DEBUG PURPOSE : m_ocdmGstSessionDecryptBufferOnce exists\n"); + } }); } @@ -190,11 +198,73 @@ MediaKeyErrorStatus OcdmSession::update(const uint8_t response[], uint32_t respo MediaKeyErrorStatus OcdmSession::decryptBuffer(GstBuffer *encrypted, GstCaps *caps) { + RIALTO_COMMON_LOG_ERROR("DEBUG PURPOSE : OcdmSession::decryptBuffer()\n"); if (!m_session) { return MediaKeyErrorStatus::FAIL; } + if (m_ocdmGstSessionDecryptBufferOnce) + { + // Extract key ID from the buffer's protection metadata + std::vector keyId; + GstProtectionMeta *pm = reinterpret_cast(gst_buffer_get_protection_meta(encrypted)); + if (pm) + { + const GValue *kidValue = gst_structure_get_value(pm->info, "kid"); + if (kidValue) + { + GstBuffer *kidBuf = gst_value_get_buffer(kidValue); + if (kidBuf) + { + GstMapInfo kidMap; + if (gst_buffer_map(kidBuf, &kidMap, GST_MAP_READ)) + { + keyId.assign(kidMap.data, kidMap.data + kidMap.size); + gst_buffer_unmap(kidBuf, &kidMap); + } + } + } + } + + // Pre-decrypt key status check: return OUTPUT_RESTRICTED immediately (no sleep) so + // the caller (MediaKeysServerInternal::decrypt) can retry from the GStreamer thread. + if (!keyId.empty()) + { + const ::KeyStatus preStatus = + opencdm_session_status(m_session, keyId.data(), static_cast(keyId.size())); + if (preStatus == OutputRestricted || preStatus == OutputRestrictedHDCP22) + { + + RIALTO_COMMON_LOG_ERROR("DEBUG PURPOSE : OcdmSession::decryptBuffer() : returning MediaKeyErrorStatus::OUTPUT_RESTRICTED(Pre decrypt)\n"); + return MediaKeyErrorStatus::OUTPUT_RESTRICTED; + } else { + RIALTO_COMMON_LOG_ERROR("DEBUG PURPOSE : OcdmSession::decryptBuffer() : returning error(Pre decrypt)\n"); + } + + } + + OpenCDMError result = m_ocdmGstSessionDecryptBufferOnce(m_session, encrypted, caps); + + // Post-decrypt status check: a failed decrypt during HDCP reauth may not carry a + // specific error code, so confirm via key status before signalling the caller to retry. + if (result != ERROR_NONE && !keyId.empty()) + { + const ::KeyStatus postStatus = + opencdm_session_status(m_session, keyId.data(), static_cast(keyId.size())); + if (postStatus == OutputRestricted || postStatus == OutputRestrictedHDCP22) + { + RIALTO_COMMON_LOG_ERROR("DEBUG PURPOSE : OcdmSession::decryptBuffer() : returning MediaKeyErrorStatus::OUTPUT_RESTRICTED(Post decrypt)\n"); + return MediaKeyErrorStatus::OUTPUT_RESTRICTED; + } else { + RIALTO_COMMON_LOG_ERROR("DEBUG PURPOSE : OcdmSession::decryptBuffer() : returning error(Post decrypt)\n"); + } + } + + return convertOpenCdmError(result); + } + + // Fallback: adapter without _once handles retries internally. OpenCDMError status = opencdm_gstreamer_session_decrypt_buffer(m_session, encrypted, caps); return convertOpenCdmError(status); }