Feature/hdmi issue 8.4#506
Conversation
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>
|
Pull request must be merged with a description containing the required fields, Summary: 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. |
There was a problem hiding this comment.
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_RESTRICTEDand 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 returnsOUTPUT_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, soopencdm_gstreamer_session_decrypt_buffer()will never be used even when_decrypt_buffer_onceis 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_onceis called directly even though you resolved it viadlsym. This defeats the optional-symbol approach and can cause link/runtime failures on platforms where the symbol is missing. Call throughm_ocdmGstSessionDecryptBufferOnce(after null-check) and fall back toopencdm_gstreamer_session_decrypt_bufferwhen 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 usingRIALTO_COMMON_LOG_ERRORwith "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_forfor up tokOutputRestrictedRetryTimeout. 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.
| if(m_ocdmGstSessionDecryptBufferOnce != NULL){ | ||
| RIALTO_COMMON_LOG_ERROR("DEBUG PURPOSE : m_ocdmGstSessionDecryptBufferOnce exists\n"); | ||
| } |
| /*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 | ||
|
|
||
|
|
| 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 |
| BUFFER_TOO_SMALL, /**< The size of the buffer is too small. */ | ||
| OUTPUT_RESTRICTED |
PR for 8p4 branch