Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions media/client/ipc/source/MediaKeysIpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
Expand Down
3 changes: 2 additions & 1 deletion media/public/include/MediaCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment on lines +295 to +296
};

/**
Expand Down
4 changes: 4 additions & 0 deletions media/server/ipc/source/MediaKeysModuleService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
2 changes: 2 additions & 0 deletions media/server/main/source/MediaKeysCapabilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
Expand Down
80 changes: 76 additions & 4 deletions media/server/main/source/MediaKeysServerInternal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,40 @@
*/

#include <stdexcept>
#include <chrono>
#include <thread>

#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


Comment on lines +27 to +54
namespace firebolt::rialto
{
const char *mediaKeyErrorStatusToString(const MediaKeyErrorStatus &status)
Expand Down Expand Up @@ -51,6 +81,9 @@ std::shared_ptr<IMediaKeysFactory> IMediaKeysFactory::createFactory()

namespace firebolt::rialto::server
{
constexpr std::chrono::milliseconds kOutputRestrictedRetryInterval{250};
constexpr std::chrono::seconds kOutputRestrictedRetryTimeout{6};

int32_t generateSessionId()
{
static int32_t keySessionId{0};
Expand Down Expand Up @@ -571,12 +604,51 @@ 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;
auto task = [&]() { status = decryptInternal(keySessionId, encrypted, caps); };
MediaKeyErrorStatus status{MediaKeyErrorStatus::FAIL};
const auto deadline = std::chrono::steady_clock::now() + kOutputRestrictedRetryTimeout;
do
{
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;
}
Comment on lines +607 to +642

if (status != MediaKeyErrorStatus::OUTPUT_RESTRICTED)
{
break;
}
RIALTO_SERVER_LOG_WARN("Decrypt returned OUTPUT_RESTRICTED, retrying after delay");
std::this_thread::sleep_for(kOutputRestrictedRetryInterval);
} while (std::chrono::steady_clock::now() < deadline);

Comment on lines +610 to 651
m_mainThread->enqueueTaskAndWait(m_mainThreadClientId, task);
return status;
}

Expand Down
6 changes: 6 additions & 0 deletions stubs/opencdm/open_cdm_adapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,10 @@ extern "C"
{
return ERROR_NONE;
}

OpenCDMError opencdm_gstreamer_session_decrypt_buffer_once(struct OpenCDMSession *session, GstBuffer *buffer,
GstCaps *caps)
{
return ERROR_NONE;
}
}
2 changes: 2 additions & 0 deletions wrappers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ target_include_directories(

PRIVATE
include
../common/interface/
../logging/include/
${GStreamerApp_INCLUDE_DIRS}
$<TARGET_PROPERTY:RialtoPlayerPublic,INTERFACE_INCLUDE_DIRECTORIES>
${WRAPPER_INCLUDES}
Expand Down
2 changes: 2 additions & 0 deletions wrappers/include/OcdmSession.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand All @@ -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.
Expand Down
65 changes: 65 additions & 0 deletions wrappers/source/OcdmSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
#include "opencdm/open_cdm_ext.h"
#include <dlfcn.h>
#include <mutex>
#include <vector>
#include "RialtoCommonLogging.h"

namespace
{
Expand Down Expand Up @@ -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)
Expand All @@ -121,6 +124,11 @@ OcdmSession::OcdmSession(struct OpenCDMSystem *systemHandle, IOcdmSessionClient
{
m_ocdmGstSessionDecryptEx =
(OcdmGstSessionDecryptExFn)dlsym(RTLD_DEFAULT, "opencdm_gstreamer_session_decrypt_ex");
m_ocdmGstSessionDecryptBufferOnce =
(OcdmGstSessionDecryptBufferOnceFn)dlsym(RTLD_DEFAULT, "opencdm_gstreamer_session_decrypt_buffer_once");
if(m_ocdmGstSessionDecryptBufferOnce != NULL){
RIALTO_COMMON_LOG_ERROR("DEBUG PURPOSE : m_ocdmGstSessionDecryptBufferOnce exists\n");
}
Comment on lines +129 to +131
});
}

Expand Down Expand Up @@ -190,11 +198,68 @@ 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;
}
Comment on lines 199 to 205

if (true)
{
// Extract key ID from the buffer's protection metadata
std::vector<uint8_t> keyId;
GstProtectionMeta *pm = reinterpret_cast<GstProtectionMeta *>(gst_buffer_get_protection_meta(encrypted));
if (pm)
{
Comment on lines +207 to +213
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<uint8_t>(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;
}
}

OpenCDMError result = opencdm_gstreamer_session_decrypt_buffer_once(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<uint8_t>(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;
}
}

return convertOpenCdmError(result);
}

// Fallback: adapter without _once handles retries internally.
OpenCDMError status = opencdm_gstreamer_session_decrypt_buffer(m_session, encrypted, caps);
return convertOpenCdmError(status);
}
Expand Down
Loading