Skip to content

Feature/hdmi switch8.4 test#508

Open
balasaraswathy-n wants to merge 21 commits into
masterfrom
feature/HDMISwitch8.4-test
Open

Feature/hdmi switch8.4 test#508
balasaraswathy-n wants to merge 21 commits into
masterfrom
feature/HDMISwitch8.4-test

Conversation

@balasaraswathy-n
Copy link
Copy Markdown

sleep for 1 sec and total wait for 4sec

aczs and others added 11 commits January 14, 2026 21:37
Summary: Change Wayland Display for TextTrack
Type: Fix
Test Plan: UT/ CT, Fullstack
Jira: ENTDAI-2218
Summary: Removed gstreamer interaction during RemoveSource
Type: Fix
Test Plan: UT/CT, Fullstack
Jira: LLAMA-18057

---------

Co-authored-by: Marcin Wojciechowski <marcin.wojciechowski@sky.uk>
Summary: Allow to switch audio codec, when audio is re-attached
Type: Fix
Test Plan: UT/CT, Fullstack
Jira: LLAMA-18057
Summary: Asure correct data & flush order during multiple flushes
Type: Fix
Test Plan: UT/CT, Fullstack
Jira: NO-JIRA
Summary: Removed blocking get_state calls from aml codec switch
Type: Fix
Test Plan: UT/CT, Fullstack
Jira: RDKEMW-13606, DELIA-70194

Co-authored-by: Adam Czynszak <92790185+aczs@users.noreply.github.com>
#484)

Summary: Fixed initialisation of playbackGroup elements in
GenericPlayerContext. Added linking of typefind and the parser after the
audio switch
Type: Feature
Test Plan: UT/CT, Fullstack
Jira: RDKEMW-16807
Summary: Do not treat server as deadlocked when outdated acks are
received
Type: Fix
Test Plan: UT/CT, Fullstack
Jira: RDKEMW-18258
Copilot AI review requested due to automatic review settings May 21, 2026 10:16
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 updates Rialto’s media pipeline and DRM/decryption flow to better handle HDMI/HDCP output restriction scenarios and to support reporting whether play() is synchronous vs asynchronous across the IPC boundary. It also refactors flush-on-preroll handling (moving state tracking to the dispatcher thread), adds audio typefind/parser relinking support, and updates a large set of unit/component tests accordingly.

Changes:

  • Add OUTPUT_RESTRICTED retry handling in the decryption path (OpenCDM wrapper + MediaKeys retry loop).
  • Extend play() API end-to-end to return an async flag (public API, IPC proto/service/client, mocks, tests).
  • Refactor flush-on-preroll flow (new controller interface/impl, dispatcher-thread integration), plus related generic-player task/test updates.

Reviewed changes

Copilot reviewed 118 out of 118 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
wrappers/source/OcdmSession.cpp Adds optional _decrypt_buffer_once path with pre/post key-status checks and debug logging.
wrappers/interface/IGstWrapper.h Adds wrapper APIs for element state queries and pad/bin operations.
wrappers/include/OcdmSession.h Declares new OpenCDM decrypt function pointer typedef/static.
wrappers/include/GstWrapper.h Implements newly added IGstWrapper methods.
wrappers/CMakeLists.txt Adds common/logging include directories to wrappers target.
serverManager/common/source/HealthcheckService.h Changes failed-ping tracking from counter to per-pingId set.
serverManager/common/source/HealthcheckService.cpp Handles “late ack” by removing specific pingId from failure set.
tests/unittests/serverManager/unittests/common/HealthcheckServiceTests.cpp Adds coverage for “late ack” behavior.
proto/mediapipelinemodule.proto Adds async field to PlayResponse and updates docstring.
media/public/include/MediaCommon.h Adds MediaKeyErrorStatus::OUTPUT_RESTRICTED.
media/public/include/IMediaPipeline.h Changes play() signature to play(bool &async).
media/server/service/include/IMediaPipelineService.h Updates play() signature to include async out param.
media/server/service/source/MediaPipelineService.h Updates service interface implementation signature.
media/server/service/source/MediaPipelineService.cpp Propagates async out param down to pipeline implementation.
media/server/ipc/source/MediaPipelineModuleService.cpp Sets PlayResponse.async based on server-side result.
media/client/ipc/interface/IMediaPipelineIpc.h Updates IPC interface play() to include async out param.
media/client/ipc/include/MediaPipelineIpc.h Updates IPC class interface accordingly.
media/client/ipc/source/MediaPipelineIpc.cpp Reads PlayResponse.async into caller-provided out param.
media/client/main/include/MediaPipeline.h Updates client MediaPipeline::play() signature.
media/client/main/source/MediaPipeline.cpp Forwards async from client API to IPC.
media/client/main/include/MediaPipelineProxy.h Updates proxy play() signature.
media/server/main/include/MediaPipelineServerInternal.h Updates server internal play() signature and docs.
media/server/main/source/MediaPipelineServerInternal.cpp Makes play() priority enqueue + removes gst-player call from removeSource.
media/server/main/source/MediaKeysServerInternal.cpp Adds OUTPUT_RESTRICTED retry loop with 1s sleep, 4s total timeout + debug logs.
media/server/main/source/MediaKeysCapabilities.cpp Adds OUTPUT_RESTRICTED to status-to-string mapping.
media/server/ipc/source/MediaKeysModuleService.cpp Adds OUTPUT_RESTRICTED conversion case (currently TODO).
media/server/gstplayer/include/IFlushOnPrerollController.h Replaces “postpone flush” API with wait/target-state based API.
media/server/gstplayer/include/FlushOnPrerollController.h Adds condition-variable based controller state.
media/server/gstplayer/source/FlushOnPrerollController.cpp Implements wait/setFlushing/prerolling/target-state transitions + logging.
media/server/gstplayer/include/IGstDispatcherThread.h Passes flush controller into dispatcher thread factory.
media/server/gstplayer/include/GstDispatcherThread.h Stores flush controller and updates constructor.
media/server/gstplayer/source/GstDispatcherThread.cpp Updates factory + reports pipeline state changes to flush controller.
media/server/gstplayer/include/GenericPlayerContext.h Stores flush controller as shared_ptr instead of value member.
media/server/gstplayer/source/tasks/generic/Flush.cpp Removes postponing logic; waits via controller and marks async flush.
media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h Extends createFlush() signature with isAsync.
media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h Updates factory interface (remove RemoveSource, extend Flush).
media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp Removes RemoveSource creation; adds isAsync into Flush creation.
media/server/gstplayer/include/tasks/generic/Flush.h Adds isAsync to task state.
media/server/gstplayer/source/tasks/generic/UpdatePlaybackGroup.cpp Optionally links typefind to parser when requested.
media/server/gstplayer/source/tasks/generic/DeepElementAdded.cpp Uses Utils type checks; tracks playsink bin and audio elements more robustly.
media/server/gstplayer/source/tasks/generic/NeedData.cpp Removes “audioSourceRemoved” suppression.
media/server/gstplayer/source/tasks/generic/HandleBusMessage.cpp Removes flush controller state updates (moved to dispatcher thread).
media/server/gstplayer/source/tasks/generic/CheckAudioUnderflow.cpp Underflow no longer depends on audioSourceRemoved flag.
media/server/gstplayer/source/tasks/generic/AttachSource.cpp Always reattaches audio via reattach path; removes playbin-flag toggling/needdata forcing.
media/server/gstplayer/source/Utils.cpp Adds isAudioParser() helper.
media/server/gstplayer/include/Utils.h Declares isAudioParser().
media/server/gstplayer/include/IGstGenericPlayerPrivate.h Changes changePipelineState() to return GstStateChangeReturn; removes postponed flush/playbin flags API.
media/server/gstplayer/interface/IGstGenericPlayer.h Changes play() to play(bool &async); removes removeSource() API.
media/server/gstplayer/include/GstGenericPlayer.h Implements new play/flush/state-change patterns; adds large audio codec switch helpers + ongoing state-change counter.
media/server/gstplayer/source/GstGenericPlayer.cpp Adds play async reporting, flush changes, and large on-the-fly audio codec switching logic.
media/server/gstplayer/source/GstWebAudioPlayer.cpp Updates dispatcher thread factory call signature (adds flush controller arg).
media/server/gstplayer/source/GstTextTrackSink.cpp Hardcodes subtitle display name instead of using WAYLAND_DISPLAY env var.
media/server/gstplayer/CMakeLists.txt Removes RemoveSource task compilation unit.
media/server/gstplayer/source/tasks/generic/RemoveSource.cpp Removes RemoveSource task implementation.
media/server/gstplayer/include/tasks/generic/RemoveSource.h Removes RemoveSource task header.
tests/common/externalLibraryMocks/GstWrapperMock.h Adds mocks for new IGstWrapper methods.
tests/unittests/media/server/gstplayer/dispatcherThread/GstDispatcherThreadTest.cpp Updates dispatcher thread tests for flush controller integration + adds new state-change tests.
tests/unittests/media/server/gstplayer/genericPlayer/FlushOnPrerollControllerTest.cpp Replaces postpone-flush tests with wait-based tests (includes a typo).
tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/UpdatePlaybackGroupTest.cpp Adds coverage for linking typefind with parser.
tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/NeedDataTest.cpp Removes test tied to audioSourceRemoved behavior.
tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/FlushTest.cpp Removes postpone-flush test.
tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/RemoveSourceTest.cpp Removes RemoveSource task tests.
tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp Removes RemoveSource factory test; updates Flush signature usage.
tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/HandleBusMessageTest.cpp Removes expectation for executePostponedFlushes().
tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/AttachSourceTest.cpp Updates audio source reattach/switch expectations.
tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h Removes RemoveSource helpers; adds typefind/parser link helpers.
tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp Updates helpers for new flush/controller behavior and type checks.
tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp Updates dispatcher thread factory call signature.
tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp Updates play tests to validate async out param and flush factory call signature.
tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp Adds extensive tests for codec-switch paths and updated getSink behavior.
tests/unittests/media/server/gstplayer/mocks/gstplayer/GstDispatcherThreadFactoryMock.h Updates mock signature with flush controller arg.
tests/unittests/media/server/gstplayer/mocks/gstplayer/FlushOnPrerollControllerMock.h Updates mock interface to new controller methods.
tests/unittests/media/server/gstplayer/mocks/gstplayer/GstGenericPlayerMock.h Updates play mock signature; removes removeSource mock.
tests/unittests/media/server/gstplayer/mocks/gstplayer/GstGenericPlayerPrivateMock.h Updates changePipelineState return type; removes obsolete methods.
tests/unittests/media/server/gstplayer/mocks/gstplayer/GenericPlayerTaskFactoryMock.h Updates Flush signature; removes RemoveSource mock.
tests/unittests/media/server/gstplayer/CMakeLists.txt Drops RemoveSource test file from build.
tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp Updates play path to priority task + async out param.
tests/unittests/media/server/main/mediaPipeline/base/MediaPipelineTestBase.h Adds priority enqueue helper.
tests/unittests/media/server/main/mediaPipeline/base/MediaPipelineTestBase.cpp Implements priority enqueue helper for tests.
tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp Updates play signature in fixture and expectations.
tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h Updates play mock signature.
tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h Updates play mock signature.
tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp Updates play expectations to include async out param.
tests/unittests/media/server/gstplayer/webAudioPlayer/CreateTest.cpp Updates dispatcher thread factory mock invocation signature.
tests/unittests/media/server/gstplayer/webAudioPlayer/common/GstWebAudioPlayerTestCommon.cpp Updates dispatcher thread factory expectation signature.
tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h Updates play mock signature.
tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h Updates play mock signature.
tests/unittests/media/client/main/mediaPipeline/PlayPauseTest.cpp Updates play tests to pass async out param.
tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp Updates play passthrough test to pass async out param.
tests/unittests/media/client/ipc/mediaPipelineIpc/PlayPauseTest.cpp Updates IPC play tests to pass async out param.
tests/componenttests/server/common/ExpectMessage.h Increases default message timeout from 400ms to 600ms.
tests/componenttests/server/fixtures/MediaPipelineTest.h Removes willRemoveAudioSource/willSetAudioAndVideoFlags helpers.
tests/componenttests/server/fixtures/MediaPipelineTest.cpp Removes audio-source removal flush+flag logic; removeSource no longer waits worker.
tests/componenttests/server/tests/CMakeLists.txt Replaces RemoveAudioPlaybackTest with SwitchAudioPlaybackTest.
tests/componenttests/server/tests/mediaPipeline/SwitchAudioPlaybackTest.cpp Adds (disabled) component test for switching audio source mid-playback.
tests/componenttests/server/tests/mediaPipeline/AudioSourceSwitchTest.cpp Updates to fetch audio-sink element and adds amlhalasink prefix handling in expectations.
tests/componenttests/server/tests/mediaPipeline/*.cpp (multiple) Removes willRemoveAudioSource expectation calls across many pipeline tests.
tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp Updates play helper to pass async out param.
media/client/ipc/source/MediaKeysIpc.cpp Adds OUTPUT_RESTRICTED to toString(); minor formatting changes.
wrappers/include/GstWrapper.h / wrappers/interface/IGstWrapper.h Adds low-level GStreamer accessors needed by new codec-switch code.
Comments suppressed due to low confidence (1)

media/server/gstplayer/source/GstGenericPlayer.cpp:879

  • Similar to firstTimeSwitchFromAC3toAAC(), gstPadGetPeer()/gstPadUnlink() are called with pad pointers that may be NULL (e.g., audioDecSrcPad/audioDecSinkPad/audioParseSrcPad/audioParseSinkPad). Please add null checks before calling these wrappers and before unref’ing pads/peers to avoid crashes when elements don’t expose the expected static pads.
    // Get AudioDecoder Src Pads
    if ((audioDecSrcPad = m_gstWrapper->gstElementGetStaticPad(m_context.playbackGroup.m_curAudioDecoder, "src")) !=
        NULL) // Unref the Pad
        RIALTO_SERVER_LOG_DEBUG("OTF -> Current AudioDecoder Src Pad = %p", audioDecSrcPad);
    // Get AudioDecoder Sink Pads
    if ((audioDecSinkPad = m_gstWrapper->gstElementGetStaticPad(m_context.playbackGroup.m_curAudioDecoder, "sink")) !=
        NULL) // Unref the Pad
        RIALTO_SERVER_LOG_DEBUG("OTF -> Current AudioDecoder Sink Pad = %p", audioDecSinkPad);
    // Get AudioDecoder Src Peer i.e. Downstream Element Pad
    if ((audioDecSrcPeerPad = m_gstWrapper->gstPadGetPeer(audioDecSrcPad)) != NULL) // Unref the Pad
        RIALTO_SERVER_LOG_DEBUG("OTF -> Current AudioDecoder Src Downstream Element Pad = %p", audioDecSrcPeerPad);
    // Get AudioDecoder Sink Peer i.e. Upstream Element Pad
    if ((audioDecSinkPeerPad = m_gstWrapper->gstPadGetPeer(audioDecSinkPad)) != NULL) // Unref the Pad
        RIALTO_SERVER_LOG_DEBUG("OTF -> Current AudioDecoder Sink Upstream Element Pad = %p", audioDecSinkPeerPad);
    // Get AudioParser Src Pads
    if ((audioParseSrcPad = m_gstWrapper->gstElementGetStaticPad(m_context.playbackGroup.m_curAudioParse, "src")) !=
        NULL) // Unref the Pad
        RIALTO_SERVER_LOG_DEBUG("OTF -> Current AudioParser Src Pad = %p", audioParseSrcPad);
    // Get AudioParser Sink Pads
    if ((audioParseSinkPad = m_gstWrapper->gstElementGetStaticPad(m_context.playbackGroup.m_curAudioParse, "sink")) !=
        NULL) // Unref the Pad
        RIALTO_SERVER_LOG_DEBUG("OTF -> Current AudioParser Sink Pad = %p", audioParseSinkPad);
    // Get AudioParser Src Peer i.e. Downstream Element Pad
    if ((audioParseSrcPeerPad = m_gstWrapper->gstPadGetPeer(audioParseSrcPad)) != NULL) // Unref the Peer Pad
        RIALTO_SERVER_LOG_DEBUG("OTF -> Current AudioParser Src Downstream Element Pad = %p", audioParseSrcPeerPad);
    // Get AudioParser Sink Peer i.e. Upstream Element Pad
    if ((audioParseSinkPeerPad = m_gstWrapper->gstPadGetPeer(audioParseSinkPad)) != NULL) // Unref the Peer Pad
        RIALTO_SERVER_LOG_DEBUG("OTF -> Current AudioParser Sink Upstream Element Pad = %p", audioParseSinkPeerPad);
    // AudioDecoder Downstream Unlink
    if (m_gstWrapper->gstPadUnlink(audioDecSrcPad, audioDecSrcPeerPad) == FALSE)
        RIALTO_SERVER_LOG_DEBUG("OTF -> AudioDecoder Downstream Unlink Failed");
    // AudioDecoder Upstream Unlink
    if (m_gstWrapper->gstPadUnlink(audioDecSinkPeerPad, audioDecSinkPad) == FALSE)
        RIALTO_SERVER_LOG_DEBUG("OTF -> AudioDecoder Upstream Unlink Failed");
    // AudioParser Downstream Unlink
    if (m_gstWrapper->gstPadUnlink(audioParseSrcPad, audioParseSrcPeerPad) == FALSE)
        RIALTO_SERVER_LOG_DEBUG("OTF -> AudioParser Downstream Unlink Failed");
    // AudioParser Upstream Unlink
    if (m_gstWrapper->gstPadUnlink(audioParseSinkPeerPad, audioParseSinkPad) == FALSE)
        RIALTO_SERVER_LOG_DEBUG("OTF -> AudioParser Upstream Unlink Failed");

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

Comment on lines +125 to +131
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");
}
Comment on lines 199 to 203
MediaKeyErrorStatus OcdmSession::decryptBuffer(GstBuffer *encrypted, GstCaps *caps)
{
RIALTO_COMMON_LOG_ERROR("DEBUG PURPOSE : OcdmSession::decryptBuffer()\n");
if (!m_session)
{
Comment on lines +236 to +243
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");
}
Comment on lines 603 to +640
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 +648
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);
Comment on lines 67 to 74
return firebolt::rialto::ProtoMediaKeyErrorStatus::FAIL;
}
case firebolt::rialto::MediaKeyErrorStatus::OUTPUT_RESTRICTED:
{
// TODO
}
}
return firebolt::rialto::ProtoMediaKeyErrorStatus::FAIL;
Comment on lines 96 to +104
if (pingId != m_currentPingId)
{
RIALTO_SERVER_MANAGER_LOG_WARN("Unexpected ack received from server id: %d. Current ping id: %d, received ping "
"id: %d",
serverId, m_currentPingId, pingId);
if (success && m_failedPings[serverId].find(pingId) != m_failedPings[serverId].end())
{
RIALTO_SERVER_MANAGER_LOG_WARN("Late ack received for server id: %d, Current ping id: %d, received ping "
"id: %d. Removing from failed pings list",
serverId, m_currentPingId, pingId);
m_failedPings[serverId].erase(pingId);
}
Comment on lines 287 to 304
bool MediaPipelineServerInternal::removeSourceInternal(int32_t id)
{
if (!m_gstPlayer)
{
RIALTO_SERVER_LOG_ERROR("Failed to remove source - Gstreamer player has not been loaded");
return false;
}
auto sourceIter = std::find_if(m_attachedSources.begin(), m_attachedSources.end(),
[id](const auto &src) { return src.second == id; });
if (sourceIter == m_attachedSources.end())
{
RIALTO_SERVER_LOG_ERROR("Failed to remove source with id %d- Source not found", id);
return false;
}

m_gstPlayer->removeSource(sourceIter->first);
m_needMediaDataTimers.erase(sourceIter->first);
m_attachedSources.erase(sourceIter);
return true;
Comment on lines +734 to +744
if ((pTypfdSrcPad = m_gstWrapper->gstElementGetStaticPad(m_context.playbackGroup.m_curAudioTypefind, "src")) !=
NULL) // Unref the Pad
RIALTO_SERVER_LOG_DEBUG("OTF -> Current Typefind SrcPad = %p", pTypfdSrcPad);
if ((pTypfdSrcPeerPad = m_gstWrapper->gstPadGetPeer(pTypfdSrcPad)) != NULL) // Unref the Pad
RIALTO_SERVER_LOG_DEBUG("OTF -> Current Typefind Src Downstream Element Pad = %p", pTypfdSrcPeerPad);
// AudioDecoder Downstream Unlink
if (m_gstWrapper->gstPadUnlink(pTypfdSrcPad, pTypfdSrcPeerPad) == FALSE)
RIALTO_SERVER_LOG_DEBUG("OTF -> Typefind Downstream Unlink Failed");
newAudioParse = m_gstWrapper->gstElementFactoryMake("aacparse", "aacparse");
newAudioDecoder = m_gstWrapper->gstElementFactoryMake("avdec_aac", "avdec_aac");
newQueue = m_gstWrapper->gstElementFactoryMake("queue", "aqueue");
};

TEST_F(FlushOnPrerollControllerTest, shouldNotPostponeFlushWhenNoFlushSet)
TEST_F(FlushOnPrerollControllerTest, shouldNotWaithWhenNoFlushSet)
Copilot AI review requested due to automatic review settings May 21, 2026 17:45
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

Copilot reviewed 118 out of 118 changed files in this pull request and generated 14 comments.

Comments suppressed due to low confidence (1)

tests/componenttests/server/tests/mediaPipeline/SwitchAudioPlaybackTest.cpp:224

  • The new component test is checked in as DISABLED_SwitchAudio, so it won’t run in CI even though the file is added to the test target. If this scenario is important for the change, please enable the test (or add an active equivalent) or document why it must remain disabled to avoid losing coverage for audio switching.

Comment on lines 66 to 74
{
return firebolt::rialto::ProtoMediaKeyErrorStatus::FAIL;
}
case firebolt::rialto::MediaKeyErrorStatus::OUTPUT_RESTRICTED:
{
// TODO
}
}
return firebolt::rialto::ProtoMediaKeyErrorStatus::FAIL;
Comment on lines 95 to +104
std::unique_lock<std::mutex> lock{m_mutex};
if (pingId != m_currentPingId)
{
RIALTO_SERVER_MANAGER_LOG_WARN("Unexpected ack received from server id: %d. Current ping id: %d, received ping "
"id: %d",
serverId, m_currentPingId, pingId);
if (success && m_failedPings[serverId].find(pingId) != m_failedPings[serverId].end())
{
RIALTO_SERVER_MANAGER_LOG_WARN("Late ack received for server id: %d, Current ping id: %d, received ping "
"id: %d. Removing from failed pings list",
serverId, m_currentPingId, pingId);
m_failedPings[serverId].erase(pingId);
}
Comment on lines +27 to +53
/*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 603 to +648
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;
}

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 121 to +131
static std::once_flag flag;
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");
}
Comment on lines +775 to +777
m_gstWrapper->gstObjectUnref(pTypfdSrcPad);
m_gstWrapper->gstObjectUnref(pTypfdSrcPeerPad);
m_gstWrapper->gstObjectUnref(pNewAudioDecoderSrcPad);
Comment on lines +840 to +853
// Get AudioDecoder Src Pads
if ((audioDecSrcPad = m_gstWrapper->gstElementGetStaticPad(m_context.playbackGroup.m_curAudioDecoder, "src")) !=
NULL) // Unref the Pad
RIALTO_SERVER_LOG_DEBUG("OTF -> Current AudioDecoder Src Pad = %p", audioDecSrcPad);
// Get AudioDecoder Sink Pads
if ((audioDecSinkPad = m_gstWrapper->gstElementGetStaticPad(m_context.playbackGroup.m_curAudioDecoder, "sink")) !=
NULL) // Unref the Pad
RIALTO_SERVER_LOG_DEBUG("OTF -> Current AudioDecoder Sink Pad = %p", audioDecSinkPad);
// Get AudioDecoder Src Peer i.e. Downstream Element Pad
if ((audioDecSrcPeerPad = m_gstWrapper->gstPadGetPeer(audioDecSrcPad)) != NULL) // Unref the Pad
RIALTO_SERVER_LOG_DEBUG("OTF -> Current AudioDecoder Src Downstream Element Pad = %p", audioDecSrcPeerPad);
// Get AudioDecoder Sink Peer i.e. Upstream Element Pad
if ((audioDecSinkPeerPad = m_gstWrapper->gstPadGetPeer(audioDecSinkPad)) != NULL) // Unref the Pad
RIALTO_SERVER_LOG_DEBUG("OTF -> Current AudioDecoder Sink Upstream Element Pad = %p", audioDecSinkPeerPad);
Comment on lines +1075 to +1076
reconfigDelayMs = now.tv_nsec > ts.tv_nsec ? (now.tv_nsec - ts.tv_nsec) / 1000000
: (1000 - (ts.tv_nsec - now.tv_nsec) / 1000000);

MediaKeyErrorStatus MediaKeysIpc::createKeySession(KeySessionType sessionType, std::weak_ptr<IMediaKeysClient> client,
bool isLDL, int32_t &keySessionId)
bool isLDL, int32_t &keySessionId)
};

TEST_F(FlushOnPrerollControllerTest, shouldNotPostponeFlushWhenNoFlushSet)
TEST_F(FlushOnPrerollControllerTest, shouldNotWaithWhenNoFlushSet)
Copilot AI review requested due to automatic review settings May 22, 2026 02:11
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

Copilot reviewed 118 out of 118 changed files in this pull request and generated 6 comments.

Comments suppressed due to low confidence (3)

wrappers/source/OcdmSession.cpp:244

  • These RIALTO_COMMON_LOG_ERROR("DEBUG PURPOSE ...") calls add noisy ERROR-level logging on every decrypt (and include embedded newlines). Also the else branches log "returning error" but do not return, which is misleading during troubleshooting. Please remove or downgrade to DEBUG/TRACE and only log real error paths; consider removing the misleading else logs or returning an actual error when intended.
    serverManager/common/source/HealthcheckService.cpp:158
  • handleError() also uses m_failedPings[serverId], which will create a new entry if serverId was never registered via onPingSent() (e.g., if handleError is called for an untracked server). Consider guarding with find()/try_emplace() to make it explicit when a server becomes tracked and avoid silent insertion on error paths.
    tests/componenttests/server/tests/mediaPipeline/SwitchAudioPlaybackTest.cpp:3
  • The file now starts with a blank line before the license header comment. If tooling expects the header at line 1 (common for license scanners), this may cause false negatives. Consider removing the leading blank line so the header begins at the top of the file.

Comment on lines +125 to +131
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");
}
Comment on lines 36 to 75
firebolt::rialto::ProtoMediaKeyErrorStatus
convertMediaKeyErrorStatus(const firebolt::rialto::MediaKeyErrorStatus &errorStatus)
{
switch (errorStatus)
{
case firebolt::rialto::MediaKeyErrorStatus::OK:
{
return firebolt::rialto::ProtoMediaKeyErrorStatus::OK;
}
case firebolt::rialto::MediaKeyErrorStatus::BAD_SESSION_ID:
{
return firebolt::rialto::ProtoMediaKeyErrorStatus::BAD_SESSION_ID;
}
case firebolt::rialto::MediaKeyErrorStatus::INTERFACE_NOT_IMPLEMENTED:
{
return firebolt::rialto::ProtoMediaKeyErrorStatus::INTERFACE_NOT_IMPLEMENTED;
}
case firebolt::rialto::MediaKeyErrorStatus::BUFFER_TOO_SMALL:
{
return firebolt::rialto::ProtoMediaKeyErrorStatus::BUFFER_TOO_SMALL;
}
case firebolt::rialto::MediaKeyErrorStatus::NOT_SUPPORTED:
{
return firebolt::rialto::ProtoMediaKeyErrorStatus::NOT_SUPPORTED;
}
case firebolt::rialto::MediaKeyErrorStatus::INVALID_STATE:
{
return firebolt::rialto::ProtoMediaKeyErrorStatus::INVALID_STATE;
}
case firebolt::rialto::MediaKeyErrorStatus::FAIL:
{
return firebolt::rialto::ProtoMediaKeyErrorStatus::FAIL;
}
case firebolt::rialto::MediaKeyErrorStatus::OUTPUT_RESTRICTED:
{
// TODO
}
}
return firebolt::rialto::ProtoMediaKeyErrorStatus::FAIL;
}
Comment on lines 603 to +648
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;
}

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 93 to +111
void HealthcheckService::onAckReceived(int serverId, int pingId, bool success)
{
std::unique_lock<std::mutex> lock{m_mutex};
if (pingId != m_currentPingId)
{
RIALTO_SERVER_MANAGER_LOG_WARN("Unexpected ack received from server id: %d. Current ping id: %d, received ping "
"id: %d",
serverId, m_currentPingId, pingId);
if (success && m_failedPings[serverId].find(pingId) != m_failedPings[serverId].end())
{
RIALTO_SERVER_MANAGER_LOG_WARN("Late ack received for server id: %d, Current ping id: %d, received ping "
"id: %d. Removing from failed pings list",
serverId, m_currentPingId, pingId);
m_failedPings[serverId].erase(pingId);
}
else
{
RIALTO_SERVER_MANAGER_LOG_ERROR("Unexpected ack received from server id: %d. Current ping id: %d, received "
"ping "
"id: %d",
serverId, m_currentPingId, pingId);
}
Comment on lines 160 to 168
static gboolean gst_rialto_text_track_sink_start(GstBaseSink *sink) // NOLINT(build/function_format)
{
const char *wayland_display = std::getenv("WAYLAND_DISPLAY");
if (!wayland_display)
{
GST_ERROR_OBJECT(sink, "Failed to get WAYLAND_DISPLAY env variable");
return false;
}

std::string display{wayland_display};
const std::string kDisplay{"westeros-asplayer-subtitles"};
GstRialtoTextTrackSink *self = GST_RIALTO_TEXT_TRACK_SINK(sink);
try
{
self->priv->m_textTrackSession =
firebolt::rialto::server::ITextTrackSessionFactory::getFactory().createTextTrackSession(display);
firebolt::rialto::server::ITextTrackSessionFactory::getFactory().createTextTrackSession(kDisplay);
}
Comment on lines +607 to +648
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);
Copilot AI review requested due to automatic review settings May 22, 2026 19:32
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

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

Comments suppressed due to low confidence (2)

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

  • decrypt() now performs a blocking sleep/retry loop on OUTPUT_RESTRICTED, potentially stalling the calling thread for up to the timeout duration. This can have significant playback/latency impact; consider making the retry non-blocking (e.g., schedule retries on the worker/main thread) or ensure the caller is explicitly designed to tolerate this delay.
    media/server/main/source/MediaKeysServerInternal.cpp:640
  • There are extensive "DEBUG PURPOSE" logs emitted at ERROR level for every decrypt attempt/status, which will be very noisy and misleading in production. Please remove these or downgrade to DEBUG with concise messages (and ideally avoid per-buffer logs unless behind a feature flag).

Comment on lines 585 to 592
RIALTO_SERVER_LOG_ERROR("DEBUG PURPOSE: ping entry")
for (const auto &mediaKeyPair : m_mediaKeys)
{
auto &mediaKeys = mediaKeyPair.second;
mediaKeys->ping(heartbeatProcedure->createHandler());
}
RIALTO_SERVER_LOG_ERROR("DEBUG PURPOSE: ping exit")
}
Comment on lines +125 to +131
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");
}
Comment on lines +201 to 203
RIALTO_COMMON_LOG_ERROR("DEBUG PURPOSE : OcdmSession::decryptBuffer()\n");
if (!m_session)
{
Comment on lines +236 to +243
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");
}
Comment on lines +84 to +86
constexpr std::chrono::milliseconds kOutputRestrictedRetryInterval{250};
constexpr std::chrono::seconds kOutputRestrictedRetryTimeout{6};

Comment on lines +27 to +53
/*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 +69 to 74
case firebolt::rialto::MediaKeyErrorStatus::OUTPUT_RESTRICTED:
{
// TODO
}
}
return firebolt::rialto::ProtoMediaKeyErrorStatus::FAIL;
};

TEST_F(FlushOnPrerollControllerTest, shouldNotPostponeFlushWhenNoFlushSet)
TEST_F(FlushOnPrerollControllerTest, shouldNotWaithWhenNoFlushSet)
return false;
}
return true;
--m_ongoingStateChangesNumber;
Copilot AI review requested due to automatic review settings May 22, 2026 19:57
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

Copilot reviewed 119 out of 119 changed files in this pull request and generated 8 comments.

Comments suppressed due to low confidence (1)

media/client/ipc/source/MediaPipelineIpc.cpp:356

  • MediaPipelineIpc::play(bool &async) returns false here without assigning async, so callers can observe an uninitialized/previous value. Please set async = false at the beginning of the method (and/or on each failure path).
{
    if (!reattachChannelIfRequired())
    {
        RIALTO_CLIENT_LOG_ERROR("Reattachment of the ipc channel failed, ipc disconnected");
        return false;

Comment on lines +84 to 88
constexpr std::chrono::milliseconds kOutputRestrictedRetryInterval{250};
constexpr std::chrono::seconds kOutputRestrictedRetryTimeout{6};

int32_t generateSessionId()
{
Comment on lines 199 to 203
MediaKeyErrorStatus OcdmSession::decryptBuffer(GstBuffer *encrypted, GstCaps *caps)
{
RIALTO_COMMON_LOG_ERROR("DEBUG PURPOSE : OcdmSession::decryptBuffer()\n");
if (!m_session)
{
Comment on lines +125 to +129
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){
Comment on lines +505 to +509
RIALTO_SERVER_LOG_ERROR("Media keys handle for mksId: %d does not exist", keySessionId);
return MediaKeyErrorStatus::FAIL;
}
mediaKeys = m_mediaKeys[mediaKeysHandleIter->second.mediaKeysHandle].get();
}
Comment on lines +69 to 73
case firebolt::rialto::MediaKeyErrorStatus::OUTPUT_RESTRICTED:
{
// TODO
}
}
Comment on lines +30 to +34
RIALTO_SERVER_LOG_DEBUG("FlushOnPrerollController: Waiting if required for %s source entry",
common::convertMediaSourceType(type));
m_conditionVariable.wait(lock, [this, &type]()
// coverity[MISSING_LOCK:FALSE]
{ return !m_isPrerolled || m_flushingSources.find(type) == m_flushingSources.end(); });
Comment on lines +734 to +738
if ((pTypfdSrcPad = m_gstWrapper->gstElementGetStaticPad(m_context.playbackGroup.m_curAudioTypefind, "src")) !=
NULL) // Unref the Pad
RIALTO_SERVER_LOG_DEBUG("OTF -> Current Typefind SrcPad = %p", pTypfdSrcPad);
if ((pTypfdSrcPeerPad = m_gstWrapper->gstPadGetPeer(pTypfdSrcPad)) != NULL) // Unref the Pad
RIALTO_SERVER_LOG_DEBUG("OTF -> Current Typefind Src Downstream Element Pad = %p", pTypfdSrcPeerPad);
Comment on lines +849 to +853
if ((audioDecSrcPeerPad = m_gstWrapper->gstPadGetPeer(audioDecSrcPad)) != NULL) // Unref the Pad
RIALTO_SERVER_LOG_DEBUG("OTF -> Current AudioDecoder Src Downstream Element Pad = %p", audioDecSrcPeerPad);
// Get AudioDecoder Sink Peer i.e. Upstream Element Pad
if ((audioDecSinkPeerPad = m_gstWrapper->gstPadGetPeer(audioDecSinkPad)) != NULL) // Unref the Pad
RIALTO_SERVER_LOG_DEBUG("OTF -> Current AudioDecoder Sink Upstream Element Pad = %p", audioDecSinkPeerPad);
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.

7 participants