Skip to content

Feature/hdmi issue 8.4#506

Open
balasaraswathy-n wants to merge 7 commits into
release/v0.15.2from
feature/HDMI_issue_8.4
Open

Feature/hdmi issue 8.4#506
balasaraswathy-n wants to merge 7 commits into
release/v0.15.2from
feature/HDMI_issue_8.4

Conversation

@balasaraswathy-n
Copy link
Copy Markdown

PR for 8p4 branch

Adler, Douglas and others added 7 commits May 20, 2026 17:18
Reason for change: HCDP reauth retry logic needed
Test Procedure: Hot pug hdmi and observe retry log messages
Risks: Low
Priority: High

Signed-off-by: Adler, Douglas <Douglas_Adler2+comcast@comcast.com>
Copilot AI review requested due to automatic review settings May 20, 2026 12:06
@github-actions
Copy link
Copy Markdown

Pull request must be merged with a description containing the required fields,

Summary:
Type: Feature/Fix/Cleanup
Test Plan:
Jira:

If there is no jira releated to this change, please put 'Jira: NO-JIRA'.

Description can be changed by editing the top comment on your pull request and making a new commit.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR appears to address an HDMI/HDCP-related decryption failure mode by introducing an OUTPUT_RESTRICTED decrypt status and adding retry logic around decryption so playback can recover during HDCP re-auth/output restriction transitions.

Changes:

  • Added MediaKeyErrorStatus::OUTPUT_RESTRICTED and updated a few status-to-string helpers.
  • Updated OCdm/GStreamer decryption to use a new “decrypt once” adapter API and added pre/post key-status checks for output restriction.
  • Added retry logic (with sleep + timeout) in MediaKeysServerInternal::decrypt() when decrypt returns OUTPUT_RESTRICTED.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
wrappers/source/OcdmSession.cpp Adds decrypt-once flow and output-restriction checks (currently includes debug logging + dead/incorrect branching).
wrappers/include/OcdmSession.h Declares function pointer type/member for decrypt-once adapter symbol.
wrappers/CMakeLists.txt Adds include paths to allow wrappers to include common/logging headers.
stubs/opencdm/open_cdm_adapter.cpp Adds stub implementation for the new decrypt-once adapter function.
media/server/main/source/MediaKeysServerInternal.cpp Adds retry loop for OUTPUT_RESTRICTED with timeout/interval and extra debug logging.
media/server/main/source/MediaKeysCapabilities.cpp Extends local status-to-string helper with OUTPUT_RESTRICTED.
media/server/ipc/source/MediaKeysModuleService.cpp Adds OUTPUT_RESTRICTED case in status conversion but leaves it as TODO (falls back to FAIL).
media/public/include/MediaCommon.h Adds MediaKeyErrorStatus::OUTPUT_RESTRICTED to the public API enum.
media/client/ipc/source/MediaKeysIpc.cpp Extends client-side status-to-string helper with OUTPUT_RESTRICTED.
Comments suppressed due to low confidence (5)

wrappers/source/OcdmSession.cpp:210

  • if (true) makes the fallback path unreachable, so opencdm_gstreamer_session_decrypt_buffer() will never be used even when _decrypt_buffer_once is unavailable. Replace this with a real capability check (e.g., if (m_ocdmGstSessionDecryptBufferOnce)), and execute the fallback when the symbol is not present.
    if (true)
    {
        // Extract key ID from the buffer's protection metadata
        std::vector<uint8_t> keyId;

wrappers/source/OcdmSession.cpp:246

  • opencdm_gstreamer_session_decrypt_buffer_once is called directly even though you resolved it via dlsym. This defeats the optional-symbol approach and can cause link/runtime failures on platforms where the symbol is missing. Call through m_ocdmGstSessionDecryptBufferOnce (after null-check) and fall back to opencdm_gstreamer_session_decrypt_buffer when null.
        }

        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

wrappers/source/OcdmSession.cpp:202

  • decryptBuffer() now logs on every call using RIALTO_COMMON_LOG_ERROR with "DEBUG PURPOSE" messages. Since this function runs per-sample, this will flood error logs in normal operation. Please remove these messages or downgrade them to DEBUG/TRACE behind a runtime flag.
MediaKeyErrorStatus OcdmSession::decryptBuffer(GstBuffer *encrypted, GstCaps *caps)
{
    RIALTO_COMMON_LOG_ERROR("DEBUG PURPOSE : OcdmSession::decryptBuffer()\n");
    if (!m_session)

media/server/main/source/MediaKeysServerInternal.cpp:648

  • The new OUTPUT_RESTRICTED retry loop blocks the calling thread via std::this_thread::sleep_for for up to kOutputRestrictedRetryTimeout. Since decryption is invoked from the GStreamer streaming path, this can stall the pipeline. Consider making retries non-blocking (e.g., return OUTPUT_RESTRICTED and let the decryptor drop/queue), or make the timeout/interval configurable and document the expected impact.
    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;
    	}

        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);

media/server/main/source/MediaKeysServerInternal.cpp:648

  • New retry behavior for OUTPUT_RESTRICTED is not covered by the existing unit tests for MediaKeys decrypt. Please add tests that verify: (1) OUTPUT_RESTRICTED triggers retries, (2) OK breaks the loop, and (3) the function returns OUTPUT_RESTRICTED after timeout.
    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;
    	}

        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);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +129 to +131
if(m_ocdmGstSessionDecryptBufferOnce != NULL){
RIALTO_COMMON_LOG_ERROR("DEBUG PURPOSE : m_ocdmGstSessionDecryptBufferOnce exists\n");
}
Comment on lines +27 to +54
/*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 +605 to +618
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::OUTPUT_RESTRICTED:
{
// TODO
Comment on lines +295 to +296
BUFFER_TOO_SMALL, /**< The size of the buffer is too small. */
OUTPUT_RESTRICTED
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants