From a7d53cdcf214e2a5b9bec8130352e69b045e30bf Mon Sep 17 00:00:00 2001 From: Marcin Wojciechowski <105790697+skywojciechowskim@users.noreply.github.com> Date: Fri, 19 Dec 2025 09:53:51 +0100 Subject: [PATCH 01/29] Performance improvements in play request and flush (#428) Summary: Performance improvements in play request and flush Type: Fix Test Plan: UT/CT, Fullstack Jira: ENTDAI-1750 ENTDAI-1738 ENTDAI-485 --- media/client/ipc/include/MediaPipelineIpc.h | 2 +- .../client/ipc/interface/IMediaPipelineIpc.h | 4 ++- media/client/ipc/source/MediaPipelineIpc.cpp | 4 ++- media/client/main/include/MediaPipeline.h | 2 +- .../client/main/include/MediaPipelineProxy.h | 2 +- media/client/main/source/MediaPipeline.cpp | 4 +-- media/public/include/IMediaPipeline.h | 7 ++-- .../gstplayer/include/GstGenericPlayer.h | 10 ++++-- .../include/IGstGenericPlayerPrivate.h | 4 +-- .../gstplayer/interface/IGstGenericPlayer.h | 11 +++--- .../gstplayer/source/GstGenericPlayer.cpp | 35 ++++++++++++++----- .../ipc/source/MediaPipelineModuleService.cpp | 4 ++- .../include/MediaPipelineServerInternal.h | 6 ++-- .../source/MediaPipelineServerInternal.cpp | 10 +++--- .../service/include/IMediaPipelineService.h | 2 +- .../service/source/MediaPipelineService.cpp | 4 +-- .../service/source/MediaPipelineService.h | 2 +- proto/mediapipelinemodule.proto | 4 ++- .../tests/base/MediaPipelineTestMethods.cpp | 6 ++-- .../ipc/mediaPipelineIpc/PlayPauseTest.cpp | 12 ++++--- .../mediaPipeline/MediaPipelineProxyTest.cpp | 5 +-- .../main/mediaPipeline/PlayPauseTest.cpp | 10 +++--- .../client/mocks/ipc/MediaPipelineIpcMock.h | 2 +- .../main/MediaPipelineAndControlClientMock.h | 2 +- .../genericPlayer/GstGenericPlayerTest.cpp | 31 ++++++++++++++-- .../common/GenericTasksTestsBase.cpp | 8 ++--- ...MediaPipelineModuleServiceTestsFixture.cpp | 4 +-- .../MiscellaneousFunctionsTest.cpp | 12 ++++--- .../base/MediaPipelineTestBase.cpp | 7 ++++ .../base/MediaPipelineTestBase.h | 1 + .../mocks/gstplayer/GstGenericPlayerMock.h | 2 +- .../gstplayer/GstGenericPlayerPrivateMock.h | 2 +- .../main/MediaPipelineServerInternalMock.h | 2 +- .../mocks/service/MediaPipelineServiceMock.h | 2 +- .../MediaPipelineServiceTestsFixture.cpp | 10 +++--- 35 files changed, 159 insertions(+), 76 deletions(-) diff --git a/media/client/ipc/include/MediaPipelineIpc.h b/media/client/ipc/include/MediaPipelineIpc.h index 5e50dd4f4..db45d00d9 100644 --- a/media/client/ipc/include/MediaPipelineIpc.h +++ b/media/client/ipc/include/MediaPipelineIpc.h @@ -79,7 +79,7 @@ class MediaPipelineIpc : public IMediaPipelineIpc, public IpcModule bool setVideoWindow(uint32_t x, uint32_t y, uint32_t width, uint32_t height) override; - bool play() override; + bool play(bool &async) override; bool pause() override; diff --git a/media/client/ipc/interface/IMediaPipelineIpc.h b/media/client/ipc/interface/IMediaPipelineIpc.h index 1d055d2b7..782a1931b 100644 --- a/media/client/ipc/interface/IMediaPipelineIpc.h +++ b/media/client/ipc/interface/IMediaPipelineIpc.h @@ -126,9 +126,11 @@ class IMediaPipelineIpc /** * @brief Request play on the playback session. * + * @param[out] async : True if play method call is asynchronous + * * @retval true on success. */ - virtual bool play() = 0; + virtual bool play(bool &async) = 0; /** * @brief Request pause on the playback session. diff --git a/media/client/ipc/source/MediaPipelineIpc.cpp b/media/client/ipc/source/MediaPipelineIpc.cpp index 23c0375da..ff1b09847 100644 --- a/media/client/ipc/source/MediaPipelineIpc.cpp +++ b/media/client/ipc/source/MediaPipelineIpc.cpp @@ -348,7 +348,7 @@ bool MediaPipelineIpc::setVideoWindow(uint32_t x, uint32_t y, uint32_t width, ui return true; } -bool MediaPipelineIpc::play() +bool MediaPipelineIpc::play(bool &async) { if (!reattachChannelIfRequired()) { @@ -375,6 +375,8 @@ bool MediaPipelineIpc::play() return false; } + async = response.async(); + return true; } diff --git a/media/client/main/include/MediaPipeline.h b/media/client/main/include/MediaPipeline.h index 74bf8d13b..13075a511 100644 --- a/media/client/main/include/MediaPipeline.h +++ b/media/client/main/include/MediaPipeline.h @@ -118,7 +118,7 @@ class MediaPipeline : public IMediaPipelineAndIControlClient, public IMediaPipel bool allSourcesAttached() override; - bool play() override; + bool play(bool &async) override; bool pause() override; diff --git a/media/client/main/include/MediaPipelineProxy.h b/media/client/main/include/MediaPipelineProxy.h index cf0283cd2..b38371963 100644 --- a/media/client/main/include/MediaPipelineProxy.h +++ b/media/client/main/include/MediaPipelineProxy.h @@ -52,7 +52,7 @@ class MediaPipelineProxy : public IMediaPipelineAndIControlClient bool allSourcesAttached() override { return m_mediaPipeline->allSourcesAttached(); } - bool play() override { return m_mediaPipeline->play(); } + bool play(bool &async) override { return m_mediaPipeline->play(async); } bool pause() override { return m_mediaPipeline->pause(); } diff --git a/media/client/main/source/MediaPipeline.cpp b/media/client/main/source/MediaPipeline.cpp index 280aff919..e529fe3b6 100644 --- a/media/client/main/source/MediaPipeline.cpp +++ b/media/client/main/source/MediaPipeline.cpp @@ -252,11 +252,11 @@ bool MediaPipeline::allSourcesAttached() return m_mediaPipelineIpc->allSourcesAttached(); } -bool MediaPipeline::play() +bool MediaPipeline::play(bool &async) { RIALTO_CLIENT_LOG_DEBUG("entry:"); - return m_mediaPipelineIpc->play(); + return m_mediaPipelineIpc->play(async); } bool MediaPipeline::pause() diff --git a/media/public/include/IMediaPipeline.h b/media/public/include/IMediaPipeline.h index 1064ccc1e..e6c68787e 100644 --- a/media/public/include/IMediaPipeline.h +++ b/media/public/include/IMediaPipeline.h @@ -1119,16 +1119,15 @@ class IMediaPipeline /** * @brief Starts playback of the media. * - * This method is considered to be asynchronous and MUST NOT block - * but should request playback and then return. - * * Once the backend is successfully playing it should notify the * media player client of playback state * IMediaPipelineClient::PlaybackState::PLAYING. * + * @param[out] async : True if play method call is asynchronous + * * @retval true on success. */ - virtual bool play() = 0; + virtual bool play(bool &async) = 0; /** * @brief Pauses playback of the media. diff --git a/media/server/gstplayer/include/GstGenericPlayer.h b/media/server/gstplayer/include/GstGenericPlayer.h index 22fd800e0..fd633cd48 100644 --- a/media/server/gstplayer/include/GstGenericPlayer.h +++ b/media/server/gstplayer/include/GstGenericPlayer.h @@ -36,6 +36,7 @@ #include "tasks/IGenericPlayerTaskFactory.h" #include "tasks/IPlayerTask.h" #include +#include #include #include #include @@ -108,7 +109,7 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva void attachSource(const std::unique_ptr &mediaSource) override; void removeSource(const MediaSourceType &mediaSourceType) override; void allSourcesAttached() override; - void play() override; + void play(bool &async) override; void pause() override; void stop() override; void attachSamples(const IMediaPipeline::MediaSegmentVector &mediaSegments) override; @@ -168,7 +169,7 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva void updateVideoCaps(int32_t width, int32_t height, Fraction frameRate, const std::shared_ptr &codecData) override; void addAudioClippingToBuffer(GstBuffer *buffer, uint64_t clippingStart, uint64_t clippingEnd) const override; - bool changePipelineState(GstState newState) override; + GstStateChangeReturn changePipelineState(GstState newState) override; int64_t getPosition(GstElement *element) override; void startPositionReportingAndCheckAudioUnderflowTimer() override; void stopPositionReportingAndCheckAudioUnderflowTimer() override; @@ -427,6 +428,11 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva * @brief The postponed flush tasks */ std::vector> m_postponedFlushes{}; + + /** + * @brief The ongoing state change operations counter + */ + std::atomic m_ongoingStateChangesNumber{0}; }; } // namespace firebolt::rialto::server diff --git a/media/server/gstplayer/include/IGstGenericPlayerPrivate.h b/media/server/gstplayer/include/IGstGenericPlayerPrivate.h index 1848c8960..7ffb26b2c 100644 --- a/media/server/gstplayer/include/IGstGenericPlayerPrivate.h +++ b/media/server/gstplayer/include/IGstGenericPlayerPrivate.h @@ -174,9 +174,9 @@ class IGstGenericPlayerPrivate * * @param[in] newState : The desired state. * - * @retval true on success. + * @retval state change status */ - virtual bool changePipelineState(GstState newState) = 0; + virtual GstStateChangeReturn changePipelineState(GstState newState) = 0; /** * @brief Gets the current position of the element diff --git a/media/server/gstplayer/interface/IGstGenericPlayer.h b/media/server/gstplayer/interface/IGstGenericPlayer.h index e01edd364..d07b56f81 100644 --- a/media/server/gstplayer/interface/IGstGenericPlayer.h +++ b/media/server/gstplayer/interface/IGstGenericPlayer.h @@ -105,14 +105,15 @@ class IGstGenericPlayer /** * @brief Starts playback of the media. * - * This method is considered to be asynchronous and MUST NOT block - * but should request playback and then return. - * * Once the backend is successfully playing it should notify the - * media player client of playback state PlaybackState::PLAYING. + * media player client of playback state + * IMediaPipelineClient::PlaybackState::PLAYING. * + * @param[out] async : True if play method call is asynchronous + * + * @retval true on success. */ - virtual void play() = 0; + virtual void play(bool &async) = 0; /** * @brief Pauses playback of the media. diff --git a/media/server/gstplayer/source/GstGenericPlayer.cpp b/media/server/gstplayer/source/GstGenericPlayer.cpp index 1b0976a73..6f229c49f 100644 --- a/media/server/gstplayer/source/GstGenericPlayer.cpp +++ b/media/server/gstplayer/source/GstGenericPlayer.cpp @@ -1186,16 +1186,32 @@ void GstGenericPlayer::cancelUnderflow(firebolt::rialto::MediaSourceType mediaSo } } -void GstGenericPlayer::play() +void GstGenericPlayer::play(bool &async) { - if (m_workerThread) + if (0 == m_ongoingStateChangesNumber) { - m_workerThread->enqueueTask(m_taskFactory->createPlay(*this)); + // Operation called on main thread, because PAUSED->PLAYING change is synchronous and needs to be done fast. + // + // m_context.pipeline can be used, because it's modified only in GstGenericPlayer + // constructor and destructor. GstGenericPlayer is created/destructed on main thread, so we won't have a crash here. + ++m_ongoingStateChangesNumber; + async = (changePipelineState(GST_STATE_PLAYING) == GST_STATE_CHANGE_ASYNC); + RIALTO_SERVER_LOG_MIL("State change to PLAYING requested"); + } + else + { + ++m_ongoingStateChangesNumber; + async = true; + if (m_workerThread) + { + m_workerThread->enqueueTask(m_taskFactory->createPlay(*this)); + } } } void GstGenericPlayer::pause() { + ++m_ongoingStateChangesNumber; if (m_workerThread) { m_workerThread->enqueueTask(m_taskFactory->createPause(m_context, *this)); @@ -1204,29 +1220,32 @@ void GstGenericPlayer::pause() void GstGenericPlayer::stop() { + ++m_ongoingStateChangesNumber; if (m_workerThread) { m_workerThread->enqueueTask(m_taskFactory->createStop(m_context, *this)); } } -bool GstGenericPlayer::changePipelineState(GstState newState) +GstStateChangeReturn GstGenericPlayer::changePipelineState(GstState newState) { if (!m_context.pipeline) { RIALTO_SERVER_LOG_ERROR("Change state failed - pipeline is nullptr"); if (m_gstPlayerClient) m_gstPlayerClient->notifyPlaybackState(PlaybackState::FAILURE); - return false; + --m_ongoingStateChangesNumber; + return GST_STATE_CHANGE_FAILURE; } - if (m_gstWrapper->gstElementSetState(m_context.pipeline, newState) == GST_STATE_CHANGE_FAILURE) + const GstStateChangeReturn result{m_gstWrapper->gstElementSetState(m_context.pipeline, newState)}; + if (result == GST_STATE_CHANGE_FAILURE) { RIALTO_SERVER_LOG_ERROR("Change state failed - Gstreamer returned an error"); if (m_gstPlayerClient) m_gstPlayerClient->notifyPlaybackState(PlaybackState::FAILURE); - return false; } - return true; + --m_ongoingStateChangesNumber; + return result; } int64_t GstGenericPlayer::getPosition(GstElement *element) diff --git a/media/server/ipc/source/MediaPipelineModuleService.cpp b/media/server/ipc/source/MediaPipelineModuleService.cpp index 77c1387aa..7e08ec2f7 100644 --- a/media/server/ipc/source/MediaPipelineModuleService.cpp +++ b/media/server/ipc/source/MediaPipelineModuleService.cpp @@ -566,11 +566,13 @@ void MediaPipelineModuleService::play(::google::protobuf::RpcController *control ::firebolt::rialto::PlayResponse *response, ::google::protobuf::Closure *done) { RIALTO_SERVER_LOG_DEBUG("entry:"); - if (!m_mediaPipelineService.play(request->session_id())) + bool async{false}; + if (!m_mediaPipelineService.play(request->session_id(), async)) { RIALTO_SERVER_LOG_ERROR("Play failed"); controller->SetFailed("Operation failed"); } + response->set_async(async); done->Run(); } diff --git a/media/server/main/include/MediaPipelineServerInternal.h b/media/server/main/include/MediaPipelineServerInternal.h index e546ee795..cfd0cd2a7 100644 --- a/media/server/main/include/MediaPipelineServerInternal.h +++ b/media/server/main/include/MediaPipelineServerInternal.h @@ -99,7 +99,7 @@ class MediaPipelineServerInternal : public IMediaPipelineServerInternal, public bool allSourcesAttached() override; - bool play() override; + bool play(bool &async) override; bool pause() override; @@ -346,9 +346,11 @@ class MediaPipelineServerInternal : public IMediaPipelineServerInternal, public /** * @brief Play internally, only to be called on the main thread. * + * @param[out] async : True if play method call is asynchronous + * * @retval true on success. */ - bool playInternal(); + bool playInternal(bool &async); /** * @brief Pause internally, only to be called on the main thread. diff --git a/media/server/main/source/MediaPipelineServerInternal.cpp b/media/server/main/source/MediaPipelineServerInternal.cpp index f76396bec..197240e6a 100644 --- a/media/server/main/source/MediaPipelineServerInternal.cpp +++ b/media/server/main/source/MediaPipelineServerInternal.cpp @@ -335,18 +335,18 @@ bool MediaPipelineServerInternal::allSourcesAttachedInternal() return true; } -bool MediaPipelineServerInternal::play() +bool MediaPipelineServerInternal::play(bool &async) { RIALTO_SERVER_LOG_DEBUG("entry:"); bool result; - auto task = [&]() { result = playInternal(); }; + auto task = [&]() { result = playInternal(async); }; - m_mainThread->enqueueTaskAndWait(m_mainThreadClientId, task); + m_mainThread->enqueuePriorityTaskAndWait(m_mainThreadClientId, task); return result; } -bool MediaPipelineServerInternal::playInternal() +bool MediaPipelineServerInternal::playInternal(bool &async) { if (!m_gstPlayer) { @@ -354,7 +354,7 @@ bool MediaPipelineServerInternal::playInternal() return false; } - m_gstPlayer->play(); + m_gstPlayer->play(async); return true; } diff --git a/media/server/service/include/IMediaPipelineService.h b/media/server/service/include/IMediaPipelineService.h index 7ba9e8a1c..ae1fc5e04 100644 --- a/media/server/service/include/IMediaPipelineService.h +++ b/media/server/service/include/IMediaPipelineService.h @@ -48,7 +48,7 @@ class IMediaPipelineService virtual bool attachSource(int sessionId, const std::unique_ptr &source) = 0; virtual bool removeSource(int sessionId, std::int32_t sourceId) = 0; virtual bool allSourcesAttached(int sessionId) = 0; - virtual bool play(int sessionId) = 0; + virtual bool play(int sessionId, bool &async) = 0; virtual bool pause(int sessionId) = 0; virtual bool stop(int sessionId) = 0; virtual bool setPlaybackRate(int sessionId, double rate) = 0; diff --git a/media/server/service/source/MediaPipelineService.cpp b/media/server/service/source/MediaPipelineService.cpp index 5e7c40063..5d2212017 100644 --- a/media/server/service/source/MediaPipelineService.cpp +++ b/media/server/service/source/MediaPipelineService.cpp @@ -169,7 +169,7 @@ bool MediaPipelineService::allSourcesAttached(int sessionId) return mediaPipelineIter->second->allSourcesAttached(); } -bool MediaPipelineService::play(int sessionId) +bool MediaPipelineService::play(int sessionId, bool &async) { RIALTO_SERVER_LOG_INFO("MediaPipelineService requested to play, session id: %d", sessionId); @@ -180,7 +180,7 @@ bool MediaPipelineService::play(int sessionId) RIALTO_SERVER_LOG_ERROR("Session with id: %d does not exists", sessionId); return false; } - return mediaPipelineIter->second->play(); + return mediaPipelineIter->second->play(async); } bool MediaPipelineService::pause(int sessionId) diff --git a/media/server/service/source/MediaPipelineService.h b/media/server/service/source/MediaPipelineService.h index d68edadff..bf9ec234f 100644 --- a/media/server/service/source/MediaPipelineService.h +++ b/media/server/service/source/MediaPipelineService.h @@ -59,7 +59,7 @@ class MediaPipelineService : public IMediaPipelineService bool attachSource(int sessionId, const std::unique_ptr &source) override; bool removeSource(int sessionId, std::int32_t sourceId) override; bool allSourcesAttached(int sessionId) override; - bool play(int sessionId) override; + bool play(int sessionId, bool &async) override; bool pause(int sessionId) override; bool stop(int sessionId) override; bool setPlaybackRate(int sessionId, double rate) override; diff --git a/proto/mediapipelinemodule.proto b/proto/mediapipelinemodule.proto index f56a19970..1807810de 100644 --- a/proto/mediapipelinemodule.proto +++ b/proto/mediapipelinemodule.proto @@ -261,7 +261,8 @@ message SetVideoWindowResponse { * @fn void play(int session_id) * @brief Starts playback on a session. * - * @param[in] session_id The id of the A/V session. + * @param[in] session_id The id of the A/V session. + * @param[out] async True if play method call is asynchronous * * This method is asynchronous. Once the backend is successfully playing it will notify the media player client of * playback state. @@ -271,6 +272,7 @@ message PlayRequest { optional int32 session_id = 1 [default = -1]; } message PlayResponse { + optional bool async = 1 [default = true]; } /** diff --git a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp index 51ef2b5d4..e91d67bbb 100644 --- a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp +++ b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp @@ -892,7 +892,8 @@ void MediaPipelineTestMethods::shouldNotifyPlaybackStateFailure() void MediaPipelineTestMethods::playFailure() { - EXPECT_EQ(m_mediaPipeline->play(), false); + bool async{false}; + EXPECT_EQ(m_mediaPipeline->play(async), false); } void MediaPipelineTestMethods::pauseFailure() @@ -2033,7 +2034,8 @@ void MediaPipelineTestMethods::haveDataInternal(const std::unique_ptr &mediaPipeline, const bool status) { - EXPECT_EQ(mediaPipeline->play(), status); + bool async{false}; + EXPECT_EQ(mediaPipeline->play(async), status); } void MediaPipelineTestMethods::sendNotifyPlaybackStateInternal(const int32_t sessionId, const PlaybackState &state) diff --git a/tests/unittests/media/client/ipc/mediaPipelineIpc/PlayPauseTest.cpp b/tests/unittests/media/client/ipc/mediaPipelineIpc/PlayPauseTest.cpp index 2db4edf0b..dec195144 100644 --- a/tests/unittests/media/client/ipc/mediaPipelineIpc/PlayPauseTest.cpp +++ b/tests/unittests/media/client/ipc/mediaPipelineIpc/PlayPauseTest.cpp @@ -43,12 +43,13 @@ class RialtoClientMediaPipelineIpcPlayPauseTest : public MediaPipelineIpcTestBas */ TEST_F(RialtoClientMediaPipelineIpcPlayPauseTest, PlaySuccess) { + bool async{false}; expectIpcApiCallSuccess(); EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("play"), m_controllerMock.get(), playRequestMatcher(m_sessionId), _, m_blockingClosureMock.get())); - EXPECT_EQ(m_mediaPipelineIpc->play(), true); + EXPECT_EQ(m_mediaPipelineIpc->play(async), true); } /** @@ -56,10 +57,11 @@ TEST_F(RialtoClientMediaPipelineIpcPlayPauseTest, PlaySuccess) */ TEST_F(RialtoClientMediaPipelineIpcPlayPauseTest, PlayChannelDisconnected) { + bool async{false}; expectIpcApiCallDisconnected(); expectUnsubscribeEvents(); - EXPECT_EQ(m_mediaPipelineIpc->play(), false); + EXPECT_EQ(m_mediaPipelineIpc->play(async), false); // Reattach channel on destroySession EXPECT_CALL(*m_ipcClientMock, getChannel()).WillOnce(Return(m_channelMock)).RetiresOnSaturation(); @@ -71,13 +73,14 @@ TEST_F(RialtoClientMediaPipelineIpcPlayPauseTest, PlayChannelDisconnected) */ TEST_F(RialtoClientMediaPipelineIpcPlayPauseTest, PlayReconnectChannel) { + bool async{false}; expectIpcApiCallReconnected(); expectUnsubscribeEvents(); expectSubscribeEvents(); EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("play"), _, _, _, _)); - EXPECT_EQ(m_mediaPipelineIpc->play(), true); + EXPECT_EQ(m_mediaPipelineIpc->play(async), true); } /** @@ -85,11 +88,12 @@ TEST_F(RialtoClientMediaPipelineIpcPlayPauseTest, PlayReconnectChannel) */ TEST_F(RialtoClientMediaPipelineIpcPlayPauseTest, PlayFailure) { + bool async{false}; expectIpcApiCallFailure(); EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("play"), _, _, _, _)); - EXPECT_EQ(m_mediaPipelineIpc->play(), false); + EXPECT_EQ(m_mediaPipelineIpc->play(async), false); } /** diff --git a/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp b/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp index be98cad21..6e22bdf4d 100644 --- a/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp +++ b/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp @@ -100,8 +100,9 @@ TEST_F(RialtoClientMediaPipelineProxyTest, TestPassthrough) ///////////////////////////////////////////// - EXPECT_CALL(*mediaPipelineMock, play()).WillOnce(Return(true)); - EXPECT_TRUE(proxy->play()); + bool async{false}; + EXPECT_CALL(*mediaPipelineMock, play(_)).WillOnce(Return(true)); + EXPECT_TRUE(proxy->play(async)); ///////////////////////////////////////////// diff --git a/tests/unittests/media/client/main/mediaPipeline/PlayPauseTest.cpp b/tests/unittests/media/client/main/mediaPipeline/PlayPauseTest.cpp index a58b80891..8c78c550f 100644 --- a/tests/unittests/media/client/main/mediaPipeline/PlayPauseTest.cpp +++ b/tests/unittests/media/client/main/mediaPipeline/PlayPauseTest.cpp @@ -42,9 +42,10 @@ class RialtoClientMediaPipelinePlayPauseTest : public MediaPipelineTestBase */ TEST_F(RialtoClientMediaPipelinePlayPauseTest, PlaySuccess) { - EXPECT_CALL(*m_mediaPipelineIpcMock, play()).WillOnce(Return(true)); + bool async{false}; + EXPECT_CALL(*m_mediaPipelineIpcMock, play(_)).WillOnce(Return(true)); - EXPECT_EQ(m_mediaPipeline->play(), true); + EXPECT_EQ(m_mediaPipeline->play(async), true); } /** @@ -52,9 +53,10 @@ TEST_F(RialtoClientMediaPipelinePlayPauseTest, PlaySuccess) */ TEST_F(RialtoClientMediaPipelinePlayPauseTest, PlayFailure) { - EXPECT_CALL(*m_mediaPipelineIpcMock, play()).WillOnce(Return(false)); + bool async{false}; + EXPECT_CALL(*m_mediaPipelineIpcMock, play(_)).WillOnce(Return(false)); - EXPECT_EQ(m_mediaPipeline->play(), false); + EXPECT_EQ(m_mediaPipeline->play(async), false); } /** diff --git a/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h b/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h index ed99bfb8e..32e72ba2c 100644 --- a/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h +++ b/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h @@ -39,7 +39,7 @@ class MediaPipelineIpcMock : public IMediaPipelineIpc MOCK_METHOD(bool, allSourcesAttached, (), (override)); MOCK_METHOD(bool, load, (MediaType type, const std::string &mimeType, const std::string &url), (override)); MOCK_METHOD(bool, setVideoWindow, (uint32_t x, uint32_t y, uint32_t width, uint32_t height), (override)); - MOCK_METHOD(bool, play, (), (override)); + MOCK_METHOD(bool, play, (bool &async), (override)); MOCK_METHOD(bool, pause, (), (override)); MOCK_METHOD(bool, stop, (), (override)); MOCK_METHOD(bool, haveData, (MediaSourceStatus status, uint32_t numFrames, uint32_t requestId), (override)); diff --git a/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h b/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h index 0d1427fd4..c69140210 100644 --- a/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h +++ b/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h @@ -39,7 +39,7 @@ class MediaPipelineAndControlClientMock : public IMediaPipelineAndIControlClient MOCK_METHOD(bool, allSourcesAttached, (), (override)); - MOCK_METHOD(bool, play, (), (override)); + MOCK_METHOD(bool, play, (bool &async), (override)); MOCK_METHOD(bool, pause, (), (override)); MOCK_METHOD(bool, stop, (), (override)); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp index b2c5158b6..97a5e0df6 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp @@ -165,13 +165,40 @@ TEST_F(GstGenericPlayerTest, shouldAllSourcesAttached) m_sut->allSourcesAttached(); } -TEST_F(GstGenericPlayerTest, shouldPlay) +TEST_F(GstGenericPlayerTest, shouldPlayOnWorkerThread) { + // Pause first + std::unique_ptr pauseTask{std::make_unique>()}; + EXPECT_CALL(dynamic_cast &>(*pauseTask), execute()); + EXPECT_CALL(m_taskFactoryMock, createPause(_, _)).WillOnce(Return(ByMove(std::move(pauseTask)))); + + m_sut->pause(); + + // ... + + bool async = false; std::unique_ptr task{std::make_unique>()}; EXPECT_CALL(dynamic_cast &>(*task), execute()); EXPECT_CALL(m_taskFactoryMock, createPlay(_)).WillOnce(Return(ByMove(std::move(task)))); - m_sut->play(); + m_sut->play(async); + EXPECT_TRUE(async); +} + +TEST_F(GstGenericPlayerTest, shouldPlayImmediatelySynchronously) +{ + bool async = false; + EXPECT_CALL(*m_gstWrapperMock, gstElementSetState(_, GST_STATE_PLAYING)).WillOnce(Return(GST_STATE_CHANGE_SUCCESS)); + m_sut->play(async); + EXPECT_FALSE(async); +} + +TEST_F(GstGenericPlayerTest, shouldPlayImmediatelyAsynchronously) +{ + bool async = false; + EXPECT_CALL(*m_gstWrapperMock, gstElementSetState(_, GST_STATE_PLAYING)).WillOnce(Return(GST_STATE_CHANGE_ASYNC)); + m_sut->play(async); + EXPECT_TRUE(async); } TEST_F(GstGenericPlayerTest, shouldPause) diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp index f40976337..87ddcf090 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp @@ -2273,7 +2273,7 @@ void GenericTasksTestsBase::shouldStopGstPlayer() videoStreamIt->second.isDataNeeded = true; audioStreamIt->second.isDataNeeded = true; EXPECT_CALL(testContext->m_gstPlayer, stopPositionReportingAndCheckAudioUnderflowTimer()); - EXPECT_CALL(testContext->m_gstPlayer, changePipelineState(GST_STATE_NULL)); + EXPECT_CALL(testContext->m_gstPlayer, changePipelineState(GST_STATE_NULL)).WillOnce(Return(GST_STATE_CHANGE_SUCCESS)); } void GenericTasksTestsBase::triggerStop() @@ -2589,12 +2589,12 @@ void GenericTasksTestsBase::checkNoEos() void GenericTasksTestsBase::shouldChangeStatePlayingSuccess() { - EXPECT_CALL(testContext->m_gstPlayer, changePipelineState(GST_STATE_PLAYING)).WillOnce(Return(true)); + EXPECT_CALL(testContext->m_gstPlayer, changePipelineState(GST_STATE_PLAYING)).WillOnce(Return(GST_STATE_CHANGE_SUCCESS)); } void GenericTasksTestsBase::shouldChangeStatePlayingFailure() { - EXPECT_CALL(testContext->m_gstPlayer, changePipelineState(GST_STATE_PLAYING)).WillOnce(Return(false)); + EXPECT_CALL(testContext->m_gstPlayer, changePipelineState(GST_STATE_PLAYING)).WillOnce(Return(GST_STATE_CHANGE_FAILURE)); } void GenericTasksTestsBase::triggerPlay() @@ -2612,7 +2612,7 @@ void GenericTasksTestsBase::triggerPing() void GenericTasksTestsBase::shouldPause() { EXPECT_CALL(testContext->m_gstPlayer, stopPositionReportingAndCheckAudioUnderflowTimer()); - EXPECT_CALL(testContext->m_gstPlayer, changePipelineState(GST_STATE_PAUSED)); + EXPECT_CALL(testContext->m_gstPlayer, changePipelineState(GST_STATE_PAUSED)).WillOnce(Return(GST_STATE_CHANGE_SUCCESS)); } void GenericTasksTestsBase::triggerPause() diff --git a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp index ac3576a74..31ec98768 100644 --- a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp +++ b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp @@ -388,13 +388,13 @@ void MediaPipelineModuleServiceTests::mediaPipelineServiceWillFailAllSourcesAtta void MediaPipelineModuleServiceTests::mediaPipelineServiceWillPlay() { expectRequestSuccess(); - EXPECT_CALL(m_mediaPipelineServiceMock, play(kHardcodedSessionId)).WillOnce(Return(true)); + EXPECT_CALL(m_mediaPipelineServiceMock, play(kHardcodedSessionId, _)).WillOnce(Return(true)); } void MediaPipelineModuleServiceTests::mediaPipelineServiceWillFailToPlay() { expectRequestFailure(); - EXPECT_CALL(m_mediaPipelineServiceMock, play(kHardcodedSessionId)).WillOnce(Return(false)); + EXPECT_CALL(m_mediaPipelineServiceMock, play(kHardcodedSessionId, _)).WillOnce(Return(false)); } void MediaPipelineModuleServiceTests::mediaPipelineServiceWillPause() diff --git a/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp b/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp index ce00bdee8..aa4f3e2f3 100644 --- a/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp +++ b/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp @@ -43,11 +43,12 @@ class RialtoServerMediaPipelineMiscellaneousFunctionsTest : public MediaPipeline */ TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, PlaySuccess) { + bool async{false}; loadGstPlayer(); - mainThreadWillEnqueueTaskAndWait(); + mainThreadWillEnqueuePriorityTaskAndWait(); - EXPECT_CALL(*m_gstPlayerMock, play()); - EXPECT_TRUE(m_mediaPipeline->play()); + EXPECT_CALL(*m_gstPlayerMock, play(_)); + EXPECT_TRUE(m_mediaPipeline->play(async)); } /** @@ -55,8 +56,9 @@ TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, PlaySuccess) */ TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, PlayFailureDueToUninitializedPlayer) { - mainThreadWillEnqueueTaskAndWait(); - EXPECT_FALSE(m_mediaPipeline->play()); + bool async{false}; + mainThreadWillEnqueuePriorityTaskAndWait(); + EXPECT_FALSE(m_mediaPipeline->play(async)); } /** diff --git a/tests/unittests/media/server/main/mediaPipeline/base/MediaPipelineTestBase.cpp b/tests/unittests/media/server/main/mediaPipeline/base/MediaPipelineTestBase.cpp index 491aec0e1..1543184af 100644 --- a/tests/unittests/media/server/main/mediaPipeline/base/MediaPipelineTestBase.cpp +++ b/tests/unittests/media/server/main/mediaPipeline/base/MediaPipelineTestBase.cpp @@ -82,6 +82,13 @@ void MediaPipelineTestBase::mainThreadWillEnqueueTaskAndWait() .RetiresOnSaturation(); } +void MediaPipelineTestBase::mainThreadWillEnqueuePriorityTaskAndWait() +{ + EXPECT_CALL(*m_mainThreadMock, enqueuePriorityTaskAndWait(m_kMainThreadClientId, _)) + .WillOnce(Invoke([](uint32_t clientId, firebolt::rialto::server::IMainThread::Task task) { task(); })) + .RetiresOnSaturation(); +} + void MediaPipelineTestBase::loadGstPlayer() { mainThreadWillEnqueueTaskAndWait(); diff --git a/tests/unittests/media/server/main/mediaPipeline/base/MediaPipelineTestBase.h b/tests/unittests/media/server/main/mediaPipeline/base/MediaPipelineTestBase.h index d31474d53..893353141 100644 --- a/tests/unittests/media/server/main/mediaPipeline/base/MediaPipelineTestBase.h +++ b/tests/unittests/media/server/main/mediaPipeline/base/MediaPipelineTestBase.h @@ -90,6 +90,7 @@ class MediaPipelineTestBase : public ::testing::Test void destroyMediaPipeline(); void mainThreadWillEnqueueTask(); void mainThreadWillEnqueueTaskAndWait(); + void mainThreadWillEnqueuePriorityTaskAndWait(); void loadGstPlayer(); int attachSource(MediaSourceType sourceType, const std::string &mimeType); void setEos(MediaSourceType sourceType); diff --git a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h index de4697646..bc2c3f07f 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h @@ -36,7 +36,7 @@ class GstGenericPlayerMock : public IGstGenericPlayer MOCK_METHOD(void, attachSource, (const std::unique_ptr &mediaSource), (override)); MOCK_METHOD(void, removeSource, (const MediaSourceType &mediaSourceType), (override)); MOCK_METHOD(void, allSourcesAttached, (), (override)); - MOCK_METHOD(void, play, (), (override)); + MOCK_METHOD(void, play, (bool &async), (override)); MOCK_METHOD(void, pause, (), (override)); MOCK_METHOD(void, stop, (), (override)); MOCK_METHOD(void, attachSamples, (const IMediaPipeline::MediaSegmentVector &mediaSegments), (override)); diff --git a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h index ceab057ff..e1f4b3efb 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h @@ -55,7 +55,7 @@ class GstGenericPlayerPrivateMock : public IGstGenericPlayerPrivate MOCK_METHOD(void, updateVideoCaps, (int32_t width, int32_t height, Fraction frameRate, const std::shared_ptr &codecData), (override)); - MOCK_METHOD(bool, changePipelineState, (GstState newState), (override)); + MOCK_METHOD(GstStateChangeReturn, changePipelineState, (GstState newState), (override)); MOCK_METHOD(int64_t, getPosition, (GstElement * element), (override)); MOCK_METHOD(void, startPositionReportingAndCheckAudioUnderflowTimer, (), (override)); MOCK_METHOD(void, stopPositionReportingAndCheckAudioUnderflowTimer, (), (override)); diff --git a/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h b/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h index 61ee32b61..27a12a686 100644 --- a/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h +++ b/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h @@ -36,7 +36,7 @@ class MediaPipelineServerInternalMock : public IMediaPipelineServerInternal MOCK_METHOD(bool, attachSource, (const std::unique_ptr &source), (override)); MOCK_METHOD(bool, removeSource, (int32_t id), (override)); MOCK_METHOD(bool, allSourcesAttached, (), (override)); - MOCK_METHOD(bool, play, (), (override)); + MOCK_METHOD(bool, play, (bool &async), (override)); MOCK_METHOD(bool, pause, (), (override)); MOCK_METHOD(bool, stop, (), (override)); MOCK_METHOD(bool, setPlaybackRate, (double rate), (override)); diff --git a/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h b/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h index 69812124d..f7b226777 100644 --- a/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h +++ b/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h @@ -38,7 +38,7 @@ class MediaPipelineServiceMock : public IMediaPipelineService MOCK_METHOD(bool, attachSource, (int, const std::unique_ptr &), (override)); MOCK_METHOD(bool, removeSource, (int, std::int32_t), (override)); MOCK_METHOD(bool, allSourcesAttached, (int), (override)); - MOCK_METHOD(bool, play, (int), (override)); + MOCK_METHOD(bool, play, (int, bool &), (override)); MOCK_METHOD(bool, pause, (int), (override)); MOCK_METHOD(bool, stop, (int), (override)); MOCK_METHOD(bool, setPlaybackRate, (int, double), (override)); diff --git a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp index 9b9e441dd..421cb1d8b 100644 --- a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp +++ b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp @@ -138,12 +138,12 @@ void MediaPipelineServiceTests::mediaPipelineWillFailToAllSourcesAttached() void MediaPipelineServiceTests::mediaPipelineWillPlay() { - EXPECT_CALL(m_mediaPipelineMock, play()).WillOnce(Return(true)); + EXPECT_CALL(m_mediaPipelineMock, play(_)).WillOnce(Return(true)); } void MediaPipelineServiceTests::mediaPipelineWillFailToPlay() { - EXPECT_CALL(m_mediaPipelineMock, play()).WillOnce(Return(false)); + EXPECT_CALL(m_mediaPipelineMock, play(_)).WillOnce(Return(false)); } void MediaPipelineServiceTests::mediaPipelineWillPause() @@ -624,12 +624,14 @@ void MediaPipelineServiceTests::allSourcesAttachedShouldFail() void MediaPipelineServiceTests::playShouldSucceed() { - EXPECT_TRUE(m_sut->play(kSessionId)); + bool isAsync{false}; + EXPECT_TRUE(m_sut->play(kSessionId, isAsync)); } void MediaPipelineServiceTests::playShouldFail() { - EXPECT_FALSE(m_sut->play(kSessionId)); + bool isAsync{false}; + EXPECT_FALSE(m_sut->play(kSessionId, isAsync)); } void MediaPipelineServiceTests::pauseShouldSucceed() From b4039ebdbd6c8b2cf8e192903398fcd48255bb5e Mon Sep 17 00:00:00 2001 From: Adam Czynszak <92790185+aczs@users.noreply.github.com> Date: Wed, 14 Jan 2026 14:29:29 +0000 Subject: [PATCH 02/29] Changed Wayland Display for TextTrack (#434) Summary: Change Wayland Display for TextTrack Type: Fix Test Plan: UT/ CT, Fullstack Jira: ENTDAI-2218 --- media/server/gstplayer/source/GstTextTrackSink.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/media/server/gstplayer/source/GstTextTrackSink.cpp b/media/server/gstplayer/source/GstTextTrackSink.cpp index b98c5b93f..16e137854 100644 --- a/media/server/gstplayer/source/GstTextTrackSink.cpp +++ b/media/server/gstplayer/source/GstTextTrackSink.cpp @@ -159,19 +159,12 @@ static void gst_rialto_text_track_sink_finalize(GObject *object) // NOLINT(build 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); } catch (const std::exception &e) { From e918186adc01e90d847a795fa9017ffb94409e52 Mon Sep 17 00:00:00 2001 From: Adam Czynszak <92790185+aczs@users.noreply.github.com> Date: Wed, 14 Jan 2026 15:14:55 +0000 Subject: [PATCH 03/29] Fixed playback info reporting for not asynchronous playbacks (#435) Summary: Fixed playback info reporting for not asynchronous playbacks Type: Fix Test Plan: UT/CT, Fullstack Jira: ENTDAI-2231 --- .../gstplayer/include/GstGenericPlayer.h | 2 + .../include/IGstGenericPlayerPrivate.h | 10 ++++ .../gstplayer/source/GstGenericPlayer.cpp | 26 ++++++--- .../gstplayer/source/tasks/generic/Flush.cpp | 1 - .../source/tasks/generic/HandleBusMessage.cpp | 8 ++- .../gstplayer/source/tasks/generic/Stop.cpp | 1 + .../server/fixtures/MediaPipelineTest.cpp | 29 ++++++++-- .../server/fixtures/MediaPipelineTest.h | 3 + .../server/tests/mediaPipeline/FlushTest.cpp | 11 +--- .../GstGenericPlayerPrivateTest.cpp | 56 +++++++++++++++---- .../common/GenericTasksTestsBase.cpp | 9 ++- .../common/GenericTasksTestsBase.h | 1 + .../tasksTests/HandleBusMessageTest.cpp | 12 +++- .../tasksTests/RemoveSourceTest.cpp | 2 + .../gstplayer/GstGenericPlayerPrivateMock.h | 2 + 15 files changed, 132 insertions(+), 41 deletions(-) diff --git a/media/server/gstplayer/include/GstGenericPlayer.h b/media/server/gstplayer/include/GstGenericPlayer.h index fd633cd48..e5d500240 100644 --- a/media/server/gstplayer/include/GstGenericPlayer.h +++ b/media/server/gstplayer/include/GstGenericPlayer.h @@ -173,6 +173,8 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva int64_t getPosition(GstElement *element) override; void startPositionReportingAndCheckAudioUnderflowTimer() override; void stopPositionReportingAndCheckAudioUnderflowTimer() override; + void startNotifyPlaybackInfoTimer() override; + void stopNotifyPlaybackInfoTimer() override; void startSubtitleClockResyncTimer() override; void stopSubtitleClockResyncTimer() override; void stopWorkerThread() override; diff --git a/media/server/gstplayer/include/IGstGenericPlayerPrivate.h b/media/server/gstplayer/include/IGstGenericPlayerPrivate.h index 7ffb26b2c..dc931c38f 100644 --- a/media/server/gstplayer/include/IGstGenericPlayerPrivate.h +++ b/media/server/gstplayer/include/IGstGenericPlayerPrivate.h @@ -197,6 +197,16 @@ class IGstGenericPlayerPrivate */ virtual void stopPositionReportingAndCheckAudioUnderflowTimer() = 0; + /** + * @brief Starts notify playback info timer. Called by the worker thread. + */ + virtual void startNotifyPlaybackInfoTimer() = 0; + + /** + * @brief Stops notify playback info timer. Called by the worker thread. + */ + virtual void stopNotifyPlaybackInfoTimer() = 0; + /** * @brief Starts subtitle clock resync. Called by the worker thread. */ diff --git a/media/server/gstplayer/source/GstGenericPlayer.cpp b/media/server/gstplayer/source/GstGenericPlayer.cpp index 6f229c49f..f140b3ac4 100644 --- a/media/server/gstplayer/source/GstGenericPlayer.cpp +++ b/media/server/gstplayer/source/GstGenericPlayer.cpp @@ -406,7 +406,6 @@ bool GstGenericPlayer::getPosition(std::int64_t &position) position = getPosition(m_context.pipeline); if (position == -1) { - RIALTO_SERVER_LOG_WARN("Query position failed"); return false; } @@ -1712,7 +1711,6 @@ bool GstGenericPlayer::setErmContext() void GstGenericPlayer::startPositionReportingAndCheckAudioUnderflowTimer() { - static constexpr std::chrono::milliseconds kPlaybackInfoTimerMs{32}; if (m_positionReportingAndCheckAudioUnderflowTimer && m_positionReportingAndCheckAudioUnderflowTimer->isActive()) { return; @@ -1729,12 +1727,6 @@ void GstGenericPlayer::startPositionReportingAndCheckAudioUnderflowTimer() } }, firebolt::rialto::common::TimerType::PERIODIC); - - notifyPlaybackInfo(); - - m_playbackInfoTimer = - m_timerFactory - ->createTimer(kPlaybackInfoTimerMs, [this]() { notifyPlaybackInfo(); }, firebolt::rialto::common::TimerType::PERIODIC); } void GstGenericPlayer::stopPositionReportingAndCheckAudioUnderflowTimer() @@ -1744,7 +1736,25 @@ void GstGenericPlayer::stopPositionReportingAndCheckAudioUnderflowTimer() m_positionReportingAndCheckAudioUnderflowTimer->cancel(); m_positionReportingAndCheckAudioUnderflowTimer.reset(); } +} +void GstGenericPlayer::startNotifyPlaybackInfoTimer() +{ + static constexpr std::chrono::milliseconds kPlaybackInfoTimerMs{32}; + if (m_playbackInfoTimer && m_playbackInfoTimer->isActive()) + { + return; + } + + notifyPlaybackInfo(); + + m_playbackInfoTimer = + m_timerFactory + ->createTimer(kPlaybackInfoTimerMs, [this]() { notifyPlaybackInfo(); }, firebolt::rialto::common::TimerType::PERIODIC); +} + +void GstGenericPlayer::stopNotifyPlaybackInfoTimer() +{ if (m_playbackInfoTimer && m_playbackInfoTimer->isActive()) { m_playbackInfoTimer->cancel(); diff --git a/media/server/gstplayer/source/tasks/generic/Flush.cpp b/media/server/gstplayer/source/tasks/generic/Flush.cpp index 9c7c5196f..2af9c5f6b 100644 --- a/media/server/gstplayer/source/tasks/generic/Flush.cpp +++ b/media/server/gstplayer/source/tasks/generic/Flush.cpp @@ -83,7 +83,6 @@ void Flush::execute() const if (GST_STATE(m_context.pipeline) >= GST_STATE_PAUSED) { - m_player.stopPositionReportingAndCheckAudioUnderflowTimer(); m_context.flushOnPrerollController.setFlushing(m_type, GST_STATE(m_context.pipeline)); // Flush source diff --git a/media/server/gstplayer/source/tasks/generic/HandleBusMessage.cpp b/media/server/gstplayer/source/tasks/generic/HandleBusMessage.cpp index a6bbdcb8f..7ecf6bcba 100644 --- a/media/server/gstplayer/source/tasks/generic/HandleBusMessage.cpp +++ b/media/server/gstplayer/source/tasks/generic/HandleBusMessage.cpp @@ -77,6 +77,8 @@ void HandleBusMessage::execute() const } case GST_STATE_PAUSED: { + m_player.startNotifyPlaybackInfoTimer(); + m_player.stopPositionReportingAndCheckAudioUnderflowTimer(); if (pending != GST_STATE_PAUSED) { m_context.flushOnPrerollController.stateReached(newState); @@ -93,8 +95,8 @@ void HandleBusMessage::execute() const // Subsequent newState==GST_STATE_PAUSED, pending!=GST_STATE_PAUSED transition will // indicate that the pipeline is prerolled and it reached GST_STATE_PAUSED state after seek. m_gstPlayerClient->notifyPlaybackState(PlaybackState::PAUSED); - m_player.notifyPlaybackInfo(); } + if (m_player.hasSourceType(MediaSourceType::SUBTITLE)) { m_player.stopSubtitleClockResyncTimer(); @@ -128,8 +130,12 @@ void HandleBusMessage::execute() const break; } case GST_STATE_VOID_PENDING: + { + break; + } case GST_STATE_READY: { + m_player.stopNotifyPlaybackInfoTimer(); break; } } diff --git a/media/server/gstplayer/source/tasks/generic/Stop.cpp b/media/server/gstplayer/source/tasks/generic/Stop.cpp index 3f82478b5..bc566d6a7 100644 --- a/media/server/gstplayer/source/tasks/generic/Stop.cpp +++ b/media/server/gstplayer/source/tasks/generic/Stop.cpp @@ -38,6 +38,7 @@ void Stop::execute() const { RIALTO_SERVER_LOG_DEBUG("Executing Stop"); m_player.stopPositionReportingAndCheckAudioUnderflowTimer(); + m_player.stopNotifyPlaybackInfoTimer(); m_player.changePipelineState(GST_STATE_NULL); for (auto &streamInfo : m_context.streamInfo) { diff --git a/tests/componenttests/server/fixtures/MediaPipelineTest.cpp b/tests/componenttests/server/fixtures/MediaPipelineTest.cpp index 03eadf4bc..bcedd3457 100644 --- a/tests/componenttests/server/fixtures/MediaPipelineTest.cpp +++ b/tests/componenttests/server/fixtures/MediaPipelineTest.cpp @@ -67,6 +67,7 @@ MediaPipelineTest::MediaPipelineTest() MediaPipelineTest::~MediaPipelineTest() { positionUpdatesShouldNotBeReceivedFromNow(); + playbackInfoUpdatesShouldNotBeReceivedFromNow(); } void MediaPipelineTest::gstPlayerWillBeCreated() @@ -108,7 +109,14 @@ void MediaPipelineTest::gstPlayerWillBeCreated() return true; })); - EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq("audio-sink"), _)).Times(AtLeast(0)); + EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq("audio-sink"), _)) + .WillRepeatedly(Invoke( + [&](gpointer object, const gchar *first_property_name, void *element) + { + GstElement **elementPtr = reinterpret_cast(element); + *elementPtr = m_audioSink; + })); + EXPECT_CALL(*m_gstWrapperMock, gstStreamVolumeGetVolume(_, GST_STREAM_VOLUME_FORMAT_LINEAR)) .Times(AtLeast(0)) .WillRepeatedly(Return(kVolume)); @@ -534,6 +542,8 @@ void MediaPipelineTest::indicateAllSourcesAttached(const std::vector(m_clientStub).send(pauseReq).expectSuccess(); positionUpdatesShouldNotBeReceivedFromNow(); @@ -830,6 +840,7 @@ void MediaPipelineTest::stop() EXPECT_EQ(receivedPlaybackStateChange->state(), ::firebolt::rialto::PlaybackStateChangeEvent_PlaybackState_STOPPED); positionUpdatesShouldNotBeReceivedFromNow(); + playbackInfoUpdatesShouldNotBeReceivedFromNow(); } void MediaPipelineTest::destroySession() @@ -853,11 +864,6 @@ void MediaPipelineTest::mayReceivePositionUpdates() { m_positionChangeEventSuppressionId = m_clientStub.addSuppression(); } - - if (-1 == m_playbackInfoEventSuppressionId) - { - m_playbackInfoEventSuppressionId = m_clientStub.addSuppression(); - } } void MediaPipelineTest::positionUpdatesShouldNotBeReceivedFromNow() @@ -867,7 +873,18 @@ void MediaPipelineTest::positionUpdatesShouldNotBeReceivedFromNow() m_clientStub.removeSuppression(m_positionChangeEventSuppressionId); m_positionChangeEventSuppressionId = -1; } +} +void MediaPipelineTest::mayReceivePlaybackInfoUpdates() +{ + if (-1 == m_playbackInfoEventSuppressionId) + { + m_playbackInfoEventSuppressionId = m_clientStub.addSuppression(); + } +} + +void MediaPipelineTest::playbackInfoUpdatesShouldNotBeReceivedFromNow() +{ if (-1 != m_playbackInfoEventSuppressionId) { m_clientStub.removeSuppression(m_playbackInfoEventSuppressionId); diff --git a/tests/componenttests/server/fixtures/MediaPipelineTest.h b/tests/componenttests/server/fixtures/MediaPipelineTest.h index efe006423..dcf76b19c 100644 --- a/tests/componenttests/server/fixtures/MediaPipelineTest.h +++ b/tests/componenttests/server/fixtures/MediaPipelineTest.h @@ -88,6 +88,8 @@ class MediaPipelineTest : public RialtoServerComponentTest void initShm(); void mayReceivePositionUpdates(); void positionUpdatesShouldNotBeReceivedFromNow(); + void mayReceivePlaybackInfoUpdates(); + void playbackInfoUpdatesShouldNotBeReceivedFromNow(); protected: int m_sessionId{-1}; @@ -120,6 +122,7 @@ class MediaPipelineTest : public RialtoServerComponentTest GstSample *m_sample{nullptr}; std::shared_ptr<::firebolt::rialto::NeedMediaDataEvent> m_lastAudioNeedData{nullptr}; std::shared_ptr<::firebolt::rialto::NeedMediaDataEvent> m_lastVideoNeedData{nullptr}; + GstElement *m_audioSink{nullptr}; // Position Update events may be received in PLAYING state. We have to suppress them // to avoid occassional test failures diff --git a/tests/componenttests/server/tests/mediaPipeline/FlushTest.cpp b/tests/componenttests/server/tests/mediaPipeline/FlushTest.cpp index 39ef64847..83e1ced28 100644 --- a/tests/componenttests/server/tests/mediaPipeline/FlushTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/FlushTest.cpp @@ -45,7 +45,6 @@ class FlushTest : public MediaPipelineTest GstEvent m_flushStartEvent{}; GstEvent m_flushStopEvent{}; GstSegment m_segment{}; - GstElement *m_audioSink{nullptr}; public: FlushTest() @@ -59,14 +58,6 @@ class FlushTest : public MediaPipelineTest void willFlush() { - EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq("audio-sink"), _)) - .WillOnce(Invoke( - [&](gpointer object, const gchar *first_property_name, void *element) - { - GstElement **elementPtr = reinterpret_cast(element); - *elementPtr = m_audioSink; - })); - EXPECT_CALL(*m_glibWrapperMock, gTypeName(_)).WillRepeatedly(Return("GstStreamVolume")); EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(m_audioSink, StrEq("async"), _)) .WillOnce(Invoke( @@ -241,7 +232,7 @@ class FlushTest : public MediaPipelineTest * * Code: */ -TEST_F(FlushTest, flushAudioSourceSuccess) +TEST_F(FlushTest, DISABLED_flushAudioSourceSuccess) { // Step 1: Create a new media session createSession(); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp index 44152f81c..e347a4510 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp @@ -1654,12 +1654,18 @@ TEST_F(GstGenericPlayerPrivateTest, shouldStartPositionReportingTimer) std::unique_ptr audioUnderflowTimerMock = std::make_unique>(); EXPECT_CALL(*m_timerFactoryMock, createTimer(kPositionReportTimerMs, _, common::TimerType::PERIODIC)) .WillOnce(Return(ByMove(std::move(audioUnderflowTimerMock)))); + + m_sut->startPositionReportingAndCheckAudioUnderflowTimer(); +} + +TEST_F(GstGenericPlayerPrivateTest, shouldStartPlaybackInfoTimer) +{ willNotifyPlaybackInfo(); std::unique_ptr playbackInfoTimerMock = std::make_unique>(); EXPECT_CALL(*m_timerFactoryMock, createTimer(kPlaybackInfoTimerMs, _, common::TimerType::PERIODIC)) .WillOnce(Return(ByMove(std::move(playbackInfoTimerMock)))); - m_sut->startPositionReportingAndCheckAudioUnderflowTimer(); + m_sut->startNotifyPlaybackInfoTimer(); } TEST_F(GstGenericPlayerPrivateTest, shouldNotStartPositionReportingTimerWhenItIsActive) @@ -1668,14 +1674,22 @@ TEST_F(GstGenericPlayerPrivateTest, shouldNotStartPositionReportingTimerWhenItIs EXPECT_CALL(dynamic_cast &>(*timerMock), isActive()).WillOnce(Return(true)); EXPECT_CALL(*m_timerFactoryMock, createTimer(kPositionReportTimerMs, _, common::TimerType::PERIODIC)) .WillOnce(Return(ByMove(std::move(timerMock)))); - willNotifyPlaybackInfo(); - std::unique_ptr playbackInfoTimerMock = std::make_unique>(); - EXPECT_CALL(*m_timerFactoryMock, createTimer(kPlaybackInfoTimerMs, _, common::TimerType::PERIODIC)) - .WillOnce(Return(ByMove(std::move(playbackInfoTimerMock)))); + m_sut->startPositionReportingAndCheckAudioUnderflowTimer(); m_sut->startPositionReportingAndCheckAudioUnderflowTimer(); } +TEST_F(GstGenericPlayerPrivateTest, shouldNotStartPlaybackInfoTimerWhenItIsActive) +{ + std::unique_ptr timerMock = std::make_unique>(); + EXPECT_CALL(dynamic_cast &>(*timerMock), isActive()).WillOnce(Return(true)); + EXPECT_CALL(*m_timerFactoryMock, createTimer(kPlaybackInfoTimerMs, _, common::TimerType::PERIODIC)) + .WillOnce(Return(ByMove(std::move(timerMock)))); + willNotifyPlaybackInfo(); + m_sut->startNotifyPlaybackInfoTimer(); + m_sut->startNotifyPlaybackInfoTimer(); +} + TEST_F(GstGenericPlayerPrivateTest, shouldScheduleReportPositionWhenPositionReportingTimerIsFired) { std::unique_ptr timerMock = std::make_unique>(); @@ -1692,6 +1706,12 @@ TEST_F(GstGenericPlayerPrivateTest, shouldScheduleReportPositionWhenPositionRepo callback(); return std::move(timerMock); })); + m_sut->startPositionReportingAndCheckAudioUnderflowTimer(); +} + +TEST_F(GstGenericPlayerPrivateTest, shouldSchedulePlaybackInfoWhenPlaybackInfoTimerIsFired) +{ + std::unique_ptr timerMock = std::make_unique>(); willNotifyPlaybackInfo(); std::unique_ptr playbackInfoTimerMock = std::make_unique>(); EXPECT_CALL(*m_timerFactoryMock, createTimer(kPlaybackInfoTimerMs, _, common::TimerType::PERIODIC)) @@ -1702,7 +1722,7 @@ TEST_F(GstGenericPlayerPrivateTest, shouldScheduleReportPositionWhenPositionRepo callback(); return std::move(playbackInfoTimerMock); })); - m_sut->startPositionReportingAndCheckAudioUnderflowTimer(); + m_sut->startNotifyPlaybackInfoTimer(); } TEST_F(GstGenericPlayerPrivateTest, shouldStopActivePositionReportingTimer) @@ -1712,14 +1732,22 @@ TEST_F(GstGenericPlayerPrivateTest, shouldStopActivePositionReportingTimer) EXPECT_CALL(dynamic_cast &>(*timerMock), cancel()); EXPECT_CALL(*m_timerFactoryMock, createTimer(kPositionReportTimerMs, _, common::TimerType::PERIODIC)) .WillOnce(Return(ByMove(std::move(timerMock)))); + + m_sut->startPositionReportingAndCheckAudioUnderflowTimer(); + m_sut->stopPositionReportingAndCheckAudioUnderflowTimer(); +} + +TEST_F(GstGenericPlayerPrivateTest, shouldStopActivePlaybackInfoTimerTimer) +{ willNotifyPlaybackInfo(); std::unique_ptr playbackInfoTimerMock = std::make_unique>(); EXPECT_CALL(dynamic_cast &>(*playbackInfoTimerMock), isActive()).WillOnce(Return(true)); EXPECT_CALL(dynamic_cast &>(*playbackInfoTimerMock), cancel()); EXPECT_CALL(*m_timerFactoryMock, createTimer(kPlaybackInfoTimerMs, _, common::TimerType::PERIODIC)) .WillOnce(Return(ByMove(std::move(playbackInfoTimerMock)))); - m_sut->startPositionReportingAndCheckAudioUnderflowTimer(); - m_sut->stopPositionReportingAndCheckAudioUnderflowTimer(); + + m_sut->startNotifyPlaybackInfoTimer(); + m_sut->stopNotifyPlaybackInfoTimer(); } TEST_F(GstGenericPlayerPrivateTest, shouldNotStopInactivePositionReportingTimer) @@ -1728,13 +1756,21 @@ TEST_F(GstGenericPlayerPrivateTest, shouldNotStopInactivePositionReportingTimer) EXPECT_CALL(dynamic_cast &>(*timerMock), isActive()).WillOnce(Return(false)); EXPECT_CALL(*m_timerFactoryMock, createTimer(kPositionReportTimerMs, _, common::TimerType::PERIODIC)) .WillOnce(Return(ByMove(std::move(timerMock)))); + + m_sut->startPositionReportingAndCheckAudioUnderflowTimer(); + m_sut->stopPositionReportingAndCheckAudioUnderflowTimer(); +} + +TEST_F(GstGenericPlayerPrivateTest, shouldNotStopInactivePlaybackInfoTimer) +{ willNotifyPlaybackInfo(); std::unique_ptr playbackInfoTimerMock = std::make_unique>(); EXPECT_CALL(dynamic_cast &>(*playbackInfoTimerMock), isActive()).WillOnce(Return(false)); EXPECT_CALL(*m_timerFactoryMock, createTimer(kPlaybackInfoTimerMs, _, common::TimerType::PERIODIC)) .WillOnce(Return(ByMove(std::move(playbackInfoTimerMock)))); - m_sut->startPositionReportingAndCheckAudioUnderflowTimer(); - m_sut->stopPositionReportingAndCheckAudioUnderflowTimer(); + + m_sut->startNotifyPlaybackInfoTimer(); + m_sut->stopNotifyPlaybackInfoTimer(); } TEST_F(GstGenericPlayerPrivateTest, shouldNotStopInactivePositionReportingTimerWhenThereIsNoTimer) diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp index 87ddcf090..0534a78e9 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp @@ -2273,9 +2273,15 @@ void GenericTasksTestsBase::shouldStopGstPlayer() videoStreamIt->second.isDataNeeded = true; audioStreamIt->second.isDataNeeded = true; EXPECT_CALL(testContext->m_gstPlayer, stopPositionReportingAndCheckAudioUnderflowTimer()); + EXPECT_CALL(testContext->m_gstPlayer, stopNotifyPlaybackInfoTimer()); EXPECT_CALL(testContext->m_gstPlayer, changePipelineState(GST_STATE_NULL)).WillOnce(Return(GST_STATE_CHANGE_SUCCESS)); } +void GenericTasksTestsBase::shouldStopPositionReportingAndCheckAudioUnderflowTimer() +{ + EXPECT_CALL(testContext->m_gstPlayer, stopPositionReportingAndCheckAudioUnderflowTimer()); +} + void GenericTasksTestsBase::triggerStop() { firebolt::rialto::server::tasks::generic::Stop task{testContext->m_context, testContext->m_gstPlayer}; @@ -3040,7 +3046,6 @@ void GenericTasksTestsBase::checkAudioSourceNotRemoved() void GenericTasksTestsBase::shouldFlushAudioSrcSuccess() { - EXPECT_CALL(testContext->m_gstPlayer, stopPositionReportingAndCheckAudioUnderflowTimer()); EXPECT_CALL(*testContext->m_gstWrapper, gstEventNewFlushStart()).WillOnce(Return(&testContext->m_event)); EXPECT_CALL(*testContext->m_gstWrapper, gstElementSendEvent(&testContext->m_appSrcAudio, &testContext->m_event)) .WillOnce(Return(TRUE)); @@ -3051,7 +3056,6 @@ void GenericTasksTestsBase::shouldFlushAudioSrcSuccess() void GenericTasksTestsBase::shouldFlushAudioSrcFailure() { - EXPECT_CALL(testContext->m_gstPlayer, stopPositionReportingAndCheckAudioUnderflowTimer()); EXPECT_CALL(*testContext->m_gstWrapper, gstEventNewFlushStart()).WillOnce(Return(&testContext->m_event)); EXPECT_CALL(*testContext->m_gstWrapper, gstElementSendEvent(&testContext->m_appSrcAudio, &testContext->m_event)) .WillOnce(Return(FALSE)); @@ -3178,7 +3182,6 @@ void GenericTasksTestsBase::checkVideoFlushed() void GenericTasksTestsBase::shouldFlushVideoSrcSuccess() { - EXPECT_CALL(testContext->m_gstPlayer, stopPositionReportingAndCheckAudioUnderflowTimer()); EXPECT_CALL(*testContext->m_gstWrapper, gstEventNewFlushStart()).WillOnce(Return(&testContext->m_event)); EXPECT_CALL(*testContext->m_gstWrapper, gstElementSendEvent(&testContext->m_appSrcVideo, &testContext->m_event)) .WillOnce(Return(TRUE)); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h index 646641e1d..f6ee3beff 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h @@ -407,6 +407,7 @@ class GenericTasksTestsBase : public ::testing::Test void triggerRemoveSourceVideo(); void checkAudioSourceRemoved(); void checkAudioSourceNotRemoved(); + void shouldStopPositionReportingAndCheckAudioUnderflowTimer(); // Flush test methods void shouldFlushAudio(); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/HandleBusMessageTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/HandleBusMessageTest.cpp index 1295f2b9b..77f230ef9 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/HandleBusMessageTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/HandleBusMessageTest.cpp @@ -281,7 +281,8 @@ TEST_F(HandleBusMessageTest, shouldHandleStateChangedToPausedMessage) EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(pending)).WillOnce(Return("Void")); EXPECT_CALL(*m_gstWrapper, gstDebugBinToDotFileWithTs(GST_BIN(&m_pipeline), _, _)); EXPECT_CALL(m_gstPlayerClient, notifyPlaybackState(firebolt::rialto::PlaybackState::PAUSED)); - EXPECT_CALL(m_gstPlayer, notifyPlaybackInfo()); + EXPECT_CALL(m_gstPlayer, stopPositionReportingAndCheckAudioUnderflowTimer()); + EXPECT_CALL(m_gstPlayer, startNotifyPlaybackInfoTimer()); EXPECT_CALL(*m_gstWrapper, gstMessageUnref(&m_message)); EXPECT_CALL(m_flushWatcherMock, isFlushOngoing()).WillRepeatedly(Return(kNoFlushOngoing)); EXPECT_CALL(m_flushWatcherMock, isAsyncFlushOngoing()).WillRepeatedly(Return(kNoFlushOngoing)); @@ -310,7 +311,8 @@ TEST_F(HandleBusMessageTest, shouldHandleStateChangedToPausedMessageWhenSyncFlus EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(pending)).WillOnce(Return("Void")); EXPECT_CALL(*m_gstWrapper, gstDebugBinToDotFileWithTs(GST_BIN(&m_pipeline), _, _)); EXPECT_CALL(m_gstPlayerClient, notifyPlaybackState(firebolt::rialto::PlaybackState::PAUSED)); - EXPECT_CALL(m_gstPlayer, notifyPlaybackInfo()); + EXPECT_CALL(m_gstPlayer, stopPositionReportingAndCheckAudioUnderflowTimer()); + EXPECT_CALL(m_gstPlayer, startNotifyPlaybackInfoTimer()); EXPECT_CALL(*m_gstWrapper, gstMessageUnref(&m_message)); EXPECT_CALL(m_flushWatcherMock, isFlushOngoing()).WillRepeatedly(Return(kFlushOngoing)); EXPECT_CALL(m_flushWatcherMock, isAsyncFlushOngoing()).WillRepeatedly(Return(kNoFlushOngoing)); @@ -337,6 +339,8 @@ TEST_F(HandleBusMessageTest, shouldSkipHandlingStateChangedToPausedMessageWhenAs EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(2).WillRepeatedly(Return("Paused")); EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(pending)).WillOnce(Return("Void")); EXPECT_CALL(*m_gstWrapper, gstDebugBinToDotFileWithTs(GST_BIN(&m_pipeline), _, _)); + EXPECT_CALL(m_gstPlayer, stopPositionReportingAndCheckAudioUnderflowTimer()); + EXPECT_CALL(m_gstPlayer, startNotifyPlaybackInfoTimer()); EXPECT_CALL(*m_gstWrapper, gstMessageUnref(&m_message)); EXPECT_CALL(m_flushWatcherMock, isFlushOngoing()).WillOnce(Return(kFlushOngoing)); EXPECT_CALL(m_flushWatcherMock, isAsyncFlushOngoing()).WillOnce(Return(kFlushOngoing)); @@ -363,6 +367,8 @@ TEST_F(HandleBusMessageTest, shouldSkipHandlingStateChangedToPausedMessageWhenAs EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(2).WillRepeatedly(Return("Paused")); EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(pending)).WillOnce(Return("Void")); EXPECT_CALL(*m_gstWrapper, gstDebugBinToDotFileWithTs(GST_BIN(&m_pipeline), _, _)); + EXPECT_CALL(m_gstPlayer, stopPositionReportingAndCheckAudioUnderflowTimer()); + EXPECT_CALL(m_gstPlayer, startNotifyPlaybackInfoTimer()); EXPECT_CALL(*m_gstWrapper, gstMessageUnref(&m_message)); EXPECT_CALL(m_flushWatcherMock, isFlushOngoing()).WillOnce(Return(kFlushOngoing)); EXPECT_CALL(m_flushWatcherMock, isAsyncFlushOngoing()).WillOnce(Return(kNoFlushOngoing)).WillOnce(Return(kFlushOngoing)); @@ -386,6 +392,8 @@ TEST_F(HandleBusMessageTest, shouldHandleStateChangedToPausedAndPendingPausedMes EXPECT_CALL(m_gstPlayer, hasSourceType(firebolt::rialto::MediaSourceType::SUBTITLE)).WillOnce(Return(false)); EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(GST_STATE_PAUSED)).WillRepeatedly(Return("Paused")); EXPECT_CALL(*m_gstWrapper, gstDebugBinToDotFileWithTs(GST_BIN(&m_pipeline), _, _)); + EXPECT_CALL(m_gstPlayer, stopPositionReportingAndCheckAudioUnderflowTimer()); + EXPECT_CALL(m_gstPlayer, startNotifyPlaybackInfoTimer()); EXPECT_CALL(*m_gstWrapper, gstMessageUnref(&m_message)); EXPECT_CALL(m_flushWatcherMock, isFlushOngoing()).WillRepeatedly(Return(kNoFlushOngoing)); EXPECT_CALL(m_flushWatcherMock, isAsyncFlushOngoing()).WillRepeatedly(Return(kNoFlushOngoing)); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/RemoveSourceTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/RemoveSourceTest.cpp index a9153ff75..4884d844d 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/RemoveSourceTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/RemoveSourceTest.cpp @@ -53,6 +53,7 @@ TEST_F(RemoveSourceTest, shouldRemoveAudioSource) shouldInvalidateActiveAudioRequests(); shouldDisableAudioFlag(); shouldFlushAudioSrcSuccess(); + shouldStopPositionReportingAndCheckAudioUnderflowTimer(); triggerRemoveSourceAudio(); checkNoMoreNeedData(); checkNoNeedDataPendingForBothSources(); @@ -65,6 +66,7 @@ TEST_F(RemoveSourceTest, shouldRemoveAudioSourceFlushEventError) shouldInvalidateActiveAudioRequests(); shouldDisableAudioFlag(); shouldFlushAudioSrcFailure(); + shouldStopPositionReportingAndCheckAudioUnderflowTimer(); triggerRemoveSourceAudio(); checkNoMoreNeedData(); checkNoNeedDataPendingForBothSources(); diff --git a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h index e1f4b3efb..346dca566 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h @@ -59,6 +59,8 @@ class GstGenericPlayerPrivateMock : public IGstGenericPlayerPrivate MOCK_METHOD(int64_t, getPosition, (GstElement * element), (override)); MOCK_METHOD(void, startPositionReportingAndCheckAudioUnderflowTimer, (), (override)); MOCK_METHOD(void, stopPositionReportingAndCheckAudioUnderflowTimer, (), (override)); + MOCK_METHOD(void, startNotifyPlaybackInfoTimer, (), (override)); + MOCK_METHOD(void, stopNotifyPlaybackInfoTimer, (), (override)); MOCK_METHOD(void, stopWorkerThread, (), (override)); MOCK_METHOD(void, cancelUnderflow, (firebolt::rialto::MediaSourceType mediaSource), (override)); MOCK_METHOD(void, setPendingPlaybackRate, (), (override)); From 19f562812d023caa23113961e81124f1b232274e Mon Sep 17 00:00:00 2001 From: Marcin Wojciechowski <105790697+skywojciechowskim@users.noreply.github.com> Date: Wed, 28 Jan 2026 08:44:04 +0100 Subject: [PATCH 04/29] Session server app changed to shared ptr to avoid invalid ptrs. Some minor changes added to make code safer. (#437) Summary: Session server app changed to shared ptr to avoid invalid ptrs. Some minor changes added to make code safer. Type: Fix Test Plan: UT/CT, Fullstack Jira: VPLAY-12448 --- .../common/source/ISessionServerAppFactory.h | 4 +- .../common/source/SessionServerApp.cpp | 1 - .../common/source/SessionServerAppFactory.cpp | 8 +- .../common/source/SessionServerAppFactory.h | 4 +- .../common/source/SessionServerAppManager.cpp | 131 +++++---- .../common/source/SessionServerAppManager.h | 24 +- serverManager/ipc/source/Client.cpp | 34 ++- serverManager/ipc/source/Client.h | 3 + .../service/source/ServerManagerService.cpp | 2 +- .../mocks/SessionServerAppFactoryMock.h | 4 +- .../SessionServerAppManagerTestsFixture.cpp | 273 +++++++++--------- .../SessionServerAppManagerTestsFixture.h | 5 +- .../common/SessionServerAppTestsFixture.cpp | 8 +- .../common/SessionServerAppTestsFixture.h | 2 +- 14 files changed, 267 insertions(+), 236 deletions(-) diff --git a/serverManager/common/source/ISessionServerAppFactory.h b/serverManager/common/source/ISessionServerAppFactory.h index 18c5323ce..7b2d9d1ab 100644 --- a/serverManager/common/source/ISessionServerAppFactory.h +++ b/serverManager/common/source/ISessionServerAppFactory.h @@ -36,11 +36,11 @@ class ISessionServerAppFactory ISessionServerAppFactory() = default; virtual ~ISessionServerAppFactory() = default; - virtual std::unique_ptr + virtual std::shared_ptr create(const std::string &appName, const firebolt::rialto::common::SessionServerState &initialState, const firebolt::rialto::common::AppConfig &appConfig, SessionServerAppManager &sessionServerAppManager, std::unique_ptr &&namedSocket) const = 0; - virtual std::unique_ptr + virtual std::shared_ptr create(SessionServerAppManager &sessionServerAppManager, std::unique_ptr &&namedSocket) const = 0; }; diff --git a/serverManager/common/source/SessionServerApp.cpp b/serverManager/common/source/SessionServerApp.cpp index 75b6388d7..ac5cac45a 100644 --- a/serverManager/common/source/SessionServerApp.cpp +++ b/serverManager/common/source/SessionServerApp.cpp @@ -298,7 +298,6 @@ void SessionServerApp::kill() if (m_pid > 0) { m_linuxWrapper->kill(m_pid, SIGKILL); - m_pid = -1; } } diff --git a/serverManager/common/source/SessionServerAppFactory.cpp b/serverManager/common/source/SessionServerAppFactory.cpp index 24e940c52..63e01c305 100644 --- a/serverManager/common/source/SessionServerAppFactory.cpp +++ b/serverManager/common/source/SessionServerAppFactory.cpp @@ -38,12 +38,12 @@ SessionServerAppFactory::SessionServerAppFactory(const std::list &e { } -std::unique_ptr SessionServerAppFactory::create( +std::shared_ptr SessionServerAppFactory::create( const std::string &appName, const firebolt::rialto::common::SessionServerState &initialState, const firebolt::rialto::common::AppConfig &appConfig, SessionServerAppManager &sessionServerAppManager, std::unique_ptr &&namedSocket) const { - return std::make_unique(appName, initialState, appConfig, + return std::make_shared(appName, initialState, appConfig, m_linuxWrapperFactory->createLinuxWrapper(), firebolt::rialto::common::ITimerFactory::getFactory(), sessionServerAppManager, m_kEnvironmentVariables, m_kSessionServerPath, @@ -51,11 +51,11 @@ std::unique_ptr SessionServerAppFactory::create( m_kSocketGroup, std::move(namedSocket)); } -std::unique_ptr +std::shared_ptr SessionServerAppFactory::create(SessionServerAppManager &sessionServerAppManager, std::unique_ptr &&namedSocket) const { - return std::make_unique(m_linuxWrapperFactory->createLinuxWrapper(), + return std::make_shared(m_linuxWrapperFactory->createLinuxWrapper(), firebolt::rialto::common::ITimerFactory::getFactory(), sessionServerAppManager, m_kEnvironmentVariables, m_kSessionServerPath, m_kSessionServerStartupTimeout, m_kSocketPermissions, m_kSocketOwner, diff --git a/serverManager/common/source/SessionServerAppFactory.h b/serverManager/common/source/SessionServerAppFactory.h index 5802b7841..c8a49b82e 100644 --- a/serverManager/common/source/SessionServerAppFactory.h +++ b/serverManager/common/source/SessionServerAppFactory.h @@ -39,11 +39,11 @@ class SessionServerAppFactory : public ISessionServerAppFactory const std::string &socketGroup); ~SessionServerAppFactory() override = default; - std::unique_ptr + std::shared_ptr create(const std::string &appName, const firebolt::rialto::common::SessionServerState &initialState, const firebolt::rialto::common::AppConfig &appConfig, SessionServerAppManager &sessionServerAppManager, std::unique_ptr &&namedSocket) const override; - std::unique_ptr + std::shared_ptr create(SessionServerAppManager &sessionServerAppManager, std::unique_ptr &&namedSocket) const override; diff --git a/serverManager/common/source/SessionServerAppManager.cpp b/serverManager/common/source/SessionServerAppManager.cpp index 7ce152a51..08b465973 100644 --- a/serverManager/common/source/SessionServerAppManager.cpp +++ b/serverManager/common/source/SessionServerAppManager.cpp @@ -24,11 +24,6 @@ #include #include -namespace -{ -const std::unique_ptr kInvalidSessionServer; -} // namespace - namespace rialto::servermanager::common { SessionServerAppManager::SessionServerAppManager( @@ -82,7 +77,7 @@ bool SessionServerAppManager::handleInitiateApplication(const std::string &appNa toString(state)); if (state != firebolt::rialto::common::SessionServerState::NOT_RUNNING && !getServerByAppName(appName)) { - const auto &preloadedServer{getPreloadedServer()}; + auto preloadedServer{getPreloadedServer()}; if (preloadedServer) { return configurePreloadedSessionServer(preloadedServer, appName, state, appConfig); @@ -153,10 +148,10 @@ std::string SessionServerAppManager::getAppConnectionInfo(const std::string &app m_eventThread->add( [&]() { - const auto &kSessionServer{getServerByAppName(appName)}; - if (kSessionServer) + auto sessionServer{getServerByAppName(appName)}; + if (sessionServer) { - return p.set_value(kSessionServer->getSessionManagementSocketName()); + return p.set_value(sessionServer->getSessionManagementSocketName()); } RIALTO_SERVER_MANAGER_LOG_ERROR("App: %s could not be found", appName.c_str()); return p.set_value(""); @@ -196,18 +191,22 @@ void SessionServerAppManager::handleRestartServer(int serverId) RIALTO_SERVER_MANAGER_LOG_DEBUG("Not restarting serverId: %d as server manager is shutting down", serverId); return; } - const auto &kSessionServer{getServerById(serverId)}; - if (!kSessionServer) + auto sessionServer{getServerById(serverId)}; + if (!sessionServer) { RIALTO_SERVER_MANAGER_LOG_WARN("Unable to restart server, serverId: %d", serverId); return; } + if (m_healthcheckService) + { + m_healthcheckService->onServerRemoved(sessionServer->getServerId()); + } // First, get all needed information from current app - const std::string kAppName{kSessionServer->getAppName()}; - const firebolt::rialto::common::SessionServerState kState{kSessionServer->getExpectedState()}; - const firebolt::rialto::common::AppConfig kAppConfig{kSessionServer->getSessionManagementSocketName(), - kSessionServer->getClientDisplayName()}; - std::unique_ptr namedSocket{std::move(kSessionServer->releaseNamedSocket())}; + const std::string kAppName{sessionServer->getAppName()}; + const firebolt::rialto::common::SessionServerState kState{sessionServer->getExpectedState()}; + const firebolt::rialto::common::AppConfig kAppConfig{sessionServer->getSessionManagementSocketName(), + sessionServer->getClientDisplayName()}; + std::unique_ptr namedSocket{std::move(sessionServer->releaseNamedSocket())}; if (firebolt::rialto::common::SessionServerState::INACTIVE != kState && firebolt::rialto::common::SessionServerState::ACTIVE != kState) { @@ -216,8 +215,9 @@ void SessionServerAppManager::handleRestartServer(int serverId) } RIALTO_SERVER_MANAGER_LOG_DEBUG("Restarting server with id: %d", serverId); // Then kill the app - kSessionServer->kill(); + sessionServer->kill(); handleSessionServerStateChange(serverId, firebolt::rialto::common::SessionServerState::NOT_RUNNING); + sessionServer.reset(); // Finally, spawn the new app with old settings and set named socket if present auto app = m_sessionServerAppFactory->create(kAppName, kState, kAppConfig, *this, std::move(namedSocket)); @@ -235,7 +235,7 @@ void SessionServerAppManager::handleRestartServer(int serverId) } } -bool SessionServerAppManager::connectSessionServer(const std::unique_ptr &kSessionServer) +bool SessionServerAppManager::connectSessionServer(const std::shared_ptr &kSessionServer) { if (!kSessionServer) { @@ -260,7 +260,7 @@ bool SessionServerAppManager::connectSessionServer(const std::unique_ptr &kSessionServer) +bool SessionServerAppManager::configureSessionServer(const std::shared_ptr &kSessionServer) { if (!kSessionServer) { @@ -274,7 +274,7 @@ bool SessionServerAppManager::configureSessionServer(const std::unique_ptr &kSessionServer, +bool SessionServerAppManager::configurePreloadedSessionServer(const std::shared_ptr &kSessionServer, const std::string &appName, const firebolt::rialto::common::SessionServerState &state, const firebolt::rialto::common::AppConfig &appConfig) @@ -302,18 +302,22 @@ bool SessionServerAppManager::changeSessionServerState(const std::string &appNam { RIALTO_SERVER_MANAGER_LOG_INFO("RialtoServerManager requests to change state of %s to %s", appName.c_str(), toString(newState)); - const auto &kSessionServer{getServerByAppName(appName)}; - if (!kSessionServer) + auto sessionServer{getServerByAppName(appName)}; + if (!sessionServer) { RIALTO_SERVER_MANAGER_LOG_ERROR("Change state of %s to %s failed - session server not found.", appName.c_str(), toString(newState)); return false; } - kSessionServer->setExpectedState(newState); - if (!m_ipcController->performSetState(kSessionServer->getServerId(), newState)) + sessionServer->setExpectedState(newState); + if (m_healthcheckService && firebolt::rialto::common::SessionServerState::NOT_RUNNING == newState) + { + m_healthcheckService->onServerRemoved(sessionServer->getServerId()); + } + if (!m_ipcController->performSetState(sessionServer->getServerId(), newState)) { RIALTO_SERVER_MANAGER_LOG_ERROR("Change state of %s to %s failed.", appName.c_str(), toString(newState)); - handleStateChangeFailure(kSessionServer, newState); + handleStateChangeFailure(sessionServer, newState); return false; } RIALTO_SERVER_MANAGER_LOG_INFO("Change state of %s to %s succeeded.", appName.c_str(), toString(newState)); @@ -324,46 +328,49 @@ void SessionServerAppManager::handleSessionServerStateChange(int serverId, firebolt::rialto::common::SessionServerState newState) { RIALTO_SERVER_MANAGER_LOG_INFO("SessionServer with id: %d changed state to %s", serverId, toString(newState)); - const auto &kSessionServer{getServerById(serverId)}; - if (!kSessionServer) + auto sessionServer{getServerById(serverId)}; + if (!sessionServer) { RIALTO_SERVER_MANAGER_LOG_WARN("SessionServer with id: %d not found", serverId); return; } - std::string appName{kSessionServer->getAppName()}; + std::string appName{sessionServer->getAppName()}; if (!appName.empty() && m_stateObserver) // empty app name is when SessionServer is preloaded { m_stateObserver->stateChanged(appName, newState); } if (firebolt::rialto::common::SessionServerState::UNINITIALIZED == newState) { - kSessionServer->cancelStartupTimer(); - if (!kSessionServer->isPreloaded() && !configureSessionServer(kSessionServer)) + sessionServer->cancelStartupTimer(); + if (!sessionServer->isPreloaded() && !configureSessionServer(sessionServer)) { handleSessionServerStateChange(serverId, firebolt::rialto::common::SessionServerState::ERROR); - kSessionServer->kill(); + sessionServer->kill(); handleSessionServerStateChange(serverId, firebolt::rialto::common::SessionServerState::NOT_RUNNING); } } - else if (newState == firebolt::rialto::common::SessionServerState::ERROR && kSessionServer->isPreloaded()) + else if (newState == firebolt::rialto::common::SessionServerState::ERROR && sessionServer->isPreloaded()) { m_ipcController->removeClient(serverId); - kSessionServer->kill(); + sessionServer->kill(); if (m_healthcheckService) { - m_healthcheckService->onServerRemoved(kSessionServer->getServerId()); + m_healthcheckService->onServerRemoved(sessionServer->getServerId()); + } + m_sessionServerApps.erase(sessionServer); + if (!m_isShuttingDown) + { + connectSessionServer(preloadSessionServer()); } - m_sessionServerApps.erase(kSessionServer); - connectSessionServer(preloadSessionServer()); } else if (newState == firebolt::rialto::common::SessionServerState::NOT_RUNNING) { m_ipcController->removeClient(serverId); if (m_healthcheckService) { - m_healthcheckService->onServerRemoved(kSessionServer->getServerId()); + m_healthcheckService->onServerRemoved(sessionServer->getServerId()); } - m_sessionServerApps.erase(kSessionServer); + m_sessionServerApps.erase(sessionServer); } } @@ -394,7 +401,7 @@ void SessionServerAppManager::shutdownAllSessionServers() m_sessionServerApps.clear(); } -const std::unique_ptr & +std::shared_ptr SessionServerAppManager::launchSessionServer(const std::string &appName, const firebolt::rialto::common::SessionServerState &kInitialState, const firebolt::rialto::common::AppConfig &appConfig) @@ -404,31 +411,29 @@ SessionServerAppManager::launchSessionServer(const std::string &appName, m_namedSocketFactory.createNamedSocket()); if (app->launch()) { - auto result = m_sessionServerApps.emplace(std::move(app)); - if (result.second) + if (m_sessionServerApps.emplace(app).second) { - return *result.first; + return app; } } - return kInvalidSessionServer; + return nullptr; } -const std::unique_ptr &SessionServerAppManager::preloadSessionServer() +std::shared_ptr SessionServerAppManager::preloadSessionServer() { RIALTO_SERVER_MANAGER_LOG_INFO("Preloading new Rialto Session Server"); auto app = m_sessionServerAppFactory->create(*this, m_namedSocketFactory.createNamedSocket()); if (app->launch()) { - auto result = m_sessionServerApps.emplace(std::move(app)); - if (result.second) + if (m_sessionServerApps.emplace(app).second) { - return *result.first; + return app; } } - return kInvalidSessionServer; + return nullptr; } -const std::unique_ptr &SessionServerAppManager::getPreloadedServer() const +std::shared_ptr SessionServerAppManager::getPreloadedServer() const { auto iter = std::find_if(m_sessionServerApps.begin(), m_sessionServerApps.end(), [](const auto &srv) { return srv->isPreloaded() && srv->isConnected(); }); @@ -436,10 +441,10 @@ const std::unique_ptr &SessionServerAppManager::getPreloadedS { return *iter; } - return kInvalidSessionServer; + return nullptr; } -const std::unique_ptr &SessionServerAppManager::getServerByAppName(const std::string &appName) const +std::shared_ptr SessionServerAppManager::getServerByAppName(const std::string &appName) const { auto iter{std::find_if(m_sessionServerApps.begin(), m_sessionServerApps.end(), [&](const auto &srv) { return srv->getAppName() == appName; })}; @@ -447,10 +452,10 @@ const std::unique_ptr &SessionServerAppManager::getServerByAp { return *iter; } - return kInvalidSessionServer; + return nullptr; } -const std::unique_ptr &SessionServerAppManager::getServerById(int serverId) const +std::shared_ptr SessionServerAppManager::getServerById(int serverId) const { auto iter{std::find_if(m_sessionServerApps.begin(), m_sessionServerApps.end(), [&](const auto &srv) { return srv->getServerId() == serverId; })}; @@ -458,10 +463,10 @@ const std::unique_ptr &SessionServerAppManager::getServerById { return *iter; } - return kInvalidSessionServer; + return nullptr; } -void SessionServerAppManager::handleStateChangeFailure(const std::unique_ptr &kSessionServer, +void SessionServerAppManager::handleStateChangeFailure(const std::shared_ptr &kSessionServer, const firebolt::rialto::common::SessionServerState &state) { if (state == firebolt::rialto::common::SessionServerState::NOT_RUNNING) @@ -477,7 +482,7 @@ void SessionServerAppManager::handleStateChangeFailure(const std::unique_ptr &kSessionServer) +bool SessionServerAppManager::configureSessionServerWithSocketName(const std::shared_ptr &kSessionServer) { RIALTO_SERVER_MANAGER_LOG_DEBUG("Configuring Session Server using socket name"); const auto kInitialState{kSessionServer->getInitialState()}; @@ -502,7 +507,7 @@ bool SessionServerAppManager::configureSessionServerWithSocketName(const std::un return true; } -bool SessionServerAppManager::configureSessionServerWithSocketFd(const std::unique_ptr &kSessionServer) +bool SessionServerAppManager::configureSessionServerWithSocketFd(const std::shared_ptr &kSessionServer) { RIALTO_SERVER_MANAGER_LOG_DEBUG("Configuring Session Server using socket fd"); const auto kInitialState{kSessionServer->getInitialState()}; @@ -530,19 +535,23 @@ void SessionServerAppManager::onServerStartupTimeout(int serverId) void SessionServerAppManager::handleServerStartupTimeout(int serverId) { - const auto &kSessionServer{getServerById(serverId)}; - if (!kSessionServer) + auto sessionServer{getServerById(serverId)}; + if (!sessionServer) { RIALTO_SERVER_MANAGER_LOG_WARN("Unable to handle startup timeout for serverId: %d", serverId); return; } - const bool isPreloaded{kSessionServer->isPreloaded()}; + const bool isPreloaded{sessionServer->isPreloaded()}; RIALTO_SERVER_MANAGER_LOG_WARN("Killing: %d", serverId); handleSessionServerStateChange(serverId, firebolt::rialto::common::SessionServerState::ERROR); if (!isPreloaded) { - kSessionServer->kill(); + if (m_healthcheckService) + { + m_healthcheckService->onServerRemoved(sessionServer->getServerId()); + } + sessionServer->kill(); handleSessionServerStateChange(serverId, firebolt::rialto::common::SessionServerState::NOT_RUNNING); } } diff --git a/serverManager/common/source/SessionServerAppManager.h b/serverManager/common/source/SessionServerAppManager.h index b1d684348..e16f26f70 100644 --- a/serverManager/common/source/SessionServerAppManager.h +++ b/serverManager/common/source/SessionServerAppManager.h @@ -64,9 +64,9 @@ class SessionServerAppManager : public ISessionServerAppManager void onServerStartupTimeout(int serverId) override; private: - bool connectSessionServer(const std::unique_ptr &sessionServer); - bool configureSessionServer(const std::unique_ptr &sessionServer); - bool configurePreloadedSessionServer(const std::unique_ptr &sessionServer, + bool connectSessionServer(const std::shared_ptr &sessionServer); + bool configureSessionServer(const std::shared_ptr &sessionServer); + bool configurePreloadedSessionServer(const std::shared_ptr &sessionServer, const std::string &appName, const firebolt::rialto::common::SessionServerState &state, const firebolt::rialto::common::AppConfig &appConfig); @@ -75,26 +75,26 @@ class SessionServerAppManager : public ISessionServerAppManager void handleSessionServerStateChange(int serverId, firebolt::rialto::common::SessionServerState newState); void handleAck(int serverId, int pingId, bool success); void shutdownAllSessionServers(); - const std::unique_ptr & + std::shared_ptr launchSessionServer(const std::string &appName, const firebolt::rialto::common::SessionServerState &initialState, const firebolt::rialto::common::AppConfig &appConfig); - void handleStateChangeFailure(const std::unique_ptr &sessionServer, + void handleStateChangeFailure(const std::shared_ptr &sessionServer, const firebolt::rialto::common::SessionServerState &state); - const std::unique_ptr &preloadSessionServer(); - const std::unique_ptr &getPreloadedServer() const; - const std::unique_ptr &getServerByAppName(const std::string &appName) const; - const std::unique_ptr &getServerById(int serverId) const; + std::shared_ptr preloadSessionServer(); + std::shared_ptr getPreloadedServer() const; + std::shared_ptr getServerByAppName(const std::string &appName) const; + std::shared_ptr getServerById(int serverId) const; bool handleInitiateApplication(const std::string &appName, const firebolt::rialto::common::SessionServerState &state, const firebolt::rialto::common::AppConfig &appConfig); void handleRestartServer(int serverId); - bool configureSessionServerWithSocketName(const std::unique_ptr &kSessionServer); - bool configureSessionServerWithSocketFd(const std::unique_ptr &kSessionServer); + bool configureSessionServerWithSocketName(const std::shared_ptr &kSessionServer); + bool configureSessionServerWithSocketFd(const std::shared_ptr &kSessionServer); void handleServerStartupTimeout(int serverId); private: std::unique_ptr &m_ipcController; std::unique_ptr m_eventThread; - std::set> m_sessionServerApps; + std::set> m_sessionServerApps; std::unique_ptr m_sessionServerAppFactory; std::shared_ptr m_stateObserver; std::unique_ptr m_healthcheckService; diff --git a/serverManager/ipc/source/Client.cpp b/serverManager/ipc/source/Client.cpp index 2690093fc..c11c0693c 100644 --- a/serverManager/ipc/source/Client.cpp +++ b/serverManager/ipc/source/Client.cpp @@ -138,6 +138,15 @@ Client::Client(std::unique_ptr &sessionServerA Client::~Client() { RIALTO_SERVER_MANAGER_LOG_INFO("Client for serverId: %d is destructed", m_serverId); + m_isShuttingDown = true; + if (m_ipcLoop && m_ipcLoop->channel()) + { + for (const auto &tag : m_eventTags) + { + m_ipcLoop->channel()->unsubscribe(tag); + } + m_eventTags.clear(); + } m_serviceStub.reset(); m_ipcLoop.reset(); } @@ -151,9 +160,18 @@ bool Client::connect() return false; } m_serviceStub = std::make_unique<::rialto::ServerManagerModule_Stub>(m_ipcLoop->channel()); - m_ipcLoop->channel()->subscribe( - std::bind(&Client::onStateChangedEvent, this, std::placeholders::_1)); - m_ipcLoop->channel()->subscribe(std::bind(&Client::onAckEvent, this, std::placeholders::_1)); + int eventTag{m_ipcLoop->channel()->subscribe( + std::bind(&Client::onStateChangedEvent, this, std::placeholders::_1))}; + if (eventTag >= 0) + { + m_eventTags.push_back(eventTag); + } + eventTag = + m_ipcLoop->channel()->subscribe(std::bind(&Client::onAckEvent, this, std::placeholders::_1)); + if (eventTag >= 0) + { + m_eventTags.push_back(eventTag); + } return true; } @@ -308,6 +326,12 @@ bool Client::setLogLevels(const service::LoggingLevels &logLevels) const void Client::onDisconnected() const { + if (!m_sessionServerAppManager || m_isShuttingDown) + { + RIALTO_SERVER_MANAGER_LOG_DEBUG("Connection to serverId: %d broken, but server manager is shutting down", + m_serverId); + return; + } RIALTO_SERVER_MANAGER_LOG_WARN("Connection to serverId: %d broken, server probably crashed. Starting recovery", m_serverId); m_sessionServerAppManager->restartServer(m_serverId); @@ -316,7 +340,7 @@ void Client::onDisconnected() const void Client::onStateChangedEvent(const std::shared_ptr &event) const { RIALTO_SERVER_MANAGER_LOG_DEBUG("StateChangedEvent received for serverId: %d", m_serverId); - if (!m_sessionServerAppManager || !event) + if (!m_sessionServerAppManager || !event || m_isShuttingDown) { RIALTO_SERVER_MANAGER_LOG_WARN("Problem during StateChangedEvent processing"); return; @@ -327,7 +351,7 @@ void Client::onStateChangedEvent(const std::shared_ptr &event) const { RIALTO_SERVER_MANAGER_LOG_DEBUG("AckEvent received for serverId: %d", m_serverId); - if (!m_sessionServerAppManager || !event) + if (!m_sessionServerAppManager || !event || m_isShuttingDown) { RIALTO_SERVER_MANAGER_LOG_WARN("Problem during AckEvent processing"); return; diff --git a/serverManager/ipc/source/Client.h b/serverManager/ipc/source/Client.h index f97c87f02..0eb84aa6f 100644 --- a/serverManager/ipc/source/Client.h +++ b/serverManager/ipc/source/Client.h @@ -26,6 +26,7 @@ #include "LoggingLevels.h" #include #include +#include namespace rialto { @@ -69,9 +70,11 @@ class Client int m_serverId; std::unique_ptr &m_sessionServerAppManager; int m_socket; + bool m_isShuttingDown{false}; std::shared_ptr<::firebolt::rialto::ipc::IChannel> m_channel; std::shared_ptr m_ipcLoop; std::unique_ptr<::rialto::ServerManagerModule_Stub> m_serviceStub; + std::vector m_eventTags; }; } // namespace rialto::servermanager::ipc diff --git a/serverManager/service/source/ServerManagerService.cpp b/serverManager/service/source/ServerManagerService.cpp index 09fa6126e..9edd1576f 100644 --- a/serverManager/service/source/ServerManagerService.cpp +++ b/serverManager/service/source/ServerManagerService.cpp @@ -59,7 +59,7 @@ ServerManagerService::ServerManagerService(std::unique_ptr &&co ServerManagerService::~ServerManagerService() { - RIALTO_SERVER_MANAGER_LOG_INFO("RialtoServerManager is closing..."); + RIALTO_SERVER_MANAGER_LOG_MIL("RialtoServerManager is closing..."); if (m_logHandler) { firebolt::rialto::logging::setLogHandler(RIALTO_COMPONENT_SERVER_MANAGER, 0, false); diff --git a/tests/unittests/serverManager/mocks/SessionServerAppFactoryMock.h b/tests/unittests/serverManager/mocks/SessionServerAppFactoryMock.h index 4b500d482..deba36825 100644 --- a/tests/unittests/serverManager/mocks/SessionServerAppFactoryMock.h +++ b/tests/unittests/serverManager/mocks/SessionServerAppFactoryMock.h @@ -33,12 +33,12 @@ class SessionServerAppFactoryMock : public ISessionServerAppFactory SessionServerAppFactoryMock() = default; virtual ~SessionServerAppFactoryMock() = default; - MOCK_METHOD(std::unique_ptr, create, + MOCK_METHOD(std::shared_ptr, create, (const std::string &appId, const firebolt::rialto::common::SessionServerState &initialState, const firebolt::rialto::common::AppConfig &appConfig, SessionServerAppManager &sessionServerAppManager, std::unique_ptr &&namedSocket), (const, override)); - MOCK_METHOD(std::unique_ptr, create, + MOCK_METHOD(std::shared_ptr, create, (SessionServerAppManager & sessionServerAppManager, std::unique_ptr &&namedSocket), (const, override)); diff --git a/tests/unittests/serverManager/unittests/common/SessionServerAppManagerTestsFixture.cpp b/tests/unittests/serverManager/unittests/common/SessionServerAppManagerTestsFixture.cpp index 3998a8a5e..83994a266 100644 --- a/tests/unittests/serverManager/unittests/common/SessionServerAppManagerTestsFixture.cpp +++ b/tests/unittests/serverManager/unittests/common/SessionServerAppManagerTestsFixture.cpp @@ -81,15 +81,13 @@ MATCHER_P(SmartPtrMatcher, expectedPtr, "") SessionServerAppManagerTests::SessionServerAppManagerTests() : m_controller{std::make_unique>()}, m_stateObserver{std::make_shared>()}, - m_sessionServerApp{std::make_unique>()}, + m_sessionServerAppMock{std::make_shared>()}, m_sessionServerAppFactory{ std::make_unique>()}, m_healthcheckServiceFactory{ std::make_unique>()}, m_healthcheckService{std::make_unique>()}, m_controllerMock{dynamic_cast &>(*m_controller)}, - m_sessionServerAppMock{ - dynamic_cast &>(*m_sessionServerApp)}, m_sessionServerAppFactoryMock{dynamic_cast &>( *m_sessionServerAppFactory)}, m_healthcheckServiceFactoryMock{ @@ -119,16 +117,16 @@ void SessionServerAppManagerTests::sessionServerLaunchWillFail(const firebolt::r EXPECT_CALL(m_namedSocketFactoryMock, createNamedSocket()).WillOnce(Return(ByMove(std::move(m_namedSocket)))); EXPECT_CALL(m_sessionServerAppFactoryMock, create(kAppName, state, kAppConfig, _, SmartPtrMatcher(&m_namedSocketMock))) - .WillOnce(Return(ByMove(std::move(m_sessionServerApp)))); - EXPECT_CALL(m_sessionServerAppMock, launch()).WillOnce(Return(false)); + .WillOnce(Return(m_sessionServerAppMock)); + EXPECT_CALL(*m_sessionServerAppMock, launch()).WillOnce(Return(false)); } void SessionServerAppManagerTests::preloadedSessionServerLaunchWillFail() { EXPECT_CALL(m_namedSocketFactoryMock, createNamedSocket()).WillOnce(Return(ByMove(std::move(m_namedSocket)))); EXPECT_CALL(m_sessionServerAppFactoryMock, create(_, SmartPtrMatcher(&m_namedSocketMock))) - .WillOnce(Return(ByMove(std::move(m_sessionServerApp)))); - EXPECT_CALL(m_sessionServerAppMock, launch()).WillOnce(Return(false)); + .WillOnce(Return(m_sessionServerAppMock)); + EXPECT_CALL(*m_sessionServerAppMock, launch()).WillOnce(Return(false)); } void SessionServerAppManagerTests::sessionServerConnectWillFail(const firebolt::rialto::common::SessionServerState &state) @@ -136,13 +134,13 @@ void SessionServerAppManagerTests::sessionServerConnectWillFail(const firebolt:: EXPECT_CALL(m_namedSocketFactoryMock, createNamedSocket()).WillOnce(Return(ByMove(std::move(m_namedSocket)))); EXPECT_CALL(m_sessionServerAppFactoryMock, create(kAppName, state, kAppConfig, _, SmartPtrMatcher(&m_namedSocketMock))) - .WillOnce(Return(ByMove(std::move(m_sessionServerApp)))); - EXPECT_CALL(m_sessionServerAppMock, launch()).WillOnce(Return(true)); - EXPECT_CALL(m_sessionServerAppMock, getAppName()).WillRepeatedly(ReturnRef(kAppName)); - EXPECT_CALL(m_sessionServerAppMock, getAppManagementSocketName()).WillOnce(Return(kAppMgmtSocket)); - EXPECT_CALL(m_sessionServerAppMock, getServerId()).WillRepeatedly(Return(kServerId)); + .WillOnce(Return(m_sessionServerAppMock)); + EXPECT_CALL(*m_sessionServerAppMock, launch()).WillOnce(Return(true)); + EXPECT_CALL(*m_sessionServerAppMock, getAppName()).WillRepeatedly(ReturnRef(kAppName)); + EXPECT_CALL(*m_sessionServerAppMock, getAppManagementSocketName()).WillOnce(Return(kAppMgmtSocket)); + EXPECT_CALL(*m_sessionServerAppMock, getServerId()).WillRepeatedly(Return(kServerId)); EXPECT_CALL(m_controllerMock, createClient(kServerId, kAppMgmtSocket)).WillOnce(Return(false)); - EXPECT_CALL(m_sessionServerAppMock, kill()); + EXPECT_CALL(*m_sessionServerAppMock, kill()); EXPECT_CALL(m_healthcheckServiceMock, onServerRemoved(kServerId)); } @@ -150,19 +148,23 @@ void SessionServerAppManagerTests::preloadedSessionServerConnectWillFail() { EXPECT_CALL(m_namedSocketFactoryMock, createNamedSocket()).WillOnce(Return(ByMove(std::move(m_namedSocket)))); EXPECT_CALL(m_sessionServerAppFactoryMock, create(_, SmartPtrMatcher(&m_namedSocketMock))) - .WillOnce(Return(ByMove(std::move(m_sessionServerApp)))); - EXPECT_CALL(m_sessionServerAppMock, launch()).WillOnce(Return(true)); - EXPECT_CALL(m_sessionServerAppMock, getServerId()).WillRepeatedly(Return(kServerId)); - EXPECT_CALL(m_sessionServerAppMock, getAppManagementSocketName()).WillOnce(Return(kAppMgmtSocket)); + .WillOnce(Return(m_sessionServerAppMock)); + EXPECT_CALL(*m_sessionServerAppMock, launch()).WillOnce(Return(true)); + EXPECT_CALL(*m_sessionServerAppMock, getServerId()).WillRepeatedly(Return(kServerId)); + EXPECT_CALL(*m_sessionServerAppMock, getAppManagementSocketName()).WillOnce(Return(kAppMgmtSocket)); EXPECT_CALL(m_controllerMock, createClient(kServerId, kAppMgmtSocket)).WillOnce(Return(false)); - EXPECT_CALL(m_sessionServerAppMock, kill()); + EXPECT_CALL(*m_sessionServerAppMock, kill()); EXPECT_CALL(m_healthcheckServiceMock, onServerRemoved(kServerId)); } void SessionServerAppManagerTests::sessionServerChangeStateWillFail(const firebolt::rialto::common::SessionServerState &state) { - EXPECT_CALL(m_sessionServerAppMock, getServerId()).WillRepeatedly(Return(kServerId)); - EXPECT_CALL(m_sessionServerAppMock, setExpectedState(state)); + EXPECT_CALL(*m_sessionServerAppMock, getServerId()).WillRepeatedly(Return(kServerId)); + EXPECT_CALL(*m_sessionServerAppMock, setExpectedState(state)); + if (firebolt::rialto::common::SessionServerState::NOT_RUNNING == state) + { + EXPECT_CALL(m_healthcheckServiceMock, onServerRemoved(kServerId)); + } EXPECT_CALL(m_controllerMock, performSetState(kServerId, state)).WillOnce(Return(false)); } @@ -171,11 +173,11 @@ void SessionServerAppManagerTests::sessionServerWillLaunch(const firebolt::rialt EXPECT_CALL(m_namedSocketFactoryMock, createNamedSocket()).WillOnce(Return(ByMove(std::move(m_namedSocket)))); EXPECT_CALL(m_sessionServerAppFactoryMock, create(kAppName, state, kAppConfig, _, SmartPtrMatcher(&m_namedSocketMock))) - .WillOnce(Return(ByMove(std::move(m_sessionServerApp)))); - EXPECT_CALL(m_sessionServerAppMock, launch()).WillOnce(Return(true)); - EXPECT_CALL(m_sessionServerAppMock, getAppName()).WillRepeatedly(ReturnRef(kAppName)); - EXPECT_CALL(m_sessionServerAppMock, getAppManagementSocketName()).WillOnce(Return(kAppMgmtSocket)); - EXPECT_CALL(m_sessionServerAppMock, getServerId()).WillRepeatedly(Return(kServerId)); + .WillOnce(Return(m_sessionServerAppMock)); + EXPECT_CALL(*m_sessionServerAppMock, launch()).WillOnce(Return(true)); + EXPECT_CALL(*m_sessionServerAppMock, getAppName()).WillRepeatedly(ReturnRef(kAppName)); + EXPECT_CALL(*m_sessionServerAppMock, getAppManagementSocketName()).WillOnce(Return(kAppMgmtSocket)); + EXPECT_CALL(*m_sessionServerAppMock, getServerId()).WillRepeatedly(Return(kServerId)); EXPECT_CALL(m_controllerMock, createClient(kServerId, kAppMgmtSocket)).WillOnce(Return(true)); } @@ -183,62 +185,60 @@ void SessionServerAppManagerTests::preloadedSessionServerWillLaunch() { EXPECT_CALL(m_namedSocketFactoryMock, createNamedSocket()).WillOnce(Return(ByMove(std::move(m_namedSocket)))); EXPECT_CALL(m_sessionServerAppFactoryMock, create(_, SmartPtrMatcher(&m_namedSocketMock))) - .WillOnce(Return(ByMove(std::move(m_sessionServerApp)))); - EXPECT_CALL(m_sessionServerAppMock, launch()).WillOnce(Return(true)); - EXPECT_CALL(m_sessionServerAppMock, getServerId()).WillRepeatedly(Return(kServerId)); - EXPECT_CALL(m_sessionServerAppMock, getAppManagementSocketName()).WillOnce(Return(kAppMgmtSocket)); + .WillOnce(Return(m_sessionServerAppMock)); + EXPECT_CALL(*m_sessionServerAppMock, launch()).WillOnce(Return(true)); + EXPECT_CALL(*m_sessionServerAppMock, getServerId()).WillRepeatedly(Return(kServerId)); + EXPECT_CALL(*m_sessionServerAppMock, getAppManagementSocketName()).WillOnce(Return(kAppMgmtSocket)); EXPECT_CALL(m_controllerMock, createClient(kServerId, kAppMgmtSocket)).WillOnce(Return(true)); } void SessionServerAppManagerTests::preloadedSessionServerWillFailToConfigure( const firebolt::rialto::common::SessionServerState &state) { - EXPECT_CALL(m_sessionServerAppMock, configure(kAppName, state, kAppConfig)).WillOnce(Return(false)); + EXPECT_CALL(*m_sessionServerAppMock, configure(kAppName, state, kAppConfig)).WillOnce(Return(false)); } void SessionServerAppManagerTests::preloadedSessionServerWillBeConfigured( const firebolt::rialto::common::SessionServerState &state) { - EXPECT_CALL(m_sessionServerAppMock, configure(kAppName, state, kAppConfig)).WillOnce(Return(true)); + EXPECT_CALL(*m_sessionServerAppMock, configure(kAppName, state, kAppConfig)).WillOnce(Return(true)); } void SessionServerAppManagerTests::preloadedSessionServerWillCloseWithError() { - m_secondSessionServerApp = std::make_unique>(); - auto &secondSessionServerAppMock{ - dynamic_cast &>(*m_secondSessionServerApp)}; - EXPECT_CALL(m_sessionServerAppMock, isPreloaded()).WillOnce(Return(true)).WillOnce(Return(false)); - EXPECT_CALL(m_sessionServerAppMock, isConnected()).WillOnce(Return(true)); - EXPECT_CALL(m_sessionServerAppMock, getAppName()).WillOnce(ReturnRef(kEmptyAppName)).RetiresOnSaturation(); + m_secondSessionServerAppMock = std::make_shared>(); + EXPECT_CALL(*m_sessionServerAppMock, isPreloaded()).WillOnce(Return(true)).WillOnce(Return(false)); + EXPECT_CALL(*m_sessionServerAppMock, isConnected()).WillOnce(Return(true)); + EXPECT_CALL(*m_sessionServerAppMock, getAppName()).WillOnce(ReturnRef(kEmptyAppName)).RetiresOnSaturation(); EXPECT_CALL(m_controllerMock, removeClient(kServerId)); - EXPECT_CALL(m_sessionServerAppMock, kill()); + EXPECT_CALL(*m_sessionServerAppMock, kill()); EXPECT_CALL(m_healthcheckServiceMock, onServerRemoved(kServerId)); EXPECT_CALL(m_namedSocketFactoryMock, createNamedSocket()).WillOnce(Return(ByMove(std::move(m_namedSocket)))); - EXPECT_CALL(m_sessionServerAppFactoryMock, create(_, _)).WillOnce(Return(ByMove(std::move(m_secondSessionServerApp)))); - EXPECT_CALL(secondSessionServerAppMock, launch()).WillOnce(Return(false)); + EXPECT_CALL(m_sessionServerAppFactoryMock, create(_, _)).WillOnce(Return(m_secondSessionServerAppMock)); + EXPECT_CALL(*m_secondSessionServerAppMock, launch()).WillOnce(Return(false)); } void SessionServerAppManagerTests::sessionServerWillChangeState(const firebolt::rialto::common::SessionServerState &state) { - EXPECT_CALL(m_sessionServerAppMock, setExpectedState(state)); + EXPECT_CALL(*m_sessionServerAppMock, setExpectedState(state)); EXPECT_CALL(m_controllerMock, performSetState(kServerId, state)).WillOnce(Return(true)); } void SessionServerAppManagerTests::sessionServerWillChangeStateToUninitialized() { - EXPECT_CALL(m_sessionServerAppMock, isNamedSocketInitialized()).WillOnce(Return(false)); - EXPECT_CALL(m_sessionServerAppMock, cancelStartupTimer()); - EXPECT_CALL(m_sessionServerAppMock, getInitialState()) + EXPECT_CALL(*m_sessionServerAppMock, isNamedSocketInitialized()).WillOnce(Return(false)); + EXPECT_CALL(*m_sessionServerAppMock, cancelStartupTimer()); + EXPECT_CALL(*m_sessionServerAppMock, getInitialState()) .WillOnce(Return(firebolt::rialto::common::SessionServerState::INACTIVE)); - EXPECT_CALL(m_sessionServerAppMock, getSessionManagementSocketName()).WillRepeatedly(Return(kSessionServerSocketName)); - EXPECT_CALL(m_sessionServerAppMock, getClientDisplayName()).WillOnce(Return(kClientDisplayName)); - EXPECT_CALL(m_sessionServerAppMock, getAppName()).WillOnce(ReturnRef(kAppName)).RetiresOnSaturation(); - EXPECT_CALL(m_sessionServerAppMock, getSessionManagementSocketPermissions()).WillOnce(Return(kSocketPermissions)); - EXPECT_CALL(m_sessionServerAppMock, getSessionManagementSocketOwner()).WillOnce(Return(kSocketOwner)); - EXPECT_CALL(m_sessionServerAppMock, getSessionManagementSocketGroup()).WillOnce(Return(kSocketGroup)); - EXPECT_CALL(m_sessionServerAppMock, getMaxPlaybackSessions()).WillOnce(Return(kMaxSessions)); - EXPECT_CALL(m_sessionServerAppMock, getMaxWebAudioPlayers()).WillOnce(Return(kMaxWebAudioPlayers)); - EXPECT_CALL(m_sessionServerAppMock, isPreloaded()).WillOnce(Return(false)); + EXPECT_CALL(*m_sessionServerAppMock, getSessionManagementSocketName()).WillRepeatedly(Return(kSessionServerSocketName)); + EXPECT_CALL(*m_sessionServerAppMock, getClientDisplayName()).WillOnce(Return(kClientDisplayName)); + EXPECT_CALL(*m_sessionServerAppMock, getAppName()).WillOnce(ReturnRef(kAppName)).RetiresOnSaturation(); + EXPECT_CALL(*m_sessionServerAppMock, getSessionManagementSocketPermissions()).WillOnce(Return(kSocketPermissions)); + EXPECT_CALL(*m_sessionServerAppMock, getSessionManagementSocketOwner()).WillOnce(Return(kSocketOwner)); + EXPECT_CALL(*m_sessionServerAppMock, getSessionManagementSocketGroup()).WillOnce(Return(kSocketGroup)); + EXPECT_CALL(*m_sessionServerAppMock, getMaxPlaybackSessions()).WillOnce(Return(kMaxSessions)); + EXPECT_CALL(*m_sessionServerAppMock, getMaxWebAudioPlayers()).WillOnce(Return(kMaxWebAudioPlayers)); + EXPECT_CALL(*m_sessionServerAppMock, isPreloaded()).WillOnce(Return(false)); EXPECT_CALL(m_controllerMock, performSetConfiguration(kServerId, firebolt::rialto::common::SessionServerState::INACTIVE, kSessionServerSocketName, kClientDisplayName, @@ -250,9 +250,9 @@ void SessionServerAppManagerTests::sessionServerWillChangeStateToUninitialized() void SessionServerAppManagerTests::preloadedSessionServerWillChangeStateToUninitialized() { - EXPECT_CALL(m_sessionServerAppMock, getAppName()).WillRepeatedly(ReturnRef(kEmptyAppName)); - EXPECT_CALL(m_sessionServerAppMock, cancelStartupTimer()); - EXPECT_CALL(m_sessionServerAppMock, isPreloaded()).WillOnce(Return(true)); + EXPECT_CALL(*m_sessionServerAppMock, getAppName()).WillRepeatedly(ReturnRef(kEmptyAppName)); + EXPECT_CALL(*m_sessionServerAppMock, cancelStartupTimer()); + EXPECT_CALL(*m_sessionServerAppMock, isPreloaded()).WillOnce(Return(true)); } void SessionServerAppManagerTests::sessionServerWillChangeStateToInactive() @@ -262,22 +262,20 @@ void SessionServerAppManagerTests::sessionServerWillChangeStateToInactive() void SessionServerAppManagerTests::preloadedSessionServerWillSetConfiguration() { - EXPECT_CALL(m_sessionServerAppMock, isNamedSocketInitialized()).WillOnce(Return(false)); - m_secondSessionServerApp = std::make_unique>(); - auto &secondSessionServerAppMock{ - dynamic_cast &>(*m_secondSessionServerApp)}; - EXPECT_CALL(m_sessionServerAppMock, isPreloaded()).WillOnce(Return(true)); - EXPECT_CALL(m_sessionServerAppMock, isConnected()).WillOnce(Return(true)); - EXPECT_CALL(m_sessionServerAppMock, getAppName()).WillRepeatedly(ReturnRef(kEmptyAppName)); - EXPECT_CALL(m_sessionServerAppMock, getInitialState()) + EXPECT_CALL(*m_sessionServerAppMock, isNamedSocketInitialized()).WillOnce(Return(false)); + m_secondSessionServerAppMock = std::make_shared>(); + EXPECT_CALL(*m_sessionServerAppMock, isPreloaded()).WillOnce(Return(true)); + EXPECT_CALL(*m_sessionServerAppMock, isConnected()).WillOnce(Return(true)); + EXPECT_CALL(*m_sessionServerAppMock, getAppName()).WillRepeatedly(ReturnRef(kEmptyAppName)); + EXPECT_CALL(*m_sessionServerAppMock, getInitialState()) .WillOnce(Return(firebolt::rialto::common::SessionServerState::INACTIVE)); - EXPECT_CALL(m_sessionServerAppMock, getSessionManagementSocketName()).WillRepeatedly(Return(kSessionServerSocketName)); - EXPECT_CALL(m_sessionServerAppMock, getClientDisplayName()).WillOnce(Return(kClientDisplayName)); - EXPECT_CALL(m_sessionServerAppMock, getSessionManagementSocketPermissions()).WillOnce(Return(kSocketPermissions)); - EXPECT_CALL(m_sessionServerAppMock, getSessionManagementSocketOwner()).WillOnce(Return(kSocketOwner)); - EXPECT_CALL(m_sessionServerAppMock, getSessionManagementSocketGroup()).WillOnce(Return(kSocketGroup)); - EXPECT_CALL(m_sessionServerAppMock, getMaxPlaybackSessions()).WillOnce(Return(kMaxSessions)); - EXPECT_CALL(m_sessionServerAppMock, getMaxWebAudioPlayers()).WillOnce(Return(kMaxWebAudioPlayers)); + EXPECT_CALL(*m_sessionServerAppMock, getSessionManagementSocketName()).WillRepeatedly(Return(kSessionServerSocketName)); + EXPECT_CALL(*m_sessionServerAppMock, getClientDisplayName()).WillOnce(Return(kClientDisplayName)); + EXPECT_CALL(*m_sessionServerAppMock, getSessionManagementSocketPermissions()).WillOnce(Return(kSocketPermissions)); + EXPECT_CALL(*m_sessionServerAppMock, getSessionManagementSocketOwner()).WillOnce(Return(kSocketOwner)); + EXPECT_CALL(*m_sessionServerAppMock, getSessionManagementSocketGroup()).WillOnce(Return(kSocketGroup)); + EXPECT_CALL(*m_sessionServerAppMock, getMaxPlaybackSessions()).WillOnce(Return(kMaxSessions)); + EXPECT_CALL(*m_sessionServerAppMock, getMaxWebAudioPlayers()).WillOnce(Return(kMaxWebAudioPlayers)); EXPECT_CALL(m_controllerMock, performSetConfiguration(kServerId, firebolt::rialto::common::SessionServerState::INACTIVE, kSessionServerSocketName, kClientDisplayName, @@ -285,48 +283,46 @@ void SessionServerAppManagerTests::preloadedSessionServerWillSetConfiguration() kSocketOwner, kSocketGroup, kEmptyAppName)) .WillOnce(Return(true)); EXPECT_CALL(m_namedSocketFactoryMock, createNamedSocket()).WillOnce(Return(ByMove(std::move(m_namedSocket)))); - EXPECT_CALL(m_sessionServerAppFactoryMock, create(_, _)).WillOnce(Return(ByMove(std::move(m_secondSessionServerApp)))); - EXPECT_CALL(secondSessionServerAppMock, launch()).WillOnce(Return(false)); + EXPECT_CALL(m_sessionServerAppFactoryMock, create(_, _)).WillOnce(Return(m_secondSessionServerAppMock)); + EXPECT_CALL(*m_secondSessionServerAppMock, launch()).WillOnce(Return(false)); } void SessionServerAppManagerTests::preloadedSessionServerWillSetConfigurationWithFd() { - EXPECT_CALL(m_sessionServerAppMock, isNamedSocketInitialized()).WillOnce(Return(true)); + EXPECT_CALL(*m_sessionServerAppMock, isNamedSocketInitialized()).WillOnce(Return(true)); EXPECT_CALL(m_namedSocketFactoryMock, createNamedSocket()).WillOnce(Return(ByMove(std::move(m_namedSocket)))); - m_secondSessionServerApp = std::make_unique>(); - auto &secondSessionServerAppMock{ - dynamic_cast &>(*m_secondSessionServerApp)}; - EXPECT_CALL(m_sessionServerAppMock, isPreloaded()).WillOnce(Return(true)); - EXPECT_CALL(m_sessionServerAppMock, isConnected()).WillOnce(Return(true)); - EXPECT_CALL(m_sessionServerAppMock, getAppName()).WillRepeatedly(ReturnRef(kEmptyAppName)); - EXPECT_CALL(m_sessionServerAppMock, getInitialState()) + m_secondSessionServerAppMock = std::make_shared>(); + EXPECT_CALL(*m_sessionServerAppMock, isPreloaded()).WillOnce(Return(true)); + EXPECT_CALL(*m_sessionServerAppMock, isConnected()).WillOnce(Return(true)); + EXPECT_CALL(*m_sessionServerAppMock, getAppName()).WillRepeatedly(ReturnRef(kEmptyAppName)); + EXPECT_CALL(*m_sessionServerAppMock, getInitialState()) .WillOnce(Return(firebolt::rialto::common::SessionServerState::INACTIVE)); - EXPECT_CALL(m_sessionServerAppMock, getSessionManagementSocketFd()).WillOnce(Return(kSessionServerSocketFd)); - EXPECT_CALL(m_sessionServerAppMock, getClientDisplayName()).WillOnce(Return(kClientDisplayName)); - EXPECT_CALL(m_sessionServerAppMock, getMaxPlaybackSessions()).WillOnce(Return(kMaxSessions)); - EXPECT_CALL(m_sessionServerAppMock, getMaxWebAudioPlayers()).WillOnce(Return(kMaxWebAudioPlayers)); + EXPECT_CALL(*m_sessionServerAppMock, getSessionManagementSocketFd()).WillOnce(Return(kSessionServerSocketFd)); + EXPECT_CALL(*m_sessionServerAppMock, getClientDisplayName()).WillOnce(Return(kClientDisplayName)); + EXPECT_CALL(*m_sessionServerAppMock, getMaxPlaybackSessions()).WillOnce(Return(kMaxSessions)); + EXPECT_CALL(*m_sessionServerAppMock, getMaxWebAudioPlayers()).WillOnce(Return(kMaxWebAudioPlayers)); EXPECT_CALL(m_controllerMock, performSetConfiguration(kServerId, firebolt::rialto::common::SessionServerState::INACTIVE, kSessionServerSocketFd, kClientDisplayName, MaxResourceMatcher(kMaxSessions, kMaxWebAudioPlayers), kEmptyAppName)) .WillOnce(Return(true)); - EXPECT_CALL(m_sessionServerAppFactoryMock, create(_, _)).WillOnce(Return(ByMove(std::move(m_secondSessionServerApp)))); - EXPECT_CALL(secondSessionServerAppMock, launch()).WillOnce(Return(false)); + EXPECT_CALL(m_sessionServerAppFactoryMock, create(_, _)).WillOnce(Return(m_secondSessionServerAppMock)); + EXPECT_CALL(*m_secondSessionServerAppMock, launch()).WillOnce(Return(false)); } void SessionServerAppManagerTests::sessionServerWillFailToSetConfigurationWithFd() { - EXPECT_CALL(m_sessionServerAppMock, isNamedSocketInitialized()).WillOnce(Return(true)); - EXPECT_CALL(m_sessionServerAppMock, getSessionManagementSocketFd()).WillOnce(Return(kSessionServerSocketFd)); - EXPECT_CALL(m_sessionServerAppMock, cancelStartupTimer()); - EXPECT_CALL(m_sessionServerAppMock, getInitialState()) + EXPECT_CALL(*m_sessionServerAppMock, isNamedSocketInitialized()).WillOnce(Return(true)); + EXPECT_CALL(*m_sessionServerAppMock, getSessionManagementSocketFd()).WillOnce(Return(kSessionServerSocketFd)); + EXPECT_CALL(*m_sessionServerAppMock, cancelStartupTimer()); + EXPECT_CALL(*m_sessionServerAppMock, getInitialState()) .WillOnce(Return(firebolt::rialto::common::SessionServerState::INACTIVE)); - EXPECT_CALL(m_sessionServerAppMock, getSessionManagementSocketName()).WillRepeatedly(Return(kSessionServerSocketName)); - EXPECT_CALL(m_sessionServerAppMock, getClientDisplayName()).WillOnce(Return(kClientDisplayName)); - EXPECT_CALL(m_sessionServerAppMock, getAppName()).WillOnce(ReturnRef(kAppName)).RetiresOnSaturation(); - EXPECT_CALL(m_sessionServerAppMock, getMaxPlaybackSessions()).WillOnce(Return(kMaxSessions)); - EXPECT_CALL(m_sessionServerAppMock, getMaxWebAudioPlayers()).WillOnce(Return(kMaxWebAudioPlayers)); - EXPECT_CALL(m_sessionServerAppMock, isPreloaded()).WillRepeatedly(Return(false)); + EXPECT_CALL(*m_sessionServerAppMock, getSessionManagementSocketName()).WillRepeatedly(Return(kSessionServerSocketName)); + EXPECT_CALL(*m_sessionServerAppMock, getClientDisplayName()).WillOnce(Return(kClientDisplayName)); + EXPECT_CALL(*m_sessionServerAppMock, getAppName()).WillOnce(ReturnRef(kAppName)).RetiresOnSaturation(); + EXPECT_CALL(*m_sessionServerAppMock, getMaxPlaybackSessions()).WillOnce(Return(kMaxSessions)); + EXPECT_CALL(*m_sessionServerAppMock, getMaxWebAudioPlayers()).WillOnce(Return(kMaxWebAudioPlayers)); + EXPECT_CALL(*m_sessionServerAppMock, isPreloaded()).WillRepeatedly(Return(false)); EXPECT_CALL(m_controllerMock, performSetConfiguration(kServerId, firebolt::rialto::common::SessionServerState::INACTIVE, kSessionServerSocketFd, kClientDisplayName, @@ -337,19 +333,19 @@ void SessionServerAppManagerTests::sessionServerWillFailToSetConfigurationWithFd void SessionServerAppManagerTests::sessionServerWillFailToSetConfiguration() { - EXPECT_CALL(m_sessionServerAppMock, isNamedSocketInitialized()).WillOnce(Return(false)); - EXPECT_CALL(m_sessionServerAppMock, cancelStartupTimer()); - EXPECT_CALL(m_sessionServerAppMock, getInitialState()) + EXPECT_CALL(*m_sessionServerAppMock, isNamedSocketInitialized()).WillOnce(Return(false)); + EXPECT_CALL(*m_sessionServerAppMock, cancelStartupTimer()); + EXPECT_CALL(*m_sessionServerAppMock, getInitialState()) .WillOnce(Return(firebolt::rialto::common::SessionServerState::INACTIVE)); - EXPECT_CALL(m_sessionServerAppMock, getSessionManagementSocketName()).WillRepeatedly(Return(kSessionServerSocketName)); - EXPECT_CALL(m_sessionServerAppMock, getClientDisplayName()).WillOnce(Return(kClientDisplayName)); - EXPECT_CALL(m_sessionServerAppMock, getAppName()).WillOnce(ReturnRef(kAppName)).RetiresOnSaturation(); - EXPECT_CALL(m_sessionServerAppMock, getSessionManagementSocketPermissions()).WillOnce(Return(kSocketPermissions)); - EXPECT_CALL(m_sessionServerAppMock, getSessionManagementSocketOwner()).WillOnce(Return(kSocketOwner)); - EXPECT_CALL(m_sessionServerAppMock, getSessionManagementSocketGroup()).WillOnce(Return(kSocketGroup)); - EXPECT_CALL(m_sessionServerAppMock, getMaxPlaybackSessions()).WillOnce(Return(kMaxSessions)); - EXPECT_CALL(m_sessionServerAppMock, getMaxWebAudioPlayers()).WillOnce(Return(kMaxWebAudioPlayers)); - EXPECT_CALL(m_sessionServerAppMock, isPreloaded()).WillRepeatedly(Return(false)); + EXPECT_CALL(*m_sessionServerAppMock, getSessionManagementSocketName()).WillRepeatedly(Return(kSessionServerSocketName)); + EXPECT_CALL(*m_sessionServerAppMock, getClientDisplayName()).WillOnce(Return(kClientDisplayName)); + EXPECT_CALL(*m_sessionServerAppMock, getAppName()).WillOnce(ReturnRef(kAppName)).RetiresOnSaturation(); + EXPECT_CALL(*m_sessionServerAppMock, getSessionManagementSocketPermissions()).WillOnce(Return(kSocketPermissions)); + EXPECT_CALL(*m_sessionServerAppMock, getSessionManagementSocketOwner()).WillOnce(Return(kSocketOwner)); + EXPECT_CALL(*m_sessionServerAppMock, getSessionManagementSocketGroup()).WillOnce(Return(kSocketGroup)); + EXPECT_CALL(*m_sessionServerAppMock, getMaxPlaybackSessions()).WillOnce(Return(kMaxSessions)); + EXPECT_CALL(*m_sessionServerAppMock, getMaxWebAudioPlayers()).WillOnce(Return(kMaxWebAudioPlayers)); + EXPECT_CALL(*m_sessionServerAppMock, isPreloaded()).WillRepeatedly(Return(false)); EXPECT_CALL(m_controllerMock, performSetConfiguration(kServerId, firebolt::rialto::common::SessionServerState::INACTIVE, kSessionServerSocketName, kClientDisplayName, @@ -361,17 +357,17 @@ void SessionServerAppManagerTests::sessionServerWillFailToSetConfiguration() void SessionServerAppManagerTests::preloadedSessionServerWillFailToSetConfiguration() { - EXPECT_CALL(m_sessionServerAppMock, isNamedSocketInitialized()).WillOnce(Return(false)); - EXPECT_CALL(m_sessionServerAppMock, getInitialState()) + EXPECT_CALL(*m_sessionServerAppMock, isNamedSocketInitialized()).WillOnce(Return(false)); + EXPECT_CALL(*m_sessionServerAppMock, getInitialState()) .WillOnce(Return(firebolt::rialto::common::SessionServerState::INACTIVE)); - EXPECT_CALL(m_sessionServerAppMock, getSessionManagementSocketName()).WillRepeatedly(Return(kSessionServerSocketName)); - EXPECT_CALL(m_sessionServerAppMock, getClientDisplayName()).WillOnce(Return(kClientDisplayName)); - EXPECT_CALL(m_sessionServerAppMock, getAppName()).WillOnce(ReturnRef(kAppName)).RetiresOnSaturation(); - EXPECT_CALL(m_sessionServerAppMock, getSessionManagementSocketPermissions()).WillOnce(Return(kSocketPermissions)); - EXPECT_CALL(m_sessionServerAppMock, getSessionManagementSocketOwner()).WillOnce(Return(kSocketOwner)); - EXPECT_CALL(m_sessionServerAppMock, getSessionManagementSocketGroup()).WillOnce(Return(kSocketGroup)); - EXPECT_CALL(m_sessionServerAppMock, getMaxPlaybackSessions()).WillOnce(Return(kMaxSessions)); - EXPECT_CALL(m_sessionServerAppMock, getMaxWebAudioPlayers()).WillOnce(Return(kMaxWebAudioPlayers)); + EXPECT_CALL(*m_sessionServerAppMock, getSessionManagementSocketName()).WillRepeatedly(Return(kSessionServerSocketName)); + EXPECT_CALL(*m_sessionServerAppMock, getClientDisplayName()).WillOnce(Return(kClientDisplayName)); + EXPECT_CALL(*m_sessionServerAppMock, getAppName()).WillOnce(ReturnRef(kAppName)).RetiresOnSaturation(); + EXPECT_CALL(*m_sessionServerAppMock, getSessionManagementSocketPermissions()).WillOnce(Return(kSocketPermissions)); + EXPECT_CALL(*m_sessionServerAppMock, getSessionManagementSocketOwner()).WillOnce(Return(kSocketOwner)); + EXPECT_CALL(*m_sessionServerAppMock, getSessionManagementSocketGroup()).WillOnce(Return(kSocketGroup)); + EXPECT_CALL(*m_sessionServerAppMock, getMaxPlaybackSessions()).WillOnce(Return(kMaxSessions)); + EXPECT_CALL(*m_sessionServerAppMock, getMaxWebAudioPlayers()).WillOnce(Return(kMaxWebAudioPlayers)); EXPECT_CALL(m_controllerMock, performSetConfiguration(kServerId, firebolt::rialto::common::SessionServerState::INACTIVE, kSessionServerSocketName, kClientDisplayName, @@ -392,7 +388,7 @@ void SessionServerAppManagerTests::sessionServerWillFailToSetLogLevels() void SessionServerAppManagerTests::clientWillBeRemoved() { - EXPECT_CALL(m_healthcheckServiceMock, onServerRemoved(kServerId)); + EXPECT_CALL(m_healthcheckServiceMock, onServerRemoved(kServerId)).RetiresOnSaturation(); EXPECT_CALL(m_controllerMock, removeClient(kServerId)); } @@ -404,12 +400,12 @@ void SessionServerAppManagerTests::sessionServerWillIndicateStateChange( void SessionServerAppManagerTests::sessionServerWillKillRunningApplication() { - EXPECT_CALL(m_sessionServerAppMock, kill()); + EXPECT_CALL(*m_sessionServerAppMock, kill()); } void SessionServerAppManagerTests::sessionServerWontBePreloaded() { - EXPECT_CALL(m_sessionServerAppMock, isPreloaded()).WillOnce(Return(false)); + EXPECT_CALL(*m_sessionServerAppMock, isPreloaded()).WillOnce(Return(false)); } void SessionServerAppManagerTests::healthcheckServiceWillHandleAck(bool success) @@ -431,27 +427,26 @@ void SessionServerAppManagerTests::pingSendToRunningAppsWillFail() void SessionServerAppManagerTests::newSessionServerWillBeLaunched() { - m_secondSessionServerApp = std::make_unique>(); - auto &secondSessionServerAppMock{ - dynamic_cast &>(*m_secondSessionServerApp)}; + m_secondSessionServerAppMock = std::make_shared>(); EXPECT_CALL(m_namedSocketFactoryMock, createNamedSocket()).WillOnce(Return(ByMove(std::move(m_namedSocket)))); - EXPECT_CALL(m_sessionServerAppMock, isPreloaded()).WillOnce(Return(true)); - EXPECT_CALL(m_sessionServerAppFactoryMock, create(_, _)).WillOnce(Return(ByMove(std::move(m_secondSessionServerApp)))); - EXPECT_CALL(secondSessionServerAppMock, launch()).WillOnce(Return(false)); + EXPECT_CALL(*m_sessionServerAppMock, isPreloaded()).WillOnce(Return(true)); + EXPECT_CALL(m_sessionServerAppFactoryMock, create(_, _)).WillOnce(Return(m_secondSessionServerAppMock)); + EXPECT_CALL(*m_secondSessionServerAppMock, launch()).WillOnce(Return(false)); } void SessionServerAppManagerTests::sessionServerWillReturnAppSocketName(const std::string &socketName) { - EXPECT_CALL(m_sessionServerAppMock, getSessionManagementSocketName()).WillOnce(Return(socketName)); + EXPECT_CALL(*m_sessionServerAppMock, getSessionManagementSocketName()).WillOnce(Return(socketName)); } void SessionServerAppManagerTests::sessionServerWillBeRestarted(const firebolt::rialto::common::SessionServerState &state) { m_namedSocket = std::make_unique>(); - EXPECT_CALL(m_sessionServerAppMock, getExpectedState()).WillOnce(Return(state)); - EXPECT_CALL(m_sessionServerAppMock, getSessionManagementSocketName()).WillOnce(Return(kSessionServerSocketName)); - EXPECT_CALL(m_sessionServerAppMock, getClientDisplayName()).WillOnce(Return(kClientDisplayName)); - EXPECT_CALL(m_sessionServerAppMock, releaseNamedSocketRef()).WillOnce(ReturnRef(m_namedSocket)); + EXPECT_CALL(m_healthcheckServiceMock, onServerRemoved(kServerId)).RetiresOnSaturation(); + EXPECT_CALL(*m_sessionServerAppMock, getExpectedState()).WillOnce(Return(state)); + EXPECT_CALL(*m_sessionServerAppMock, getSessionManagementSocketName()).WillOnce(Return(kSessionServerSocketName)); + EXPECT_CALL(*m_sessionServerAppMock, getClientDisplayName()).WillOnce(Return(kClientDisplayName)); + EXPECT_CALL(*m_sessionServerAppMock, releaseNamedSocketRef()).WillOnce(ReturnRef(m_namedSocket)); sessionServerWillKillRunningApplication(); sessionServerWillIndicateStateChange(firebolt::rialto::common::SessionServerState::NOT_RUNNING); clientWillBeRemoved(); @@ -468,17 +463,19 @@ void SessionServerAppManagerTests::sessionServerWillBeRestarted(const firebolt:: void SessionServerAppManagerTests::sessionServerWillRestartWillBeSkipped() { - EXPECT_CALL(m_sessionServerAppMock, getExpectedState()) + EXPECT_CALL(*m_sessionServerAppMock, getExpectedState()) .WillOnce(Return(firebolt::rialto::common::SessionServerState::UNINITIALIZED)); - EXPECT_CALL(m_sessionServerAppMock, getSessionManagementSocketName()).WillOnce(Return(kSessionServerSocketName)); - EXPECT_CALL(m_sessionServerAppMock, getClientDisplayName()).WillOnce(Return(kClientDisplayName)); - EXPECT_CALL(m_sessionServerAppMock, releaseNamedSocketRef()).WillOnce(ReturnRef(m_namedSocket)); + EXPECT_CALL(m_healthcheckServiceMock, onServerRemoved(kServerId)).RetiresOnSaturation(); + EXPECT_CALL(*m_sessionServerAppMock, getSessionManagementSocketName()).WillOnce(Return(kSessionServerSocketName)); + EXPECT_CALL(*m_sessionServerAppMock, getClientDisplayName()).WillOnce(Return(kClientDisplayName)); + EXPECT_CALL(*m_sessionServerAppMock, releaseNamedSocketRef()).WillOnce(ReturnRef(m_namedSocket)); } void SessionServerAppManagerTests::sessionServerWillHandleServerStartupTimeout() { sessionServerWillIndicateStateChange(firebolt::rialto::common::SessionServerState::ERROR); - EXPECT_CALL(m_sessionServerAppMock, isPreloaded()).WillRepeatedly(Return(false)); + EXPECT_CALL(*m_sessionServerAppMock, isPreloaded()).WillRepeatedly(Return(false)); + EXPECT_CALL(m_healthcheckServiceMock, onServerRemoved(kServerId)).RetiresOnSaturation(); sessionServerWillKillRunningApplication(); sessionServerWillIndicateStateChange(firebolt::rialto::common::SessionServerState::NOT_RUNNING); clientWillBeRemoved(); diff --git a/tests/unittests/serverManager/unittests/common/SessionServerAppManagerTestsFixture.h b/tests/unittests/serverManager/unittests/common/SessionServerAppManagerTestsFixture.h index 1bac26634..5dfa7f42a 100644 --- a/tests/unittests/serverManager/unittests/common/SessionServerAppManagerTestsFixture.h +++ b/tests/unittests/serverManager/unittests/common/SessionServerAppManagerTestsFixture.h @@ -92,13 +92,12 @@ class SessionServerAppManagerTests : public testing::Test private: std::unique_ptr m_controller; std::shared_ptr> m_stateObserver; - std::unique_ptr m_sessionServerApp; - std::unique_ptr m_secondSessionServerApp; + std::shared_ptr> m_sessionServerAppMock; + std::shared_ptr> m_secondSessionServerAppMock; std::unique_ptr m_sessionServerAppFactory; std::unique_ptr m_healthcheckServiceFactory; std::unique_ptr m_healthcheckService; StrictMock &m_controllerMock; - StrictMock &m_sessionServerAppMock; StrictMock &m_sessionServerAppFactoryMock; StrictMock &m_healthcheckServiceFactoryMock; StrictMock &m_healthcheckServiceMock; diff --git a/tests/unittests/serverManager/unittests/common/SessionServerAppTestsFixture.cpp b/tests/unittests/serverManager/unittests/common/SessionServerAppTestsFixture.cpp index 0d5a1c653..fd275bbbf 100644 --- a/tests/unittests/serverManager/unittests/common/SessionServerAppTestsFixture.cpp +++ b/tests/unittests/serverManager/unittests/common/SessionServerAppTestsFixture.cpp @@ -56,7 +56,7 @@ using testing::StrictMock; void SessionServerAppTests::createPreloadedAppSut() { - m_sut = std::make_unique(std::move(m_linuxWrapper), + m_sut = std::make_shared(std::move(m_linuxWrapper), m_timerFactoryMock, m_sessionServerAppManagerMock, kEnvironmentVariables, kSessionServerPath, @@ -79,7 +79,7 @@ void SessionServerAppTests::createPreloadedAppSut() void SessionServerAppTests::createAppSut(const firebolt::rialto::common::AppConfig &appConfig) { EXPECT_CALL(m_namedSocketMock, bind(_)).WillOnce(Return(true)); - m_sut = std::make_unique(kAppName, kInitialState, appConfig, + m_sut = std::make_shared(kAppName, kInitialState, appConfig, std::move(m_linuxWrapper), m_timerFactoryMock, m_sessionServerAppManagerMock, @@ -104,7 +104,7 @@ void SessionServerAppTests::createAppSut(const firebolt::rialto::common::AppConf void SessionServerAppTests::createAppSutWithDisabledTimer(const firebolt::rialto::common::AppConfig &appConfig) { EXPECT_CALL(m_namedSocketMock, bind(_)).WillOnce(Return(true)); - m_sut = std::make_unique(kAppName, kInitialState, appConfig, + m_sut = std::make_shared(kAppName, kInitialState, appConfig, std::move(m_linuxWrapper), m_timerFactoryMock, m_sessionServerAppManagerMock, @@ -177,7 +177,7 @@ void SessionServerAppTests::willKillAppOnDestruction() const EXPECT_CALL(*killTimer, cancel()); EXPECT_CALL(*m_timerFactoryMock, createTimer(kKillTimeout, _, firebolt::rialto::common::TimerType::ONE_SHOT)) .WillOnce(DoAll(InvokeArgument<1>(), Return(ByMove(std::move(killTimer))))); - EXPECT_CALL(m_linuxWrapperMock, waitpid(-1, nullptr, 0)) + EXPECT_CALL(m_linuxWrapperMock, waitpid(kPid, nullptr, 0)) .WillOnce(Return(-1)); // -1 here as pid, because we invoked timer with kill earlier. EXPECT_CALL(m_linuxWrapperMock, close(kSocketPair[0])).WillOnce(Return(0)); } diff --git a/tests/unittests/serverManager/unittests/common/SessionServerAppTestsFixture.h b/tests/unittests/serverManager/unittests/common/SessionServerAppTestsFixture.h index 00a5ae59b..30d15a0ea 100644 --- a/tests/unittests/serverManager/unittests/common/SessionServerAppTestsFixture.h +++ b/tests/unittests/serverManager/unittests/common/SessionServerAppTestsFixture.h @@ -70,7 +70,7 @@ class SessionServerAppTests : public testing::Test testing::StrictMock &m_namedSocketMock{*m_namedSocket}; protected: - std::unique_ptr m_sut; + std::shared_ptr m_sut; }; #endif // SESSION_SERVER_APP_TESTS_FIXTURE_H_ From 20dd3e8a891290ee722e5a31c5b7f7ecdb039a53 Mon Sep 17 00:00:00 2001 From: Marcin Wojciechowski <105790697+skywojciechowskim@users.noreply.github.com> Date: Wed, 11 Feb 2026 11:01:00 +0100 Subject: [PATCH 05/29] Asure correct data & flush order during postponed flushes (#436) Summary: Asure correct data & flush order during multiple flushes Type: Fix Test Plan: UT/CT, Fullstack Jira: NO-JIRA --- .../include/FlushOnPrerollController.h | 10 +- .../gstplayer/include/GenericPlayerContext.h | 2 +- .../gstplayer/include/GstDispatcherThread.h | 7 + .../gstplayer/include/GstGenericPlayer.h | 7 - .../include/IFlushOnPrerollController.h | 6 +- .../gstplayer/include/IGstDispatcherThread.h | 2 + .../include/IGstGenericPlayerPrivate.h | 13 -- .../include/tasks/IGenericPlayerTaskFactory.h | 5 +- .../gstplayer/include/tasks/generic/Flush.h | 5 +- .../tasks/generic/GenericPlayerTaskFactory.h | 4 +- .../source/FlushOnPrerollController.cpp | 42 ++++-- .../gstplayer/source/GstDispatcherThread.cpp | 26 +++- .../gstplayer/source/GstGenericPlayer.cpp | 26 +--- .../gstplayer/source/GstWebAudioPlayer.cpp | 4 +- .../gstplayer/source/tasks/generic/Flush.cpp | 20 ++- .../generic/GenericPlayerTaskFactory.cpp | 4 +- .../source/tasks/generic/HandleBusMessage.cpp | 4 - .../source/MediaPipelineServerInternal.cpp | 2 + .../server/common/ExpectMessage.h | 2 +- .../GstDispatcherThreadTest.cpp | 121 +++++++++++++++++- .../FlushOnPrerollControllerTest.cpp | 61 ++++++--- .../GstGenericPlayerPrivateTest.cpp | 12 -- .../genericPlayer/GstGenericPlayerTest.cpp | 2 +- .../common/GenericTasksTestsBase.cpp | 12 +- .../common/GenericTasksTestsBase.h | 1 - .../common/GstGenericPlayerTestCommon.cpp | 2 +- .../genericPlayer/tasksTests/FlushTest.cpp | 6 - .../GenericPlayerTaskFactoryTest.cpp | 2 +- .../tasksTests/HandleBusMessageTest.cpp | 5 - .../gstplayer/webAudioPlayer/CreateTest.cpp | 2 +- .../common/GstWebAudioPlayerTestCommon.cpp | 2 +- .../gstplayer/FlushOnPrerollControllerMock.h | 6 +- .../gstplayer/GenericPlayerTaskFactoryMock.h | 2 +- .../GstDispatcherThreadFactoryMock.h | 1 + .../gstplayer/GstGenericPlayerPrivateMock.h | 2 - 35 files changed, 282 insertions(+), 148 deletions(-) diff --git a/media/server/gstplayer/include/FlushOnPrerollController.h b/media/server/gstplayer/include/FlushOnPrerollController.h index 3b17dfaed..8ae19bfc3 100644 --- a/media/server/gstplayer/include/FlushOnPrerollController.h +++ b/media/server/gstplayer/include/FlushOnPrerollController.h @@ -21,6 +21,7 @@ #define FIREBOLT_RIALTO_SERVER_FLUSH_ONPREROLL_CONTROLLER_H_ #include "IFlushOnPrerollController.h" +#include #include #include #include @@ -37,13 +38,16 @@ class FlushOnPrerollController : public IFlushOnPrerollController FlushOnPrerollController() = default; ~FlushOnPrerollController() override = default; - bool shouldPostponeFlush(const MediaSourceType &type) const override; - void setFlushing(const MediaSourceType &type, const GstState ¤tPipelineState) override; + void waitIfRequired(const MediaSourceType &type) override; + void setFlushing(const MediaSourceType &type) override; + void setPrerolling() override; void stateReached(const GstState &newPipelineState) override; + void setTargetState(const GstState &state) override; void reset() override; private: - mutable std::mutex m_mutex{}; + std::mutex m_mutex{}; + std::condition_variable m_conditionVariable{}; std::set m_flushingSources{}; std::optional m_targetState{std::nullopt}; bool m_isPrerolled{false}; diff --git a/media/server/gstplayer/include/GenericPlayerContext.h b/media/server/gstplayer/include/GenericPlayerContext.h index a2f214535..2a6b06352 100644 --- a/media/server/gstplayer/include/GenericPlayerContext.h +++ b/media/server/gstplayer/include/GenericPlayerContext.h @@ -270,7 +270,7 @@ struct GenericPlayerContext /** * @brief Workaround for the gstreamer flush issue */ - FlushOnPrerollController flushOnPrerollController; + std::shared_ptr flushOnPrerollController{std::make_shared()}; }; } // namespace firebolt::rialto::server diff --git a/media/server/gstplayer/include/GstDispatcherThread.h b/media/server/gstplayer/include/GstDispatcherThread.h index 540f161ca..0db6fe2d9 100644 --- a/media/server/gstplayer/include/GstDispatcherThread.h +++ b/media/server/gstplayer/include/GstDispatcherThread.h @@ -34,6 +34,7 @@ class GstDispatcherThreadFactory : public IGstDispatcherThreadFactory ~GstDispatcherThreadFactory() override = default; std::unique_ptr createGstDispatcherThread(IGstDispatcherThreadClient &client, GstElement *pipeline, + const std::shared_ptr &flushOnPrerollController, const std::shared_ptr &gstWrapper) const override; }; @@ -41,6 +42,7 @@ class GstDispatcherThread : public IGstDispatcherThread { public: GstDispatcherThread(IGstDispatcherThreadClient &client, GstElement *pipeline, + const std::shared_ptr &flushOnPrerollController, const std::shared_ptr &gstWrapper); ~GstDispatcherThread() override; @@ -58,6 +60,11 @@ class GstDispatcherThread : public IGstDispatcherThread */ IGstDispatcherThreadClient &m_client; + /** + * @brief The flush on preroll controller. + */ + std::shared_ptr m_flushOnPrerollController; + /** * @brief The gstreamer wrapper object. */ diff --git a/media/server/gstplayer/include/GstGenericPlayer.h b/media/server/gstplayer/include/GstGenericPlayer.h index e5d500240..412d81a70 100644 --- a/media/server/gstplayer/include/GstGenericPlayer.h +++ b/media/server/gstplayer/include/GstGenericPlayer.h @@ -195,8 +195,6 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva GstElement *getSink(const MediaSourceType &mediaSourceType) const override; void setSourceFlushed(const MediaSourceType &mediaSourceType) override; bool isAsync(const MediaSourceType &mediaSourceType) const; - void postponeFlush(const MediaSourceType &mediaSourceType, bool resetTime) override; - void executePostponedFlushes() override; void notifyPlaybackInfo() override; private: @@ -426,11 +424,6 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva */ std::unique_ptr m_flushWatcher; - /** - * @brief The postponed flush tasks - */ - std::vector> m_postponedFlushes{}; - /** * @brief The ongoing state change operations counter */ diff --git a/media/server/gstplayer/include/IFlushOnPrerollController.h b/media/server/gstplayer/include/IFlushOnPrerollController.h index 64379b7af..76469a523 100644 --- a/media/server/gstplayer/include/IFlushOnPrerollController.h +++ b/media/server/gstplayer/include/IFlushOnPrerollController.h @@ -34,9 +34,11 @@ class IFlushOnPrerollController public: virtual ~IFlushOnPrerollController() = default; - virtual bool shouldPostponeFlush(const MediaSourceType &type) const = 0; - virtual void setFlushing(const MediaSourceType &type, const GstState ¤tPipelineState) = 0; + virtual void waitIfRequired(const MediaSourceType &type) = 0; + virtual void setFlushing(const MediaSourceType &type) = 0; + virtual void setPrerolling() = 0; virtual void stateReached(const GstState &newPipelineState) = 0; + virtual void setTargetState(const GstState &state) = 0; virtual void reset() = 0; }; } // namespace firebolt::rialto::server diff --git a/media/server/gstplayer/include/IGstDispatcherThread.h b/media/server/gstplayer/include/IGstDispatcherThread.h index 15d6083e1..a72e2a0ad 100644 --- a/media/server/gstplayer/include/IGstDispatcherThread.h +++ b/media/server/gstplayer/include/IGstDispatcherThread.h @@ -20,6 +20,7 @@ #ifndef FIREBOLT_RIALTO_SERVER_I_GST_DISPATCHER_THREAD_H_ #define FIREBOLT_RIALTO_SERVER_I_GST_DISPATCHER_THREAD_H_ +#include "IFlushOnPrerollController.h" #include "IGstDispatcherThreadClient.h" #include "IGstWrapper.h" #include @@ -36,6 +37,7 @@ class IGstDispatcherThreadFactory virtual std::unique_ptr createGstDispatcherThread(IGstDispatcherThreadClient &client, GstElement *pipeline, + const std::shared_ptr &flushOnPrerollController, const std::shared_ptr &gstWrapper) const = 0; }; diff --git a/media/server/gstplayer/include/IGstGenericPlayerPrivate.h b/media/server/gstplayer/include/IGstGenericPlayerPrivate.h index dc931c38f..64d72518d 100644 --- a/media/server/gstplayer/include/IGstGenericPlayerPrivate.h +++ b/media/server/gstplayer/include/IGstGenericPlayerPrivate.h @@ -321,19 +321,6 @@ class IGstGenericPlayerPrivate */ virtual void setSourceFlushed(const MediaSourceType &mediaSourceType) = 0; - /** - * @brief Postpones flush for the given source type - * - * @param[in] mediaSourceType : the source type that has been flushed - * @param[in] resetTime : whether to reset the time after flush - */ - virtual void postponeFlush(const MediaSourceType &mediaSourceType, bool resetTime) = 0; - - /** - * @brief Queues postponed flushes for execution - */ - virtual void executePostponedFlushes() = 0; - /** * @brief Sends PlaybackInfo notification. Called by the worker thread. */ diff --git a/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h b/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h index 527261652..35b90cbad 100644 --- a/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h +++ b/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h @@ -429,12 +429,13 @@ class IGenericPlayerTaskFactory * @param[in] context : The GstPlayer context * @param[in] type : The media source type to flush * @param[in] resetTime : True if time should be reset + * @param[in] isAsync : True if flushed source is asynchronous * * @retval the new Flush task instance. */ virtual std::unique_ptr createFlush(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, - const firebolt::rialto::MediaSourceType &type, - bool resetTime) const = 0; + const firebolt::rialto::MediaSourceType &type, bool resetTime, + bool isAsync) const = 0; /** * @brief Creates a SetSourcePosition task. diff --git a/media/server/gstplayer/include/tasks/generic/Flush.h b/media/server/gstplayer/include/tasks/generic/Flush.h index 6345b1d00..f3729068e 100644 --- a/media/server/gstplayer/include/tasks/generic/Flush.h +++ b/media/server/gstplayer/include/tasks/generic/Flush.h @@ -33,8 +33,8 @@ class Flush : public IPlayerTask { public: Flush(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, IGstGenericPlayerClient *client, - std::shared_ptr gstWrapper, const MediaSourceType &type, - bool resetTime); + const std::shared_ptr &gstWrapper, const MediaSourceType &type, + bool resetTime, bool isAsync); ~Flush() override; void execute() const override; @@ -45,6 +45,7 @@ class Flush : public IPlayerTask std::shared_ptr m_gstWrapper; MediaSourceType m_type; bool m_resetTime; + bool m_isAsync; }; } // namespace firebolt::rialto::server::tasks::generic diff --git a/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h b/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h index 49ec0db1b..76b419ead 100644 --- a/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h +++ b/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h @@ -108,8 +108,8 @@ class GenericPlayerTaskFactory : public IGenericPlayerTaskFactory IGstGenericPlayerPrivate &player) const override; std::unique_ptr createPing(std::unique_ptr &&heartbeatHandler) const override; std::unique_ptr createFlush(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, - const firebolt::rialto::MediaSourceType &type, - bool resetTime) const override; + const firebolt::rialto::MediaSourceType &type, bool resetTime, + bool isAsync) const override; std::unique_ptr createSetSourcePosition(GenericPlayerContext &context, const firebolt::rialto::MediaSourceType &type, std::int64_t position, bool resetTime, double appliedRate, diff --git a/media/server/gstplayer/source/FlushOnPrerollController.cpp b/media/server/gstplayer/source/FlushOnPrerollController.cpp index 8ffce5cbc..1218b892c 100644 --- a/media/server/gstplayer/source/FlushOnPrerollController.cpp +++ b/media/server/gstplayer/source/FlushOnPrerollController.cpp @@ -18,41 +18,67 @@ */ #include "FlushOnPrerollController.h" +#include "RialtoServerLogging.h" +#include "TypeConverters.h" +#include namespace firebolt::rialto::server { -bool FlushOnPrerollController::shouldPostponeFlush(const MediaSourceType &type) const +void FlushOnPrerollController::waitIfRequired(const MediaSourceType &type) { std::unique_lock lock{m_mutex}; - return m_isPrerolled && m_flushingSources.find(type) != m_flushingSources.end(); + 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(); }); + RIALTO_SERVER_LOG_DEBUG("FlushOnPrerollController: Waiting if required for %s source exit", + common::convertMediaSourceType(type)); } -void FlushOnPrerollController::setFlushing(const MediaSourceType &type, const GstState ¤tPipelineState) +void FlushOnPrerollController::setFlushing(const MediaSourceType &type) { + RIALTO_SERVER_LOG_DEBUG("FlushOnPrerollController: Set flushing for: %s", common::convertMediaSourceType(type)); std::unique_lock lock{m_mutex}; m_flushingSources.insert(type); +} + +void FlushOnPrerollController::setPrerolling() +{ + RIALTO_SERVER_LOG_DEBUG("FlushOnPrerollController: Set prerolling"); + std::unique_lock lock{m_mutex}; m_isPrerolled = false; - if (!m_targetState.has_value()) - { - m_targetState = currentPipelineState; - } + m_conditionVariable.notify_all(); } void FlushOnPrerollController::stateReached(const GstState &newPipelineState) { + RIALTO_SERVER_LOG_DEBUG("FlushOnPrerollController: State reached %s", gst_element_state_get_name(newPipelineState)); std::unique_lock lock{m_mutex}; m_isPrerolled = true; if (m_targetState.has_value() && newPipelineState == m_targetState.value()) { + RIALTO_SERVER_LOG_DEBUG("FlushOnPrerollController: Clear state after state reached %s", + gst_element_state_get_name(newPipelineState)); m_flushingSources.clear(); - m_targetState = std::nullopt; } + m_conditionVariable.notify_all(); +} + +void FlushOnPrerollController::setTargetState(const GstState &state) +{ + std::unique_lock lock{m_mutex}; + m_targetState = state; + RIALTO_SERVER_LOG_DEBUG("FlushOnPrerollController: Set target state %s", gst_element_state_get_name(state)); } void FlushOnPrerollController::reset() { + RIALTO_SERVER_LOG_DEBUG("Reset FlushOnPrerollController"); std::unique_lock lock{m_mutex}; + m_isPrerolled = false; m_flushingSources.clear(); m_targetState = std::nullopt; + m_conditionVariable.notify_all(); } } // namespace firebolt::rialto::server diff --git a/media/server/gstplayer/source/GstDispatcherThread.cpp b/media/server/gstplayer/source/GstDispatcherThread.cpp index 90953a27f..289ab77d7 100644 --- a/media/server/gstplayer/source/GstDispatcherThread.cpp +++ b/media/server/gstplayer/source/GstDispatcherThread.cpp @@ -24,14 +24,17 @@ namespace firebolt::rialto::server { std::unique_ptr GstDispatcherThreadFactory::createGstDispatcherThread( IGstDispatcherThreadClient &client, GstElement *pipeline, + const std::shared_ptr &flushOnPrerollController, const std::shared_ptr &gstWrapper) const { - return std::make_unique(client, pipeline, gstWrapper); + return std::make_unique(client, pipeline, flushOnPrerollController, gstWrapper); } GstDispatcherThread::GstDispatcherThread(IGstDispatcherThreadClient &client, GstElement *pipeline, + const std::shared_ptr &flushOnPrerollController, const std::shared_ptr &gstWrapper) - : m_client{client}, m_gstWrapper{gstWrapper}, m_isGstreamerDispatcherActive{true} + : m_client{client}, m_flushOnPrerollController{flushOnPrerollController}, m_gstWrapper{gstWrapper}, + m_isGstreamerDispatcherActive{true} { RIALTO_SERVER_LOG_INFO("GstDispatcherThread is starting"); m_gstBusDispatcherThread = std::thread(&GstDispatcherThread::gstBusEventHandler, this, pipeline); @@ -80,11 +83,30 @@ void GstDispatcherThread::gstBusEventHandler(GstElement *pipeline) case GST_STATE_NULL: { m_isGstreamerDispatcherActive = false; + if (m_flushOnPrerollController) + { + m_flushOnPrerollController->reset(); + } break; } case GST_STATE_PAUSED: + { + if (m_flushOnPrerollController && pending != GST_STATE_PAUSED) + { + m_flushOnPrerollController->stateReached(newState); + } + else if (m_flushOnPrerollController && pending == GST_STATE_PAUSED) + { + m_flushOnPrerollController->setPrerolling(); + } + break; + } case GST_STATE_PLAYING: { + if (m_flushOnPrerollController) + { + m_flushOnPrerollController->stateReached(newState); + } break; } case GST_STATE_READY: diff --git a/media/server/gstplayer/source/GstGenericPlayer.cpp b/media/server/gstplayer/source/GstGenericPlayer.cpp index f140b3ac4..a0fc7e385 100644 --- a/media/server/gstplayer/source/GstGenericPlayer.cpp +++ b/media/server/gstplayer/source/GstGenericPlayer.cpp @@ -202,8 +202,9 @@ GstGenericPlayer::GstGenericPlayer( RIALTO_SERVER_LOG_MIL("Primary video playback selected"); } - m_gstDispatcherThread = - gstDispatcherThreadFactory->createGstDispatcherThread(*this, m_context.pipeline, m_gstWrapper); + m_gstDispatcherThread = gstDispatcherThreadFactory->createGstDispatcherThread(*this, m_context.pipeline, + m_context.flushOnPrerollController, + m_gstWrapper); } GstGenericPlayer::~GstGenericPlayer() @@ -252,7 +253,6 @@ void GstGenericPlayer::initMsePipeline() void GstGenericPlayer::resetWorkerThread() { - m_postponedFlushes.clear(); // Shutdown task thread m_workerThread->enqueueTask(m_taskFactory->createShutdown(*this)); m_workerThread->join(); @@ -468,23 +468,6 @@ void GstGenericPlayer::setSourceFlushed(const MediaSourceType &mediaSourceType) m_flushWatcher->setFlushed(mediaSourceType); } -void GstGenericPlayer::postponeFlush(const MediaSourceType &mediaSourceType, bool resetTime) -{ - m_postponedFlushes.emplace_back(std::make_pair(mediaSourceType, resetTime)); -} - -void GstGenericPlayer::executePostponedFlushes() -{ - if (m_workerThread) - { - for (const auto &[mediaSourceType, resetTime] : m_postponedFlushes) - { - m_workerThread->enqueueTask(m_taskFactory->createFlush(m_context, *this, mediaSourceType, resetTime)); - } - } - m_postponedFlushes.clear(); -} - void GstGenericPlayer::notifyPlaybackInfo() { PlaybackInfo info; @@ -1236,6 +1219,7 @@ GstStateChangeReturn GstGenericPlayer::changePipelineState(GstState newState) --m_ongoingStateChangesNumber; return GST_STATE_CHANGE_FAILURE; } + m_context.flushOnPrerollController->setTargetState(newState); const GstStateChangeReturn result{m_gstWrapper->gstElementSetState(m_context.pipeline, newState)}; if (result == GST_STATE_CHANGE_FAILURE) { @@ -2070,7 +2054,7 @@ void GstGenericPlayer::flush(const MediaSourceType &mediaSourceType, bool resetT { async = isAsync(mediaSourceType); m_flushWatcher->setFlushing(mediaSourceType, async); - m_workerThread->enqueueTask(m_taskFactory->createFlush(m_context, *this, mediaSourceType, resetTime)); + m_workerThread->enqueueTask(m_taskFactory->createFlush(m_context, *this, mediaSourceType, resetTime, async)); } } diff --git a/media/server/gstplayer/source/GstWebAudioPlayer.cpp b/media/server/gstplayer/source/GstWebAudioPlayer.cpp index ef6aa666a..551730e15 100644 --- a/media/server/gstplayer/source/GstWebAudioPlayer.cpp +++ b/media/server/gstplayer/source/GstWebAudioPlayer.cpp @@ -125,8 +125,8 @@ GstWebAudioPlayer::GstWebAudioPlayer(IGstWebAudioPlayerClient *client, const uin } if ((!gstDispatcherThreadFactory) || - (!(m_gstDispatcherThread = - gstDispatcherThreadFactory->createGstDispatcherThread(*this, m_context.pipeline, m_gstWrapper)))) + (!(m_gstDispatcherThread = gstDispatcherThreadFactory->createGstDispatcherThread(*this, m_context.pipeline, + nullptr, m_gstWrapper)))) { termWebAudioPipeline(); resetWorkerThread(); diff --git a/media/server/gstplayer/source/tasks/generic/Flush.cpp b/media/server/gstplayer/source/tasks/generic/Flush.cpp index 2af9c5f6b..ed76e7576 100644 --- a/media/server/gstplayer/source/tasks/generic/Flush.cpp +++ b/media/server/gstplayer/source/tasks/generic/Flush.cpp @@ -25,10 +25,10 @@ namespace firebolt::rialto::server::tasks::generic { Flush::Flush(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, IGstGenericPlayerClient *client, - std::shared_ptr gstWrapper, const MediaSourceType &type, - bool resetTime) + const std::shared_ptr &gstWrapper, const MediaSourceType &type, + bool resetTime, bool isAsync) : m_context{context}, m_player{player}, m_gstPlayerClient{client}, m_gstWrapper{gstWrapper}, m_type{type}, - m_resetTime{resetTime} + m_resetTime{resetTime}, m_isAsync{isAsync} { RIALTO_SERVER_LOG_DEBUG("Constructing Flush"); } @@ -40,13 +40,6 @@ Flush::~Flush() void Flush::execute() const { - if (m_context.flushOnPrerollController.shouldPostponeFlush(m_type)) - { - RIALTO_SERVER_LOG_WARN("Postponing Flush for %s source", common::convertMediaSourceType(m_type)); - m_player.postponeFlush(m_type, m_resetTime); - return; - } - RIALTO_SERVER_LOG_DEBUG("Executing Flush for %s source", common::convertMediaSourceType(m_type)); // Get source first @@ -83,7 +76,7 @@ void Flush::execute() const if (GST_STATE(m_context.pipeline) >= GST_STATE_PAUSED) { - m_context.flushOnPrerollController.setFlushing(m_type, GST_STATE(m_context.pipeline)); + m_context.flushOnPrerollController->waitIfRequired(m_type); // Flush source GstEvent *flushStart = m_gstWrapper->gstEventNewFlushStart(); @@ -97,6 +90,11 @@ void Flush::execute() const { RIALTO_SERVER_LOG_WARN("failed to send flush-stop event for %s", common::convertMediaSourceType(m_type)); } + + if (m_isAsync) + { + m_context.flushOnPrerollController->setFlushing(m_type); + } } else { diff --git a/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp b/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp index 97b2d3452..4cf83e0df 100644 --- a/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp +++ b/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp @@ -295,9 +295,9 @@ std::unique_ptr GenericPlayerTaskFactory::createPing(std::unique_pt std::unique_ptr GenericPlayerTaskFactory::createFlush(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, const firebolt::rialto::MediaSourceType &type, - bool resetTime) const + bool resetTime, bool isAsync) const { - return std::make_unique(context, player, m_client, m_gstWrapper, type, resetTime); + return std::make_unique(context, player, m_client, m_gstWrapper, type, resetTime, isAsync); } std::unique_ptr diff --git a/media/server/gstplayer/source/tasks/generic/HandleBusMessage.cpp b/media/server/gstplayer/source/tasks/generic/HandleBusMessage.cpp index 7ecf6bcba..9bb55e116 100644 --- a/media/server/gstplayer/source/tasks/generic/HandleBusMessage.cpp +++ b/media/server/gstplayer/source/tasks/generic/HandleBusMessage.cpp @@ -71,7 +71,6 @@ void HandleBusMessage::execute() const { case GST_STATE_NULL: { - m_context.flushOnPrerollController.reset(); m_gstPlayerClient->notifyPlaybackState(PlaybackState::STOPPED); break; } @@ -81,7 +80,6 @@ void HandleBusMessage::execute() const m_player.stopPositionReportingAndCheckAudioUnderflowTimer(); if (pending != GST_STATE_PAUSED) { - m_context.flushOnPrerollController.stateReached(newState); // If async flush was requested before HandleBusMessage task creation (but it was not executed yet) // or if async flush was created after HandleBusMessage task creation (but before its execution) // we can't report playback state, because async flush causes state loss - reported state is probably invalid. @@ -105,8 +103,6 @@ void HandleBusMessage::execute() const } case GST_STATE_PLAYING: { - m_context.flushOnPrerollController.stateReached(newState); - m_player.executePostponedFlushes(); // If async flush was requested before HandleBusMessage task creation (but it was not executed yet) // or if async flush was created after HandleBusMessage task creation (but before its execution) // we can't report playback state, because async flush causes state loss - reported state is probably invalid. diff --git a/media/server/main/source/MediaPipelineServerInternal.cpp b/media/server/main/source/MediaPipelineServerInternal.cpp index 197240e6a..660753f03 100644 --- a/media/server/main/source/MediaPipelineServerInternal.cpp +++ b/media/server/main/source/MediaPipelineServerInternal.cpp @@ -1152,6 +1152,8 @@ bool MediaPipelineServerInternal::flushInternal(int32_t sourceId, bool resetTime m_gstPlayer->flush(sourceIter->first, resetTime, async); + m_needMediaDataTimers.erase(sourceIter->first); + // Reset Eos on flush auto it = m_isMediaTypeEosMap.find(sourceIter->first); if (it != m_isMediaTypeEosMap.end() && it->second) diff --git a/tests/componenttests/server/common/ExpectMessage.h b/tests/componenttests/server/common/ExpectMessage.h index 839319e37..bd140f92a 100644 --- a/tests/componenttests/server/common/ExpectMessage.h +++ b/tests/componenttests/server/common/ExpectMessage.h @@ -70,7 +70,7 @@ template class ExpectMessage EventRanger &m_eventRanger; std::shared_ptr m_message{nullptr}; std::function m_filter{[](const MessageType &) { return true; }}; - std::chrono::milliseconds m_timeout{400}; + std::chrono::milliseconds m_timeout{600}; }; } // namespace firebolt::rialto::server::ct diff --git a/tests/unittests/media/server/gstplayer/dispatcherThread/GstDispatcherThreadTest.cpp b/tests/unittests/media/server/gstplayer/dispatcherThread/GstDispatcherThreadTest.cpp index 54aeffd82..2805d68ab 100644 --- a/tests/unittests/media/server/gstplayer/dispatcherThread/GstDispatcherThreadTest.cpp +++ b/tests/unittests/media/server/gstplayer/dispatcherThread/GstDispatcherThreadTest.cpp @@ -18,6 +18,7 @@ */ #include "GstDispatcherThread.h" +#include "FlushOnPrerollControllerMock.h" #include "GenericPlayerTaskFactoryMock.h" #include "GstDispatcherThreadClientMock.h" #include "GstWrapperMock.h" @@ -56,6 +57,8 @@ class GstDispatcherThreadTest : public ::testing::Test dynamic_cast &>(*workerThreadFactory)}; std::unique_ptr workerThread{std::make_unique>()}; StrictMock &m_workerThreadMock{dynamic_cast &>(*workerThread)}; + std::shared_ptr> m_flushOnPrerollControllerMock{ + std::make_shared>()}; std::mutex m_dispatcherThreadMutex; std::condition_variable m_dispatcherThreadCond; @@ -88,7 +91,8 @@ TEST_F(GstDispatcherThreadTest, PollTimeout) m_dispatcherThreadCond.notify_all(); })); - auto sut = std::make_unique(m_client, &m_pipeline, m_gstWrapperMock); + auto sut = + std::make_unique(m_client, &m_pipeline, m_flushOnPrerollControllerMock, m_gstWrapperMock); // wait for dispatcher thread std::unique_lock dispatcherLock(m_dispatcherThreadMutex); @@ -98,7 +102,7 @@ TEST_F(GstDispatcherThreadTest, PollTimeout) } /** - * Test that a GST_MESSAGE_STATE_CHANGED message is handled correctly. + * Test that a GST_MESSAGE_STATE_CHANGED message (to GST_STATE_PAUSED) is handled correctly. */ TEST_F(GstDispatcherThreadTest, StateChangedToPaused) { @@ -127,7 +131,57 @@ TEST_F(GstDispatcherThreadTest, StateChangedToPaused) EXPECT_CALL(*m_gstWrapperMock, gstBusTimedPopFiltered(&m_bus, 100 * GST_MSECOND, _)).WillOnce(Return(&messageError)); EXPECT_CALL(m_client, handleBusMessage(_)); } + EXPECT_CALL(*m_flushOnPrerollControllerMock, stateReached(GST_STATE_PAUSED)); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(&m_bus)) + .WillOnce(Invoke( + [this](gpointer bus) + { + std::unique_lock lock(m_dispatcherThreadMutex); + m_dispatcherThreadDone = true; + m_dispatcherThreadCond.notify_all(); + })); + + auto sut = + std::make_unique(m_client, &m_pipeline, m_flushOnPrerollControllerMock, m_gstWrapperMock); + + // wait for dispatcher thread + std::unique_lock dispatcherLock(m_dispatcherThreadMutex); + bool status = m_dispatcherThreadCond.wait_for(dispatcherLock, std::chrono::milliseconds(200), + [this]() { return m_dispatcherThreadDone; }); + EXPECT_TRUE(status); +} + +/** + * Test that a GST_MESSAGE_STATE_CHANGED message (to GST_STATE_PLAYING) is handled correctly. + */ +TEST_F(GstDispatcherThreadTest, StateChangedToPlaying) +{ + GST_MESSAGE_SRC(&m_message) = GST_OBJECT(&m_pipeline); + GST_MESSAGE_TYPE(&m_message) = GST_MESSAGE_STATE_CHANGED; + + GstState oldState = GST_STATE_READY; + GstState newState = GST_STATE_PLAYING; + GstState pending = GST_STATE_VOID_PENDING; + + GstMessage messageError = {}; + GST_MESSAGE_SRC(&messageError) = GST_OBJECT(&m_pipeline); + GST_MESSAGE_TYPE(&messageError) = GST_MESSAGE_ERROR; + + EXPECT_CALL(*m_gstWrapperMock, gstPipelineGetBus(GST_PIPELINE(&m_pipeline))).WillOnce(Return(&m_bus)); + + EXPECT_CALL(*m_gstWrapperMock, gstMessageParseStateChanged(&m_message, _, _, _)) + .WillOnce(DoAll(SetArgPointee<1>(oldState), SetArgPointee<2>(newState), SetArgPointee<3>(pending))); + { + InSequence seq; + EXPECT_CALL(*m_gstWrapperMock, gstBusTimedPopFiltered(&m_bus, 100 * GST_MSECOND, _)).WillOnce(Return(&m_message)); + EXPECT_CALL(m_client, handleBusMessage(_)); + + // Signal error to stop the thread + EXPECT_CALL(*m_gstWrapperMock, gstBusTimedPopFiltered(&m_bus, 100 * GST_MSECOND, _)).WillOnce(Return(&messageError)); + EXPECT_CALL(m_client, handleBusMessage(_)); + } + EXPECT_CALL(*m_flushOnPrerollControllerMock, stateReached(GST_STATE_PLAYING)); EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(&m_bus)) .WillOnce(Invoke( [this](gpointer bus) @@ -137,7 +191,8 @@ TEST_F(GstDispatcherThreadTest, StateChangedToPaused) m_dispatcherThreadCond.notify_all(); })); - auto sut = std::make_unique(m_client, &m_pipeline, m_gstWrapperMock); + auto sut = + std::make_unique(m_client, &m_pipeline, m_flushOnPrerollControllerMock, m_gstWrapperMock); // wait for dispatcher thread std::unique_lock dispatcherLock(m_dispatcherThreadMutex); @@ -163,6 +218,57 @@ TEST_F(GstDispatcherThreadTest, StateChangedToStop) EXPECT_CALL(*m_gstWrapperMock, gstPipelineGetBus(GST_PIPELINE(&m_pipeline))).WillOnce(Return(&m_bus)); EXPECT_CALL(*m_gstWrapperMock, gstBusTimedPopFiltered(&m_bus, 100 * GST_MSECOND, _)).WillOnce(Return(&m_message)); EXPECT_CALL(m_client, handleBusMessage(_)); + EXPECT_CALL(*m_flushOnPrerollControllerMock, reset()); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(&m_bus)) + .WillOnce(Invoke( + [this](gpointer bus) + { + std::unique_lock lock(m_dispatcherThreadMutex); + m_dispatcherThreadDone = true; + m_dispatcherThreadCond.notify_all(); + })); + + auto sut = + std::make_unique(m_client, &m_pipeline, m_flushOnPrerollControllerMock, m_gstWrapperMock); + + // wait for dispatcher thread + std::unique_lock dispatcherLock(m_dispatcherThreadMutex); + bool status = m_dispatcherThreadCond.wait_for(dispatcherLock, std::chrono::milliseconds(200), + [this]() { return m_dispatcherThreadDone; }); + EXPECT_TRUE(status); +} + +/** + * Test that a GST_MESSAGE_STATE_CHANGED message (to GST_STATE_PAUSED, pending PAUSED) is handled correctly. + */ +TEST_F(GstDispatcherThreadTest, StateChangedToPrerolling) +{ + GST_MESSAGE_SRC(&m_message) = GST_OBJECT(&m_pipeline); + GST_MESSAGE_TYPE(&m_message) = GST_MESSAGE_STATE_CHANGED; + + GstState oldState = GST_STATE_READY; + GstState newState = GST_STATE_PAUSED; + GstState pending = GST_STATE_PAUSED; + + GstMessage messageError = {}; + GST_MESSAGE_SRC(&messageError) = GST_OBJECT(&m_pipeline); + GST_MESSAGE_TYPE(&messageError) = GST_MESSAGE_ERROR; + + EXPECT_CALL(*m_gstWrapperMock, gstPipelineGetBus(GST_PIPELINE(&m_pipeline))).WillOnce(Return(&m_bus)); + + EXPECT_CALL(*m_gstWrapperMock, gstMessageParseStateChanged(&m_message, _, _, _)) + .WillOnce(DoAll(SetArgPointee<1>(oldState), SetArgPointee<2>(newState), SetArgPointee<3>(pending))); + + { + InSequence seq; + EXPECT_CALL(*m_gstWrapperMock, gstBusTimedPopFiltered(&m_bus, 100 * GST_MSECOND, _)).WillOnce(Return(&m_message)); + EXPECT_CALL(m_client, handleBusMessage(_)); + + // Signal error to stop the thread + EXPECT_CALL(*m_gstWrapperMock, gstBusTimedPopFiltered(&m_bus, 100 * GST_MSECOND, _)).WillOnce(Return(&messageError)); + EXPECT_CALL(m_client, handleBusMessage(_)); + } + EXPECT_CALL(*m_flushOnPrerollControllerMock, setPrerolling()); EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(&m_bus)) .WillOnce(Invoke( [this](gpointer bus) @@ -172,7 +278,8 @@ TEST_F(GstDispatcherThreadTest, StateChangedToStop) m_dispatcherThreadCond.notify_all(); })); - auto sut = std::make_unique(m_client, &m_pipeline, m_gstWrapperMock); + auto sut = + std::make_unique(m_client, &m_pipeline, m_flushOnPrerollControllerMock, m_gstWrapperMock); // wait for dispatcher thread std::unique_lock dispatcherLock(m_dispatcherThreadMutex); @@ -200,7 +307,8 @@ TEST_F(GstDispatcherThreadTest, Error) m_dispatcherThreadCond.notify_all(); })); - auto sut = std::make_unique(m_client, &m_pipeline, m_gstWrapperMock); + auto sut = + std::make_unique(m_client, &m_pipeline, m_flushOnPrerollControllerMock, m_gstWrapperMock); // wait for dispatcher thread std::unique_lock dispatcherLock(m_dispatcherThreadMutex); @@ -243,7 +351,8 @@ TEST_F(GstDispatcherThreadTest, StateChangedToPausedNonPipeline) m_dispatcherThreadCond.notify_all(); })); - auto sut = std::make_unique(m_client, &m_pipeline, m_gstWrapperMock); + auto sut = + std::make_unique(m_client, &m_pipeline, m_flushOnPrerollControllerMock, m_gstWrapperMock); // wait for dispatcher thread std::unique_lock dispatcherLock(m_dispatcherThreadMutex); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/FlushOnPrerollControllerTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/FlushOnPrerollControllerTest.cpp index 803ae1eb3..ed5a0904b 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/FlushOnPrerollControllerTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/FlushOnPrerollControllerTest.cpp @@ -19,6 +19,7 @@ #include "FlushOnPrerollController.h" #include +#include using firebolt::rialto::MediaSourceType; using firebolt::rialto::server::FlushOnPrerollController; @@ -29,37 +30,65 @@ class FlushOnPrerollControllerTest : public ::testing::Test FlushOnPrerollController m_sut; }; -TEST_F(FlushOnPrerollControllerTest, shouldNotPostponeFlushWhenNoFlushSet) +TEST_F(FlushOnPrerollControllerTest, shouldNotWaithWhenNoFlushSet) { - EXPECT_FALSE(m_sut.shouldPostponeFlush(MediaSourceType::AUDIO)); + m_sut.waitIfRequired(MediaSourceType::AUDIO); + // No deadlock here } -TEST_F(FlushOnPrerollControllerTest, shouldNotPostponeFlushWhenNotPrerolled) +TEST_F(FlushOnPrerollControllerTest, shouldNotWaitWhenNotPrerolled) { - m_sut.setFlushing(MediaSourceType::AUDIO, GST_STATE_PLAYING); - EXPECT_FALSE(m_sut.shouldPostponeFlush(MediaSourceType::AUDIO)); + m_sut.setTargetState(GST_STATE_PLAYING); + m_sut.setFlushing(MediaSourceType::AUDIO); + m_sut.waitIfRequired(MediaSourceType::AUDIO); + // No deadlock here } -TEST_F(FlushOnPrerollControllerTest, shouldPostponeAudioFlush) +TEST_F(FlushOnPrerollControllerTest, shouldNotWaitWhenReset) { - m_sut.setFlushing(MediaSourceType::AUDIO, GST_STATE_PLAYING); + m_sut.setTargetState(GST_STATE_PLAYING); + m_sut.setFlushing(MediaSourceType::AUDIO); m_sut.stateReached(GST_STATE_PAUSED); - EXPECT_TRUE(m_sut.shouldPostponeFlush(MediaSourceType::AUDIO)); - EXPECT_FALSE(m_sut.shouldPostponeFlush(MediaSourceType::VIDEO)); + m_sut.reset(); + m_sut.waitIfRequired(MediaSourceType::AUDIO); + // No deadlock here } -TEST_F(FlushOnPrerollControllerTest, shouldNotPostponeAudioFlushWhenReset) +TEST_F(FlushOnPrerollControllerTest, shouldNotWaitWhenPrerolling) { - m_sut.setFlushing(MediaSourceType::AUDIO, GST_STATE_PLAYING); + m_sut.setTargetState(GST_STATE_PLAYING); + m_sut.setFlushing(MediaSourceType::AUDIO); + m_sut.setPrerolling(); + m_sut.waitIfRequired(MediaSourceType::AUDIO); + // No deadlock here +} + +TEST_F(FlushOnPrerollControllerTest, shouldNotWaitWhenPreviousProcedureIsFinished) +{ + m_sut.setTargetState(GST_STATE_PLAYING); + m_sut.setFlushing(MediaSourceType::AUDIO); m_sut.stateReached(GST_STATE_PAUSED); - m_sut.reset(); - EXPECT_FALSE(m_sut.shouldPostponeFlush(MediaSourceType::AUDIO)); + m_sut.stateReached(GST_STATE_PLAYING); + m_sut.waitIfRequired(MediaSourceType::AUDIO); + // No deadlock here +} + +TEST_F(FlushOnPrerollControllerTest, shouldNotWaitWithVideoFlushWhenOnlyAudioIsOngoing) +{ + m_sut.setTargetState(GST_STATE_PLAYING); + m_sut.setFlushing(MediaSourceType::AUDIO); + m_sut.stateReached(GST_STATE_PAUSED); + m_sut.waitIfRequired(MediaSourceType::VIDEO); + // No deadlock here } -TEST_F(FlushOnPrerollControllerTest, shouldNotPostponeAudioFlushWhenPreviousProcedureIsFinished) +TEST_F(FlushOnPrerollControllerTest, shouldWaitForAudioFlushFinish) { - m_sut.setFlushing(MediaSourceType::AUDIO, GST_STATE_PLAYING); + m_sut.setTargetState(GST_STATE_PLAYING); + m_sut.setFlushing(MediaSourceType::AUDIO); m_sut.stateReached(GST_STATE_PAUSED); + std::thread waitThread([this]() { m_sut.waitIfRequired(MediaSourceType::AUDIO); }); m_sut.stateReached(GST_STATE_PLAYING); - EXPECT_FALSE(m_sut.shouldPostponeFlush(MediaSourceType::AUDIO)); + waitThread.join(); + // No deadlock here } diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp index e347a4510..15da389bb 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp @@ -2143,15 +2143,3 @@ TEST_F(GstGenericPlayerPrivateTest, shouldSetShowVideoWindow) EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_realElement)); EXPECT_TRUE(m_sut->setShowVideoWindow()); } - -TEST_F(GstGenericPlayerPrivateTest, shouldExecutePostponedFlush) -{ - constexpr MediaSourceType kSourceType{MediaSourceType::AUDIO}; - constexpr bool kResetTime{true}; - m_sut->postponeFlush(kSourceType, kResetTime); - - std::unique_ptr task{std::make_unique>()}; - EXPECT_CALL(dynamic_cast &>(*task), execute()); - EXPECT_CALL(m_taskFactoryMock, createFlush(_, _, kSourceType, kResetTime)).WillOnce(Return(ByMove(std::move(task)))); - m_sut->executePostponedFlushes(); -} diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp index 97a5e0df6..da41f2533 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp @@ -917,7 +917,7 @@ TEST_F(GstGenericPlayerTest, shouldFlush) })); EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_element)); EXPECT_CALL(dynamic_cast &>(*task), execute()); - EXPECT_CALL(m_taskFactoryMock, createFlush(_, _, MediaSourceType::VIDEO, kResetTime)) + EXPECT_CALL(m_taskFactoryMock, createFlush(_, _, MediaSourceType::VIDEO, kResetTime, isAsync)) .WillOnce(Return(ByMove(std::move(task)))); m_sut->flush(MediaSourceType::VIDEO, kResetTime, isAsync); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp index 0534a78e9..1a2599972 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp @@ -139,6 +139,7 @@ constexpr uint64_t kStopPosition{4523}; const std::vector kStreamHeaderVector{1, 2, 3, 4}; constexpr bool kFramed{true}; constexpr uint64_t kDisplayOffset{35}; +constexpr bool kIsAsync{true}; firebolt::rialto::IMediaPipeline::MediaSegmentVector buildAudioSamples() { @@ -3146,7 +3147,8 @@ void GenericTasksTestsBase::triggerFlush(firebolt::rialto::MediaSourceType sourc &testContext->m_gstPlayerClient, testContext->m_gstWrapper, sourceType, - kResetTime}; + kResetTime, + kIsAsync}; task.execute(); } @@ -3190,14 +3192,6 @@ void GenericTasksTestsBase::shouldFlushVideoSrcSuccess() .WillOnce(Return(TRUE)); } -void GenericTasksTestsBase::shouldPostponeVideoFlush() -{ - testContext->m_context.flushOnPrerollController.setFlushing(firebolt::rialto::MediaSourceType::VIDEO, - GST_STATE_PLAYING); - testContext->m_context.flushOnPrerollController.stateReached(GST_STATE_PAUSED); - EXPECT_CALL(testContext->m_gstPlayer, postponeFlush(firebolt::rialto::MediaSourceType::VIDEO, kResetTime)); -} - void GenericTasksTestsBase::shouldSetSubtitleSourcePosition() { EXPECT_CALL(*testContext->m_glibWrapper, gObjectSetStub(&testContext->m_textTrackSink, StrEq("position"))); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h index f6ee3beff..729f8f66c 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h @@ -416,7 +416,6 @@ class GenericTasksTestsBase : public ::testing::Test void checkAudioFlushed(); void checkVideoFlushed(); void shouldFlushVideoSrcSuccess(); - void shouldPostponeVideoFlush(); // Set Source Position test methods void shouldSetSubtitleSourcePosition(); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp index 22c7b56b5..3920d589a 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp @@ -199,7 +199,7 @@ void GstGenericPlayerTestCommon::expectCheckPlaySink() void GstGenericPlayerTestCommon::expectSetMessageCallback() { - EXPECT_CALL(m_gstDispatcherThreadFactoryMock, createGstDispatcherThread(_, _, _)) + EXPECT_CALL(m_gstDispatcherThreadFactoryMock, createGstDispatcherThread(_, _, _, _)) .WillOnce(Return(ByMove(std::move(gstDispatcherThread)))); } diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/FlushTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/FlushTest.cpp index 64017d24d..6603d681b 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/FlushTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/FlushTest.cpp @@ -108,9 +108,3 @@ TEST_F(FlushTest, ShouldFlushVideoWithNeedData) triggerFlush(firebolt::rialto::MediaSourceType::VIDEO); checkVideoFlushed(); } - -TEST_F(FlushTest, ShouldPostponeFlush) -{ - shouldPostponeVideoFlush(); - triggerFlush(firebolt::rialto::MediaSourceType::VIDEO); -} diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp index 7693164c0..6bd95d4a8 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp @@ -326,7 +326,7 @@ TEST_F(GenericPlayerTaskFactoryTest, ShouldCreatePing) TEST_F(GenericPlayerTaskFactoryTest, ShouldCreateFlush) { - auto task = m_sut.createFlush(m_context, m_gstPlayer, firebolt::rialto::MediaSourceType::AUDIO, true); + auto task = m_sut.createFlush(m_context, m_gstPlayer, firebolt::rialto::MediaSourceType::AUDIO, true, true); EXPECT_NE(task, nullptr); EXPECT_NO_THROW(dynamic_cast(*task)); } diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/HandleBusMessageTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/HandleBusMessageTest.cpp index 77f230ef9..783c88cd1 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/HandleBusMessageTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/HandleBusMessageTest.cpp @@ -425,7 +425,6 @@ TEST_F(HandleBusMessageTest, shouldHandleStateChangedToPlayingMessage) EXPECT_CALL(*m_gstWrapper, gstMessageUnref(&m_message)); EXPECT_CALL(m_flushWatcherMock, isFlushOngoing()).WillRepeatedly(Return(kNoFlushOngoing)); EXPECT_CALL(m_flushWatcherMock, isAsyncFlushOngoing()).WillRepeatedly(Return(kNoFlushOngoing)); - EXPECT_CALL(m_gstPlayer, executePostponedFlushes()); firebolt::rialto::server::tasks::generic::HandleBusMessage task{m_context, m_gstPlayer, &m_gstPlayerClient, m_gstWrapper, m_glibWrapper, &m_message, @@ -458,7 +457,6 @@ TEST_F(HandleBusMessageTest, shouldHandleStateChangedToPlayingMessageWhenSyncFlu EXPECT_CALL(*m_gstWrapper, gstMessageUnref(&m_message)); EXPECT_CALL(m_flushWatcherMock, isFlushOngoing()).WillRepeatedly(Return(kIsFlushOngoing)); EXPECT_CALL(m_flushWatcherMock, isAsyncFlushOngoing()).WillRepeatedly(Return(kNoFlushOngoing)); - EXPECT_CALL(m_gstPlayer, executePostponedFlushes()); firebolt::rialto::server::tasks::generic::HandleBusMessage task{m_context, m_gstPlayer, &m_gstPlayerClient, m_gstWrapper, m_glibWrapper, &m_message, @@ -488,7 +486,6 @@ TEST_F(HandleBusMessageTest, shouldSkipHandlingStateChangedToPlayingMessageWhenA EXPECT_CALL(*m_gstWrapper, gstMessageUnref(&m_message)); EXPECT_CALL(m_flushWatcherMock, isFlushOngoing()).WillOnce(Return(kIsFlushOngoing)); EXPECT_CALL(m_flushWatcherMock, isAsyncFlushOngoing()).WillOnce(Return(kIsFlushOngoing)); - EXPECT_CALL(m_gstPlayer, executePostponedFlushes()); firebolt::rialto::server::tasks::generic::HandleBusMessage task{m_context, m_gstPlayer, &m_gstPlayerClient, m_gstWrapper, m_glibWrapper, &m_message, @@ -516,7 +513,6 @@ TEST_F(HandleBusMessageTest, shouldSkipHandlingStateChangedToPlayingMessageWhenA EXPECT_CALL(*m_gstWrapper, gstMessageUnref(&m_message)); EXPECT_CALL(m_flushWatcherMock, isFlushOngoing()).WillOnce(Return(kIsFlushOngoing)); EXPECT_CALL(m_flushWatcherMock, isAsyncFlushOngoing()).WillOnce(Return(kNoFlushOngoing)).WillOnce(Return(kIsFlushOngoing)); - EXPECT_CALL(m_gstPlayer, executePostponedFlushes()); firebolt::rialto::server::tasks::generic::HandleBusMessage task{m_context, m_gstPlayer, &m_gstPlayerClient, m_gstWrapper, m_glibWrapper, &m_message, @@ -546,7 +542,6 @@ TEST_F(HandleBusMessageTest, shouldHandleStateChangedToPlayingMessageAndSetPendi EXPECT_CALL(*m_gstWrapper, gstMessageUnref(&m_message)); EXPECT_CALL(m_flushWatcherMock, isFlushOngoing()).WillRepeatedly(Return(kNoFlushOngoing)); EXPECT_CALL(m_flushWatcherMock, isAsyncFlushOngoing()).WillRepeatedly(Return(kNoFlushOngoing)); - EXPECT_CALL(m_gstPlayer, executePostponedFlushes()); firebolt::rialto::server::tasks::generic::HandleBusMessage task{m_context, m_gstPlayer, &m_gstPlayerClient, m_gstWrapper, m_glibWrapper, &m_message, diff --git a/tests/unittests/media/server/gstplayer/webAudioPlayer/CreateTest.cpp b/tests/unittests/media/server/gstplayer/webAudioPlayer/CreateTest.cpp index 0a61ceeb5..a0fa43a6b 100644 --- a/tests/unittests/media/server/gstplayer/webAudioPlayer/CreateTest.cpp +++ b/tests/unittests/media/server/gstplayer/webAudioPlayer/CreateTest.cpp @@ -500,7 +500,7 @@ TEST_F(RialtoServerCreateGstWebAudioPlayerTest, createGstDispatcherThreadFailure expectInitAppSrc(); expectAddElementsAutoAudioSink(); expectInitWorkerThread(); - EXPECT_CALL(m_gstDispatcherThreadFactoryMock, createGstDispatcherThread(_, _, _)).WillOnce(Return(nullptr)); + EXPECT_CALL(m_gstDispatcherThreadFactoryMock, createGstDispatcherThread(_, _, _, _)).WillOnce(Return(nullptr)); // Reset worker thread and pipeline on failure gstPlayerWillBeDestroyed(); diff --git a/tests/unittests/media/server/gstplayer/webAudioPlayer/common/GstWebAudioPlayerTestCommon.cpp b/tests/unittests/media/server/gstplayer/webAudioPlayer/common/GstWebAudioPlayerTestCommon.cpp index 96980db37..42e890c8d 100644 --- a/tests/unittests/media/server/gstplayer/webAudioPlayer/common/GstWebAudioPlayerTestCommon.cpp +++ b/tests/unittests/media/server/gstplayer/webAudioPlayer/common/GstWebAudioPlayerTestCommon.cpp @@ -107,7 +107,7 @@ void GstWebAudioPlayerTestCommon::expectInitWorkerThread() void GstWebAudioPlayerTestCommon::expectInitThreads() { expectInitWorkerThread(); - EXPECT_CALL(m_gstDispatcherThreadFactoryMock, createGstDispatcherThread(_, _, _)) + EXPECT_CALL(m_gstDispatcherThreadFactoryMock, createGstDispatcherThread(_, _, _, _)) .WillOnce(Return(ByMove(std::move(gstDispatcherThread)))); } diff --git a/tests/unittests/media/server/mocks/gstplayer/FlushOnPrerollControllerMock.h b/tests/unittests/media/server/mocks/gstplayer/FlushOnPrerollControllerMock.h index 0a4d3fff1..b3daeec0e 100644 --- a/tests/unittests/media/server/mocks/gstplayer/FlushOnPrerollControllerMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/FlushOnPrerollControllerMock.h @@ -28,9 +28,11 @@ namespace firebolt::rialto::server class FlushOnPrerollControllerMock : public IFlushOnPrerollController { public: - MOCK_METHOD(bool, shouldPostponeFlush, (const MediaSourceType &type), (const, override)); - MOCK_METHOD(void, setFlushing, (const MediaSourceType &type, const GstState ¤tPipelineState), (override)); + MOCK_METHOD(void, waitIfRequired, (const MediaSourceType &type), (override)); + MOCK_METHOD(void, setFlushing, (const MediaSourceType &type), (override)); + MOCK_METHOD(void, setPrerolling, (), (override)); MOCK_METHOD(void, stateReached, (const GstState &newPipelineState), (override)); + MOCK_METHOD(void, setTargetState, (const GstState &state), (override)); MOCK_METHOD(void, reset, (), (override)); }; } // namespace firebolt::rialto::server diff --git a/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h b/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h index 902309e50..12624a047 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h @@ -119,7 +119,7 @@ class GenericPlayerTaskFactoryMock : public IGenericPlayerTaskFactory (const, override)); MOCK_METHOD(std::unique_ptr, createFlush, (GenericPlayerContext & context, IGstGenericPlayerPrivate &player, - const firebolt::rialto::MediaSourceType &type, bool resetTime), + const firebolt::rialto::MediaSourceType &type, bool resetTime, bool isAsync), (const, override)); MOCK_METHOD(std::unique_ptr, createSetSourcePosition, (GenericPlayerContext & context, const firebolt::rialto::MediaSourceType &type, std::int64_t position, diff --git a/tests/unittests/media/server/mocks/gstplayer/GstDispatcherThreadFactoryMock.h b/tests/unittests/media/server/mocks/gstplayer/GstDispatcherThreadFactoryMock.h index 95b1d947a..49b6675d0 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GstDispatcherThreadFactoryMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GstDispatcherThreadFactoryMock.h @@ -31,6 +31,7 @@ class GstDispatcherThreadFactoryMock : public IGstDispatcherThreadFactory public: MOCK_METHOD(std::unique_ptr, createGstDispatcherThread, (IGstDispatcherThreadClient & client, GstElement *pipeline, + const std::shared_ptr &flushOnPrerollController, const std::shared_ptr &gstWrapper), (const, override)); }; diff --git a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h index 346dca566..ffb866ee7 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h @@ -80,8 +80,6 @@ class GstGenericPlayerPrivateMock : public IGstGenericPlayerPrivate MOCK_METHOD(void, startSubtitleClockResyncTimer, (), (override)); MOCK_METHOD(void, stopSubtitleClockResyncTimer, (), (override)); MOCK_METHOD(bool, hasSourceType, (const MediaSourceType &mediaSourceType), (const, override)); - MOCK_METHOD(void, postponeFlush, (const MediaSourceType &mediaSourceType, bool resetTime), (override)); - MOCK_METHOD(void, executePostponedFlushes, (), (override)); MOCK_METHOD(void, notifyPlaybackInfo, (), (override)); }; } // namespace firebolt::rialto::server From 6c81198434f67f1aef46ee00ad8bcb8a48917769 Mon Sep 17 00:00:00 2001 From: Marcin Wojciechowski <105790697+skywojciechowskim@users.noreply.github.com> Date: Fri, 13 Feb 2026 10:12:24 +0100 Subject: [PATCH 06/29] Removed gstreamer interaction during RemoveSource (#444) Summary: Removed gstreamer interaction during RemoveSource Type: Fix Test Plan: UT/CT, Fullstack Jira: LLAMA-18057 --- media/server/gstplayer/CMakeLists.txt | 1 - .../gstplayer/include/GenericPlayerContext.h | 7 - .../gstplayer/include/GstGenericPlayer.h | 9 +- .../include/IGstGenericPlayerPrivate.h | 7 - .../include/tasks/IGenericPlayerTaskFactory.h | 13 -- .../include/tasks/generic/AttachSource.h | 1 - .../tasks/generic/GenericPlayerTaskFactory.h | 2 - .../include/tasks/generic/RemoveSource.h | 49 ----- .../gstplayer/interface/IGstGenericPlayer.h | 8 - .../gstplayer/source/GstGenericPlayer.cpp | 10 +- .../source/tasks/generic/AttachSource.cpp | 22 --- .../tasks/generic/CheckAudioUnderflow.cpp | 2 +- .../generic/GenericPlayerTaskFactory.cpp | 8 - .../source/tasks/generic/NeedData.cpp | 5 - .../source/tasks/generic/RemoveSource.cpp | 81 -------- .../source/MediaPipelineServerInternal.cpp | 1 - .../server/fixtures/MediaPipelineTest.cpp | 36 ---- .../server/fixtures/MediaPipelineTest.h | 2 - .../server/tests/CMakeLists.txt | 2 +- .../mediaPipeline/AudioOnlyPlaybackTest.cpp | 1 - .../mediaPipeline/AudioSourceSwitchTest.cpp | 1 - .../mediaPipeline/DualVideoPlaybackTest.cpp | 2 - .../mediaPipeline/EncryptedPlaybackTest.cpp | 1 - .../server/tests/mediaPipeline/FlushTest.cpp | 1 - .../mediaPipeline/HaveDataFailureTest.cpp | 1 - .../server/tests/mediaPipeline/MuteTest.cpp | 1 - .../NonFatalPlayerErrorUpdatesTest.cpp | 1 - .../mediaPipeline/PipelinePropertyTest.cpp | 2 - .../tests/mediaPipeline/PlaybackTest.cpp | 1 - .../mediaPipeline/PositionUpdatesTest.cpp | 3 - .../tests/mediaPipeline/QosUpdatesTest.cpp | 2 - .../tests/mediaPipeline/RenderFrameTest.cpp | 2 - .../tests/mediaPipeline/SetPositionTest.cpp | 2 - .../mediaPipeline/SetSourcePositionTest.cpp | 1 - .../mediaPipeline/SetVideoWindowTest.cpp | 1 - .../server/tests/mediaPipeline/SourceTest.cpp | 2 - ...ckTest.cpp => SwitchAudioPlaybackTest.cpp} | 176 ++++++++++-------- .../tests/mediaPipeline/UnderflowTest.cpp | 1 - .../server/tests/mediaPipeline/VolumeTest.cpp | 1 - .../tests/mediaPipeline/WriteSegmentsTest.cpp | 1 - .../media/server/gstplayer/CMakeLists.txt | 1 - .../GstGenericPlayerPrivateTest.cpp | 31 +-- .../genericPlayer/GstGenericPlayerTest.cpp | 10 - .../common/GenericTasksTestsBase.cpp | 72 ------- .../common/GenericTasksTestsBase.h | 13 -- .../tasksTests/AttachSourceTest.cpp | 18 -- .../GenericPlayerTaskFactoryTest.cpp | 8 - .../genericPlayer/tasksTests/NeedDataTest.cpp | 10 - .../tasksTests/RemoveSourceTest.cpp | 74 -------- .../server/main/mediaPipeline/SourceTest.cpp | 2 - .../gstplayer/GenericPlayerTaskFactoryMock.h | 4 - .../mocks/gstplayer/GstGenericPlayerMock.h | 1 - .../gstplayer/GstGenericPlayerPrivateMock.h | 1 - 53 files changed, 106 insertions(+), 609 deletions(-) delete mode 100644 media/server/gstplayer/include/tasks/generic/RemoveSource.h delete mode 100644 media/server/gstplayer/source/tasks/generic/RemoveSource.cpp rename tests/componenttests/server/tests/mediaPipeline/{RemoveAudioPlaybackTest.cpp => SwitchAudioPlaybackTest.cpp} (57%) delete mode 100644 tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/RemoveSourceTest.cpp diff --git a/media/server/gstplayer/CMakeLists.txt b/media/server/gstplayer/CMakeLists.txt index 98a77ca4c..6756184da 100644 --- a/media/server/gstplayer/CMakeLists.txt +++ b/media/server/gstplayer/CMakeLists.txt @@ -49,7 +49,6 @@ add_library( source/tasks/generic/Play.cpp source/tasks/generic/ProcessAudioGap.cpp source/tasks/generic/ReadShmDataAndAttachSamples.cpp - source/tasks/generic/RemoveSource.cpp source/tasks/generic/RenderFrame.cpp source/tasks/generic/ReportPosition.cpp source/tasks/generic/SetBufferingLimit.cpp diff --git a/media/server/gstplayer/include/GenericPlayerContext.h b/media/server/gstplayer/include/GenericPlayerContext.h index 2a6b06352..d97deb4b1 100644 --- a/media/server/gstplayer/include/GenericPlayerContext.h +++ b/media/server/gstplayer/include/GenericPlayerContext.h @@ -206,13 +206,6 @@ struct GenericPlayerContext */ IDecryptionService *decryptionService{nullptr}; - /** - * @brief Flag used to check, if audio source has been recently removed - * - * Flag can be used only in worker thread - */ - bool audioSourceRemoved{false}; - /** * @brief Audio elements of gst pipeline. * diff --git a/media/server/gstplayer/include/GstGenericPlayer.h b/media/server/gstplayer/include/GstGenericPlayer.h index 412d81a70..123a8df85 100644 --- a/media/server/gstplayer/include/GstGenericPlayer.h +++ b/media/server/gstplayer/include/GstGenericPlayer.h @@ -107,7 +107,6 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva virtual ~GstGenericPlayer(); void attachSource(const std::unique_ptr &mediaSource) override; - void removeSource(const MediaSourceType &mediaSourceType) override; void allSourcesAttached() override; void play(bool &async) override; void pause() override; @@ -188,7 +187,6 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva void addAutoAudioSinkChild(GObject *object) override; void removeAutoVideoSinkChild(GObject *object) override; void removeAutoAudioSinkChild(GObject *object) override; - void setPlaybinFlags(bool enableAudio = true) override; void pushSampleIfRequired(GstElement *source, const std::string &typeStr) override; bool reattachSource(const std::unique_ptr &source) override; bool hasSourceType(const MediaSourceType &mediaSourceType) const override; @@ -341,6 +339,13 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva */ void pushAdditionalSegmentIfRequired(GstElement *source); + /** + * @brief Sets the audio and video flags on the pipeline based on the input. + * + * @param[in] enableAudio : Whether to enable audio flags. + */ + void setPlaybinFlags(bool enableAudio = true); + private: /** * @brief The player context. diff --git a/media/server/gstplayer/include/IGstGenericPlayerPrivate.h b/media/server/gstplayer/include/IGstGenericPlayerPrivate.h index 64d72518d..fb48a2816 100644 --- a/media/server/gstplayer/include/IGstGenericPlayerPrivate.h +++ b/media/server/gstplayer/include/IGstGenericPlayerPrivate.h @@ -281,13 +281,6 @@ class IGstGenericPlayerPrivate */ virtual GstElement *getSink(const MediaSourceType &mediaSourceType) const = 0; - /** - * @brief Sets the audio and video flags on the pipeline based on the input. - * - * @param[in] enableAudio : Whether to enable audio flags. - */ - virtual void setPlaybinFlags(bool enableAudio) = 0; - /** * @brief Pushes GstSample if playback position has changed or new segment needs to be sent. * diff --git a/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h b/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h index 35b90cbad..f8de67cb4 100644 --- a/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h +++ b/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h @@ -176,19 +176,6 @@ class IGenericPlayerTaskFactory createReadShmDataAndAttachSamples(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, const std::shared_ptr &dataReader) const = 0; - /** - * @brief Creates a Remove Source task. - * - * @param[in] context : The GstPlayer context - * @param[in] player : The GstGenericPlayer instance - * @param[in] type : The media source type to remove - * - * @retval the new Remove Source task instance. - */ - virtual std::unique_ptr createRemoveSource(GenericPlayerContext &context, - IGstGenericPlayerPrivate &player, - const firebolt::rialto::MediaSourceType &type) const = 0; - /** * @brief Creates a ReportPosition task. * diff --git a/media/server/gstplayer/include/tasks/generic/AttachSource.h b/media/server/gstplayer/include/tasks/generic/AttachSource.h index 0f77545d4..f6d8144fe 100644 --- a/media/server/gstplayer/include/tasks/generic/AttachSource.h +++ b/media/server/gstplayer/include/tasks/generic/AttachSource.h @@ -47,7 +47,6 @@ class AttachSource : public IPlayerTask private: void addSource() const; - void reattachAudioSource() const; GenericPlayerContext &m_context; std::shared_ptr m_gstWrapper; diff --git a/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h b/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h index 76b419ead..aa9a06e55 100644 --- a/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h +++ b/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h @@ -65,8 +65,6 @@ class GenericPlayerTaskFactory : public IGenericPlayerTaskFactory std::unique_ptr createReadShmDataAndAttachSamples(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, const std::shared_ptr &dataReader) const override; - std::unique_ptr createRemoveSource(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, - const firebolt::rialto::MediaSourceType &type) const override; std::unique_ptr createReportPosition(GenericPlayerContext &context, IGstGenericPlayerPrivate &player) const override; std::unique_ptr createCheckAudioUnderflow(GenericPlayerContext &context, diff --git a/media/server/gstplayer/include/tasks/generic/RemoveSource.h b/media/server/gstplayer/include/tasks/generic/RemoveSource.h deleted file mode 100644 index 7d1923ff6..000000000 --- a/media/server/gstplayer/include/tasks/generic/RemoveSource.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2023 Sky UK - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef FIREBOLT_RIALTO_SERVER_TASKS_GENERIC_REMOVE_SOURCE_H_ -#define FIREBOLT_RIALTO_SERVER_TASKS_GENERIC_REMOVE_SOURCE_H_ - -#include "GenericPlayerContext.h" -#include "IGstGenericPlayerClient.h" -#include "IGstGenericPlayerPrivate.h" -#include "IGstWrapper.h" -#include "IPlayerTask.h" -#include - -namespace firebolt::rialto::server::tasks::generic -{ -class RemoveSource : public IPlayerTask -{ -public: - RemoveSource(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, IGstGenericPlayerClient *client, - std::shared_ptr gstWrapper, const MediaSourceType &type); - ~RemoveSource() override; - void execute() const override; - -private: - GenericPlayerContext &m_context; - IGstGenericPlayerPrivate &m_player; - IGstGenericPlayerClient *m_gstPlayerClient; - std::shared_ptr m_gstWrapper; - MediaSourceType m_type; -}; -} // namespace firebolt::rialto::server::tasks::generic - -#endif // FIREBOLT_RIALTO_SERVER_TASKS_GENERIC_REMOVE_SOURCE_H_ diff --git a/media/server/gstplayer/interface/IGstGenericPlayer.h b/media/server/gstplayer/interface/IGstGenericPlayer.h index d07b56f81..5e6842926 100644 --- a/media/server/gstplayer/interface/IGstGenericPlayer.h +++ b/media/server/gstplayer/interface/IGstGenericPlayer.h @@ -88,14 +88,6 @@ class IGstGenericPlayer */ virtual void attachSource(const std::unique_ptr &mediaSource) = 0; - /** - * @brief Unattaches a source. - * - * @param[in] mediaSourceType : The media source type. - * - */ - virtual void removeSource(const MediaSourceType &mediaSourceType) = 0; - /** * @brief Handles notification that all sources were attached * diff --git a/media/server/gstplayer/source/GstGenericPlayer.cpp b/media/server/gstplayer/source/GstGenericPlayer.cpp index a0fc7e385..c0e2fad13 100644 --- a/media/server/gstplayer/source/GstGenericPlayer.cpp +++ b/media/server/gstplayer/source/GstGenericPlayer.cpp @@ -351,14 +351,6 @@ void GstGenericPlayer::attachSource(const std::unique_ptrenqueueTask(m_taskFactory->createRemoveSource(m_context, *this, mediaSourceType)); - } -} - void GstGenericPlayer::allSourcesAttached() { if (m_workerThread) @@ -1131,7 +1123,7 @@ void GstGenericPlayer::scheduleAudioUnderflow() { if (m_workerThread) { - bool underflowEnabled = m_context.isPlaying && !m_context.audioSourceRemoved; + bool underflowEnabled = m_context.isPlaying; m_workerThread->enqueueTask( m_taskFactory->createUnderflow(m_context, *this, underflowEnabled, MediaSourceType::AUDIO)); } diff --git a/media/server/gstplayer/source/tasks/generic/AttachSource.cpp b/media/server/gstplayer/source/tasks/generic/AttachSource.cpp index f65daa212..86d3e53b8 100644 --- a/media/server/gstplayer/source/tasks/generic/AttachSource.cpp +++ b/media/server/gstplayer/source/tasks/generic/AttachSource.cpp @@ -59,10 +59,6 @@ void AttachSource::execute() const { addSource(); } - else if (m_attachedSource->getType() == MediaSourceType::AUDIO && m_context.audioSourceRemoved) - { - reattachAudioSource(); - } else { RIALTO_SERVER_LOG_ERROR("cannot update caps"); @@ -110,22 +106,4 @@ void AttachSource::addSource() const if (caps) m_gstWrapper->gstCapsUnref(caps); } - -void AttachSource::reattachAudioSource() const -{ - if (!m_player.reattachSource(m_attachedSource)) - { - RIALTO_SERVER_LOG_ERROR("Reattaching source failed!"); - return; - } - - // Restart audio sink - m_player.setPlaybinFlags(true); - - m_context.streamInfo[m_attachedSource->getType()].isDataNeeded = true; - m_context.audioSourceRemoved = false; - m_player.notifyNeedMediaData(MediaSourceType::AUDIO); - - RIALTO_SERVER_LOG_MIL("Audio source reattached"); -} } // namespace firebolt::rialto::server::tasks::generic diff --git a/media/server/gstplayer/source/tasks/generic/CheckAudioUnderflow.cpp b/media/server/gstplayer/source/tasks/generic/CheckAudioUnderflow.cpp index c027c69a2..6cbeec528 100644 --- a/media/server/gstplayer/source/tasks/generic/CheckAudioUnderflow.cpp +++ b/media/server/gstplayer/source/tasks/generic/CheckAudioUnderflow.cpp @@ -55,7 +55,7 @@ void CheckAudioUnderflow::execute() const { RIALTO_SERVER_LOG_WARN("Audio stream underflow! Position %" PRIu64 ", lastAudioSampleTimestamps: %" PRIu64, position, m_context.lastAudioSampleTimestamps); - bool underflowEnabled = m_context.isPlaying && !m_context.audioSourceRemoved; + bool underflowEnabled = m_context.isPlaying; Underflow task(m_context, m_player, m_gstPlayerClient, underflowEnabled, MediaSourceType::AUDIO); task.execute(); } diff --git a/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp b/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp index 4cf83e0df..318f04379 100644 --- a/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp +++ b/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp @@ -33,7 +33,6 @@ #include "tasks/generic/Play.h" #include "tasks/generic/ProcessAudioGap.h" #include "tasks/generic/ReadShmDataAndAttachSamples.h" -#include "tasks/generic/RemoveSource.h" #include "tasks/generic/RenderFrame.h" #include "tasks/generic/ReportPosition.h" #include "tasks/generic/SetBufferingLimit.h" @@ -147,13 +146,6 @@ std::unique_ptr GenericPlayerTaskFactory::createReadShmDataAndAttac return std::make_unique(context, m_gstWrapper, player, dataReader); } -std::unique_ptr -GenericPlayerTaskFactory::createRemoveSource(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, - const firebolt::rialto::MediaSourceType &type) const -{ - return std::make_unique(context, player, m_client, m_gstWrapper, type); -} - std::unique_ptr GenericPlayerTaskFactory::createReportPosition(GenericPlayerContext &context, IGstGenericPlayerPrivate &player) const { diff --git a/media/server/gstplayer/source/tasks/generic/NeedData.cpp b/media/server/gstplayer/source/tasks/generic/NeedData.cpp index ca6c1865e..acf7417c5 100644 --- a/media/server/gstplayer/source/tasks/generic/NeedData.cpp +++ b/media/server/gstplayer/source/tasks/generic/NeedData.cpp @@ -59,11 +59,6 @@ void NeedData::execute() const if (m_gstPlayerClient && !elem.second.isNeedDataPending) { - if (sourceType == MediaSourceType::AUDIO && m_context.audioSourceRemoved) - { - RIALTO_SERVER_LOG_DEBUG("Audio source is removed, no need to request data"); - break; - } elem.second.isNeedDataPending = m_gstPlayerClient->notifyNeedMediaData(sourceType); } break; diff --git a/media/server/gstplayer/source/tasks/generic/RemoveSource.cpp b/media/server/gstplayer/source/tasks/generic/RemoveSource.cpp deleted file mode 100644 index 29127c08c..000000000 --- a/media/server/gstplayer/source/tasks/generic/RemoveSource.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2023 Sky UK - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "tasks/generic/RemoveSource.h" -#include "RialtoServerLogging.h" -#include "TypeConverters.h" - -namespace firebolt::rialto::server::tasks::generic -{ -RemoveSource::RemoveSource(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, - IGstGenericPlayerClient *client, - std::shared_ptr gstWrapper, - const MediaSourceType &type) - : m_context{context}, m_player{player}, m_gstPlayerClient{client}, m_gstWrapper{gstWrapper}, m_type{type} -{ - RIALTO_SERVER_LOG_DEBUG("Constructing RemoveSource"); -} - -RemoveSource::~RemoveSource() -{ - RIALTO_SERVER_LOG_DEBUG("RemoveSource finished"); -} - -void RemoveSource::execute() const -{ - RIALTO_SERVER_LOG_DEBUG("Executing RemoveSource for %s source", common::convertMediaSourceType(m_type)); - if (MediaSourceType::AUDIO != m_type) - { - RIALTO_SERVER_LOG_DEBUG("RemoveSource not supported for type != AUDIO"); - return; - } - m_context.audioSourceRemoved = true; - m_gstPlayerClient->invalidateActiveRequests(m_type); - GstElement *source{nullptr}; - auto sourceElem = m_context.streamInfo.find(m_type); - if (sourceElem != m_context.streamInfo.end()) - { - source = sourceElem->second.appSrc; - } - if (!source) - { - RIALTO_SERVER_LOG_WARN("failed to flush - source is NULL"); - return; - } - sourceElem->second.buffers.clear(); - sourceElem->second.isDataNeeded = false; - sourceElem->second.isNeedDataPending = false; - m_player.stopPositionReportingAndCheckAudioUnderflowTimer(); - GstEvent *flushStart = m_gstWrapper->gstEventNewFlushStart(); - if (!m_gstWrapper->gstElementSendEvent(source, flushStart)) - { - RIALTO_SERVER_LOG_WARN("failed to send flush-start event"); - } - GstEvent *flushStop = m_gstWrapper->gstEventNewFlushStop(FALSE); - if (!m_gstWrapper->gstElementSendEvent(source, flushStop)) - { - RIALTO_SERVER_LOG_WARN("failed to send flush-stop event"); - } - - // Turn audio off, removing audio sink from playsink - m_player.setPlaybinFlags(false); - - RIALTO_SERVER_LOG_MIL("%s source removed", common::convertMediaSourceType(m_type)); -} -} // namespace firebolt::rialto::server::tasks::generic diff --git a/media/server/main/source/MediaPipelineServerInternal.cpp b/media/server/main/source/MediaPipelineServerInternal.cpp index 660753f03..3cd73fdfd 100644 --- a/media/server/main/source/MediaPipelineServerInternal.cpp +++ b/media/server/main/source/MediaPipelineServerInternal.cpp @@ -299,7 +299,6 @@ bool MediaPipelineServerInternal::removeSourceInternal(int32_t id) return false; } - m_gstPlayer->removeSource(sourceIter->first); m_needMediaDataTimers.erase(sourceIter->first); m_attachedSources.erase(sourceIter); return true; diff --git a/tests/componenttests/server/fixtures/MediaPipelineTest.cpp b/tests/componenttests/server/fixtures/MediaPipelineTest.cpp index bcedd3457..34fd4738a 100644 --- a/tests/componenttests/server/fixtures/MediaPipelineTest.cpp +++ b/tests/componenttests/server/fixtures/MediaPipelineTest.cpp @@ -405,25 +405,6 @@ void MediaPipelineTest::willEos(GstAppSrc *appSrc) EXPECT_CALL(*m_gstWrapperMock, gstAppSrcEndOfStream(appSrc)).WillOnce(Return(GST_FLOW_OK)); } -void MediaPipelineTest::willRemoveAudioSource() -{ - EXPECT_CALL(*m_gstWrapperMock, gstEventNewFlushStart()).WillOnce(Return(&m_flushStartEvent)); - EXPECT_CALL(*m_gstWrapperMock, gstElementSendEvent(GST_ELEMENT(&m_audioAppSrc), &m_flushStartEvent)) - .WillOnce(Return(TRUE)); - EXPECT_CALL(*m_gstWrapperMock, gstEventNewFlushStop(0)).WillOnce(Return(&m_flushStopEvent)); - EXPECT_CALL(*m_gstWrapperMock, gstElementSendEvent(GST_ELEMENT(&m_audioAppSrc), &m_flushStopEvent)) - .WillOnce(Return(TRUE)); - - EXPECT_CALL(*m_glibWrapperMock, gTypeFromName(StrEq("GstPlayFlags"))).Times(3).WillRepeatedly(Return(kGstPlayFlagsType)); - EXPECT_CALL(*m_glibWrapperMock, gTypeClassRef(kGstPlayFlagsType)).Times(3).WillRepeatedly(Return(&m_flagsClass)); - EXPECT_CALL(*m_glibWrapperMock, gFlagsGetValueByNick(&m_flagsClass, StrEq("video"))).WillOnce(Return(&m_videoFlag)); - EXPECT_CALL(*m_glibWrapperMock, gFlagsGetValueByNick(&m_flagsClass, StrEq("native-video"))) - .WillOnce(Return(&m_nativeVideoFlag)); - EXPECT_CALL(*m_glibWrapperMock, gFlagsGetValueByNick(&m_flagsClass, StrEq("text"))).WillOnce(Return(&m_subtitleFlag)); - EXPECT_CALL(*m_glibWrapperMock, gObjectSetStub(&m_pipeline, StrEq("flags"))) - .WillOnce(Invoke(this, &MediaPipelineTest::workerFinished)); -} - void MediaPipelineTest::willStop() { EXPECT_CALL(*m_gstWrapperMock, gstElementSetState(&m_pipeline, GST_STATE_NULL)) @@ -436,19 +417,6 @@ void MediaPipelineTest::willStop() EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(&m_bus)); } -void MediaPipelineTest::willSetAudioAndVideoFlags() -{ - EXPECT_CALL(*m_glibWrapperMock, gTypeFromName(StrEq("GstPlayFlags"))).Times(4).WillRepeatedly(Return(kGstPlayFlagsType)); - EXPECT_CALL(*m_glibWrapperMock, gTypeClassRef(kGstPlayFlagsType)).Times(4).WillRepeatedly(Return(&m_flagsClass)); - EXPECT_CALL(*m_gstWrapperMock, gstElementFactoryFind(StrEq("brcmaudiosink"))).WillOnce(Return(nullptr)); - EXPECT_CALL(*m_glibWrapperMock, gFlagsGetValueByNick(&m_flagsClass, StrEq("audio"))).WillOnce(Return(&m_audioFlag)); - EXPECT_CALL(*m_glibWrapperMock, gFlagsGetValueByNick(&m_flagsClass, StrEq("video"))).WillOnce(Return(&m_videoFlag)); - EXPECT_CALL(*m_glibWrapperMock, gFlagsGetValueByNick(&m_flagsClass, StrEq("native-video"))) - .WillOnce(Return(&m_nativeVideoFlag)); - EXPECT_CALL(*m_glibWrapperMock, gFlagsGetValueByNick(&m_flagsClass, StrEq("text"))).WillOnce(Return(&m_subtitleFlag)); - EXPECT_CALL(*m_glibWrapperMock, gObjectSetStub(&m_pipeline, StrEq("flags"))); -} - void MediaPipelineTest::createSession() { // Use matchResponse to store session id @@ -819,10 +787,6 @@ void MediaPipelineTest::removeSource(int sourceId) { auto removeSourceReq{createRemoveSourceRequest(m_sessionId, sourceId)}; ConfigureAction(m_clientStub).send(removeSourceReq).expectSuccess(); - - // Sources other than audio do not do anything for RemoveSource - if (m_audioSourceId == sourceId) - waitWorker(); } void MediaPipelineTest::stop() diff --git a/tests/componenttests/server/fixtures/MediaPipelineTest.h b/tests/componenttests/server/fixtures/MediaPipelineTest.h index dcf76b19c..b87393ee3 100644 --- a/tests/componenttests/server/fixtures/MediaPipelineTest.h +++ b/tests/componenttests/server/fixtures/MediaPipelineTest.h @@ -57,9 +57,7 @@ class MediaPipelineTest : public RialtoServerComponentTest void willNotifyPaused(); void willPlay(); void willEos(GstAppSrc *appSrc); - void willRemoveAudioSource(); void willStop(); - void willSetAudioAndVideoFlags(); void willSetStateInvalidForQueryPosition(); void createSession(); diff --git a/tests/componenttests/server/tests/CMakeLists.txt b/tests/componenttests/server/tests/CMakeLists.txt index 9fe1d15bb..72a5a04e7 100644 --- a/tests/componenttests/server/tests/CMakeLists.txt +++ b/tests/componenttests/server/tests/CMakeLists.txt @@ -56,13 +56,13 @@ add_gtests ( mediaPipeline/PositionUpdatesTest.cpp mediaPipeline/ProcessAudioGapTest.cpp mediaPipeline/QosUpdatesTest.cpp - mediaPipeline/RemoveAudioPlaybackTest.cpp mediaPipeline/RenderFrameTest.cpp mediaPipeline/SetPlaybackRateTest.cpp mediaPipeline/SetPositionTest.cpp mediaPipeline/SetSourcePositionTest.cpp mediaPipeline/SetVideoWindowTest.cpp mediaPipeline/SourceTest.cpp + mediaPipeline/SwitchAudioPlaybackTest.cpp mediaPipeline/UnderflowTest.cpp mediaPipeline/VolumeTest.cpp mediaPipeline/WriteSegmentsTest.cpp diff --git a/tests/componenttests/server/tests/mediaPipeline/AudioOnlyPlaybackTest.cpp b/tests/componenttests/server/tests/mediaPipeline/AudioOnlyPlaybackTest.cpp index a53f69159..67bcf5283 100644 --- a/tests/componenttests/server/tests/mediaPipeline/AudioOnlyPlaybackTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/AudioOnlyPlaybackTest.cpp @@ -185,7 +185,6 @@ TEST_F(MediaPipelineTest, AudioOnlyPlayback) gstNotifyEos(); // Step 12: Remove source - willRemoveAudioSource(); removeSource(m_audioSourceId); // Step 13: Stop diff --git a/tests/componenttests/server/tests/mediaPipeline/AudioSourceSwitchTest.cpp b/tests/componenttests/server/tests/mediaPipeline/AudioSourceSwitchTest.cpp index b9710bbf1..b2e694ecd 100644 --- a/tests/componenttests/server/tests/mediaPipeline/AudioSourceSwitchTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/AudioSourceSwitchTest.cpp @@ -179,7 +179,6 @@ TEST_F(AudioSourceSwitchTest, SwitchAudioSource) switchAudioSource(); // Step 6: Remove sources - willRemoveAudioSource(); removeSource(m_audioSourceId); removeSource(m_videoSourceId); diff --git a/tests/componenttests/server/tests/mediaPipeline/DualVideoPlaybackTest.cpp b/tests/componenttests/server/tests/mediaPipeline/DualVideoPlaybackTest.cpp index 7daa9fad3..ec861b42b 100644 --- a/tests/componenttests/server/tests/mediaPipeline/DualVideoPlaybackTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/DualVideoPlaybackTest.cpp @@ -731,7 +731,6 @@ TEST_F(DualVideoPlaybackTest, playbackFullDualVideo) destroySecondarySession(); // Step 16: Terminate the primary media session - willRemoveAudioSource(); removeSource(m_audioSourceId); removeSource(m_videoSourceId); willStop(); @@ -984,7 +983,6 @@ TEST_F(DualVideoPlaybackTest, playbackNoResouceManagerSecondaryVideo) destroySecondarySession(); // Step 16: Terminate the primary media session - willRemoveAudioSource(); removeSource(m_audioSourceId); removeSource(m_videoSourceId); willStop(); diff --git a/tests/componenttests/server/tests/mediaPipeline/EncryptedPlaybackTest.cpp b/tests/componenttests/server/tests/mediaPipeline/EncryptedPlaybackTest.cpp index 89d8f9fae..c4b7399ea 100644 --- a/tests/componenttests/server/tests/mediaPipeline/EncryptedPlaybackTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/EncryptedPlaybackTest.cpp @@ -331,7 +331,6 @@ TEST_F(EncryptedPlaybackTest, EncryptedPlayback) // Step 14: Notify end of stream gstNotifyEos(); - willRemoveAudioSource(); // Step 15: Remove sources removeSource(m_audioSourceId); diff --git a/tests/componenttests/server/tests/mediaPipeline/FlushTest.cpp b/tests/componenttests/server/tests/mediaPipeline/FlushTest.cpp index 83e1ced28..132aebe3a 100644 --- a/tests/componenttests/server/tests/mediaPipeline/FlushTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/FlushTest.cpp @@ -294,7 +294,6 @@ TEST_F(FlushTest, DISABLED_flushAudioSourceSuccess) gstNotifyEos(); // Step 13: Remove sources - willRemoveAudioSource(); removeSource(m_audioSourceId); removeSource(m_videoSourceId); diff --git a/tests/componenttests/server/tests/mediaPipeline/HaveDataFailureTest.cpp b/tests/componenttests/server/tests/mediaPipeline/HaveDataFailureTest.cpp index 3b2cbdf7b..601c13bdd 100644 --- a/tests/componenttests/server/tests/mediaPipeline/HaveDataFailureTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/HaveDataFailureTest.cpp @@ -168,7 +168,6 @@ TEST_F(HaveDataFailureTest, HaveDataError) failHaveData(m_lastVideoNeedData); // Step 14: Remove sources - willRemoveAudioSource(); removeSource(m_audioSourceId); removeSource(m_videoSourceId); diff --git a/tests/componenttests/server/tests/mediaPipeline/MuteTest.cpp b/tests/componenttests/server/tests/mediaPipeline/MuteTest.cpp index 4f6bebdf6..4f155cd90 100644 --- a/tests/componenttests/server/tests/mediaPipeline/MuteTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/MuteTest.cpp @@ -159,7 +159,6 @@ TEST_F(MuteTest, Mute) getMute(); // Step 7: Remove sources - willRemoveAudioSource(); removeSource(m_audioSourceId); removeSource(m_videoSourceId); diff --git a/tests/componenttests/server/tests/mediaPipeline/NonFatalPlayerErrorUpdatesTest.cpp b/tests/componenttests/server/tests/mediaPipeline/NonFatalPlayerErrorUpdatesTest.cpp index 233c27409..f448344f4 100644 --- a/tests/componenttests/server/tests/mediaPipeline/NonFatalPlayerErrorUpdatesTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/NonFatalPlayerErrorUpdatesTest.cpp @@ -286,7 +286,6 @@ TEST_F(NonFatalPlayerErrorUpdatesTest, warningMessage) // Step 15: Notify end of stream gstNotifyEos(); - willRemoveAudioSource(); // Step 16: Remove sources removeSource(m_audioSourceId); diff --git a/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp b/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp index dc42d23ae..2d11ee4f6 100644 --- a/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp @@ -580,7 +580,6 @@ TEST_F(PipelinePropertyTest, pipelinePropertyGetAndSetSuccess) getUseBuffering(); // Step 16: Remove sources - willRemoveAudioSource(); removeSource(m_audioSourceId); removeSource(m_videoSourceId); @@ -786,7 +785,6 @@ TEST_F(PipelinePropertyTest, pipelinePropertyGetAndSetFailures) setUseBufferingFailure(); // Step 16: Remove sources - willRemoveAudioSource(); removeSource(m_audioSourceId); removeSource(m_videoSourceId); diff --git a/tests/componenttests/server/tests/mediaPipeline/PlaybackTest.cpp b/tests/componenttests/server/tests/mediaPipeline/PlaybackTest.cpp index c82a0ac8a..e4f9bceb6 100644 --- a/tests/componenttests/server/tests/mediaPipeline/PlaybackTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/PlaybackTest.cpp @@ -211,7 +211,6 @@ TEST_F(MediaPipelineTest, playback) // Step 14: Notify end of stream gstNotifyEos(); - willRemoveAudioSource(); // Step 15: Remove sources removeSource(m_audioSourceId); diff --git a/tests/componenttests/server/tests/mediaPipeline/PositionUpdatesTest.cpp b/tests/componenttests/server/tests/mediaPipeline/PositionUpdatesTest.cpp index e683a603b..906278f5a 100644 --- a/tests/componenttests/server/tests/mediaPipeline/PositionUpdatesTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/PositionUpdatesTest.cpp @@ -244,7 +244,6 @@ TEST_F(PositionUpdatesTest, PositionUpdate) gstNotifyEos(); // Step 15: Remove sources - willRemoveAudioSource(); removeSource(m_audioSourceId); removeSource(m_videoSourceId); @@ -442,7 +441,6 @@ TEST_F(PositionUpdatesTest, GetPositionSuccess) // Step 18: Remove sources // Step 19: Remove sources - willRemoveAudioSource(); removeSource(m_audioSourceId); removeSource(m_videoSourceId); @@ -549,7 +547,6 @@ TEST_F(PositionUpdatesTest, getPositionFailure) getPositionFailure(); // Step 7: Remove sources - willRemoveAudioSource(); removeSource(m_audioSourceId); removeSource(m_videoSourceId); diff --git a/tests/componenttests/server/tests/mediaPipeline/QosUpdatesTest.cpp b/tests/componenttests/server/tests/mediaPipeline/QosUpdatesTest.cpp index eefd6a982..f3503df5a 100644 --- a/tests/componenttests/server/tests/mediaPipeline/QosUpdatesTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/QosUpdatesTest.cpp @@ -342,7 +342,6 @@ TEST_F(QosUpdatesTest, QosUpdates) // Step 14: Notify end of stream gstNotifyEos(); - willRemoveAudioSource(); // Step 15: Remove sources removeSource(m_audioSourceId); @@ -526,7 +525,6 @@ TEST_F(QosUpdatesTest, StatsFailure) // Step 12: Notify end of stream gstNotifyEos(); - willRemoveAudioSource(); // Step 13: Remove sources removeSource(m_audioSourceId); diff --git a/tests/componenttests/server/tests/mediaPipeline/RenderFrameTest.cpp b/tests/componenttests/server/tests/mediaPipeline/RenderFrameTest.cpp index 4c73eeafe..9a8987e05 100644 --- a/tests/componenttests/server/tests/mediaPipeline/RenderFrameTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/RenderFrameTest.cpp @@ -189,7 +189,6 @@ TEST_F(RenderFrameTest, RenderFrameSuccess) renderFrame(); // Step 6: Remove sources - willRemoveAudioSource(); removeSource(m_audioSourceId); removeSource(m_videoSourceId); @@ -303,7 +302,6 @@ TEST_F(RenderFrameTest, renderFrameFailure) renderFrameFailure(); // Step 6: Remove sources - willRemoveAudioSource(); removeSource(m_audioSourceId); removeSource(m_videoSourceId); diff --git a/tests/componenttests/server/tests/mediaPipeline/SetPositionTest.cpp b/tests/componenttests/server/tests/mediaPipeline/SetPositionTest.cpp index a93b0d624..6256fb8e0 100644 --- a/tests/componenttests/server/tests/mediaPipeline/SetPositionTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/SetPositionTest.cpp @@ -311,7 +311,6 @@ TEST_F(SetPositionTest, SetPosition) gstNotifyEos(); // Step 14: Remove sources - willRemoveAudioSource(); removeSource(m_audioSourceId); removeSource(m_videoSourceId); @@ -463,7 +462,6 @@ TEST_F(SetPositionTest, SetPositionFailure) SetPositionFailure(); // Step 9: Remove sources - willRemoveAudioSource(); removeSource(m_audioSourceId); removeSource(m_videoSourceId); diff --git a/tests/componenttests/server/tests/mediaPipeline/SetSourcePositionTest.cpp b/tests/componenttests/server/tests/mediaPipeline/SetSourcePositionTest.cpp index ca5f2c3b0..96f36b0a8 100644 --- a/tests/componenttests/server/tests/mediaPipeline/SetSourcePositionTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/SetSourcePositionTest.cpp @@ -227,7 +227,6 @@ TEST_F(SetSourcePositionTest, setSourcePositionSuccess) gstNotifyEos(); // Step 13: Remove sources - willRemoveAudioSource(); removeSource(m_audioSourceId); removeSource(m_videoSourceId); diff --git a/tests/componenttests/server/tests/mediaPipeline/SetVideoWindowTest.cpp b/tests/componenttests/server/tests/mediaPipeline/SetVideoWindowTest.cpp index 326842175..66e66bf1e 100644 --- a/tests/componenttests/server/tests/mediaPipeline/SetVideoWindowTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/SetVideoWindowTest.cpp @@ -173,7 +173,6 @@ TEST_F(SetVideoWindowTest, SetVideoWindow) setVideoWindow(); // Step 6: Remove sources - willRemoveAudioSource(); removeSource(m_audioSourceId); removeSource(m_videoSourceId); diff --git a/tests/componenttests/server/tests/mediaPipeline/SourceTest.cpp b/tests/componenttests/server/tests/mediaPipeline/SourceTest.cpp index dfa103706..a956bed92 100644 --- a/tests/componenttests/server/tests/mediaPipeline/SourceTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/SourceTest.cpp @@ -99,7 +99,6 @@ TEST_F(MediaPipelineTest, shouldAttachAudioSourceOnly) indicateAllSourcesAttached({&m_audioAppSrc}); // Step 4: Remove sources - willRemoveAudioSource(); removeSource(m_audioSourceId); // Step 5: Stop @@ -196,7 +195,6 @@ TEST_F(MediaPipelineTest, shouldAttachBothSources) indicateAllSourcesAttached({&m_audioAppSrc, &m_videoAppSrc}); // Step 4: Remove sources - willRemoveAudioSource(); removeSource(m_audioSourceId); removeSource(m_videoSourceId); diff --git a/tests/componenttests/server/tests/mediaPipeline/RemoveAudioPlaybackTest.cpp b/tests/componenttests/server/tests/mediaPipeline/SwitchAudioPlaybackTest.cpp similarity index 57% rename from tests/componenttests/server/tests/mediaPipeline/RemoveAudioPlaybackTest.cpp rename to tests/componenttests/server/tests/mediaPipeline/SwitchAudioPlaybackTest.cpp index f5d613c95..1959300c1 100644 --- a/tests/componenttests/server/tests/mediaPipeline/RemoveAudioPlaybackTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/SwitchAudioPlaybackTest.cpp @@ -17,71 +17,110 @@ * limitations under the License. */ +#include "ActionTraits.h" +#include "ConfigureAction.h" #include "Constants.h" #include "ExpectMessage.h" #include "Matchers.h" #include "MediaPipelineTest.h" +#include "MessageBuilders.h" using testing::_; +using testing::Invoke; using testing::Return; using testing::StrEq; namespace { constexpr int kFramesToPush{3}; +constexpr bool kResetTime{false}; +constexpr bool kAsync{true}; } // namespace namespace firebolt::rialto::server::ct { -class RemoveAudioPlaybackTest : public MediaPipelineTest +class SwitchAudioPlaybackTest : public MediaPipelineTest { public: - RemoveAudioPlaybackTest() = default; - ~RemoveAudioPlaybackTest() = default; + SwitchAudioPlaybackTest() = default; + ~SwitchAudioPlaybackTest() = default; - void willReattachAudioSource() + void willFlushAudioSource() { - EXPECT_CALL(*m_gstWrapperMock, gstCapsNewEmptySimple(StrEq("audio/mpeg"))).WillOnce(Return(&m_audioCaps)); + EXPECT_CALL(*m_gstWrapperMock, gstEventNewFlushStart()).WillOnce(Return(&m_flushStartEvent)); + EXPECT_CALL(*m_gstWrapperMock, gstElementSendEvent(GST_ELEMENT(&m_audioAppSrc), &m_flushStartEvent)) + .WillOnce(Return(true)); + EXPECT_CALL(*m_gstWrapperMock, gstEventNewFlushStop(kResetTime)).WillOnce(Return(&m_flushStopEvent)); + EXPECT_CALL(*m_gstWrapperMock, gstElementSendEvent(GST_ELEMENT(&m_audioAppSrc), &m_flushStopEvent)) + .WillOnce(Return(true)); + } + + void flushAudioSource() + { + // After successful Flush procedure, SourceFlushedEvent is sent. + ExpectMessage expectedSourceFlushed{m_clientStub}; + expectedSourceFlushed.setFilter([&](const SourceFlushedEvent &event) + { return event.source_id() == m_audioSourceId; }); + + // After successful Flush, NeedData for source is sent. + ExpectMessage expectedAudioNeedData{m_clientStub}; + expectedAudioNeedData.setFilter([&](const NeedMediaDataEvent &event) + { return event.source_id() == m_audioSourceId; }); + + // Send FlushRequest and expect success + auto request{createFlushRequest(m_sessionId, m_audioSourceId, kResetTime)}; + ConfigureAction(m_clientStub) + .send(request) + .expectSuccess() + .matchResponse([&](const FlushResponse &response) { EXPECT_EQ(kAsync, response.async()); }); + + // Check received SourceFlushedEvent events + auto receivedSourceFlushed{expectedSourceFlushed.getMessage()}; + ASSERT_TRUE(receivedSourceFlushed); + EXPECT_EQ(receivedSourceFlushed->session_id(), m_sessionId); + EXPECT_EQ(receivedSourceFlushed->source_id(), m_audioSourceId); + + // Check received NeedDataReqs + auto receivedAudioNeedData{expectedAudioNeedData.getMessage()}; + ASSERT_TRUE(receivedAudioNeedData); + EXPECT_EQ(receivedAudioNeedData->session_id(), m_sessionId); + EXPECT_EQ(receivedAudioNeedData->source_id(), m_audioSourceId); + m_lastAudioNeedData = receivedAudioNeedData; + } + + void willSwitchAudioSource() + { + EXPECT_CALL(*m_gstWrapperMock, gstCapsNewEmptySimple(StrEq("audio/mpeg"))).WillOnce(Return(&m_newCaps)); EXPECT_CALL(*m_gstWrapperMock, - gstCapsSetSimpleStringStub(&m_audioCaps, StrEq("alignment"), G_TYPE_STRING, StrEq("nal"))); + gstCapsSetSimpleStringStub(&m_newCaps, StrEq("alignment"), G_TYPE_STRING, StrEq("nal"))); EXPECT_CALL(*m_gstWrapperMock, - gstCapsSetSimpleStringStub(&m_audioCaps, StrEq("stream-format"), G_TYPE_STRING, StrEq("raw"))); - EXPECT_CALL(*m_gstWrapperMock, gstCapsSetSimpleIntStub(&m_audioCaps, StrEq("mpegversion"), G_TYPE_INT, 4)); + gstCapsSetSimpleStringStub(&m_newCaps, StrEq("stream-format"), G_TYPE_STRING, StrEq("raw"))); + EXPECT_CALL(*m_gstWrapperMock, gstCapsSetSimpleIntStub(&m_newCaps, StrEq("mpegversion"), G_TYPE_INT, 4)); EXPECT_CALL(*m_gstWrapperMock, - gstCapsSetSimpleIntStub(&m_audioCaps, StrEq("channels"), G_TYPE_INT, kNumOfChannels)); - EXPECT_CALL(*m_gstWrapperMock, gstCapsSetSimpleIntStub(&m_audioCaps, StrEq("rate"), G_TYPE_INT, kSampleRate)); - EXPECT_CALL(*m_gstWrapperMock, gstAppSrcGetCaps(&m_audioAppSrc)).WillOnce(Return(&m_oldCaps)); - EXPECT_CALL(*m_gstWrapperMock, gstCapsIsEqual(&m_audioCaps, &m_oldCaps)).WillOnce(Return(TRUE)); - EXPECT_CALL(*m_gstWrapperMock, gstCapsUnref(&m_oldCaps)); + gstCapsSetSimpleIntStub(&m_newCaps, StrEq("channels"), G_TYPE_INT, kNumOfChannels)); + EXPECT_CALL(*m_gstWrapperMock, gstCapsSetSimpleIntStub(&m_newCaps, StrEq("rate"), G_TYPE_INT, kSampleRate)); + EXPECT_CALL(*m_gstWrapperMock, gstAppSrcGetCaps(&m_audioAppSrc)).WillOnce(Return(&m_audioCaps)); + EXPECT_CALL(*m_gstWrapperMock, gstCapsIsEqual(&m_newCaps, &m_audioCaps)).WillOnce(Return(true)); + EXPECT_CALL(*m_gstWrapperMock, gstCapsUnref(&m_newCaps)); EXPECT_CALL(*m_gstWrapperMock, gstCapsUnref(&m_audioCaps)).WillOnce(Invoke(this, &MediaPipelineTest::workerFinished)); - - willSetAudioAndVideoFlags(); } - void reattachAudioSource() + void switchAudioSource() { - ExpectMessage expectedNeedData{m_clientStub}; - - attachAudioSource(); - - auto receivedNeedData{expectedNeedData.getMessage()}; - ASSERT_TRUE(receivedNeedData); - EXPECT_EQ(receivedNeedData->session_id(), m_sessionId); - EXPECT_EQ(receivedNeedData->source_id(), m_audioSourceId); - EXPECT_EQ(receivedNeedData->frame_count(), 24); - m_lastAudioNeedData = receivedNeedData; + auto attachAudioSourceReq{createAttachAudioSourceRequest(m_sessionId)}; + attachAudioSourceReq.set_switch_source(true); + ConfigureAction(m_clientStub).send(attachAudioSourceReq).expectSuccess(); + waitWorker(); } private: - GstCaps m_oldCaps{}; - gchar m_oldCapsStr{}; + GstCaps m_newCaps{}; }; /* - * Component Test: Playback content when audio source has been removed and reattached. + * Component Test: Playback content when audio source has been switched. * Test Objective: - * Test that video only playback can continue if the audio source is removed, and that audio can be restarted - * when it is reattached. + * Test that audio source can be switched mid playback and that video playback is unaffected. * * Sequence Diagrams: * Rialto Dynamic Audio Stream Switching @@ -138,49 +177,35 @@ class RemoveAudioPlaybackTest : public MediaPipelineTest * Expect that gstreamer pipeline is paused. * Expect that server notifies the client that the Network state has changed to PAUSED. * - * Step 8: Remove Audio Source - * Remove the audio source. - * Expect that audio source is removed. + * Step 8: Flush Audio Source + * Flush the audio source. + * Expect that audio source is flushed. * - * Step 9: Write video frames - * Write video frames. + * Step 9: Switch Audio Source + * Switch the audio source. + * Expect that audio source is switched. * - * Step 10: Play - * Play the content. - * Expect that gstreamer pipeline is playing. - * Expect that server notifies the client that the Network state has changed to PLAYING. - * - * Step 11: Pause - * Pause the content. - * Expect that gstreamer pipeline is paused. - * Expect that server notifies the client that the Network state has changed to PAUSED. - * - * Step 12: Reattach audio source - * Attach the audio source again. - * Expect that reattach procedure is triggered. - * Expect that audio source is attached. - * - * Step 13: Write video and audio frames + * Step 10: Write video and audio frames * Write video frames. * Write audio frames. * - * Step 14: Play + * Step 11: Play * Play the content. * Expect that gstreamer pipeline is playing. * Expect that server notifies the client that the Network state has changed to PLAYING. * - * Step 15: Remove sources + * Step 12: Remove sources * Remove the audio source. * Expect that audio source is removed. * Remove the video source. * Expect that video source is removed. * - * Step 16: Stop + * Step 13: Stop * Stop the playback. * Expect that stop propagated to the gstreamer pipeline. * Expect that server notifies the client that the Playback state has changed to STOPPED. * - * Step 17: Destroy media session + * Step 14: Destroy media session * Send DestroySessionRequest. * Expect that the session is destroyed on the server. * @@ -194,7 +219,7 @@ class RemoveAudioPlaybackTest : public MediaPipelineTest * * Code: */ -TEST_F(RemoveAudioPlaybackTest, RemoveAudio) +TEST_F(SwitchAudioPlaybackTest, SwitchAudio) { // Step 1: Create a new media session createSession(); @@ -243,46 +268,33 @@ TEST_F(RemoveAudioPlaybackTest, RemoveAudio) pause(); willNotifyPaused(); notifyPaused(); + GST_STATE(&m_pipeline) = GST_STATE_PAUSED; - // Step 8: Remove Audio Source - willRemoveAudioSource(); - removeSource(m_audioSourceId); - - // Step 9: Write video frames - pushVideoData(kFramesToPush); - - // Step 10: Play - willPlay(); - play(); - - // Step 11: Pause - willPause(); - pause(); - willNotifyPaused(); - notifyPaused(); + // Step 8: Flush Audio Source + willFlushAudioSource(); + flushAudioSource(); - // Step 12: Reattach audio source - willReattachAudioSource(); - reattachAudioSource(); + // Step 9: Switch Audio Source + willSwitchAudioSource(); + switchAudioSource(); - // Step 13: Write video and audio frames + // Step 10: Write video and audio frames pushAudioData(kFramesToPush); pushVideoData(kFramesToPush); - // Step 14: Play + // Step 11: Play willPlay(); play(); - // Step 15: Remove sources - willRemoveAudioSource(); + // Step 12: Remove sources removeSource(m_audioSourceId); removeSource(m_videoSourceId); - // Step 16: Stop + // Step 13: Stop willStop(); stop(); - // Step 17: Destroy media session + // Step 14: Destroy media session gstPlayerWillBeDestructed(); destroySession(); } diff --git a/tests/componenttests/server/tests/mediaPipeline/UnderflowTest.cpp b/tests/componenttests/server/tests/mediaPipeline/UnderflowTest.cpp index 00eb3d6b6..ed58398c2 100644 --- a/tests/componenttests/server/tests/mediaPipeline/UnderflowTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/UnderflowTest.cpp @@ -400,7 +400,6 @@ TEST_F(UnderflowTest, underflow) // Step 15: Notify end of stream gstNotifyEos(); - willRemoveAudioSource(); // Step 16: Remove sources removeSource(m_audioSourceId); diff --git a/tests/componenttests/server/tests/mediaPipeline/VolumeTest.cpp b/tests/componenttests/server/tests/mediaPipeline/VolumeTest.cpp index 80df4d42b..d54ee116a 100644 --- a/tests/componenttests/server/tests/mediaPipeline/VolumeTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/VolumeTest.cpp @@ -253,7 +253,6 @@ TEST_F(VolumeTest, Volume) getVolume(); // Step 9: Remove sources - willRemoveAudioSource(); removeSource(m_audioSourceId); removeSource(m_videoSourceId); diff --git a/tests/componenttests/server/tests/mediaPipeline/WriteSegmentsTest.cpp b/tests/componenttests/server/tests/mediaPipeline/WriteSegmentsTest.cpp index 15b0015a1..a6cd32e64 100644 --- a/tests/componenttests/server/tests/mediaPipeline/WriteSegmentsTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/WriteSegmentsTest.cpp @@ -206,7 +206,6 @@ TEST_F(MediaPipelineTest, WriteSegments) // Step 13: Notify end of stream gstNotifyEos(); - willRemoveAudioSource(); // Step 14: Remove sources removeSource(m_audioSourceId); diff --git a/tests/unittests/media/server/gstplayer/CMakeLists.txt b/tests/unittests/media/server/gstplayer/CMakeLists.txt index 7f6edfd76..ed6635e3b 100644 --- a/tests/unittests/media/server/gstplayer/CMakeLists.txt +++ b/tests/unittests/media/server/gstplayer/CMakeLists.txt @@ -44,7 +44,6 @@ add_gtests(RialtoServerGstPlayerUnitTests genericPlayer/tasksTests/PlayTest.cpp genericPlayer/tasksTests/ProcessAudioGapTest.cpp genericPlayer/tasksTests/ReadShmDataAndAttachSamplesTest.cpp - genericPlayer/tasksTests/RemoveSourceTest.cpp genericPlayer/tasksTests/RenderFrameTest.cpp genericPlayer/tasksTests/ReportPositionTest.cpp genericPlayer/tasksTests/SetBufferingLimitTest.cpp diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp index 15da389bb..20ad4904e 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp @@ -231,12 +231,7 @@ TEST_F(GstGenericPlayerPrivateTest, shouldScheduleEnoughDataData) TEST_F(GstGenericPlayerPrivateTest, shouldScheduleAudioUnderflowWithUnderflowEnabled) { - modifyContext( - [&](GenericPlayerContext &context) - { - context.isPlaying = true; - context.audioSourceRemoved = false; - }); + modifyContext([&](GenericPlayerContext &context) { context.isPlaying = true; }); std::unique_ptr task{std::make_unique>()}; EXPECT_CALL(dynamic_cast &>(*task), execute()); @@ -248,29 +243,7 @@ TEST_F(GstGenericPlayerPrivateTest, shouldScheduleAudioUnderflowWithUnderflowEna TEST_F(GstGenericPlayerPrivateTest, shouldScheduleAudioUnderflowWithUnderflowDisabledNotPlaying) { - modifyContext( - [&](GenericPlayerContext &context) - { - context.isPlaying = false; - context.audioSourceRemoved = false; - }); - - std::unique_ptr task{std::make_unique>()}; - EXPECT_CALL(dynamic_cast &>(*task), execute()); - EXPECT_CALL(m_taskFactoryMock, createUnderflow(_, _, false, MediaSourceType::AUDIO)) - .WillOnce(Return(ByMove(std::move(task)))); - - m_sut->scheduleAudioUnderflow(); -} - -TEST_F(GstGenericPlayerPrivateTest, shouldScheduleAudioUnderflowWithUnderflowDisabledRemoveSource) -{ - modifyContext( - [&](GenericPlayerContext &context) - { - context.isPlaying = true; - context.audioSourceRemoved = true; - }); + modifyContext([&](GenericPlayerContext &context) { context.isPlaying = false; }); std::unique_ptr task{std::make_unique>()}; EXPECT_CALL(dynamic_cast &>(*task), execute()); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp index da41f2533..f633cb441 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp @@ -146,16 +146,6 @@ TEST_F(GstGenericPlayerTest, shouldAttachSource) m_sut->attachSource(source); } -TEST_F(GstGenericPlayerTest, shouldRemoveSource) -{ - std::unique_ptr task{std::make_unique>()}; - EXPECT_CALL(dynamic_cast &>(*task), execute()); - EXPECT_CALL(m_taskFactoryMock, createRemoveSource(_, _, MediaSourceType::AUDIO)) - .WillOnce(Return(ByMove(std::move(task)))); - - m_sut->removeSource(MediaSourceType::AUDIO); -} - TEST_F(GstGenericPlayerTest, shouldAllSourcesAttached) { std::unique_ptr task{std::make_unique>()}; diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp index 1a2599972..f379b5884 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp @@ -38,7 +38,6 @@ #include "tasks/generic/Play.h" #include "tasks/generic/ProcessAudioGap.h" #include "tasks/generic/ReadShmDataAndAttachSamples.h" -#include "tasks/generic/RemoveSource.h" #include "tasks/generic/RenderFrame.h" #include "tasks/generic/ReportPosition.h" #include "tasks/generic/SetBufferingLimit.h" @@ -386,24 +385,11 @@ void GenericTasksTestsBase::setContextSourceNull() testContext->m_context.source = nullptr; } -void GenericTasksTestsBase::setContextAudioSourceRemoved() -{ - testContext->m_context.audioSourceRemoved = true; -} - void GenericTasksTestsBase::setContextStreamInfoEmpty() { testContext->m_context.streamInfo.clear(); } -void GenericTasksTestsBase::setContextNeedDataAudioOnly() -{ - auto audioStreamIt{testContext->m_context.streamInfo.find(firebolt::rialto::MediaSourceType::AUDIO)}; - ASSERT_NE(testContext->m_context.streamInfo.end(), audioStreamIt); - - audioStreamIt->second.isDataNeeded = true; -} - void GenericTasksTestsBase::setContextSetupSourceFinished() { testContext->m_context.setupSourceFinished = true; @@ -1863,12 +1849,6 @@ void GenericTasksTestsBase::shouldReattachAudioSource() EXPECT_CALL(testContext->m_gstPlayer, reattachSource(_)).WillOnce(Return(true)); } -void GenericTasksTestsBase::shouldEnableAudioFlagsAndSendNeedData() -{ - EXPECT_CALL(testContext->m_gstPlayer, setPlaybinFlags(true)); - EXPECT_CALL(testContext->m_gstPlayer, notifyNeedMediaData(MediaSourceType::AUDIO)); -} - void GenericTasksTestsBase::shouldFailToReattachAudioSource() { EXPECT_CALL(testContext->m_gstPlayer, reattachSource(_)).WillOnce(Return(false)); @@ -1879,15 +1859,6 @@ void GenericTasksTestsBase::triggerReattachAudioSource() triggerAttachAudioSource(); } -void GenericTasksTestsBase::checkNewAudioSourceAttached() -{ - auto audioStreamIt{testContext->m_context.streamInfo.find(firebolt::rialto::MediaSourceType::AUDIO)}; - ASSERT_NE(testContext->m_context.streamInfo.end(), audioStreamIt); - - EXPECT_TRUE(audioStreamIt->second.isDataNeeded); - EXPECT_FALSE(testContext->m_context.audioSourceRemoved); -} - void GenericTasksTestsBase::shouldQueryPositionAndSetToZero() { EXPECT_CALL(testContext->m_gstPlayer, getPosition(NotNullMatcher())).WillOnce(Return(0)); @@ -2278,11 +2249,6 @@ void GenericTasksTestsBase::shouldStopGstPlayer() EXPECT_CALL(testContext->m_gstPlayer, changePipelineState(GST_STATE_NULL)).WillOnce(Return(GST_STATE_CHANGE_SUCCESS)); } -void GenericTasksTestsBase::shouldStopPositionReportingAndCheckAudioUnderflowTimer() -{ - EXPECT_CALL(testContext->m_gstPlayer, stopPositionReportingAndCheckAudioUnderflowTimer()); -} - void GenericTasksTestsBase::triggerStop() { firebolt::rialto::server::tasks::generic::Stop task{testContext->m_context, testContext->m_gstPlayer}; @@ -3007,44 +2973,6 @@ void GenericTasksTestsBase::triggerRenderFrame() task.execute(); } -void GenericTasksTestsBase::shouldInvalidateActiveAudioRequests() -{ - EXPECT_CALL(testContext->m_gstPlayerClient, invalidateActiveRequests(firebolt::rialto::MediaSourceType::AUDIO)); -} - -void GenericTasksTestsBase::shouldDisableAudioFlag() -{ - EXPECT_CALL(testContext->m_gstPlayer, setPlaybinFlags(false)); -} - -void GenericTasksTestsBase::triggerRemoveSourceAudio() -{ - firebolt::rialto::server::tasks::generic::RemoveSource task{testContext->m_context, testContext->m_gstPlayer, - &testContext->m_gstPlayerClient, - testContext->m_gstWrapper, - firebolt::rialto::MediaSourceType::AUDIO}; - task.execute(); -} - -void GenericTasksTestsBase::triggerRemoveSourceVideo() -{ - firebolt::rialto::server::tasks::generic::RemoveSource task{testContext->m_context, testContext->m_gstPlayer, - &testContext->m_gstPlayerClient, - testContext->m_gstWrapper, - firebolt::rialto::MediaSourceType::VIDEO}; - task.execute(); -} - -void GenericTasksTestsBase::checkAudioSourceRemoved() -{ - EXPECT_TRUE(testContext->m_context.audioSourceRemoved); -} - -void GenericTasksTestsBase::checkAudioSourceNotRemoved() -{ - EXPECT_FALSE(testContext->m_context.audioSourceRemoved); -} - void GenericTasksTestsBase::shouldFlushAudioSrcSuccess() { EXPECT_CALL(*testContext->m_gstWrapper, gstEventNewFlushStart()).WillOnce(Return(&testContext->m_event)); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h index 729f8f66c..29f64e16a 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h @@ -72,9 +72,7 @@ class GenericTasksTestsBase : public ::testing::Test void setContextVideoBuffer(); void setContextPlaybackRate(); void setContextSourceNull(); - void setContextAudioSourceRemoved(); void setContextStreamInfoEmpty(); - void setContextNeedDataAudioOnly(); void setContextSetupSourceFinished(); // SetupElement test methods @@ -190,10 +188,8 @@ class GenericTasksTestsBase : public ::testing::Test void shouldAttachVideoSourceWithDolbyVisionSource(); void triggerAttachVideoSourceWithDolbyVisionSource(); void shouldReattachAudioSource(); - void shouldEnableAudioFlagsAndSendNeedData(); void shouldFailToReattachAudioSource(); void triggerReattachAudioSource(); - void checkNewAudioSourceAttached(); void triggerFailToCastAudioSource(); void triggerFailToCastVideoSource(); void triggerFailToCastDolbyVisionSource(); @@ -400,15 +396,6 @@ class GenericTasksTestsBase : public ::testing::Test void triggerReadShmDataAndAttachSamplesVideo(); void triggerReadShmDataAndAttachSamples(); - // RemoveSource test methods - void shouldInvalidateActiveAudioRequests(); - void shouldDisableAudioFlag(); - void triggerRemoveSourceAudio(); - void triggerRemoveSourceVideo(); - void checkAudioSourceRemoved(); - void checkAudioSourceNotRemoved(); - void shouldStopPositionReportingAndCheckAudioUnderflowTimer(); - // Flush test methods void shouldFlushAudio(); void shouldFlushVideo(); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/AttachSourceTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/AttachSourceTest.cpp index 2b241b9fd..fe39e7f95 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/AttachSourceTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/AttachSourceTest.cpp @@ -167,24 +167,6 @@ TEST_F(AttachSourceTest, shouldSkipSwitchAudioSourceWhenSourceIsNotRemoved) triggerReattachAudioSource(); } -TEST_F(AttachSourceTest, shouldReattachAudioSource) -{ - setContextStreamInfo(firebolt::rialto::MediaSourceType::AUDIO); - setContextAudioSourceRemoved(); - shouldReattachAudioSource(); - shouldEnableAudioFlagsAndSendNeedData(); - triggerReattachAudioSource(); - checkNewAudioSourceAttached(); -} - -TEST_F(AttachSourceTest, shouldFailToReattachAudioSource) -{ - setContextStreamInfo(firebolt::rialto::MediaSourceType::AUDIO); - setContextAudioSourceRemoved(); - shouldFailToReattachAudioSource(); - triggerReattachAudioSource(); -} - TEST_F(AttachSourceTest, shouldFailToCastAudioSource) { triggerFailToCastAudioSource(); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp index 6bd95d4a8..70c305e22 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp @@ -45,7 +45,6 @@ #include "tasks/generic/Play.h" #include "tasks/generic/ProcessAudioGap.h" #include "tasks/generic/ReadShmDataAndAttachSamples.h" -#include "tasks/generic/RemoveSource.h" #include "tasks/generic/RenderFrame.h" #include "tasks/generic/ReportPosition.h" #include "tasks/generic/SetBufferingLimit.h" @@ -181,13 +180,6 @@ TEST_F(GenericPlayerTaskFactoryTest, ShouldCreateReadShmDataAndAttachSamples) EXPECT_NO_THROW(dynamic_cast(*task)); } -TEST_F(GenericPlayerTaskFactoryTest, ShouldCreateRemoveSource) -{ - auto task = m_sut.createRemoveSource(m_context, m_gstPlayer, firebolt::rialto::MediaSourceType::AUDIO); - EXPECT_NE(task, nullptr); - EXPECT_NO_THROW(dynamic_cast(*task)); -} - TEST_F(GenericPlayerTaskFactoryTest, ShouldCreateReportPosition) { auto task = m_sut.createReportPosition(m_context, m_gstPlayer); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/NeedDataTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/NeedDataTest.cpp index af3ccd199..46e67cd0e 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/NeedDataTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/NeedDataTest.cpp @@ -70,16 +70,6 @@ TEST_F(NeedDataTest, shouldSkipToNotifyNeedAudioDataWhenAnotherOneIsPending) checkNeedDataPendingForAudioOnly(); } -TEST_F(NeedDataTest, shouldSkipToNotifyNeedAudioDataWhenAudioSourceIsRemoved) -{ - setContextStreamInfo(firebolt::rialto::MediaSourceType::AUDIO); - setContextStreamInfo(firebolt::rialto::MediaSourceType::VIDEO); - setContextAudioSourceRemoved(); - triggerNeedDataAudio(); - checkNeedDataForAudioOnly(); - checkNoNeedDataPendingForBothSources(); -} - TEST_F(NeedDataTest, shouldNotifyNeedVideoData) { setContextStreamInfo(firebolt::rialto::MediaSourceType::AUDIO); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/RemoveSourceTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/RemoveSourceTest.cpp deleted file mode 100644 index 4884d844d..000000000 --- a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/RemoveSourceTest.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2022 Sky UK - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "GenericTasksTestsBase.h" - -class RemoveSourceTest : public GenericTasksTestsBase -{ -protected: - RemoveSourceTest() - { - setContextStreamInfo(firebolt::rialto::MediaSourceType::AUDIO); - setContextStreamInfo(firebolt::rialto::MediaSourceType::VIDEO); - setContextNeedDataAudioOnly(); - setContextAudioBuffer(); - setContextNeedDataPendingAudioOnly(true); - } -}; - -TEST_F(RemoveSourceTest, shouldRemoveAudioSourceWithoutFlushing) -{ - setContextStreamInfoEmpty(); - shouldInvalidateActiveAudioRequests(); - triggerRemoveSourceAudio(); - checkAudioSourceRemoved(); -} - -TEST_F(RemoveSourceTest, shouldNotRemoveVideoSource) -{ - triggerRemoveSourceVideo(); - checkNeedDataForAudioOnly(); - checkNeedDataPendingForAudioOnly(); - checkAudioSourceNotRemoved(); -} - -TEST_F(RemoveSourceTest, shouldRemoveAudioSource) -{ - shouldInvalidateActiveAudioRequests(); - shouldDisableAudioFlag(); - shouldFlushAudioSrcSuccess(); - shouldStopPositionReportingAndCheckAudioUnderflowTimer(); - triggerRemoveSourceAudio(); - checkNoMoreNeedData(); - checkNoNeedDataPendingForBothSources(); - checkAudioSourceRemoved(); - checkBuffersEmpty(); -} - -TEST_F(RemoveSourceTest, shouldRemoveAudioSourceFlushEventError) -{ - shouldInvalidateActiveAudioRequests(); - shouldDisableAudioFlag(); - shouldFlushAudioSrcFailure(); - shouldStopPositionReportingAndCheckAudioUnderflowTimer(); - triggerRemoveSourceAudio(); - checkNoMoreNeedData(); - checkNoNeedDataPendingForBothSources(); - checkAudioSourceRemoved(); -} diff --git a/tests/unittests/media/server/main/mediaPipeline/SourceTest.cpp b/tests/unittests/media/server/main/mediaPipeline/SourceTest.cpp index 18717e95b..56eae34ee 100644 --- a/tests/unittests/media/server/main/mediaPipeline/SourceTest.cpp +++ b/tests/unittests/media/server/main/mediaPipeline/SourceTest.cpp @@ -97,7 +97,6 @@ TEST_F(RialtoServerMediaPipelineSourceTest, RemoveSourceSuccess) std::int32_t sourceId{mediaSource->getId()}; mainThreadWillEnqueueTaskAndWait(); - EXPECT_CALL(*m_gstPlayerMock, removeSource(MediaSourceType::AUDIO)); EXPECT_EQ(m_mediaPipeline->removeSource(sourceId), true); } @@ -137,7 +136,6 @@ TEST_F(RialtoServerMediaPipelineSourceTest, AttachRemoveAttachSourceDifferentId) std::int32_t firstSourceId{mediaSource->getId()}; mainThreadWillEnqueueTaskAndWait(); - EXPECT_CALL(*m_gstPlayerMock, removeSource(MediaSourceType::AUDIO)); EXPECT_EQ(m_mediaPipeline->removeSource(firstSourceId), true); mainThreadWillEnqueueTaskAndWait(); diff --git a/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h b/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h index 12624a047..2cb7f0a35 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h @@ -62,10 +62,6 @@ class GenericPlayerTaskFactoryMock : public IGenericPlayerTaskFactory (GenericPlayerContext & context, IGstGenericPlayerPrivate &player, const std::shared_ptr &dataReader), (const, override)); - MOCK_METHOD(std::unique_ptr, createRemoveSource, - (GenericPlayerContext & context, (IGstGenericPlayerPrivate & player), - const firebolt::rialto::MediaSourceType &type), - (const, override)); MOCK_METHOD(std::unique_ptr, createReportPosition, (GenericPlayerContext & context, IGstGenericPlayerPrivate &player), (const, override)); MOCK_METHOD(std::unique_ptr, createCheckAudioUnderflow, diff --git a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h index bc2c3f07f..c38c1d9eb 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h @@ -34,7 +34,6 @@ class GstGenericPlayerMock : public IGstGenericPlayer virtual ~GstGenericPlayerMock() = default; MOCK_METHOD(void, attachSource, (const std::unique_ptr &mediaSource), (override)); - MOCK_METHOD(void, removeSource, (const MediaSourceType &mediaSourceType), (override)); MOCK_METHOD(void, allSourcesAttached, (), (override)); MOCK_METHOD(void, play, (bool &async), (override)); MOCK_METHOD(void, pause, (), (override)); diff --git a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h index ffb866ee7..e28c70d8d 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h @@ -70,7 +70,6 @@ class GstGenericPlayerPrivateMock : public IGstGenericPlayerPrivate MOCK_METHOD(void, removeAutoVideoSinkChild, (GObject * object), (override)); MOCK_METHOD(void, removeAutoAudioSinkChild, (GObject * object), (override)); MOCK_METHOD(GstElement *, getSink, (const MediaSourceType &mediaSourceType), (const, override)); - MOCK_METHOD(void, setPlaybinFlags, (bool enableAudio), (override)); MOCK_METHOD(void, addAudioClippingToBuffer, (GstBuffer * buffer, uint64_t clippingStart, uint64_t clippingEnd), (const, override)); From ef0589a35ac20118790520ba9bc267e995d32e64 Mon Sep 17 00:00:00 2001 From: Marcin Wojciechowski <105790697+skywojciechowskim@users.noreply.github.com> Date: Wed, 18 Feb 2026 14:38:53 +0100 Subject: [PATCH 07/29] Allow to switch audio codec, when audio is re-attached (#448) Summary: Allow to switch audio codec, when audio is re-attached Type: Fix Test Plan: UT/CT, Fullstack Jira: LLAMA-18057 --- .../gstplayer/include/tasks/generic/AttachSource.h | 1 + .../source/tasks/generic/AttachSource.cpp | 14 ++++++++++++++ .../genericPlayer/tasksTests/AttachSourceTest.cpp | 10 +++++++++- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/media/server/gstplayer/include/tasks/generic/AttachSource.h b/media/server/gstplayer/include/tasks/generic/AttachSource.h index f6d8144fe..0f77545d4 100644 --- a/media/server/gstplayer/include/tasks/generic/AttachSource.h +++ b/media/server/gstplayer/include/tasks/generic/AttachSource.h @@ -47,6 +47,7 @@ class AttachSource : public IPlayerTask private: void addSource() const; + void reattachAudioSource() const; GenericPlayerContext &m_context; std::shared_ptr m_gstWrapper; diff --git a/media/server/gstplayer/source/tasks/generic/AttachSource.cpp b/media/server/gstplayer/source/tasks/generic/AttachSource.cpp index 86d3e53b8..8396a4d1b 100644 --- a/media/server/gstplayer/source/tasks/generic/AttachSource.cpp +++ b/media/server/gstplayer/source/tasks/generic/AttachSource.cpp @@ -59,6 +59,10 @@ void AttachSource::execute() const { addSource(); } + else if (m_attachedSource->getType() == MediaSourceType::AUDIO) + { + reattachAudioSource(); + } else { RIALTO_SERVER_LOG_ERROR("cannot update caps"); @@ -106,4 +110,14 @@ void AttachSource::addSource() const if (caps) m_gstWrapper->gstCapsUnref(caps); } + +void AttachSource::reattachAudioSource() const +{ + if (!m_player.reattachSource(m_attachedSource)) + { + RIALTO_SERVER_LOG_ERROR("Reattaching source failed!"); + return; + } + RIALTO_SERVER_LOG_MIL("Audio source reattached"); +} } // namespace firebolt::rialto::server::tasks::generic diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/AttachSourceTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/AttachSourceTest.cpp index fe39e7f95..746827b33 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/AttachSourceTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/AttachSourceTest.cpp @@ -161,9 +161,17 @@ TEST_F(AttachSourceTest, shouldFailToAttachUnknownSource) triggerAttachUnknownSource(); } -TEST_F(AttachSourceTest, shouldSkipSwitchAudioSourceWhenSourceIsNotRemoved) +TEST_F(AttachSourceTest, shouldSwitchAudioSourceWhenSourceIsReattached) { setContextStreamInfo(firebolt::rialto::MediaSourceType::AUDIO); + shouldReattachAudioSource(); + triggerReattachAudioSource(); +} + +TEST_F(AttachSourceTest, shouldFailToSwitchAudioSourceWhenSourceIsReattached) +{ + setContextStreamInfo(firebolt::rialto::MediaSourceType::AUDIO); + shouldFailToReattachAudioSource(); triggerReattachAudioSource(); } From 100ecf2f92d1dddc4f6203346d6f47a8b0169cf0 Mon Sep 17 00:00:00 2001 From: Marcin Wojciechowski <105790697+skywojciechowskim@users.noreply.github.com> Date: Mon, 23 Feb 2026 07:26:54 +0100 Subject: [PATCH 08/29] Microsoft Playready support for Amazon Prime RDK app (#449) Summary: Microsoft Playready support for Amazon Prime RDK app Type: Feature Test Plan: UT/CT, Fullstack Jira: NO-JIRA --- media/client/ipc/include/MediaKeysIpc.h | 5 +- media/client/ipc/source/MediaKeysIpc.cpp | 25 +++++- media/client/main/include/MediaKeys.h | 5 +- media/client/main/source/MediaKeys.cpp | 9 ++- media/public/include/IMediaKeys.h | 10 ++- media/public/include/MediaCommon.h | 10 +++ .../server/gstplayer/source/GstDecryptor.cpp | 2 +- .../ipc/source/MediaKeysModuleService.cpp | 27 +++++-- media/server/main/include/IMediaKeySession.h | 15 +--- media/server/main/include/MediaKeySession.h | 31 +++---- .../main/include/MediaKeysServerInternal.h | 14 ++-- .../main/interface/IDecryptionService.h | 2 +- .../main/interface/IMediaKeysServerInternal.h | 7 -- media/server/main/source/MediaKeySession.cpp | 77 +++++++++++------- .../main/source/MediaKeysServerInternal.cpp | 24 +++--- media/server/service/include/ICdmService.h | 5 +- media/server/service/source/CdmService.cpp | 33 +++++--- media/server/service/source/CdmService.h | 10 +-- proto/mediakeysmodule.proto | 10 ++- .../matchers/MediaKeysProtoRequestMatchers.h | 5 +- tests/common/protoUtils/MediaKeysProtoUtils.h | 15 ++++ .../tests/base/MediaKeysTestMethods.cpp | 12 +-- .../server/common/MessageBuilders.cpp | 7 +- .../server/common/MessageBuilders.h | 3 +- .../tests/mediaKeys/LicenseRenewalTest.cpp | 74 ++++++++++++++--- .../server/tests/mediaKeys/MediaKeysTest.cpp | 2 - .../tests/mediaKeys/MediaKeysTestMethods.cpp | 80 +++++++++++++++++++ .../tests/mediaKeys/MediaKeysTestMethods.h | 8 ++ .../SessionReadyForDecryptionTest.cpp | 5 +- .../tests/mediaKeys/SetDrmHeaderTest.cpp | 33 +++++++- .../ipc/mediaKeysIpc/CreateKeySessionTest.cpp | 14 ++-- .../base/MediaKeysIpcTestBase.cpp | 2 +- .../client/main/mediaKeys/KeySessionTest.cpp | 13 ++- .../main/mediaKeys/NetflixKeySessionTest.cpp | 8 +- .../media/interface/mocks/MediaKeysMock.h | 7 +- .../gstplayer/decryptor/DecryptTest.cpp | 4 +- .../MediaKeysModuleServiceTestsFixture.cpp | 19 ++--- .../media/server/main/CMakeLists.txt | 2 - .../main/mediaKeySession/CallbacksTest.cpp | 2 +- .../mediaKeySession/CloseKeySessionTest.cpp | 9 +++ .../main/mediaKeySession/CreateTest.cpp | 11 +-- .../mediaKeySession/GenerateRequestTest.cpp | 62 ++++++-------- .../IsNetflixPlayreadyKeySystemTest.cpp | 56 ------------- .../main/mediaKeySession/SetDrmHeaderTest.cpp | 29 ++++++- .../mediaKeySession/UpdateSessionTest.cpp | 8 ++ .../base/MediaKeySessionTestBase.cpp | 44 +++++++++- .../base/MediaKeySessionTestBase.h | 24 ++++++ .../main/mediaKeys/CreateKeySessionTest.cpp | 10 +-- .../main/mediaKeys/GenerateRequestTest.cpp | 12 +-- .../IsNetflixPlayreadyKeySystemTest.cpp | 45 ----------- .../main/mediaKeys/base/MediaKeysTestBase.cpp | 4 +- .../server/mocks/main/DecryptionServiceMock.h | 2 +- .../mocks/main/MediaKeySessionFactoryMock.h | 2 +- .../server/mocks/main/MediaKeySessionMock.h | 4 +- .../mocks/main/MediaKeysServerInternalMock.h | 8 +- .../server/mocks/service/CdmServiceMock.h | 4 +- .../service/cdmService/CdmServiceTests.cpp | 58 ++++++-------- .../cdmService/CdmServiceTestsFixture.cpp | 39 +++++---- .../cdmService/CdmServiceTestsFixture.h | 5 +- 59 files changed, 655 insertions(+), 417 deletions(-) delete mode 100644 tests/unittests/media/server/main/mediaKeySession/IsNetflixPlayreadyKeySystemTest.cpp delete mode 100644 tests/unittests/media/server/main/mediaKeys/IsNetflixPlayreadyKeySystemTest.cpp diff --git a/media/client/ipc/include/MediaKeysIpc.h b/media/client/ipc/include/MediaKeysIpc.h index 6302d383a..f962fc85d 100644 --- a/media/client/ipc/include/MediaKeysIpc.h +++ b/media/client/ipc/include/MediaKeysIpc.h @@ -69,11 +69,12 @@ class MediaKeysIpc : public IMediaKeys, public IpcModule bool containsKey(int32_t keySessionId, const std::vector &keyId) override; - MediaKeyErrorStatus createKeySession(KeySessionType sessionType, std::weak_ptr client, bool isLDL, + MediaKeyErrorStatus createKeySession(KeySessionType sessionType, std::weak_ptr client, int32_t &keySessionId) override; MediaKeyErrorStatus generateRequest(int32_t keySessionId, InitDataType initDataType, - const std::vector &initData) override; + const std::vector &initData, + const LimitedDurationLicense &ldlState) override; MediaKeyErrorStatus loadSession(int32_t keySessionId) override; diff --git a/media/client/ipc/source/MediaKeysIpc.cpp b/media/client/ipc/source/MediaKeysIpc.cpp index 24909664e..761566218 100644 --- a/media/client/ipc/source/MediaKeysIpc.cpp +++ b/media/client/ipc/source/MediaKeysIpc.cpp @@ -334,7 +334,7 @@ bool MediaKeysIpc::containsKey(int32_t keySessionId, const std::vector } MediaKeyErrorStatus MediaKeysIpc::createKeySession(KeySessionType sessionType, std::weak_ptr client, - bool isLDL, int32_t &keySessionId) + int32_t &keySessionId) { if (!reattachChannelIfRequired()) { @@ -362,7 +362,6 @@ MediaKeyErrorStatus MediaKeysIpc::createKeySession(KeySessionType sessionType, s firebolt::rialto::CreateKeySessionRequest request; request.set_media_keys_handle(m_mediaKeysHandle); request.set_session_type(protoSessionType); - request.set_is_ldl(isLDL); firebolt::rialto::CreateKeySessionResponse response; // Default error status to FAIL @@ -387,7 +386,8 @@ MediaKeyErrorStatus MediaKeysIpc::createKeySession(KeySessionType sessionType, s } MediaKeyErrorStatus MediaKeysIpc::generateRequest(int32_t keySessionId, InitDataType initDataType, - const std::vector &initData) + const std::vector &initData, + const LimitedDurationLicense &ldlState) { if (!reattachChannelIfRequired()) { @@ -415,10 +415,29 @@ MediaKeyErrorStatus MediaKeysIpc::generateRequest(int32_t keySessionId, InitData break; } + GenerateRequestRequest_LimitedDurationLicense protoLimitedDurationLicense{ + GenerateRequestRequest_LimitedDurationLicense_NOT_SPECIFIED}; + switch (ldlState) + { + case LimitedDurationLicense::NOT_SPECIFIED: + protoLimitedDurationLicense = GenerateRequestRequest_LimitedDurationLicense_NOT_SPECIFIED; + break; + case LimitedDurationLicense::ENABLED: + protoLimitedDurationLicense = GenerateRequestRequest_LimitedDurationLicense_ENABLED; + break; + case LimitedDurationLicense::DISABLED: + protoLimitedDurationLicense = GenerateRequestRequest_LimitedDurationLicense_DISABLED; + break; + default: + RIALTO_CLIENT_LOG_WARN("Received unknown limited duration license state"); + break; + } + firebolt::rialto::GenerateRequestRequest request; request.set_media_keys_handle(m_mediaKeysHandle); request.set_key_session_id(keySessionId); request.set_init_data_type(protoInitDataType); + request.set_ldl_state(protoLimitedDurationLicense); for (auto it = initData.begin(); it != initData.end(); it++) { diff --git a/media/client/main/include/MediaKeys.h b/media/client/main/include/MediaKeys.h index 4f27f8ffa..c997cd090 100644 --- a/media/client/main/include/MediaKeys.h +++ b/media/client/main/include/MediaKeys.h @@ -79,11 +79,12 @@ class MediaKeys : public IMediaKeys bool containsKey(int32_t keySessionId, const std::vector &keyId) override; - MediaKeyErrorStatus createKeySession(KeySessionType sessionType, std::weak_ptr client, bool isLDL, + MediaKeyErrorStatus createKeySession(KeySessionType sessionType, std::weak_ptr client, int32_t &keySessionId) override; MediaKeyErrorStatus generateRequest(int32_t keySessionId, InitDataType initDataType, - const std::vector &initData) override; + const std::vector &initData, + const LimitedDurationLicense &ldlState) override; MediaKeyErrorStatus loadSession(int32_t keySessionId) override; diff --git a/media/client/main/source/MediaKeys.cpp b/media/client/main/source/MediaKeys.cpp index 1a60d44e1..4d5afa337 100644 --- a/media/client/main/source/MediaKeys.cpp +++ b/media/client/main/source/MediaKeys.cpp @@ -115,11 +115,11 @@ bool MediaKeys::containsKey(int32_t keySessionId, const std::vector &ke } MediaKeyErrorStatus MediaKeys::createKeySession(KeySessionType sessionType, std::weak_ptr client, - bool isLDL, int32_t &keySessionId) + int32_t &keySessionId) { RIALTO_CLIENT_LOG_DEBUG("entry:"); - auto result{m_mediaKeysIpc->createKeySession(sessionType, client, isLDL, keySessionId)}; + auto result{m_mediaKeysIpc->createKeySession(sessionType, client, keySessionId)}; if (isNetflixPlayready(m_keySystem) && MediaKeyErrorStatus::OK == result) { KeyIdMap::instance().addSession(keySessionId); @@ -128,11 +128,12 @@ MediaKeyErrorStatus MediaKeys::createKeySession(KeySessionType sessionType, std: } MediaKeyErrorStatus MediaKeys::generateRequest(int32_t keySessionId, InitDataType initDataType, - const std::vector &initData) + const std::vector &initData, + const LimitedDurationLicense &ldlState) { RIALTO_CLIENT_LOG_DEBUG("entry:"); - return m_mediaKeysIpc->generateRequest(keySessionId, initDataType, initData); + return m_mediaKeysIpc->generateRequest(keySessionId, initDataType, initData, ldlState); } MediaKeyErrorStatus MediaKeys::loadSession(int32_t keySessionId) diff --git a/media/public/include/IMediaKeys.h b/media/public/include/IMediaKeys.h index 6408d0056..c2e4ced3d 100644 --- a/media/public/include/IMediaKeys.h +++ b/media/public/include/IMediaKeys.h @@ -110,13 +110,12 @@ class IMediaKeys * * @param[in] sessionType : The session type. * @param[in] client : Client object for callbacks - * @param[in] isLDL : Is this an LDL * @param[out] keySessionId: The key session id * * @retval an error status. */ virtual MediaKeyErrorStatus createKeySession(KeySessionType sessionType, std::weak_ptr client, - bool isLDL, int32_t &keySessionId) = 0; + int32_t &keySessionId) = 0; /** * @brief Generates a licence request. @@ -130,11 +129,14 @@ class IMediaKeys * @param[in] keySessionId : The key session id for the session. * @param[in] initDataType : The init data type. * @param[in] initData : The init data. + * @param[in] ldlState : The Limited Duration License state. Most of key systems do not need this parameter, + * so the default value is NOT_SPECIFIED. * * @retval an error status. */ - virtual MediaKeyErrorStatus generateRequest(int32_t keySessionId, InitDataType initDataType, - const std::vector &initData) = 0; + virtual MediaKeyErrorStatus + generateRequest(int32_t keySessionId, InitDataType initDataType, const std::vector &initData, + const LimitedDurationLicense &ldlState = LimitedDurationLicense::NOT_SPECIFIED) = 0; /** * @brief Loads an existing key session diff --git a/media/public/include/MediaCommon.h b/media/public/include/MediaCommon.h index 92b544b8c..0dd829f6a 100644 --- a/media/public/include/MediaCommon.h +++ b/media/public/include/MediaCommon.h @@ -473,6 +473,16 @@ struct PlaybackInfo int64_t currentPosition{-1}; /**< The current playback position */ double volume{1.0}; /**< The current volume */ }; + +/** + * @brief Limited duration license state. + */ +enum class LimitedDurationLicense +{ + NOT_SPECIFIED, /**< The license duration is not specified */ + ENABLED, /**< The license has a limited duration */ + DISABLED /**< The license does not have a limited duration */ +}; } // namespace firebolt::rialto #endif // FIREBOLT_RIALTO_MEDIA_COMMON_H_ diff --git a/media/server/gstplayer/source/GstDecryptor.cpp b/media/server/gstplayer/source/GstDecryptor.cpp index 0882b36d3..d3e499e45 100644 --- a/media/server/gstplayer/source/GstDecryptor.cpp +++ b/media/server/gstplayer/source/GstDecryptor.cpp @@ -280,7 +280,7 @@ GstFlowReturn GstRialtoDecryptorPrivate::decrypt(GstBuffer *buffer, GstCaps *cap } } - if (protectionData->key && m_decryptionService->isNetflixPlayreadyKeySystem(protectionData->keySessionId)) + if (protectionData->key && m_decryptionService->isExtendedInterfaceUsed(protectionData->keySessionId)) { GstMapInfo keyMap; if (m_gstWrapper->gstBufferMap(protectionData->key, &keyMap, GST_MAP_READ)) diff --git a/media/server/ipc/source/MediaKeysModuleService.cpp b/media/server/ipc/source/MediaKeysModuleService.cpp index 4cb5f6293..6f69d69dd 100644 --- a/media/server/ipc/source/MediaKeysModuleService.cpp +++ b/media/server/ipc/source/MediaKeysModuleService.cpp @@ -100,6 +100,22 @@ firebolt::rialto::InitDataType covertInitDataType(firebolt::rialto::GenerateRequ return firebolt::rialto::InitDataType::UNKNOWN; } } + +firebolt::rialto::LimitedDurationLicense +covertLimitedDurationLicense(firebolt::rialto::GenerateRequestRequest_LimitedDurationLicense protoLimitedDurationLicense) +{ + switch (protoLimitedDurationLicense) + { + case firebolt::rialto::GenerateRequestRequest_LimitedDurationLicense::GenerateRequestRequest_LimitedDurationLicense_NOT_SPECIFIED: + return firebolt::rialto::LimitedDurationLicense::NOT_SPECIFIED; + case firebolt::rialto::GenerateRequestRequest_LimitedDurationLicense::GenerateRequestRequest_LimitedDurationLicense_ENABLED: + return firebolt::rialto::LimitedDurationLicense::ENABLED; + case firebolt::rialto::GenerateRequestRequest_LimitedDurationLicense::GenerateRequestRequest_LimitedDurationLicense_DISABLED: + return firebolt::rialto::LimitedDurationLicense::DISABLED; + default: + return firebolt::rialto::LimitedDurationLicense::NOT_SPECIFIED; + } +} } // namespace namespace firebolt::rialto::server::ipc @@ -262,7 +278,7 @@ void MediaKeysModuleService::createKeySession(::google::protobuf::RpcController m_cdmService.createKeySession(request->media_keys_handle(), convertKeySessionType(request->session_type()), std::make_shared(request->media_keys_handle(), ipcController->getClient()), - request->is_ldl(), keySessionId); + keySessionId); if (MediaKeyErrorStatus::OK == status) { response->set_key_session_id(keySessionId); @@ -278,10 +294,11 @@ void MediaKeysModuleService::generateRequest(::google::protobuf::RpcController * { 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()}); + 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()}, + covertLimitedDurationLicense(request->ldl_state())); response->set_error_status(convertMediaKeyErrorStatus(status)); done->Run(); } diff --git a/media/server/main/include/IMediaKeySession.h b/media/server/main/include/IMediaKeySession.h index bddb27155..5bb552cc1 100644 --- a/media/server/main/include/IMediaKeySession.h +++ b/media/server/main/include/IMediaKeySession.h @@ -55,14 +55,13 @@ class IMediaKeySessionFactory * @param[in] ocdmSystem : The ocdm system object to create the session on. * @param[in] sessionType : The session type. * @param[in] client : Client object for callbacks. - * @param[in] isLDL : Is this an LDL. * * @retval the new media keys instance or null on error. */ virtual std::unique_ptr createMediaKeySession(const std::string &keySystem, int32_t keySessionId, const firebolt::rialto::wrappers::IOcdmSystem &ocdmSystem, KeySessionType sessionType, - std::weak_ptr client, bool isLDL) const = 0; + std::weak_ptr client) const = 0; }; /** @@ -84,11 +83,12 @@ class IMediaKeySession * * @param[in] initDataType : The init data type. * @param[in] initData : The init data. + * @param[in] ldlState : The Limited Duration License state. Most of key systems do not need this parameter. * * @retval an error status. */ - virtual MediaKeyErrorStatus generateRequest(InitDataType initDataType, const std::vector &initData) = 0; - + virtual MediaKeyErrorStatus generateRequest(InitDataType initDataType, const std::vector &initData, + const LimitedDurationLicense &ldlState) = 0; /** * @brief Loads the existing key session. * @@ -179,13 +179,6 @@ class IMediaKeySession * @retval an error status. */ virtual MediaKeyErrorStatus selectKeyId(const std::vector &keyId) = 0; - - /** - * @brief Checks, if key system of media key session is Netflix playready. - * - * @retval true if key system is Netflix playready - */ - virtual bool isNetflixPlayreadyKeySystem() const = 0; }; } // namespace firebolt::rialto::server diff --git a/media/server/main/include/MediaKeySession.h b/media/server/main/include/MediaKeySession.h index 0a05fee9c..e4eeca6a8 100644 --- a/media/server/main/include/MediaKeySession.h +++ b/media/server/main/include/MediaKeySession.h @@ -43,8 +43,7 @@ class MediaKeySessionFactory : public IMediaKeySessionFactory std::unique_ptr createMediaKeySession(const std::string &keySystem, int32_t keySessionId, const firebolt::rialto::wrappers::IOcdmSystem &ocdmSystem, KeySessionType sessionType, - std::weak_ptr client, - bool isLDL) const override; + std::weak_ptr client) const override; }; /** @@ -61,20 +60,19 @@ class MediaKeySession : public IMediaKeySession, public firebolt::rialto::wrappe * @param[in] ocdmSystem : The ocdm system object to create the session on. * @param[in] sessionType : The session type. * @param[in] client : Client object for callbacks. - * @param[in] isLDL : Is this an LDL. * @param[in] mainThreadFactory : The main thread factory. */ MediaKeySession(const std::string &keySystem, int32_t keySessionId, const firebolt::rialto::wrappers::IOcdmSystem &ocdmSystem, KeySessionType sessionType, - std::weak_ptr client, bool isLDL, - const std::shared_ptr &mainThreadFactory); + std::weak_ptr client, const std::shared_ptr &mainThreadFactory); /** * @brief Virtual destructor. */ virtual ~MediaKeySession(); - MediaKeyErrorStatus generateRequest(InitDataType initDataType, const std::vector &initData) override; + MediaKeyErrorStatus generateRequest(InitDataType initDataType, const std::vector &initData, + const LimitedDurationLicense &ldlState) override; MediaKeyErrorStatus loadSession() override; @@ -96,8 +94,6 @@ class MediaKeySession : public IMediaKeySession, public firebolt::rialto::wrappe MediaKeyErrorStatus selectKeyId(const std::vector &keyId) override; - bool isNetflixPlayreadyKeySystem() const override; - void onProcessChallenge(const char url[], const uint8_t challenge[], const uint16_t challengeLength) override; void onKeyUpdated(const uint8_t keyId[], const uint8_t keyIdLength) override; @@ -137,11 +133,6 @@ class MediaKeySession : public IMediaKeySession, public firebolt::rialto::wrappe */ std::shared_ptr m_mainThread; - /** - * @brief Is the session LDL. - */ - const bool m_kIsLDL; - /** * @brief Is the ocdm session constructed. */ @@ -187,12 +178,24 @@ class MediaKeySession : public IMediaKeySession, public firebolt::rialto::wrappe */ std::mutex m_ocdmErrorMutex; + /** + * @brief Drm header to be set once the session is constructed + */ + std::vector m_queuedDrmHeader; + + /** + * @brief Flag used to check if extended interface is used + */ + bool m_extendedInterfaceInUse{false}; + /** * @brief Posts a getChallenge task onto the main thread. * + * @param[in] ldlState : The Limited Duration License state. + * * The challenge data is retrieved from ocdm and notified on a onLicenseRequest. */ - void getChallenge(); + void getChallenge(const LimitedDurationLicense &ldlState); /** * @brief Initalises the ocdm error data which checks for onError callbacks. diff --git a/media/server/main/include/MediaKeysServerInternal.h b/media/server/main/include/MediaKeysServerInternal.h index 6e934e004..bf9680651 100644 --- a/media/server/main/include/MediaKeysServerInternal.h +++ b/media/server/main/include/MediaKeysServerInternal.h @@ -76,11 +76,12 @@ class MediaKeysServerInternal : public IMediaKeysServerInternal bool containsKey(int32_t keySessionId, const std::vector &keyId) override; - MediaKeyErrorStatus createKeySession(KeySessionType sessionType, std::weak_ptr client, bool isLDL, + MediaKeyErrorStatus createKeySession(KeySessionType sessionType, std::weak_ptr client, int32_t &keySessionId) override; MediaKeyErrorStatus generateRequest(int32_t keySessionId, InitDataType initDataType, - const std::vector &initData) override; + const std::vector &initData, + const LimitedDurationLicense &ldlState) override; MediaKeyErrorStatus loadSession(int32_t keySessionId) override; @@ -114,8 +115,6 @@ class MediaKeysServerInternal : public IMediaKeysServerInternal MediaKeyErrorStatus getMetricSystemData(std::vector &buffer) override; - bool isNetflixPlayreadyKeySystem() const override; - void ping(std::unique_ptr &&heartbeatHandler) override; private: @@ -154,13 +153,12 @@ class MediaKeysServerInternal : public IMediaKeysServerInternal * * @param[in] sessionType : The session type. * @param[in] client : Client object for callbacks - * @param[in] isLDL : Is this an LDL * @param[out] keySessionId: The key session id * * @retval an error status. */ MediaKeyErrorStatus createKeySessionInternal(KeySessionType sessionType, std::weak_ptr client, - bool isLDL, int32_t &keySessionId); + int32_t &keySessionId); /** * @brief Generate internally, only to be called on the main thread. @@ -168,11 +166,13 @@ class MediaKeysServerInternal : public IMediaKeysServerInternal * @param[in] keySessionId : The key session id for the session. * @param[in] initDataType : The init data type. * @param[in] initData : The init data. + * @param[in] ldlState : The Limited Duration License state. Most of key systems do not need this parameter. * * @retval an error status. */ MediaKeyErrorStatus generateRequestInternal(int32_t keySessionId, InitDataType initDataType, - const std::vector &initData); + const std::vector &initData, + const LimitedDurationLicense &ldlState); /** * @brief Load internally, only to be called on the main thread. diff --git a/media/server/main/interface/IDecryptionService.h b/media/server/main/interface/IDecryptionService.h index 322b7aad4..3d52056f5 100644 --- a/media/server/main/interface/IDecryptionService.h +++ b/media/server/main/interface/IDecryptionService.h @@ -32,7 +32,7 @@ class IDecryptionService public: virtual ~IDecryptionService() = default; virtual MediaKeyErrorStatus decrypt(int32_t keySessionId, GstBuffer *encrypted, GstCaps *caps) = 0; - virtual bool isNetflixPlayreadyKeySystem(int32_t keySessionId) = 0; + virtual bool isExtendedInterfaceUsed(int32_t keySessionId) = 0; virtual MediaKeyErrorStatus selectKeyId(int32_t keySessionId, const std::vector &keyId) = 0; virtual void incrementSessionIdUsageCounter(int32_t keySessionId) = 0; virtual void decrementSessionIdUsageCounter(int32_t keySessionId) = 0; diff --git a/media/server/main/interface/IMediaKeysServerInternal.h b/media/server/main/interface/IMediaKeysServerInternal.h index 45069b19a..5de4ffc60 100644 --- a/media/server/main/interface/IMediaKeysServerInternal.h +++ b/media/server/main/interface/IMediaKeysServerInternal.h @@ -89,13 +89,6 @@ class IMediaKeysServerInternal : public IMediaKeys */ virtual MediaKeyErrorStatus decrypt(int32_t keySessionId, GstBuffer *encrypted, GstCaps *caps) = 0; - /** - * @brief Checks, if key system of media key session is Netflix Playready. - * - * @retval true if key system is Playready - */ - virtual bool isNetflixPlayreadyKeySystem() const = 0; - /** * @brief Checks, if MediaKeys main thread is not deadlocked * diff --git a/media/server/main/source/MediaKeySession.cpp b/media/server/main/source/MediaKeySession.cpp index 414856cd8..ab23b05a4 100644 --- a/media/server/main/source/MediaKeySession.cpp +++ b/media/server/main/source/MediaKeySession.cpp @@ -41,15 +41,16 @@ std::shared_ptr IMediaKeySessionFactory::createFactory( return factory; } -std::unique_ptr MediaKeySessionFactory::createMediaKeySession( - const std::string &keySystem, int32_t keySessionId, const firebolt::rialto::wrappers::IOcdmSystem &ocdmSystem, - KeySessionType sessionType, std::weak_ptr client, bool isLDL) const +std::unique_ptr +MediaKeySessionFactory::createMediaKeySession(const std::string &keySystem, int32_t keySessionId, + const firebolt::rialto::wrappers::IOcdmSystem &ocdmSystem, + KeySessionType sessionType, std::weak_ptr client) const { std::unique_ptr mediaKeys; try { mediaKeys = std::make_unique(keySystem, keySessionId, ocdmSystem, sessionType, client, - isLDL, server::IMainThreadFactory::createFactory()); + server::IMainThreadFactory::createFactory()); } catch (const std::exception &e) { @@ -61,11 +62,11 @@ std::unique_ptr MediaKeySessionFactory::createMediaKeySession( MediaKeySession::MediaKeySession(const std::string &keySystem, int32_t keySessionId, const firebolt::rialto::wrappers::IOcdmSystem &ocdmSystem, KeySessionType sessionType, - std::weak_ptr client, bool isLDL, + std::weak_ptr client, const std::shared_ptr &mainThreadFactory) : m_kKeySystem(keySystem), m_kKeySessionId(keySessionId), m_kSessionType(sessionType), m_mediaKeysClient(client), - m_kIsLDL(isLDL), m_isSessionConstructed(false), m_isSessionClosed(false), m_licenseRequested(false), - m_ongoingOcdmOperation(false), m_ocdmError(false) + m_isSessionConstructed(false), m_isSessionClosed(false), m_licenseRequested(false), m_ongoingOcdmOperation(false), + m_ocdmError(false) { RIALTO_SERVER_LOG_DEBUG("entry:"); @@ -106,19 +107,28 @@ MediaKeySession::~MediaKeySession() m_mainThread->unregisterClient(m_mainThreadClientId); } -MediaKeyErrorStatus MediaKeySession::generateRequest(InitDataType initDataType, const std::vector &initData) +MediaKeyErrorStatus MediaKeySession::generateRequest(InitDataType initDataType, const std::vector &initData, + const LimitedDurationLicense &ldlState) { RIALTO_SERVER_LOG_DEBUG("entry:"); - // Set the request flag for the onLicenseRequest callback - m_licenseRequested = true; + if (LimitedDurationLicense::NOT_SPECIFIED != ldlState) + { + m_extendedInterfaceInUse = true; + } + else + { + // Set the request flag for the onLicenseRequest callback + m_licenseRequested = true; + } + + auto status = MediaKeyErrorStatus::OK; // Only construct session if it hasnt previously been constructed if (!m_isSessionConstructed) { initOcdmErrorChecking(); - MediaKeyErrorStatus status = - m_ocdmSession->constructSession(m_kSessionType, initDataType, &initData[0], initData.size()); + status = m_ocdmSession->constructSession(m_kSessionType, initDataType, &initData[0], initData.size()); if (MediaKeyErrorStatus::OK != status) { RIALTO_SERVER_LOG_ERROR("Failed to construct the key session"); @@ -127,11 +137,11 @@ MediaKeyErrorStatus MediaKeySession::generateRequest(InitDataType initDataType, else { m_isSessionConstructed = true; - if (isNetflixPlayreadyKeySystem()) + if (!m_queuedDrmHeader.empty()) { - // Ocdm-playready does not notify onProcessChallenge when complete. - // Fetch the challenge manually. - getChallenge(); + RIALTO_SERVER_LOG_DEBUG("Setting queued drm header after session construction"); + setDrmHeader(m_queuedDrmHeader); + m_queuedDrmHeader.clear(); } } @@ -139,27 +149,33 @@ MediaKeyErrorStatus MediaKeySession::generateRequest(InitDataType initDataType, { status = MediaKeyErrorStatus::FAIL; } + } - return status; + if (m_isSessionConstructed && m_extendedInterfaceInUse) + { + // Ocdm-playready does not notify onProcessChallenge when complete. + // Fetch the challenge manually. + getChallenge(ldlState); } - return MediaKeyErrorStatus::OK; + return status; } -void MediaKeySession::getChallenge() +void MediaKeySession::getChallenge(const LimitedDurationLicense &ldlState) { RIALTO_SERVER_LOG_DEBUG("entry:"); auto task = [&]() { + const bool kIsLdl{LimitedDurationLicense::ENABLED == ldlState}; uint32_t challengeSize = 0; - MediaKeyErrorStatus status = m_ocdmSession->getChallengeData(m_kIsLDL, nullptr, &challengeSize); + MediaKeyErrorStatus status = m_ocdmSession->getChallengeData(kIsLdl, nullptr, &challengeSize); if (challengeSize == 0) { RIALTO_SERVER_LOG_ERROR("Failed to get the challenge data size, no onLicenseRequest will be generated"); return; } std::vector challenge(challengeSize, 0x00); - status = m_ocdmSession->getChallengeData(m_kIsLDL, &challenge[0], &challengeSize); + status = m_ocdmSession->getChallengeData(kIsLdl, &challenge[0], &challengeSize); if (MediaKeyErrorStatus::OK != status) { RIALTO_SERVER_LOG_ERROR("Failed to get the challenge data, no onLicenseRequest will be generated"); @@ -167,6 +183,7 @@ void MediaKeySession::getChallenge() } std::string url; + m_licenseRequested = true; onProcessChallenge(url.c_str(), &challenge[0], challengeSize); }; m_mainThread->enqueueTask(m_mainThreadClientId, task); @@ -195,7 +212,7 @@ MediaKeyErrorStatus MediaKeySession::updateSession(const std::vector &r initOcdmErrorChecking(); MediaKeyErrorStatus status; - if (isNetflixPlayreadyKeySystem()) + if (m_extendedInterfaceInUse) { status = m_ocdmSession->storeLicenseData(&responseData[0], responseData.size()); if (MediaKeyErrorStatus::OK != status) @@ -243,7 +260,7 @@ MediaKeyErrorStatus MediaKeySession::closeKeySession() initOcdmErrorChecking(); MediaKeyErrorStatus status; - if (isNetflixPlayreadyKeySystem()) + if (m_extendedInterfaceInUse) { if (MediaKeyErrorStatus::OK != m_ocdmSession->cancelChallengeData()) { @@ -322,6 +339,14 @@ MediaKeyErrorStatus MediaKeySession::setDrmHeader(const std::vector &re { initOcdmErrorChecking(); + if (!m_isSessionConstructed) + { + RIALTO_SERVER_LOG_INFO("Session not yet constructed, queueing drm header to be set after construction"); + m_extendedInterfaceInUse = true; + m_queuedDrmHeader = requestData; + return MediaKeyErrorStatus::OK; + } + MediaKeyErrorStatus status = m_ocdmSession->setDrmHeader(requestData.data(), requestData.size()); if (MediaKeyErrorStatus::OK != status) { @@ -363,6 +388,7 @@ MediaKeyErrorStatus MediaKeySession::selectKeyId(const std::vector &key initOcdmErrorChecking(); + m_extendedInterfaceInUse = true; MediaKeyErrorStatus status = m_ocdmSession->selectKeyId(keyId.size(), keyId.data()); if (MediaKeyErrorStatus::OK == status) { @@ -378,11 +404,6 @@ MediaKeyErrorStatus MediaKeySession::selectKeyId(const std::vector &key return status; } -bool MediaKeySession::isNetflixPlayreadyKeySystem() const -{ - return m_kKeySystem.find("netflix") != std::string::npos; -} - void MediaKeySession::onProcessChallenge(const char url[], const uint8_t challenge[], const uint16_t challengeLength) { std::string urlStr = url; diff --git a/media/server/main/source/MediaKeysServerInternal.cpp b/media/server/main/source/MediaKeysServerInternal.cpp index 7fd1b58bf..aa74faa74 100644 --- a/media/server/main/source/MediaKeysServerInternal.cpp +++ b/media/server/main/source/MediaKeysServerInternal.cpp @@ -210,13 +210,13 @@ bool MediaKeysServerInternal::containsKeyInternal(int32_t keySessionId, const st } MediaKeyErrorStatus MediaKeysServerInternal::createKeySession(KeySessionType sessionType, - std::weak_ptr client, bool isLDL, + std::weak_ptr client, int32_t &keySessionId) { RIALTO_SERVER_LOG_DEBUG("entry:"); MediaKeyErrorStatus status; - auto task = [&]() { status = createKeySessionInternal(sessionType, client, isLDL, keySessionId); }; + auto task = [&]() { status = createKeySessionInternal(sessionType, client, keySessionId); }; m_mainThread->enqueueTaskAndWait(m_mainThreadClientId, task); return status; @@ -224,12 +224,12 @@ MediaKeyErrorStatus MediaKeysServerInternal::createKeySession(KeySessionType ses MediaKeyErrorStatus MediaKeysServerInternal::createKeySessionInternal(KeySessionType sessionType, std::weak_ptr client, - bool isLDL, int32_t &keySessionId) + int32_t &keySessionId) { int32_t keySessionIdTemp = generateSessionId(); std::unique_ptr mediaKeySession = m_mediaKeySessionFactory->createMediaKeySession(m_kKeySystem, keySessionIdTemp, *m_ocdmSystem, sessionType, - client, isLDL); + client); if (!mediaKeySession) { RIALTO_SERVER_LOG_ERROR("Failed to create a new media key session"); @@ -242,19 +242,21 @@ MediaKeyErrorStatus MediaKeysServerInternal::createKeySessionInternal(KeySession } MediaKeyErrorStatus MediaKeysServerInternal::generateRequest(int32_t keySessionId, InitDataType initDataType, - const std::vector &initData) + const std::vector &initData, + const LimitedDurationLicense &ldlState) { RIALTO_SERVER_LOG_DEBUG("entry:"); MediaKeyErrorStatus status; - auto task = [&]() { status = generateRequestInternal(keySessionId, initDataType, initData); }; + auto task = [&]() { status = generateRequestInternal(keySessionId, initDataType, initData, ldlState); }; m_mainThread->enqueueTaskAndWait(m_mainThreadClientId, task); return status; } MediaKeyErrorStatus MediaKeysServerInternal::generateRequestInternal(int32_t keySessionId, InitDataType initDataType, - const std::vector &initData) + const std::vector &initData, + const LimitedDurationLicense &ldlState) { auto sessionIter = m_mediaKeySessions.find(keySessionId); if (sessionIter == m_mediaKeySessions.end()) @@ -263,7 +265,7 @@ MediaKeyErrorStatus MediaKeysServerInternal::generateRequestInternal(int32_t key return MediaKeyErrorStatus::BAD_SESSION_ID; } - MediaKeyErrorStatus status = sessionIter->second->generateRequest(initDataType, initData); + MediaKeyErrorStatus status = sessionIter->second->generateRequest(initDataType, initData, ldlState); if (MediaKeyErrorStatus::OK != status) { RIALTO_SERVER_LOG_ERROR("Failed to generate request for the key session %d", keySessionId); @@ -598,12 +600,6 @@ MediaKeyErrorStatus MediaKeysServerInternal::decryptInternal(int32_t keySessionI return status; } -bool MediaKeysServerInternal::isNetflixPlayreadyKeySystem() const -{ - RIALTO_SERVER_LOG_DEBUG("entry:"); - return m_kKeySystem.find("netflix") != std::string::npos; -} - void MediaKeysServerInternal::ping(std::unique_ptr &&heartbeatHandler) { RIALTO_SERVER_LOG_DEBUG("entry:"); diff --git a/media/server/service/include/ICdmService.h b/media/server/service/include/ICdmService.h index fdcbc95e5..d33d7b17b 100644 --- a/media/server/service/include/ICdmService.h +++ b/media/server/service/include/ICdmService.h @@ -48,10 +48,11 @@ class ICdmService virtual bool createMediaKeys(int mediaKeysHandle, std::string keySystem) = 0; virtual bool destroyMediaKeys(int mediaKeysHandle) = 0; virtual MediaKeyErrorStatus createKeySession(int mediaKeysHandle, KeySessionType sessionType, - const std::shared_ptr &client, bool isLDL, + const std::shared_ptr &client, int32_t &keySessionId) = 0; virtual MediaKeyErrorStatus generateRequest(int mediaKeysHandle, int32_t keySessionId, InitDataType initDataType, - const std::vector &initData) = 0; + const std::vector &initData, + const LimitedDurationLicense &ldlState) = 0; virtual MediaKeyErrorStatus loadSession(int mediaKeysHandle, int32_t keySessionId) = 0; virtual MediaKeyErrorStatus updateSession(int mediaKeysHandle, int32_t keySessionId, const std::vector &responseData) = 0; diff --git a/media/server/service/source/CdmService.cpp b/media/server/service/source/CdmService.cpp index 8ea716f61..78ff7eebc 100644 --- a/media/server/service/source/CdmService.cpp +++ b/media/server/service/source/CdmService.cpp @@ -121,8 +121,7 @@ bool CdmService::destroyMediaKeys(int mediaKeysHandle) } MediaKeyErrorStatus CdmService::createKeySession(int mediaKeysHandle, KeySessionType sessionType, - const std::shared_ptr &client, bool isLDL, - int32_t &keySessionId) + const std::shared_ptr &client, int32_t &keySessionId) { RIALTO_SERVER_LOG_DEBUG("CdmService requested to create key session: %d", mediaKeysHandle); @@ -134,7 +133,7 @@ MediaKeyErrorStatus CdmService::createKeySession(int mediaKeysHandle, KeySession return MediaKeyErrorStatus::FAIL; } - MediaKeyErrorStatus status = mediaKeysIter->second->createKeySession(sessionType, client, isLDL, keySessionId); + MediaKeyErrorStatus status = mediaKeysIter->second->createKeySession(sessionType, client, keySessionId); if (MediaKeyErrorStatus::OK == status) { if (m_mediaKeysClients.find(keySessionId) != m_mediaKeysClients.end()) @@ -143,9 +142,7 @@ MediaKeyErrorStatus CdmService::createKeySession(int mediaKeysHandle, KeySession static_cast(removeKeySessionInternal(mediaKeysHandle, keySessionId)); return MediaKeyErrorStatus::FAIL; } - m_sessionInfo.emplace( - std::make_pair(keySessionId, - MediaKeySessionInfo{mediaKeysHandle, mediaKeysIter->second->isNetflixPlayreadyKeySystem()})); + m_sessionInfo.emplace(std::make_pair(keySessionId, MediaKeySessionInfo{mediaKeysHandle})); m_mediaKeysClients.emplace(std::make_pair(keySessionId, client)); } @@ -153,7 +150,8 @@ MediaKeyErrorStatus CdmService::createKeySession(int mediaKeysHandle, KeySession } MediaKeyErrorStatus CdmService::generateRequest(int mediaKeysHandle, int32_t keySessionId, InitDataType initDataType, - const std::vector &initData) + const std::vector &initData, + const LimitedDurationLicense &ldlState) { RIALTO_SERVER_LOG_DEBUG("CdmService requested to generate request: %d", mediaKeysHandle); @@ -164,7 +162,11 @@ MediaKeyErrorStatus CdmService::generateRequest(int mediaKeysHandle, int32_t key RIALTO_SERVER_LOG_ERROR("Media keys handle: %d does not exists", mediaKeysHandle); return MediaKeyErrorStatus::FAIL; } - return mediaKeysIter->second->generateRequest(keySessionId, initDataType, initData); + if (LimitedDurationLicense::NOT_SPECIFIED != ldlState && m_sessionInfo.find(keySessionId) != m_sessionInfo.end()) + { + m_sessionInfo[keySessionId].isExtendedInterfaceUsed = true; + } + return mediaKeysIter->second->generateRequest(keySessionId, initDataType, initData, ldlState); } MediaKeyErrorStatus CdmService::loadSession(int mediaKeysHandle, int32_t keySessionId) @@ -289,7 +291,10 @@ MediaKeyErrorStatus CdmService::setDrmHeader(int mediaKeysHandle, int32_t keySes RIALTO_SERVER_LOG_ERROR("Media keys handle: %d does not exists", mediaKeysHandle); return MediaKeyErrorStatus::FAIL; } - + if (m_sessionInfo.find(keySessionId) != m_sessionInfo.end()) + { + m_sessionInfo[keySessionId].isExtendedInterfaceUsed = true; + } return mediaKeysIter->second->setDrmHeader(keySessionId, requestData); } @@ -504,9 +509,9 @@ MediaKeyErrorStatus CdmService::decrypt(int32_t keySessionId, GstBuffer *encrypt return m_mediaKeys[mediaKeysHandleIter->second.mediaKeysHandle]->decrypt(keySessionId, encrypted, caps); } -bool CdmService::isNetflixPlayreadyKeySystem(int32_t keySessionId) +bool CdmService::isExtendedInterfaceUsed(int32_t keySessionId) { - RIALTO_SERVER_LOG_DEBUG("CdmService requested to check if key system is Netflix Playready, key session id: %d", + RIALTO_SERVER_LOG_DEBUG("CdmService requested to check if extended interface is used, key session id: %d", keySessionId); std::lock_guard lock{m_mediaKeysMutex}; @@ -516,7 +521,7 @@ bool CdmService::isNetflixPlayreadyKeySystem(int32_t keySessionId) RIALTO_SERVER_LOG_ERROR("Media keys handle for mksId: %d does not exists", keySessionId); return false; } - return mediaKeysHandleIter->second.isNetflixPlayready; + return mediaKeysHandleIter->second.isExtendedInterfaceUsed; } MediaKeyErrorStatus CdmService::selectKeyId(int32_t keySessionId, const std::vector &keyId) @@ -530,6 +535,10 @@ MediaKeyErrorStatus CdmService::selectKeyId(int32_t keySessionId, const std::vec RIALTO_SERVER_LOG_ERROR("Media keys handle for mksId: %d does not exists", keySessionId); return MediaKeyErrorStatus::FAIL; } + if (m_sessionInfo.find(keySessionId) != m_sessionInfo.end()) + { + m_sessionInfo[keySessionId].isExtendedInterfaceUsed = true; + } return m_mediaKeys[mediaKeysHandleIter->second.mediaKeysHandle]->selectKeyId(keySessionId, keyId); } diff --git a/media/server/service/source/CdmService.h b/media/server/service/source/CdmService.h index 468f6a706..b97639685 100644 --- a/media/server/service/source/CdmService.h +++ b/media/server/service/source/CdmService.h @@ -37,7 +37,7 @@ class CdmService : public ICdmService, public IDecryptionService struct MediaKeySessionInfo { int mediaKeysHandle; - bool isNetflixPlayready{false}; + bool isExtendedInterfaceUsed{false}; uint32_t refCounter{0}; bool shouldBeClosed{false}; bool shouldBeReleased{false}; @@ -54,10 +54,10 @@ class CdmService : public ICdmService, public IDecryptionService bool createMediaKeys(int mediaKeysHandle, std::string keySystem) override; bool destroyMediaKeys(int mediaKeysHandle) override; MediaKeyErrorStatus createKeySession(int mediaKeysHandle, KeySessionType sessionType, - const std::shared_ptr &client, bool isLDL, - int32_t &keySessionId) override; + const std::shared_ptr &client, int32_t &keySessionId) override; MediaKeyErrorStatus generateRequest(int mediaKeysHandle, int32_t keySessionId, InitDataType initDataType, - const std::vector &initData) override; + const std::vector &initData, + const LimitedDurationLicense &ldlState) override; MediaKeyErrorStatus loadSession(int mediaKeysHandle, int32_t keySessionId) override; MediaKeyErrorStatus updateSession(int mediaKeysHandle, int32_t keySessionId, const std::vector &responseData) override; @@ -83,7 +83,7 @@ class CdmService : public ICdmService, public IDecryptionService bool getSupportedKeySystemVersion(const std::string &keySystem, std::string &version) override; bool isServerCertificateSupported(const std::string &keySystem) override; MediaKeyErrorStatus decrypt(int32_t keySessionId, GstBuffer *encrypted, GstCaps *caps) override; - bool isNetflixPlayreadyKeySystem(int32_t keySessionId) override; + bool isExtendedInterfaceUsed(int32_t keySessionId) override; MediaKeyErrorStatus selectKeyId(int32_t keySessionId, const std::vector &keyId) override; void incrementSessionIdUsageCounter(int32_t keySessionId) override; void decrementSessionIdUsageCounter(int32_t keySessionId) override; diff --git a/proto/mediakeysmodule.proto b/proto/mediakeysmodule.proto index 0fa4905ad..204b85f48 100644 --- a/proto/mediakeysmodule.proto +++ b/proto/mediakeysmodule.proto @@ -203,6 +203,8 @@ message CreateKeySessionResponse { * @param[in] key_session_id The key session id for the session. * @param[in] init_data_type The init data type. * @param[in] init_data The init data. + * @param[in] ldlState The Limited Duration License state. Most of key systems do not need this parameter, + * so the default value is NOT_SPECIFIED. * * @retval an error status. */ @@ -215,11 +217,17 @@ message GenerateRequestRequest { DRMHEADER = 4; ///< The init data is in DrmHeader format. }; + enum LimitedDurationLicense { + NOT_SPECIFIED = 0; ///< The license duration is not specified + ENABLED = 1; ///< The license has a limited duration + DISABLED = 2; ///< The license does not have a limited duration + }; + optional int32 media_keys_handle = 1 [default = -1]; optional int32 key_session_id = 2 [default = -1]; optional InitDataType init_data_type = 3; repeated uint32 init_data = 4; - + optional LimitedDurationLicense ldl_state = 5 [default = NOT_SPECIFIED]; } message GenerateRequestResponse { optional ProtoMediaKeyErrorStatus error_status = 1 [default = FAIL]; diff --git a/tests/common/matchers/MediaKeysProtoRequestMatchers.h b/tests/common/matchers/MediaKeysProtoRequestMatchers.h index 4784bc0a3..ec147a8ed 100644 --- a/tests/common/matchers/MediaKeysProtoRequestMatchers.h +++ b/tests/common/matchers/MediaKeysProtoRequestMatchers.h @@ -42,12 +42,11 @@ MATCHER_P(destroyMediaKeysRequestMatcher, mediaKeysHandle, "") return (kRequest->media_keys_handle() == mediaKeysHandle); } -MATCHER_P3(createKeySessionRequestMatcher, mediaKeysHandle, sessionType, isLdl, "") +MATCHER_P2(createKeySessionRequestMatcher, mediaKeysHandle, sessionType, "") { const ::firebolt::rialto::CreateKeySessionRequest *kRequest = dynamic_cast(arg); - return ((kRequest->media_keys_handle() == mediaKeysHandle) && (kRequest->session_type() == sessionType) && - (kRequest->is_ldl() == isLdl)); + return ((kRequest->media_keys_handle() == mediaKeysHandle) && (kRequest->session_type() == sessionType)); } MATCHER_P4(generateRequestRequestMatcher, mediaKeysHandle, keySessionId, initDataType, initData, "") diff --git a/tests/common/protoUtils/MediaKeysProtoUtils.h b/tests/common/protoUtils/MediaKeysProtoUtils.h index 56ca3e58d..5d8a94132 100644 --- a/tests/common/protoUtils/MediaKeysProtoUtils.h +++ b/tests/common/protoUtils/MediaKeysProtoUtils.h @@ -163,6 +163,21 @@ convertInitDataType(const firebolt::rialto::InitDataType &initDataType) } } +inline firebolt::rialto::GenerateRequestRequest_LimitedDurationLicense +convertLimitedDurationLicense(const firebolt::rialto::LimitedDurationLicense &ldlState) +{ + switch (ldlState) + { + case firebolt::rialto::LimitedDurationLicense::ENABLED: + return firebolt::rialto::GenerateRequestRequest_LimitedDurationLicense_ENABLED; + case firebolt::rialto::LimitedDurationLicense::DISABLED: + return firebolt::rialto::GenerateRequestRequest_LimitedDurationLicense_DISABLED; + case firebolt::rialto::LimitedDurationLicense::NOT_SPECIFIED: + default: + return firebolt::rialto::GenerateRequestRequest_LimitedDurationLicense_NOT_SPECIFIED; + } +} + inline firebolt::rialto::KeyStatus convertKeyStatus(const firebolt::rialto::KeyStatusesChangedEvent_KeyStatus &keyStatus) { switch (keyStatus) diff --git a/tests/componenttests/client/tests/base/MediaKeysTestMethods.cpp b/tests/componenttests/client/tests/base/MediaKeysTestMethods.cpp index 59341e47a..494efd4f1 100644 --- a/tests/componenttests/client/tests/base/MediaKeysTestMethods.cpp +++ b/tests/componenttests/client/tests/base/MediaKeysTestMethods.cpp @@ -32,7 +32,6 @@ const std::string kKeySystemWidevine{"com.widevine.alpha"}; const std::string kKeySystemPlayready{"com.netflix.playready"}; constexpr int32_t kMediaKeysHandle{1}; constexpr firebolt::rialto::KeySessionType kSessionTypeTemp{firebolt::rialto::KeySessionType::TEMPORARY}; -constexpr bool kIsNotLdl{false}; constexpr firebolt::rialto::MediaKeyErrorStatus kStatusOk{firebolt::rialto::MediaKeyErrorStatus::OK}; constexpr firebolt::rialto::MediaKeyErrorStatus kStatusFailed{firebolt::rialto::MediaKeyErrorStatus::FAIL}; constexpr firebolt::rialto::MediaKeyErrorStatus kStatusInterfaceNotImplemented{ @@ -108,8 +107,7 @@ void MediaKeysTestMethods::shouldCreateKeySession() { EXPECT_CALL(*m_mediaKeysModuleMock, createKeySession(_, - createKeySessionRequestMatcher(kMediaKeysHandle, - convertKeySessionType(kSessionTypeTemp), kIsNotLdl), + createKeySessionRequestMatcher(kMediaKeysHandle, convertKeySessionType(kSessionTypeTemp)), _, _)) .WillOnce(DoAll(SetArgPointee<2>(m_mediaKeysModuleMock->createKeySessionResponse(kStatusOk, kKeySessionId)), WithArgs<0, 3>(Invoke(&(*m_mediaKeysModuleMock), &MediaKeysModuleMock::defaultReturn)))); @@ -119,8 +117,7 @@ void MediaKeysTestMethods::shouldCreateKeySessionFailure() { EXPECT_CALL(*m_mediaKeysModuleMock, createKeySession(_, - createKeySessionRequestMatcher(kMediaKeysHandle, - convertKeySessionType(kSessionTypeTemp), kIsNotLdl), + createKeySessionRequestMatcher(kMediaKeysHandle, convertKeySessionType(kSessionTypeTemp)), _, _)) .WillOnce(DoAll(SetArgPointee<2>(m_mediaKeysModuleMock->createKeySessionResponse(kStatusFailed, kKeySessionId)), WithArgs<0, 3>(Invoke(&(*m_mediaKeysModuleMock), &MediaKeysModuleMock::defaultReturn)))); @@ -129,15 +126,14 @@ void MediaKeysTestMethods::shouldCreateKeySessionFailure() void MediaKeysTestMethods::createKeySession() { int32_t keySessionId; - EXPECT_EQ(m_mediaKeys->createKeySession(kSessionTypeTemp, m_mediaKeysClientMock, kIsNotLdl, keySessionId), kStatusOk); + EXPECT_EQ(m_mediaKeys->createKeySession(kSessionTypeTemp, m_mediaKeysClientMock, keySessionId), kStatusOk); EXPECT_EQ(keySessionId, kKeySessionId); } void MediaKeysTestMethods::createKeySessionFailure() { int32_t keySessionId; - EXPECT_EQ(m_mediaKeys->createKeySession(kSessionTypeTemp, m_mediaKeysClientMock, kIsNotLdl, keySessionId), - kStatusFailed); + EXPECT_EQ(m_mediaKeys->createKeySession(kSessionTypeTemp, m_mediaKeysClientMock, keySessionId), kStatusFailed); } void MediaKeysTestMethods::shouldGenerateRequest() diff --git a/tests/componenttests/server/common/MessageBuilders.cpp b/tests/componenttests/server/common/MessageBuilders.cpp index 848b925f8..a1ae941e1 100644 --- a/tests/componenttests/server/common/MessageBuilders.cpp +++ b/tests/componenttests/server/common/MessageBuilders.cpp @@ -430,7 +430,8 @@ ::firebolt::rialto::CreateKeySessionRequest createCreateKeySessionRequest(int me } ::firebolt::rialto::GenerateRequestRequest createGenerateRequestRequest(int mediaKeysHandle, int keySessionId, - const std::vector &initData) + const std::vector &initData, + bool extendedInterface) { ::firebolt::rialto::GenerateRequestRequest request; request.set_media_keys_handle(mediaKeysHandle); @@ -440,6 +441,10 @@ ::firebolt::rialto::GenerateRequestRequest createGenerateRequestRequest(int medi { request.add_init_data(i); } + if (extendedInterface) + { + request.set_ldl_state(::firebolt::rialto::GenerateRequestRequest_LimitedDurationLicense_DISABLED); + } return request; } diff --git a/tests/componenttests/server/common/MessageBuilders.h b/tests/componenttests/server/common/MessageBuilders.h index dae4679ae..815266713 100644 --- a/tests/componenttests/server/common/MessageBuilders.h +++ b/tests/componenttests/server/common/MessageBuilders.h @@ -89,7 +89,8 @@ ::firebolt::rialto::CreateMediaKeysRequest createCreateMediaKeysRequestWidevine( ::firebolt::rialto::CreateMediaKeysRequest createCreateMediaKeysRequestNetflix(); ::firebolt::rialto::CreateKeySessionRequest createCreateKeySessionRequest(int mediaKeysHandle); ::firebolt::rialto::GenerateRequestRequest createGenerateRequestRequest(int mediaKeysHandle, int keySessionId, - const std::vector &initData); + const std::vector &initData, + bool extendedInterface = false); ::firebolt::rialto::UpdateSessionRequest createUpdateSessionRequest(int mediaKeysHandle, int keySessionId, const std::vector &response); ::firebolt::rialto::ContainsKeyRequest createContainsKeyRequest(int mediaKeysHandle, int keySessionId, diff --git a/tests/componenttests/server/tests/mediaKeys/LicenseRenewalTest.cpp b/tests/componenttests/server/tests/mediaKeys/LicenseRenewalTest.cpp index 68a42b773..49a3249e9 100644 --- a/tests/componenttests/server/tests/mediaKeys/LicenseRenewalTest.cpp +++ b/tests/componenttests/server/tests/mediaKeys/LicenseRenewalTest.cpp @@ -130,12 +130,7 @@ void LicenseRenewalTest::updateOneKey() * Server notifies the client that of license renewal. * Expect that the license renewal notification is processed by the client. * - * Step 2: Update session - * updateSession with the updated license. - * Expect that updateSession is processed by the server. - * Api call returns with success. - * - * Step 3: Notify key statuses changed + * Step 2: Notify key statuses changed * Server notifies the client of key statuses changed. * Expect that the key statuses changed notification is processed by the client. * @@ -149,20 +144,77 @@ void LicenseRenewalTest::updateOneKey() */ TEST_F(LicenseRenewalTest, licenseRenewal) { - createMediaKeysNetflix(); + createMediaKeysWidevine(); ocdmSessionWillBeCreated(); createKeySession(); // Step 1: Notify license renewal licenseRenew(); - // Step 2: Update session + // Step 2: Notify key statuses changed + updateOneKey(); + updateAllKeys(); +} + +/* + * Component Test: License renewal sequence for netflix playready. + * Test Objective: + * Test the notification of license renewal and updating of the new license. + * + * Sequence Diagrams: + * License Renewal - Cobalt/OCDM, Update MKS - Cobalt/OCDM, "Destroy" MKS - Cobalt/OCDM + * - https://wiki.rdkcentral.com/display/ASP/Rialto+Media+Key+Session+Management+Design + * + * Test Setup: + * Language: C++ + * Testing Framework: Google Test + * Components: MediaKeys + * + * Test Initialize: + * RialtoServerComponentTest::RialtoServerComponentTest() will set up wrappers and + * starts rialtoServer running in its own thread + * send a CreateMediaKeys message to rialtoServer + * expect a "createSession" call (to OCDM mock) + * send a CreateKeySession message to rialtoServer + * generate request message for playready and send it to the client + * expect the client to process the generate request message + * + * + * Test Steps: + * Step 1: Update session + * updateSession with the updated license. + * Expect that updateSession is processed by the server. + * Api call returns with success. + * + * Step 2: Close session + * closeSession. + * Expect that closeSession is processed by the server. + * Api call returns with success. + * + * Test Tear-down: + * Server is terminated. + * + * Expected Results: + * Client can be notified of license renewal and update the key session successfully. + * + * Code: + */ +TEST_F(LicenseRenewalTest, licenseRenewalNetflix) +{ + createMediaKeysNetflix(); + ocdmSessionWillBeCreated(); + createKeySession(); + willGenerateRequestPlayready(); + generateRequestPlayready(); + + // Step 1: Update session willUpdateSessionNetflix(); updateSessionNetflix(); - // Step 3: Notify key statuses changed - updateOneKey(); - updateAllKeys(); + // Step 2: Close session + willCloseKeySessionPlayready(); + closeKeySessionPlayready(); + willRelease(); } } // namespace firebolt::rialto::server::ct diff --git a/tests/componenttests/server/tests/mediaKeys/MediaKeysTest.cpp b/tests/componenttests/server/tests/mediaKeys/MediaKeysTest.cpp index c954b8af5..def0de62d 100644 --- a/tests/componenttests/server/tests/mediaKeys/MediaKeysTest.cpp +++ b/tests/componenttests/server/tests/mediaKeys/MediaKeysTest.cpp @@ -42,8 +42,6 @@ class MediaKeysTest : public MediaKeysTestMethods void generateRequestFail(); void shouldFailToCreateKeySessionWhenMksIdIsWrong(); - - const std::vector m_kInitData{1, 2, 7}; }; void MediaKeysTest::willGenerateRequestFail() diff --git a/tests/componenttests/server/tests/mediaKeys/MediaKeysTestMethods.cpp b/tests/componenttests/server/tests/mediaKeys/MediaKeysTestMethods.cpp index 9bfd4e182..b9e91dc4e 100644 --- a/tests/componenttests/server/tests/mediaKeys/MediaKeysTestMethods.cpp +++ b/tests/componenttests/server/tests/mediaKeys/MediaKeysTestMethods.cpp @@ -85,6 +85,69 @@ void MediaKeysTestMethods::ocdmSessionWillBeCreated() })); } +void MediaKeysTestMethods::willGenerateRequestPlayready() +{ + EXPECT_CALL(m_ocdmSessionMock, constructSession(KeySessionType::TEMPORARY, InitDataType::CENC, _, m_kInitData.size())) + .WillOnce(testing::Invoke( + [&](KeySessionType sessionType, InitDataType initDataType, const uint8_t initData[], + uint32_t initDataSize) -> MediaKeyErrorStatus + { + for (uint32_t i = 0; i < initDataSize; ++i) + { + EXPECT_EQ(initData[i], m_kInitData[i]); + } + + return MediaKeyErrorStatus::OK; + })); + + EXPECT_CALL(m_ocdmSessionMock, getChallengeData(false, _, _)) + .WillOnce(testing::Invoke( + [&](bool isLDL, const uint8_t *challenge, uint32_t *challengeSize) -> MediaKeyErrorStatus + { + // This first call asks for the size of the data + EXPECT_EQ(challenge, nullptr); + *challengeSize = m_kLicenseRequestMessage.size(); + return MediaKeyErrorStatus::OK; + })) + .WillOnce(testing::Invoke( + [&](bool isLDL, uint8_t *challenge, const uint32_t *challengeSize) -> MediaKeyErrorStatus + { + // This second call asks for the data + EXPECT_EQ(*challengeSize, m_kLicenseRequestMessage.size()); + for (size_t i = 0; i < m_kLicenseRequestMessage.size(); ++i) + { + challenge[i] = m_kLicenseRequestMessage[i]; + } + return MediaKeyErrorStatus::OK; + })); +} + +void MediaKeysTestMethods::generateRequestPlayready() +{ + constexpr bool kUseExtendedInterface{true}; + auto request{createGenerateRequestRequest(m_mediaKeysHandle, m_mediaKeySessionId, m_kInitData, kUseExtendedInterface)}; + + ExpectMessage<::firebolt::rialto::LicenseRequestEvent> expectedMessage(m_clientStub); + + ConfigureAction(m_clientStub) + .send(request) + .expectSuccess() + .matchResponse([&](const firebolt::rialto::GenerateRequestResponse &resp) + { EXPECT_EQ(resp.error_status(), ProtoMediaKeyErrorStatus::OK); }); + + auto message = expectedMessage.getMessage(); + ASSERT_TRUE(message); + ASSERT_EQ(message->media_keys_handle(), m_mediaKeysHandle); + ASSERT_EQ(message->key_session_id(), m_mediaKeySessionId); + EXPECT_EQ(message->url(), ""); + const unsigned int kMax = message->license_request_message_size(); + ASSERT_EQ(kMax, m_kLicenseRequestMessage.size()); + for (unsigned int i = 0; i < kMax; ++i) + { + ASSERT_EQ(message->license_request_message(i), m_kLicenseRequestMessage[i]); + } +} + void MediaKeysTestMethods::willUpdateSessionNetflix() { EXPECT_CALL(m_ocdmSessionMock, storeLicenseData(_, m_kUpdateSessionNetflixResponse.size())) @@ -109,6 +172,23 @@ void MediaKeysTestMethods::updateSessionNetflix() { EXPECT_EQ(resp.error_status(), ProtoMediaKeyErrorStatus::OK); }); } +void MediaKeysTestMethods::willCloseKeySessionPlayready() +{ + EXPECT_CALL(m_ocdmSessionMock, cancelChallengeData()).WillOnce(Return(MediaKeyErrorStatus::OK)); + EXPECT_CALL(m_ocdmSessionMock, cleanDecryptContext()).WillOnce(Return(MediaKeyErrorStatus::OK)); +} + +void MediaKeysTestMethods::closeKeySessionPlayready() +{ + auto request{createCloseKeySessionRequest(m_mediaKeysHandle, m_mediaKeySessionId)}; + + ConfigureAction(m_clientStub) + .send(request) + .expectSuccess() + .matchResponse([&](const firebolt::rialto::CloseKeySessionResponse &resp) + { EXPECT_EQ(resp.error_status(), ProtoMediaKeyErrorStatus::OK); }); +} + void MediaKeysTestMethods::willTeardown() { // For teardown... diff --git a/tests/componenttests/server/tests/mediaKeys/MediaKeysTestMethods.h b/tests/componenttests/server/tests/mediaKeys/MediaKeysTestMethods.h index 0cfdea24c..65f16b0fb 100644 --- a/tests/componenttests/server/tests/mediaKeys/MediaKeysTestMethods.h +++ b/tests/componenttests/server/tests/mediaKeys/MediaKeysTestMethods.h @@ -41,9 +41,15 @@ class MediaKeysTestMethods : public RialtoServerComponentTest void createKeySession(); void ocdmSessionWillBeCreated(); + void willGenerateRequestPlayready(); + void generateRequestPlayready(); + void willUpdateSessionNetflix(); void updateSessionNetflix(); + void willCloseKeySessionPlayready(); + void closeKeySessionPlayready(); + void willTeardown(); void willRelease(); @@ -59,6 +65,8 @@ class MediaKeysTestMethods : public RialtoServerComponentTest firebolt::rialto::wrappers::IOcdmSessionClient *m_ocdmSessionClient{0}; const std::vector m_kUpdateSessionNetflixResponse{5, 6}; + const std::vector m_kInitData{1, 2, 7}; + const std::vector m_kLicenseRequestMessage{'d', 'z', 'f'}; }; } // namespace firebolt::rialto::server::ct diff --git a/tests/componenttests/server/tests/mediaKeys/SessionReadyForDecryptionTest.cpp b/tests/componenttests/server/tests/mediaKeys/SessionReadyForDecryptionTest.cpp index bd2e0f97f..b98e4802d 100644 --- a/tests/componenttests/server/tests/mediaKeys/SessionReadyForDecryptionTest.cpp +++ b/tests/componenttests/server/tests/mediaKeys/SessionReadyForDecryptionTest.cpp @@ -58,8 +58,6 @@ class SessionReadyForDecryptionTest : public virtual MediaKeysTestMethods void destroyMediaKeysRequest(); const std::vector kResponse{4, 1, 3}; - const std::vector m_kInitData{1, 2, 7}; - const std::vector m_kLicenseRequestMessage{'d', 'z', 'f'}; }; void SessionReadyForDecryptionTest::willGenerateRequestWidevine() @@ -149,7 +147,8 @@ void SessionReadyForDecryptionTest::willGenerateRequestNetflix() void SessionReadyForDecryptionTest::generateRequestNetflix() { - auto request{createGenerateRequestRequest(m_mediaKeysHandle, m_mediaKeySessionId, m_kInitData)}; + constexpr bool kUseExtendedInterface{true}; + auto request{createGenerateRequestRequest(m_mediaKeysHandle, m_mediaKeySessionId, m_kInitData, kUseExtendedInterface)}; ExpectMessage<::firebolt::rialto::LicenseRequestEvent> expectedMessage(m_clientStub); diff --git a/tests/componenttests/server/tests/mediaKeys/SetDrmHeaderTest.cpp b/tests/componenttests/server/tests/mediaKeys/SetDrmHeaderTest.cpp index a8aaa4de1..e9e6ac27d 100644 --- a/tests/componenttests/server/tests/mediaKeys/SetDrmHeaderTest.cpp +++ b/tests/componenttests/server/tests/mediaKeys/SetDrmHeaderTest.cpp @@ -87,16 +87,32 @@ void SetDrmHeaderTest::setDrmHeader(const std::vector &kKeyId) * * * Test Steps: - * Step 1: Set the drm header + * Step 1: generateRequest + * client sends generateRequest message to rialtoServer + * rialtoServer passes request to OCDM library + * ocdm lib returns success + * rialtoServer returns a success message to the client + * + * rialtoServer calls OCDM library get_challenge_data() + * rialtoServer should forward this request, via an onLicenceRequest + * message, to the client. The content of this message should match + * the details from the ocdm library + + * Step 2: Set the drm header * setDrmHeader first header. * Expect that setDrmHeader is processed by the server. * Api call returns with success. * - * Step 2: Set the drm header for a second time with different header + * Step 3: Set the drm header for a second time with different header * setDrmHeader second header. * Expect that setDrmHeader is processed by the server. * Api call returns with success. * + * Step 4: Close session + * closeSession. + * Expect that closeSession is processed by the server. + * Api call returns with success. + * * Test Tear-down: * Server is terminated. * @@ -111,13 +127,22 @@ TEST_F(SetDrmHeaderTest, multiple) ocdmSessionWillBeCreated(); createKeySession(); - // Step 1: Set the drm header + // Step 1: generateRequest + willGenerateRequestPlayready(); + generateRequestPlayready(); + + // Step 2: Set the drm header willSetDrmHeader(kKeyId1); setDrmHeader(kKeyId1); - // Step 2: Set the drm header for a second time with different header + // Step 3: Set the drm header for a second time with different header willSetDrmHeader(kKeyId2); setDrmHeader(kKeyId2); + + // Step 4: Close session + willCloseKeySessionPlayready(); + closeKeySessionPlayready(); + willRelease(); } } // namespace firebolt::rialto::server::ct diff --git a/tests/unittests/media/client/ipc/mediaKeysIpc/CreateKeySessionTest.cpp b/tests/unittests/media/client/ipc/mediaKeysIpc/CreateKeySessionTest.cpp index 527f55d4e..c19048ad2 100644 --- a/tests/unittests/media/client/ipc/mediaKeysIpc/CreateKeySessionTest.cpp +++ b/tests/unittests/media/client/ipc/mediaKeysIpc/CreateKeySessionTest.cpp @@ -25,7 +25,6 @@ class RialtoClientMediaKeysIpcCreateKeySessionTest : public MediaKeysIpcTestBase { protected: KeySessionType m_keySessionType = KeySessionType::PERSISTENT_LICENCE; - bool m_isLdl = false; RialtoClientMediaKeysIpcCreateKeySessionTest() { createMediaKeysIpc(); } @@ -51,13 +50,12 @@ TEST_F(RialtoClientMediaKeysIpcCreateKeySessionTest, Success) EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("createKeySession"), m_controllerMock.get(), - createKeySessionRequestMatcher(m_mediaKeysHandle, convertKeySessionType(m_keySessionType), - m_isLdl), + createKeySessionRequestMatcher(m_mediaKeysHandle, convertKeySessionType(m_keySessionType)), _, m_blockingClosureMock.get())) .WillOnce(WithArgs<3>( Invoke(this, &RialtoClientMediaKeysIpcCreateKeySessionTest::setCreateKeySessionResponseSuccess))); - EXPECT_EQ(m_mediaKeysIpc->createKeySession(m_keySessionType, m_mediaKeysClientMock, m_isLdl, returnKeySessionid), + EXPECT_EQ(m_mediaKeysIpc->createKeySession(m_keySessionType, m_mediaKeysClientMock, returnKeySessionid), MediaKeyErrorStatus::OK); EXPECT_EQ(returnKeySessionid, m_kKeySessionId); @@ -76,7 +74,7 @@ TEST_F(RialtoClientMediaKeysIpcCreateKeySessionTest, ChannelDisconnected) expectIpcApiCallDisconnected(); expectUnsubscribeEvents(); - EXPECT_EQ(m_mediaKeysIpc->createKeySession(m_keySessionType, m_mediaKeysClientMock, m_isLdl, returnKeySessionid), + EXPECT_EQ(m_mediaKeysIpc->createKeySession(m_keySessionType, m_mediaKeysClientMock, returnKeySessionid), MediaKeyErrorStatus::FAIL); // Reattach channel on destroySession @@ -98,7 +96,7 @@ TEST_F(RialtoClientMediaKeysIpcCreateKeySessionTest, ReconnectChannel) .WillOnce(WithArgs<3>( Invoke(this, &RialtoClientMediaKeysIpcCreateKeySessionTest::setCreateKeySessionResponseSuccess))); - EXPECT_EQ(m_mediaKeysIpc->createKeySession(m_keySessionType, m_mediaKeysClientMock, m_isLdl, returnKeySessionid), + EXPECT_EQ(m_mediaKeysIpc->createKeySession(m_keySessionType, m_mediaKeysClientMock, returnKeySessionid), MediaKeyErrorStatus::OK); } @@ -112,7 +110,7 @@ TEST_F(RialtoClientMediaKeysIpcCreateKeySessionTest, Failure) EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("createKeySession"), _, _, _, _)); - EXPECT_EQ(m_mediaKeysIpc->createKeySession(m_keySessionType, m_mediaKeysClientMock, m_isLdl, returnKeySessionid), + EXPECT_EQ(m_mediaKeysIpc->createKeySession(m_keySessionType, m_mediaKeysClientMock, returnKeySessionid), MediaKeyErrorStatus::FAIL); } @@ -128,6 +126,6 @@ TEST_F(RialtoClientMediaKeysIpcCreateKeySessionTest, ErrorReturn) .WillOnce( WithArgs<3>(Invoke(this, &RialtoClientMediaKeysIpcCreateKeySessionTest::setCreateKeySessionResponseFailed))); - EXPECT_EQ(m_mediaKeysIpc->createKeySession(m_keySessionType, m_mediaKeysClientMock, m_isLdl, returnKeySessionid), + EXPECT_EQ(m_mediaKeysIpc->createKeySession(m_keySessionType, m_mediaKeysClientMock, returnKeySessionid), m_errorStatus); } diff --git a/tests/unittests/media/client/ipc/mediaKeysIpc/base/MediaKeysIpcTestBase.cpp b/tests/unittests/media/client/ipc/mediaKeysIpc/base/MediaKeysIpcTestBase.cpp index e4de26e0d..c0ad54e56 100644 --- a/tests/unittests/media/client/ipc/mediaKeysIpc/base/MediaKeysIpcTestBase.cpp +++ b/tests/unittests/media/client/ipc/mediaKeysIpc/base/MediaKeysIpcTestBase.cpp @@ -103,7 +103,7 @@ void MediaKeysIpcTestBase::createKeySession() EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("createKeySession"), _, _, _, _)) .WillOnce(WithArgs<3>(Invoke(this, &MediaKeysIpcTestBase::setCreateKeySessionResponseSuccess))); - EXPECT_EQ(m_mediaKeysIpc->createKeySession(KeySessionType::PERSISTENT_LICENCE, m_mediaKeysClientMock, false, + EXPECT_EQ(m_mediaKeysIpc->createKeySession(KeySessionType::PERSISTENT_LICENCE, m_mediaKeysClientMock, returnKeySessionid), MediaKeyErrorStatus::OK); } diff --git a/tests/unittests/media/client/main/mediaKeys/KeySessionTest.cpp b/tests/unittests/media/client/main/mediaKeys/KeySessionTest.cpp index b4040ac2c..91f501c43 100644 --- a/tests/unittests/media/client/main/mediaKeys/KeySessionTest.cpp +++ b/tests/unittests/media/client/main/mediaKeys/KeySessionTest.cpp @@ -65,14 +65,12 @@ TEST_F(RialtoClientMediaKeysKeySessionTest, CreateKeySession) KeySessionType sessionType = KeySessionType::PERSISTENT_LICENCE; std::shared_ptr> mediaKeysClientMock = std::make_shared>(); - bool isLDL = false; int32_t returnKeySessionId; - EXPECT_CALL(*m_mediaKeysIpcMock, createKeySession(sessionType, _, isLDL, _)) - .WillOnce(DoAll(SetArgReferee<3>(m_kKeySessionId), Return(m_mediaKeyErrorStatus))); + EXPECT_CALL(*m_mediaKeysIpcMock, createKeySession(sessionType, _, _)) + .WillOnce(DoAll(SetArgReferee<2>(m_kKeySessionId), Return(m_mediaKeyErrorStatus))); - EXPECT_EQ(m_mediaKeys->createKeySession(sessionType, mediaKeysClientMock, isLDL, returnKeySessionId), - m_mediaKeyErrorStatus); + EXPECT_EQ(m_mediaKeys->createKeySession(sessionType, mediaKeysClientMock, returnKeySessionId), m_mediaKeyErrorStatus); EXPECT_EQ(returnKeySessionId, m_kKeySessionId); } @@ -83,11 +81,12 @@ TEST_F(RialtoClientMediaKeysKeySessionTest, GenerateRequest) { InitDataType initDataType = InitDataType::KEY_IDS; std::vector initData{7, 8, 9}; + LimitedDurationLicense ldlState{LimitedDurationLicense::NOT_SPECIFIED}; - EXPECT_CALL(*m_mediaKeysIpcMock, generateRequest(m_kKeySessionId, initDataType, initData)) + EXPECT_CALL(*m_mediaKeysIpcMock, generateRequest(m_kKeySessionId, initDataType, initData, ldlState)) .WillOnce(Return(m_mediaKeyErrorStatus)); - EXPECT_EQ(m_mediaKeys->generateRequest(m_kKeySessionId, initDataType, initData), m_mediaKeyErrorStatus); + EXPECT_EQ(m_mediaKeys->generateRequest(m_kKeySessionId, initDataType, initData, ldlState), m_mediaKeyErrorStatus); } /** diff --git a/tests/unittests/media/client/main/mediaKeys/NetflixKeySessionTest.cpp b/tests/unittests/media/client/main/mediaKeys/NetflixKeySessionTest.cpp index ee2001606..16548c9cf 100644 --- a/tests/unittests/media/client/main/mediaKeys/NetflixKeySessionTest.cpp +++ b/tests/unittests/media/client/main/mediaKeys/NetflixKeySessionTest.cpp @@ -69,14 +69,12 @@ TEST_F(RialtoClientMediaKeysNetflixKeySessionTest, CreateKeySession) KeySessionType sessionType = KeySessionType::PERSISTENT_LICENCE; std::shared_ptr> mediaKeysClientMock = std::make_shared>(); - bool isLDL = false; int32_t returnKeySessionId; - EXPECT_CALL(*m_mediaKeysIpcMock, createKeySession(sessionType, _, isLDL, _)) - .WillOnce(DoAll(SetArgReferee<3>(m_keySessionId), Return(m_mediaKeyErrorStatus))); + EXPECT_CALL(*m_mediaKeysIpcMock, createKeySession(sessionType, _, _)) + .WillOnce(DoAll(SetArgReferee<2>(m_keySessionId), Return(m_mediaKeyErrorStatus))); - EXPECT_EQ(m_mediaKeys->createKeySession(sessionType, mediaKeysClientMock, isLDL, returnKeySessionId), - m_mediaKeyErrorStatus); + EXPECT_EQ(m_mediaKeys->createKeySession(sessionType, mediaKeysClientMock, returnKeySessionId), m_mediaKeyErrorStatus); EXPECT_EQ(returnKeySessionId, m_keySessionId); // Update key should be possible, as keySession should be present in KeyIdMap EXPECT_TRUE(KeyIdMap::instance().updateKey(m_keySessionId, m_keyId)); diff --git a/tests/unittests/media/interface/mocks/MediaKeysMock.h b/tests/unittests/media/interface/mocks/MediaKeysMock.h index 450fd0197..14d3a6171 100644 --- a/tests/unittests/media/interface/mocks/MediaKeysMock.h +++ b/tests/unittests/media/interface/mocks/MediaKeysMock.h @@ -37,10 +37,11 @@ class MediaKeysMock : public IMediaKeys MOCK_METHOD(MediaKeyErrorStatus, selectKeyId, (int32_t keySessionId, const std::vector &keyId), (override)); MOCK_METHOD(bool, containsKey, (int32_t keySessionId, const std::vector &keyId), (override)); MOCK_METHOD(MediaKeyErrorStatus, createKeySession, - (KeySessionType sessionType, std::weak_ptr client, bool isLDL, int32_t &keySessionId), - (override)); + (KeySessionType sessionType, std::weak_ptr client, int32_t &keySessionId), (override)); MOCK_METHOD(MediaKeyErrorStatus, generateRequest, - (int32_t keySessionId, InitDataType initDataType, const std::vector &initData), (override)); + (int32_t keySessionId, InitDataType initDataType, const std::vector &initData, + const LimitedDurationLicense &ldlState), + (override)); MOCK_METHOD(MediaKeyErrorStatus, loadSession, (int32_t keySessionId), (override)); MOCK_METHOD(MediaKeyErrorStatus, updateSession, (int32_t keySessionId, const std::vector &responseData), (override)); diff --git a/tests/unittests/media/server/gstplayer/decryptor/DecryptTest.cpp b/tests/unittests/media/server/gstplayer/decryptor/DecryptTest.cpp index 42d82fc60..ec941d3f4 100644 --- a/tests/unittests/media/server/gstplayer/decryptor/DecryptTest.cpp +++ b/tests/unittests/media/server/gstplayer/decryptor/DecryptTest.cpp @@ -144,12 +144,12 @@ class RialtoServerDecryptorPrivateDecryptTest : public ::testing::Test void expectWidevineKeySystem() { - EXPECT_CALL(*m_decryptionServiceMock, isNetflixPlayreadyKeySystem(m_keySessionId)).WillOnce(Return(false)); + EXPECT_CALL(*m_decryptionServiceMock, isExtendedInterfaceUsed(m_keySessionId)).WillOnce(Return(false)); } void expectPlayreadyKeySystem() { - EXPECT_CALL(*m_decryptionServiceMock, isNetflixPlayreadyKeySystem(m_keySessionId)).WillOnce(Return(true)); + EXPECT_CALL(*m_decryptionServiceMock, isExtendedInterfaceUsed(m_keySessionId)).WillOnce(Return(true)); } void expectKeyMappingFailure() diff --git a/tests/unittests/media/server/ipc/mediaKeysModuleService/MediaKeysModuleServiceTestsFixture.cpp b/tests/unittests/media/server/ipc/mediaKeysModuleService/MediaKeysModuleServiceTestsFixture.cpp index 44720cfae..e1b3dfd5b 100644 --- a/tests/unittests/media/server/ipc/mediaKeysModuleService/MediaKeysModuleServiceTestsFixture.cpp +++ b/tests/unittests/media/server/ipc/mediaKeysModuleService/MediaKeysModuleServiceTestsFixture.cpp @@ -39,7 +39,6 @@ namespace const std::string keySystem{"expectedKeySystem"}; constexpr int kHardcodedMediaKeysHandle{2}; constexpr firebolt::rialto::KeySessionType kKeySessionType{firebolt::rialto::KeySessionType::TEMPORARY}; -constexpr bool kIsLDL{false}; constexpr int kKeySessionId{3}; constexpr firebolt::rialto::MediaKeyErrorStatus kErrorStatus{firebolt::rialto::MediaKeyErrorStatus::FAIL}; constexpr firebolt::rialto::InitDataType kInitDataType{firebolt::rialto::InitDataType::CENC}; @@ -50,6 +49,7 @@ const std::vector kDrmHeader{6, 3, 8}; const std::vector kLicenseRequestMessage{3, 2, 1}; const std::vector kLicenseRenewalMessage{0, 4, 8}; const std::string kUrl{"http://"}; +constexpr firebolt::rialto::LimitedDurationLicense kLdlState{firebolt::rialto::LimitedDurationLicense::NOT_SPECIFIED}; } // namespace MATCHER_P4(LicenseRequestEventMatcher, kKeySessionId, mediaKeysHandle, requestMessage, kUrl, "") @@ -142,29 +142,31 @@ void MediaKeysModuleServiceTests::cdmServiceWillCreateKeySession() { expectRequestSuccess(); EXPECT_CALL(*m_controllerMock, getClient()).WillOnce(Return(m_clientMock)); - EXPECT_CALL(m_cdmServiceMock, createKeySession(kHardcodedMediaKeysHandle, kKeySessionType, _, kIsLDL, _)) - .WillOnce(DoAll(SetArgReferee<4>(kKeySessionId), Return(firebolt::rialto::MediaKeyErrorStatus::OK))); + EXPECT_CALL(m_cdmServiceMock, createKeySession(kHardcodedMediaKeysHandle, kKeySessionType, _, _)) + .WillOnce(DoAll(SetArgReferee<3>(kKeySessionId), Return(firebolt::rialto::MediaKeyErrorStatus::OK))); } void MediaKeysModuleServiceTests::cdmServiceWillFailToCreateKeySession() { expectRequestSuccess(); EXPECT_CALL(*m_controllerMock, getClient()).WillOnce(Return(m_clientMock)); - EXPECT_CALL(m_cdmServiceMock, createKeySession(kHardcodedMediaKeysHandle, kKeySessionType, _, kIsLDL, _)) + EXPECT_CALL(m_cdmServiceMock, createKeySession(kHardcodedMediaKeysHandle, kKeySessionType, _, _)) .WillOnce(Return(kErrorStatus)); } void MediaKeysModuleServiceTests::cdmServiceWillGenerateRequest() { expectRequestSuccess(); - EXPECT_CALL(m_cdmServiceMock, generateRequest(kHardcodedMediaKeysHandle, kKeySessionId, kInitDataType, kInitData)) + EXPECT_CALL(m_cdmServiceMock, + generateRequest(kHardcodedMediaKeysHandle, kKeySessionId, kInitDataType, kInitData, kLdlState)) .WillOnce(Return(firebolt::rialto::MediaKeyErrorStatus::OK)); } void MediaKeysModuleServiceTests::cdmServiceWillFailToGenerateRequest() { expectRequestSuccess(); - EXPECT_CALL(m_cdmServiceMock, generateRequest(kHardcodedMediaKeysHandle, kKeySessionId, kInitDataType, kInitData)) + EXPECT_CALL(m_cdmServiceMock, + generateRequest(kHardcodedMediaKeysHandle, kKeySessionId, kInitDataType, kInitData, kLdlState)) .WillOnce(Return(kErrorStatus)); } @@ -483,7 +485,6 @@ void MediaKeysModuleServiceTests::sendCreateKeySessionRequestAndReceiveResponse( request.set_media_keys_handle(kHardcodedMediaKeysHandle); request.set_session_type(convertKeySessionType(kKeySessionType)); - request.set_is_ldl(kIsLDL); m_service->createKeySession(m_controllerMock.get(), &request, &response, m_closureMock.get()); EXPECT_GE(response.key_session_id(), -1); @@ -497,7 +498,6 @@ void MediaKeysModuleServiceTests::sendCreateKeySessionRequestAndReceiveErrorResp request.set_media_keys_handle(kHardcodedMediaKeysHandle); request.set_session_type(convertKeySessionType(kKeySessionType)); - request.set_is_ldl(kIsLDL); m_service->createKeySession(m_controllerMock.get(), &request, &response, m_closureMock.get()); EXPECT_GE(response.key_session_id(), -1); @@ -511,7 +511,6 @@ void MediaKeysModuleServiceTests::sendCreateKeySessionRequestWithInvalidIpcAndRe request.set_media_keys_handle(kHardcodedMediaKeysHandle); request.set_session_type(convertKeySessionType(kKeySessionType)); - request.set_is_ldl(kIsLDL); m_service->createKeySession(m_invalidControllerMock.get(), &request, &response, m_closureMock.get()); } @@ -524,6 +523,7 @@ void MediaKeysModuleServiceTests::sendGenerateRequestRequestAndReceiveResponse() request.set_media_keys_handle(kHardcodedMediaKeysHandle); request.set_key_session_id(kKeySessionId); request.set_init_data_type(convertInitDataType(kInitDataType)); + request.set_ldl_state(convertLimitedDurationLicense(kLdlState)); for (auto it = kInitData.begin(); it != kInitData.end(); it++) { @@ -542,6 +542,7 @@ void MediaKeysModuleServiceTests::sendGenerateRequestRequestAndReceiveErrorRespo request.set_media_keys_handle(kHardcodedMediaKeysHandle); request.set_key_session_id(kKeySessionId); request.set_init_data_type(convertInitDataType(kInitDataType)); + request.set_ldl_state(convertLimitedDurationLicense(kLdlState)); for (auto it = kInitData.begin(); it != kInitData.end(); it++) { diff --git a/tests/unittests/media/server/main/CMakeLists.txt b/tests/unittests/media/server/main/CMakeLists.txt index 59d2ded8e..39943cbc4 100644 --- a/tests/unittests/media/server/main/CMakeLists.txt +++ b/tests/unittests/media/server/main/CMakeLists.txt @@ -68,7 +68,6 @@ add_gtests ( mediaKeys/GetDrmTimeTest.cpp mediaKeys/GetLastDrmErrorTest.cpp mediaKeys/SelectKeyIdTest.cpp - mediaKeys/IsNetflixPlayreadyKeySystemTest.cpp mediaKeys/PingTest.cpp mediaKeys/ReleaseKeySessionTest.cpp mediaKeys/GetMetricSystemDataTest.cpp @@ -91,7 +90,6 @@ add_gtests ( mediaKeySession/SetDrmHeaderTest.cpp mediaKeySession/GetLastDrmErrorTest.cpp mediaKeySession/SelectKeyIdTest.cpp - mediaKeySession/IsNetflixPlayreadyKeySystemTest.cpp sharedMemoryBuffer/SharedMemoryBufferTestsFixture.cpp sharedMemoryBuffer/SharedMemoryBufferTests.cpp diff --git a/tests/unittests/media/server/main/mediaKeySession/CallbacksTest.cpp b/tests/unittests/media/server/main/mediaKeySession/CallbacksTest.cpp index 948ce3c90..03a4f2d2b 100644 --- a/tests/unittests/media/server/main/mediaKeySession/CallbacksTest.cpp +++ b/tests/unittests/media/server/main/mediaKeySession/CallbacksTest.cpp @@ -60,7 +60,7 @@ TEST_F(RialtoServerMediaKeySessionCallbacksTest, ProcessChallengeNoGenerateReque /** * Test that onProcessChallenge after a generateRequest for none Netflix key system notifies licenseRequest. */ -TEST_F(RialtoServerMediaKeySessionCallbacksTest, ProcessChallengeGenerateRequestNoneNetflix) +TEST_F(RialtoServerMediaKeySessionCallbacksTest, ProcessChallengeGenerateRequest) { generateRequest(); mainThreadWillEnqueueTask(); diff --git a/tests/unittests/media/server/main/mediaKeySession/CloseKeySessionTest.cpp b/tests/unittests/media/server/main/mediaKeySession/CloseKeySessionTest.cpp index 8552b6741..569ee760e 100644 --- a/tests/unittests/media/server/main/mediaKeySession/CloseKeySessionTest.cpp +++ b/tests/unittests/media/server/main/mediaKeySession/CloseKeySessionTest.cpp @@ -31,11 +31,14 @@ class RialtoServerMediaKeySessionCloseKeySessionTest : public MediaKeySessionTes TEST_F(RialtoServerMediaKeySessionCloseKeySessionTest, SuccessNetflix) { createKeySession(kNetflixKeySystem); + generateRequestPlayready(); EXPECT_CALL(*m_ocdmSessionMock, cancelChallengeData()).WillOnce(Return(MediaKeyErrorStatus::OK)); EXPECT_CALL(*m_ocdmSessionMock, cleanDecryptContext()).WillOnce(Return(MediaKeyErrorStatus::OK)); EXPECT_EQ(MediaKeyErrorStatus::OK, m_mediaKeySession->closeKeySession()); + + EXPECT_CALL(*m_ocdmSessionMock, destructSession()).WillOnce(Return(MediaKeyErrorStatus::OK)); } /** @@ -56,11 +59,14 @@ TEST_F(RialtoServerMediaKeySessionCloseKeySessionTest, SuccessNoneNetflix) TEST_F(RialtoServerMediaKeySessionCloseKeySessionTest, OcdmSessionCancelChallengeDataFailure) { createKeySession(kNetflixKeySystem); + generateRequestPlayready(); EXPECT_CALL(*m_ocdmSessionMock, cancelChallengeData()).WillOnce(Return(MediaKeyErrorStatus::INVALID_STATE)); EXPECT_CALL(*m_ocdmSessionMock, cleanDecryptContext()).WillOnce(Return(MediaKeyErrorStatus::OK)); EXPECT_EQ(MediaKeyErrorStatus::OK, m_mediaKeySession->closeKeySession()); + + EXPECT_CALL(*m_ocdmSessionMock, destructSession()).WillOnce(Return(MediaKeyErrorStatus::OK)); } /** @@ -69,11 +75,14 @@ TEST_F(RialtoServerMediaKeySessionCloseKeySessionTest, OcdmSessionCancelChalleng TEST_F(RialtoServerMediaKeySessionCloseKeySessionTest, OcdmSessionCleanDecryptContextFailure) { createKeySession(kNetflixKeySystem); + generateRequestPlayready(); EXPECT_CALL(*m_ocdmSessionMock, cancelChallengeData()).WillOnce(Return(MediaKeyErrorStatus::OK)); EXPECT_CALL(*m_ocdmSessionMock, cleanDecryptContext()).WillOnce(Return(MediaKeyErrorStatus::NOT_SUPPORTED)); EXPECT_EQ(MediaKeyErrorStatus::OK, m_mediaKeySession->closeKeySession()); + + EXPECT_CALL(*m_ocdmSessionMock, destructSession()).WillOnce(Return(MediaKeyErrorStatus::OK)); } /** diff --git a/tests/unittests/media/server/main/mediaKeySession/CreateTest.cpp b/tests/unittests/media/server/main/mediaKeySession/CreateTest.cpp index 0e70388a2..a0b6c8da8 100644 --- a/tests/unittests/media/server/main/mediaKeySession/CreateTest.cpp +++ b/tests/unittests/media/server/main/mediaKeySession/CreateTest.cpp @@ -34,8 +34,7 @@ TEST_F(RialtoServerCreateMediaKeySessionTest, Create) EXPECT_NO_THROW(m_mediaKeySession = std::make_unique(kNetflixKeySystem, m_kKeySessionId, *m_ocdmSystemMock, m_keySessionType, - m_mediaKeysClientMock, m_isLDL, - m_mainThreadFactoryMock)); + m_mediaKeysClientMock, m_mainThreadFactoryMock)); EXPECT_NE(m_mediaKeySession, nullptr); destroyKeySession(); @@ -52,7 +51,7 @@ TEST_F(RialtoServerCreateMediaKeySessionTest, FactoryCreatesObject) EXPECT_CALL(*m_ocdmSystemMock, createSession(_)).WillOnce(Return(ByMove(std::move(m_ocdmSession)))); EXPECT_NE(factory->createMediaKeySession(kNetflixKeySystem, m_kKeySessionId, *m_ocdmSystemMock, m_keySessionType, - m_mediaKeysClientMock, m_isLDL), + m_mediaKeysClientMock), nullptr); } @@ -66,8 +65,7 @@ TEST_F(RialtoServerCreateMediaKeySessionTest, CreateMainThreadFailure) EXPECT_THROW(m_mediaKeySession = std::make_unique(kNetflixKeySystem, m_kKeySessionId, *m_ocdmSystemMock, m_keySessionType, - m_mediaKeysClientMock, m_isLDL, - m_mainThreadFactoryMock), + m_mediaKeysClientMock, m_mainThreadFactoryMock), std::runtime_error); } @@ -83,7 +81,6 @@ TEST_F(RialtoServerCreateMediaKeySessionTest, CreateOcdmSessionFailure) EXPECT_THROW(m_mediaKeySession = std::make_unique(kNetflixKeySystem, m_kKeySessionId, *m_ocdmSystemMock, m_keySessionType, - m_mediaKeysClientMock, m_isLDL, - m_mainThreadFactoryMock), + m_mediaKeysClientMock, m_mainThreadFactoryMock), std::runtime_error); } diff --git a/tests/unittests/media/server/main/mediaKeySession/GenerateRequestTest.cpp b/tests/unittests/media/server/main/mediaKeySession/GenerateRequestTest.cpp index 2e0cf179d..d967ac7de 100644 --- a/tests/unittests/media/server/main/mediaKeySession/GenerateRequestTest.cpp +++ b/tests/unittests/media/server/main/mediaKeySession/GenerateRequestTest.cpp @@ -22,28 +22,9 @@ using ::testing::DoAll; using ::testing::SetArgPointee; -MATCHER(nullptrMatcher, "") -{ - return arg == nullptr; -} - -MATCHER(notNullptrMatcher, "") -{ - return arg != nullptr; -} - -ACTION_P(memcpyChallenge, vec) -{ - memcpy(arg1, &vec[0], vec.size()); -} - class RialtoServerMediaKeySessionGenerateRequestTest : public MediaKeySessionTestBase { protected: - const InitDataType m_kInitDataType = InitDataType::CENC; - const std::vector m_kInitData{1, 2, 3}; - const std::vector m_kChallenge{'d', 'e', 'f'}; - ~RialtoServerMediaKeySessionGenerateRequestTest() { destroyKeySession(); } }; @@ -58,7 +39,7 @@ TEST_F(RialtoServerMediaKeySessionGenerateRequestTest, SuccessNoneNetflix) constructSession(m_keySessionType, m_kInitDataType, &m_kInitData[0], m_kInitData.size())) .WillOnce(Return(MediaKeyErrorStatus::OK)); - EXPECT_EQ(MediaKeyErrorStatus::OK, m_mediaKeySession->generateRequest(m_kInitDataType, m_kInitData)); + EXPECT_EQ(MediaKeyErrorStatus::OK, m_mediaKeySession->generateRequest(m_kInitDataType, m_kInitData, m_kLdlState)); // Close ocdm before destroying expectCloseKeySession(kWidevineKeySystem); @@ -71,18 +52,20 @@ TEST_F(RialtoServerMediaKeySessionGenerateRequestTest, SuccessNetflix) { createKeySession(kNetflixKeySystem); - EXPECT_CALL(*m_ocdmSessionMock, - constructSession(m_keySessionType, m_kInitDataType, &m_kInitData[0], m_kInitData.size())) - .WillOnce(Return(MediaKeyErrorStatus::OK)); - mainThreadWillEnqueueTask(); - EXPECT_CALL(*m_ocdmSessionMock, getChallengeData(m_isLDL, nullptrMatcher(), _)) - .WillOnce(DoAll(SetArgPointee<2>(m_kChallenge.size()), Return(MediaKeyErrorStatus::OK))); - EXPECT_CALL(*m_ocdmSessionMock, getChallengeData(m_isLDL, notNullptrMatcher(), _)) - .WillOnce(DoAll(memcpyChallenge(m_kChallenge), Return(MediaKeyErrorStatus::OK))); - mainThreadWillEnqueueTask(); - EXPECT_CALL(*m_mediaKeysClientMock, onLicenseRequest(m_kKeySessionId, m_kChallenge, _)); + generateRequestPlayready(); + + // Close ocdm before destroying + expectCloseKeySession(kNetflixKeySystem); +} + +/** + * Test that GenerateRequest can generate request successfully for a netflix keysystem. + */ +TEST_F(RialtoServerMediaKeySessionGenerateRequestTest, SuccessNetflixWithTwoGenerateChallengeCalls) +{ + createKeySession(kNetflixKeySystem); - EXPECT_EQ(MediaKeyErrorStatus::OK, m_mediaKeySession->generateRequest(m_kInitDataType, m_kInitData)); + generateRequestPlayreadyWithTwoCalls(); // Close ocdm before destroying expectCloseKeySession(kNetflixKeySystem); @@ -102,7 +85,9 @@ TEST_F(RialtoServerMediaKeySessionGenerateRequestTest, FailNetflixWhenChallengeD EXPECT_CALL(*m_ocdmSessionMock, getChallengeData(m_isLDL, nullptrMatcher(), _)) .WillOnce(Return(MediaKeyErrorStatus::OK)); - EXPECT_EQ(MediaKeyErrorStatus::OK, m_mediaKeySession->generateRequest(m_kInitDataType, m_kInitData)); + EXPECT_EQ(MediaKeyErrorStatus::OK, + m_mediaKeySession->generateRequest(m_kInitDataType, m_kInitData, + firebolt::rialto::LimitedDurationLicense::DISABLED)); // Close ocdm before destroying expectCloseKeySession(kNetflixKeySystem); @@ -124,7 +109,9 @@ TEST_F(RialtoServerMediaKeySessionGenerateRequestTest, FailNetflixWhenGettingCha EXPECT_CALL(*m_ocdmSessionMock, getChallengeData(m_isLDL, notNullptrMatcher(), _)) .WillOnce(DoAll(memcpyChallenge(m_kChallenge), Return(MediaKeyErrorStatus::FAIL))); - EXPECT_EQ(MediaKeyErrorStatus::OK, m_mediaKeySession->generateRequest(m_kInitDataType, m_kInitData)); + EXPECT_EQ(MediaKeyErrorStatus::OK, + m_mediaKeySession->generateRequest(m_kInitDataType, m_kInitData, + firebolt::rialto::LimitedDurationLicense::DISABLED)); // Close ocdm before destroying expectCloseKeySession(kNetflixKeySystem); @@ -140,10 +127,10 @@ TEST_F(RialtoServerMediaKeySessionGenerateRequestTest, SessionAlreadyConstructed EXPECT_CALL(*m_ocdmSessionMock, constructSession(m_keySessionType, m_kInitDataType, &m_kInitData[0], m_kInitData.size())) .WillOnce(Return(MediaKeyErrorStatus::OK)); - EXPECT_EQ(MediaKeyErrorStatus::OK, m_mediaKeySession->generateRequest(m_kInitDataType, m_kInitData)); + EXPECT_EQ(MediaKeyErrorStatus::OK, m_mediaKeySession->generateRequest(m_kInitDataType, m_kInitData, m_kLdlState)); // Generate request again should just return OK - EXPECT_EQ(MediaKeyErrorStatus::OK, m_mediaKeySession->generateRequest(m_kInitDataType, m_kInitData)); + EXPECT_EQ(MediaKeyErrorStatus::OK, m_mediaKeySession->generateRequest(m_kInitDataType, m_kInitData, m_kLdlState)); // OcdmSession will be closed on destruction expectCloseKeySession(kWidevineKeySystem); @@ -158,7 +145,8 @@ TEST_F(RialtoServerMediaKeySessionGenerateRequestTest, OcdmSessionFailure) EXPECT_CALL(*m_ocdmSessionMock, constructSession(m_keySessionType, m_kInitDataType, &m_kInitData[0], m_kInitData.size())) .WillOnce(Return(MediaKeyErrorStatus::NOT_SUPPORTED)); - EXPECT_EQ(MediaKeyErrorStatus::NOT_SUPPORTED, m_mediaKeySession->generateRequest(m_kInitDataType, m_kInitData)); + EXPECT_EQ(MediaKeyErrorStatus::NOT_SUPPORTED, + m_mediaKeySession->generateRequest(m_kInitDataType, m_kInitData, m_kLdlState)); } /** @@ -176,7 +164,7 @@ TEST_F(RialtoServerMediaKeySessionGenerateRequestTest, OnErrorFailure) return MediaKeyErrorStatus::OK; })); - EXPECT_EQ(MediaKeyErrorStatus::FAIL, m_mediaKeySession->generateRequest(m_kInitDataType, m_kInitData)); + EXPECT_EQ(MediaKeyErrorStatus::FAIL, m_mediaKeySession->generateRequest(m_kInitDataType, m_kInitData, m_kLdlState)); // OcdmSession will be closed on destruction expectCloseKeySession(kWidevineKeySystem); diff --git a/tests/unittests/media/server/main/mediaKeySession/IsNetflixPlayreadyKeySystemTest.cpp b/tests/unittests/media/server/main/mediaKeySession/IsNetflixPlayreadyKeySystemTest.cpp deleted file mode 100644 index 989a4ea2a..000000000 --- a/tests/unittests/media/server/main/mediaKeySession/IsNetflixPlayreadyKeySystemTest.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2023 Sky UK - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "MediaKeySessionTestBase.h" - -class RialtoServerMediaKeySessionIsNetflixPlayreadyKeySystemTest : public MediaKeySessionTestBase -{ -protected: - ~RialtoServerMediaKeySessionIsNetflixPlayreadyKeySystemTest() { destroyKeySession(); } -}; - -/** - * Test that isNetflixPlayreadyKeySystem returns false for microsoft playready key system - */ -TEST_F(RialtoServerMediaKeySessionIsNetflixPlayreadyKeySystemTest, ReturnFalseForMsPlayready) -{ - createKeySession(kPlayreadyKeySystem); - - EXPECT_FALSE(m_mediaKeySession->isNetflixPlayreadyKeySystem()); -} - -/** - * Test that isNetflixPlayreadyKeySystem returns true for netflix key system - */ -TEST_F(RialtoServerMediaKeySessionIsNetflixPlayreadyKeySystemTest, ReturnTrueForNetflix) -{ - createKeySession(kNetflixKeySystem); - - EXPECT_TRUE(m_mediaKeySession->isNetflixPlayreadyKeySystem()); -} - -/** - * Test that isNetflixPlayreadyKeySystem returns false for widevine key system - */ -TEST_F(RialtoServerMediaKeySessionIsNetflixPlayreadyKeySystemTest, ReturnFalseForWidevine) -{ - createKeySession(kWidevineKeySystem); - - EXPECT_FALSE(m_mediaKeySession->isNetflixPlayreadyKeySystem()); -} diff --git a/tests/unittests/media/server/main/mediaKeySession/SetDrmHeaderTest.cpp b/tests/unittests/media/server/main/mediaKeySession/SetDrmHeaderTest.cpp index 18b027a32..7969dac84 100644 --- a/tests/unittests/media/server/main/mediaKeySession/SetDrmHeaderTest.cpp +++ b/tests/unittests/media/server/main/mediaKeySession/SetDrmHeaderTest.cpp @@ -19,6 +19,18 @@ #include "MediaKeySessionTestBase.h" +MATCHER_P(drmHeaderMatcher, header, "") +{ + for (size_t i = 0; i < header.size(); ++i) + { + if (arg[i] != header[i]) + { + return false; + } + } + return true; +} + class RialtoServerMediaKeySessionSetDrmHeaderTest : public MediaKeySessionTestBase { protected: @@ -33,10 +45,15 @@ TEST_F(RialtoServerMediaKeySessionSetDrmHeaderTest, Success) { createKeySession(kNetflixKeySystem); + generateRequestPlayready(); + EXPECT_CALL(*m_ocdmSessionMock, setDrmHeader(&m_kDrmHeader[0], m_kDrmHeader.size())) .WillOnce(Return(MediaKeyErrorStatus::OK)); EXPECT_EQ(MediaKeyErrorStatus::OK, m_mediaKeySession->setDrmHeader(m_kDrmHeader)); + + // Close ocdm before destroying + expectCloseKeySession(kNetflixKeySystem); } /** @@ -46,10 +63,15 @@ TEST_F(RialtoServerMediaKeySessionSetDrmHeaderTest, OcdmSessionFailure) { createKeySession(kNetflixKeySystem); + generateRequestPlayready(); + EXPECT_CALL(*m_ocdmSessionMock, setDrmHeader(&m_kDrmHeader[0], m_kDrmHeader.size())) .WillOnce(Return(MediaKeyErrorStatus::FAIL)); EXPECT_EQ(MediaKeyErrorStatus::FAIL, m_mediaKeySession->setDrmHeader(m_kDrmHeader)); + + // Close ocdm before destroying + expectCloseKeySession(kNetflixKeySystem); } /** @@ -57,7 +79,9 @@ TEST_F(RialtoServerMediaKeySessionSetDrmHeaderTest, OcdmSessionFailure) */ TEST_F(RialtoServerMediaKeySessionSetDrmHeaderTest, OnErrorFailure) { - createKeySession(kWidevineKeySystem); + createKeySession(kNetflixKeySystem); + + generateRequestPlayready(); EXPECT_CALL(*m_ocdmSessionMock, setDrmHeader(&m_kDrmHeader[0], m_kDrmHeader.size())) .WillOnce(Invoke( @@ -68,4 +92,7 @@ TEST_F(RialtoServerMediaKeySessionSetDrmHeaderTest, OnErrorFailure) })); EXPECT_EQ(MediaKeyErrorStatus::FAIL, m_mediaKeySession->setDrmHeader(m_kDrmHeader)); + + // Close ocdm before destroying + expectCloseKeySession(kNetflixKeySystem); } diff --git a/tests/unittests/media/server/main/mediaKeySession/UpdateSessionTest.cpp b/tests/unittests/media/server/main/mediaKeySession/UpdateSessionTest.cpp index d6ae82c0e..7c826a14f 100644 --- a/tests/unittests/media/server/main/mediaKeySession/UpdateSessionTest.cpp +++ b/tests/unittests/media/server/main/mediaKeySession/UpdateSessionTest.cpp @@ -33,11 +33,15 @@ class RialtoServerMediaKeySessionUpdateSessionTest : public MediaKeySessionTestB TEST_F(RialtoServerMediaKeySessionUpdateSessionTest, SuccessNetflix) { createKeySession(kNetflixKeySystem); + generateRequestPlayready(); EXPECT_CALL(*m_ocdmSessionMock, storeLicenseData(&m_kResponseData[0], m_kResponseData.size())) .WillOnce(Return(MediaKeyErrorStatus::OK)); EXPECT_EQ(MediaKeyErrorStatus::OK, m_mediaKeySession->updateSession(m_kResponseData)); + + // Close ocdm before destroying + expectCloseKeySession(kNetflixKeySystem); } /** @@ -59,11 +63,15 @@ TEST_F(RialtoServerMediaKeySessionUpdateSessionTest, SuccessNoneNetflix) TEST_F(RialtoServerMediaKeySessionUpdateSessionTest, OcdmSessionStoreLicenseDataFailure) { createKeySession(kNetflixKeySystem); + generateRequestPlayready(); EXPECT_CALL(*m_ocdmSessionMock, storeLicenseData(&m_kResponseData[0], m_kResponseData.size())) .WillOnce(Return(MediaKeyErrorStatus::INVALID_STATE)); EXPECT_EQ(MediaKeyErrorStatus::INVALID_STATE, m_mediaKeySession->updateSession(m_kResponseData)); + + // Close ocdm before destroying + expectCloseKeySession(kNetflixKeySystem); } /** diff --git a/tests/unittests/media/server/main/mediaKeySession/base/MediaKeySessionTestBase.cpp b/tests/unittests/media/server/main/mediaKeySession/base/MediaKeySessionTestBase.cpp index 05f9184cf..12eb74230 100644 --- a/tests/unittests/media/server/main/mediaKeySession/base/MediaKeySessionTestBase.cpp +++ b/tests/unittests/media/server/main/mediaKeySession/base/MediaKeySessionTestBase.cpp @@ -42,7 +42,7 @@ void MediaKeySessionTestBase::createKeySession(const std::string &keySystem) EXPECT_NO_THROW(m_mediaKeySession = std::make_unique(keySystem, m_kKeySessionId, *m_ocdmSystemMock, m_keySessionType, m_mediaKeysClientMock, - m_isLDL, m_mainThreadFactoryMock)); + m_mainThreadFactoryMock)); EXPECT_NE(m_mediaKeySession, nullptr); } @@ -74,7 +74,47 @@ void MediaKeySessionTestBase::generateRequest() EXPECT_CALL(*m_ocdmSessionMock, constructSession(m_keySessionType, m_initDataType, &m_initData[0], m_initData.size())) .WillOnce(Return(MediaKeyErrorStatus::OK)); - EXPECT_EQ(MediaKeyErrorStatus::OK, m_mediaKeySession->generateRequest(m_initDataType, m_initData)); + EXPECT_EQ(MediaKeyErrorStatus::OK, m_mediaKeySession->generateRequest(m_initDataType, m_initData, m_kLdlState)); +} + +void MediaKeySessionTestBase::generateRequestPlayready() +{ + EXPECT_CALL(*m_ocdmSessionMock, + constructSession(m_keySessionType, m_kInitDataType, &m_kInitData[0], m_kInitData.size())) + .WillOnce(Return(MediaKeyErrorStatus::OK)); + mainThreadWillEnqueueTask(); + EXPECT_CALL(*m_ocdmSessionMock, getChallengeData(m_isLDL, nullptrMatcher(), _)) + .WillOnce(DoAll(SetArgPointee<2>(m_kChallenge.size()), Return(MediaKeyErrorStatus::OK))); + EXPECT_CALL(*m_ocdmSessionMock, getChallengeData(m_isLDL, notNullptrMatcher(), _)) + .WillOnce(DoAll(memcpyChallenge(m_kChallenge), Return(MediaKeyErrorStatus::OK))); + mainThreadWillEnqueueTask(); + EXPECT_CALL(*m_mediaKeysClientMock, onLicenseRequest(m_kKeySessionId, m_kChallenge, _)); + + EXPECT_EQ(MediaKeyErrorStatus::OK, + m_mediaKeySession->generateRequest(m_kInitDataType, m_kInitData, + firebolt::rialto::LimitedDurationLicense::DISABLED)); +} + +void MediaKeySessionTestBase::generateRequestPlayreadyWithTwoCalls() +{ + EXPECT_CALL(*m_ocdmSessionMock, + constructSession(m_keySessionType, m_kInitDataType, &m_kInitData[0], m_kInitData.size())) + .WillOnce(Return(MediaKeyErrorStatus::OK)); + + EXPECT_EQ(MediaKeyErrorStatus::OK, + m_mediaKeySession->generateRequest(m_kInitDataType, m_kInitData, + firebolt::rialto::LimitedDurationLicense::NOT_SPECIFIED)); + mainThreadWillEnqueueTask(); + EXPECT_CALL(*m_ocdmSessionMock, getChallengeData(m_isLDL, nullptrMatcher(), _)) + .WillOnce(DoAll(SetArgPointee<2>(m_kChallenge.size()), Return(MediaKeyErrorStatus::OK))); + EXPECT_CALL(*m_ocdmSessionMock, getChallengeData(m_isLDL, notNullptrMatcher(), _)) + .WillOnce(DoAll(memcpyChallenge(m_kChallenge), Return(MediaKeyErrorStatus::OK))); + mainThreadWillEnqueueTask(); + EXPECT_CALL(*m_mediaKeysClientMock, onLicenseRequest(m_kKeySessionId, m_kChallenge, _)); + + EXPECT_EQ(MediaKeyErrorStatus::OK, + m_mediaKeySession->generateRequest(m_kInitDataType, m_kInitData, + firebolt::rialto::LimitedDurationLicense::DISABLED)); } void MediaKeySessionTestBase::mainThreadWillEnqueueTask() diff --git a/tests/unittests/media/server/main/mediaKeySession/base/MediaKeySessionTestBase.h b/tests/unittests/media/server/main/mediaKeySession/base/MediaKeySessionTestBase.h index b13514d76..5bf10e5c8 100644 --- a/tests/unittests/media/server/main/mediaKeySession/base/MediaKeySessionTestBase.h +++ b/tests/unittests/media/server/main/mediaKeySession/base/MediaKeySessionTestBase.h @@ -30,6 +30,7 @@ #include #include #include +#include using namespace firebolt::rialto; using namespace firebolt::rialto::server; @@ -38,10 +39,27 @@ using namespace firebolt::rialto::server::mock; using ::testing::_; using ::testing::ByMove; +using ::testing::DoAll; using ::testing::Invoke; using ::testing::Return; +using ::testing::SetArgPointee; using ::testing::StrictMock; +MATCHER(nullptrMatcher, "") +{ + return arg == nullptr; +} + +MATCHER(notNullptrMatcher, "") +{ + return arg != nullptr; +} + +ACTION_P(memcpyChallenge, vec) +{ + memcpy(arg1, &vec[0], vec.size()); +} + class MediaKeySessionTestBase : public ::testing::Test { public: @@ -65,11 +83,17 @@ class MediaKeySessionTestBase : public ::testing::Test const int32_t m_kMainThreadClientId = {5}; KeySessionType m_keySessionType = KeySessionType::PERSISTENT_RELEASE_MESSAGE; bool m_isLDL = false; + const InitDataType m_kInitDataType{InitDataType::CENC}; + const std::vector m_kInitData{1, 2, 3}; + const std::vector m_kChallenge{'d', 'e', 'f'}; + const LimitedDurationLicense m_kLdlState{LimitedDurationLicense::NOT_SPECIFIED}; void createKeySession(const std::string &keySystem); void destroyKeySession(); void expectCloseKeySession(const std::string &keySystem); void generateRequest(); + void generateRequestPlayready(); + void generateRequestPlayreadyWithTwoCalls(); void mainThreadWillEnqueueTask(); }; diff --git a/tests/unittests/media/server/main/mediaKeys/CreateKeySessionTest.cpp b/tests/unittests/media/server/main/mediaKeys/CreateKeySessionTest.cpp index 40ea6c374..4ab26341c 100644 --- a/tests/unittests/media/server/main/mediaKeys/CreateKeySessionTest.cpp +++ b/tests/unittests/media/server/main/mediaKeys/CreateKeySessionTest.cpp @@ -34,12 +34,11 @@ TEST_F(RialtoServerMediaKeysCreateKeySessionTest, Success) int32_t returnKeySessionId = -1; mainThreadWillEnqueueTaskAndWait(); - EXPECT_CALL(*m_mediaKeySessionFactoryMock, - createMediaKeySession(kNetflixKeySystem, _, _, m_keySessionType, _, m_isLDL)) + EXPECT_CALL(*m_mediaKeySessionFactoryMock, createMediaKeySession(kNetflixKeySystem, _, _, m_keySessionType, _)) .WillOnce(Return(ByMove(std::move(m_mediaKeySession)))); EXPECT_EQ(MediaKeyErrorStatus::OK, - m_mediaKeys->createKeySession(m_keySessionType, m_mediaKeysClientMock, m_isLDL, returnKeySessionId)); + m_mediaKeys->createKeySession(m_keySessionType, m_mediaKeysClientMock, returnKeySessionId)); EXPECT_GE(returnKeySessionId, -1); } @@ -51,10 +50,9 @@ TEST_F(RialtoServerMediaKeysCreateKeySessionTest, OcdmSystemFailure) int32_t returnKeySessionId = -1; mainThreadWillEnqueueTaskAndWait(); - EXPECT_CALL(*m_mediaKeySessionFactoryMock, - createMediaKeySession(kNetflixKeySystem, _, _, m_keySessionType, _, m_isLDL)) + EXPECT_CALL(*m_mediaKeySessionFactoryMock, createMediaKeySession(kNetflixKeySystem, _, _, m_keySessionType, _)) .WillOnce(Return(ByMove(nullptr))); EXPECT_EQ(MediaKeyErrorStatus::FAIL, - m_mediaKeys->createKeySession(m_keySessionType, m_mediaKeysClientMock, m_isLDL, returnKeySessionId)); + m_mediaKeys->createKeySession(m_keySessionType, m_mediaKeysClientMock, returnKeySessionId)); } diff --git a/tests/unittests/media/server/main/mediaKeys/GenerateRequestTest.cpp b/tests/unittests/media/server/main/mediaKeys/GenerateRequestTest.cpp index d69ac3cf8..50becd378 100644 --- a/tests/unittests/media/server/main/mediaKeys/GenerateRequestTest.cpp +++ b/tests/unittests/media/server/main/mediaKeys/GenerateRequestTest.cpp @@ -24,6 +24,7 @@ class RialtoServerMediaKeysGenerateRequestTest : public MediaKeysTestBase protected: InitDataType m_initDataType = InitDataType::CENC; std::vector m_initData{1, 2, 3}; + LimitedDurationLicense m_ldlState{LimitedDurationLicense::NOT_SPECIFIED}; RialtoServerMediaKeysGenerateRequestTest() { @@ -39,10 +40,11 @@ class RialtoServerMediaKeysGenerateRequestTest : public MediaKeysTestBase TEST_F(RialtoServerMediaKeysGenerateRequestTest, Success) { mainThreadWillEnqueueTaskAndWait(); - EXPECT_CALL(*m_mediaKeySessionMock, generateRequest(m_initDataType, m_initData)) + EXPECT_CALL(*m_mediaKeySessionMock, generateRequest(m_initDataType, m_initData, m_ldlState)) .WillOnce(Return(MediaKeyErrorStatus::OK)); - EXPECT_EQ(MediaKeyErrorStatus::OK, m_mediaKeys->generateRequest(m_kKeySessionId, m_initDataType, m_initData)); + EXPECT_EQ(MediaKeyErrorStatus::OK, + m_mediaKeys->generateRequest(m_kKeySessionId, m_initDataType, m_initData, m_ldlState)); } /** @@ -52,7 +54,7 @@ TEST_F(RialtoServerMediaKeysGenerateRequestTest, SessionDoesNotExistFailure) { mainThreadWillEnqueueTaskAndWait(); EXPECT_EQ(MediaKeyErrorStatus::BAD_SESSION_ID, - m_mediaKeys->generateRequest(m_kKeySessionId + 1, m_initDataType, m_initData)); + m_mediaKeys->generateRequest(m_kKeySessionId + 1, m_initDataType, m_initData, m_ldlState)); } /** @@ -61,9 +63,9 @@ TEST_F(RialtoServerMediaKeysGenerateRequestTest, SessionDoesNotExistFailure) TEST_F(RialtoServerMediaKeysGenerateRequestTest, SessionFailure) { mainThreadWillEnqueueTaskAndWait(); - EXPECT_CALL(*m_mediaKeySessionMock, generateRequest(m_initDataType, m_initData)) + EXPECT_CALL(*m_mediaKeySessionMock, generateRequest(m_initDataType, m_initData, m_ldlState)) .WillOnce(Return(MediaKeyErrorStatus::NOT_SUPPORTED)); EXPECT_EQ(MediaKeyErrorStatus::NOT_SUPPORTED, - m_mediaKeys->generateRequest(m_kKeySessionId, m_initDataType, m_initData)); + m_mediaKeys->generateRequest(m_kKeySessionId, m_initDataType, m_initData, m_ldlState)); } diff --git a/tests/unittests/media/server/main/mediaKeys/IsNetflixPlayreadyKeySystemTest.cpp b/tests/unittests/media/server/main/mediaKeys/IsNetflixPlayreadyKeySystemTest.cpp deleted file mode 100644 index f9d1fcbad..000000000 --- a/tests/unittests/media/server/main/mediaKeys/IsNetflixPlayreadyKeySystemTest.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2022 Sky UK - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "MediaKeysTestBase.h" - -class RialtoServerMediaKeysIsNetflixPlayreadyKeySystemTest : public MediaKeysTestBase -{ -protected: - RialtoServerMediaKeysIsNetflixPlayreadyKeySystemTest() = default; - ~RialtoServerMediaKeysIsNetflixPlayreadyKeySystemTest() { destroyMediaKeys(); } -}; - -/** - * Test that isNetflixPlayreadyKeySystem returns true. - */ -TEST_F(RialtoServerMediaKeysIsNetflixPlayreadyKeySystemTest, ReturnTrue) -{ - createMediaKeys(kNetflixKeySystem); - EXPECT_TRUE(m_mediaKeys->isNetflixPlayreadyKeySystem()); -} - -/** - * Test that isNetflixPlayreadyKeySystem returns false - */ -TEST_F(RialtoServerMediaKeysIsNetflixPlayreadyKeySystemTest, ReturnFalse) -{ - createMediaKeys(kWidevineKeySystem); - EXPECT_FALSE(m_mediaKeys->isNetflixPlayreadyKeySystem()); -} diff --git a/tests/unittests/media/server/main/mediaKeys/base/MediaKeysTestBase.cpp b/tests/unittests/media/server/main/mediaKeys/base/MediaKeysTestBase.cpp index d6a9f91f7..d12682c95 100644 --- a/tests/unittests/media/server/main/mediaKeys/base/MediaKeysTestBase.cpp +++ b/tests/unittests/media/server/main/mediaKeys/base/MediaKeysTestBase.cpp @@ -61,11 +61,11 @@ void MediaKeysTestBase::destroyMediaKeys() void MediaKeysTestBase::createKeySession(std::string keySystem) { mainThreadWillEnqueueTaskAndWait(); - EXPECT_CALL(*m_mediaKeySessionFactoryMock, createMediaKeySession(keySystem, _, _, m_keySessionType, _, m_isLDL)) + EXPECT_CALL(*m_mediaKeySessionFactoryMock, createMediaKeySession(keySystem, _, _, m_keySessionType, _)) .WillOnce(Return(ByMove(std::move(m_mediaKeySession)))); EXPECT_EQ(MediaKeyErrorStatus::OK, - m_mediaKeys->createKeySession(m_keySessionType, m_mediaKeysClientMock, m_isLDL, m_kKeySessionId)); + m_mediaKeys->createKeySession(m_keySessionType, m_mediaKeysClientMock, m_kKeySessionId)); } void MediaKeysTestBase::mainThreadWillEnqueueTaskAndWait() diff --git a/tests/unittests/media/server/mocks/main/DecryptionServiceMock.h b/tests/unittests/media/server/mocks/main/DecryptionServiceMock.h index 64f57c40c..c6a600510 100644 --- a/tests/unittests/media/server/mocks/main/DecryptionServiceMock.h +++ b/tests/unittests/media/server/mocks/main/DecryptionServiceMock.h @@ -30,7 +30,7 @@ class DecryptionServiceMock : public IDecryptionService { public: MOCK_METHOD(MediaKeyErrorStatus, decrypt, (int32_t keySessionId, GstBuffer *encrypted, GstCaps *caps), (override)); - MOCK_METHOD(bool, isNetflixPlayreadyKeySystem, (int32_t keySessionId), (override)); + MOCK_METHOD(bool, isExtendedInterfaceUsed, (int32_t keySessionId), (override)); MOCK_METHOD(MediaKeyErrorStatus, selectKeyId, (int32_t keySessionId, const std::vector &keyId), (override)); MOCK_METHOD(void, incrementSessionIdUsageCounter, (int32_t keySessionId), (override)); MOCK_METHOD(void, decrementSessionIdUsageCounter, (int32_t keySessionId), (override)); diff --git a/tests/unittests/media/server/mocks/main/MediaKeySessionFactoryMock.h b/tests/unittests/media/server/mocks/main/MediaKeySessionFactoryMock.h index d7ada3a1c..fc2835f40 100644 --- a/tests/unittests/media/server/mocks/main/MediaKeySessionFactoryMock.h +++ b/tests/unittests/media/server/mocks/main/MediaKeySessionFactoryMock.h @@ -33,7 +33,7 @@ class MediaKeySessionFactoryMock : public IMediaKeySessionFactory MOCK_METHOD(std::unique_ptr, createMediaKeySession, (const std::string &keySystem, int32_t keySessionId, const firebolt::rialto::wrappers::IOcdmSystem &ocdmSystem, KeySessionType sessionType, - std::weak_ptr client, bool isLDL), + std::weak_ptr client), (const, override)); }; } // namespace firebolt::rialto::server diff --git a/tests/unittests/media/server/mocks/main/MediaKeySessionMock.h b/tests/unittests/media/server/mocks/main/MediaKeySessionMock.h index b813d80eb..8bbdc8284 100644 --- a/tests/unittests/media/server/mocks/main/MediaKeySessionMock.h +++ b/tests/unittests/media/server/mocks/main/MediaKeySessionMock.h @@ -30,7 +30,8 @@ namespace firebolt::rialto::server class MediaKeySessionMock : public IMediaKeySession { public: - MOCK_METHOD(MediaKeyErrorStatus, generateRequest, (InitDataType initDataType, const std::vector &initData), + MOCK_METHOD(MediaKeyErrorStatus, generateRequest, + (InitDataType initDataType, const std::vector &initData, const LimitedDurationLicense &ldlState), (override)); MOCK_METHOD(MediaKeyErrorStatus, loadSession, (), (override)); MOCK_METHOD(MediaKeyErrorStatus, updateSession, (const std::vector &responseData), (override)); @@ -42,7 +43,6 @@ class MediaKeySessionMock : public IMediaKeySession MOCK_METHOD(MediaKeyErrorStatus, setDrmHeader, (const std::vector &requestData), (override)); MOCK_METHOD(MediaKeyErrorStatus, getLastDrmError, (uint32_t & errorCode), (override)); MOCK_METHOD(MediaKeyErrorStatus, selectKeyId, (const std::vector &keyId), (override)); - MOCK_METHOD(bool, isNetflixPlayreadyKeySystem, (), (const, override)); }; } // namespace firebolt::rialto::server diff --git a/tests/unittests/media/server/mocks/main/MediaKeysServerInternalMock.h b/tests/unittests/media/server/mocks/main/MediaKeysServerInternalMock.h index 7333feede..4ee5df08f 100644 --- a/tests/unittests/media/server/mocks/main/MediaKeysServerInternalMock.h +++ b/tests/unittests/media/server/mocks/main/MediaKeysServerInternalMock.h @@ -37,10 +37,11 @@ class MediaKeysServerInternalMock : public IMediaKeysServerInternal MOCK_METHOD(MediaKeyErrorStatus, selectKeyId, (int32_t keySessionId, const std::vector &keyId), (override)); MOCK_METHOD(bool, containsKey, (int32_t keySessionId, const std::vector &keyId), (override)); MOCK_METHOD(MediaKeyErrorStatus, createKeySession, - (KeySessionType sessionType, std::weak_ptr client, bool isLDL, int32_t &keySessionId), - (override)); + (KeySessionType sessionType, std::weak_ptr client, int32_t &keySessionId), (override)); MOCK_METHOD(MediaKeyErrorStatus, generateRequest, - (int32_t keySessionId, InitDataType initDataType, const std::vector &initData), (override)); + (int32_t keySessionId, InitDataType initDataType, const std::vector &initData, + const LimitedDurationLicense &ldlState), + (override)); MOCK_METHOD(MediaKeyErrorStatus, loadSession, (int32_t keySessionId), (override)); MOCK_METHOD(MediaKeyErrorStatus, updateSession, (int32_t keySessionId, const std::vector &responseData), (override)); @@ -58,7 +59,6 @@ class MediaKeysServerInternalMock : public IMediaKeysServerInternal MOCK_METHOD(MediaKeyErrorStatus, getCdmKeySessionId, (int32_t keySessionId, std::string &cdmKeySessionId), (override)); MOCK_METHOD(MediaKeyErrorStatus, decrypt, (int32_t keySessionId, GstBuffer *encrypted, GstCaps *caps), (override)); - MOCK_METHOD(bool, isNetflixPlayreadyKeySystem, (), (const, override)); MOCK_METHOD(void, ping, (std::unique_ptr && heartbeatHandler), (override)); MOCK_METHOD(MediaKeyErrorStatus, releaseKeySession, (int32_t keySessionId), (override)); MOCK_METHOD(MediaKeyErrorStatus, getMetricSystemData, (std::vector & buffer), (override)); diff --git a/tests/unittests/media/server/mocks/service/CdmServiceMock.h b/tests/unittests/media/server/mocks/service/CdmServiceMock.h index 4c0699c46..9196ff498 100644 --- a/tests/unittests/media/server/mocks/service/CdmServiceMock.h +++ b/tests/unittests/media/server/mocks/service/CdmServiceMock.h @@ -37,11 +37,11 @@ class CdmServiceMock : public ICdmService MOCK_METHOD(bool, destroyMediaKeys, (int mediaKeysHandle), (override)); MOCK_METHOD(MediaKeyErrorStatus, createKeySession, (int mediaKeysHandle, KeySessionType sessionType, const std::shared_ptr &client, - bool isLDL, int32_t &keySessionId), + int32_t &keySessionId), (override)); MOCK_METHOD(MediaKeyErrorStatus, generateRequest, (int mediaKeysHandle, int32_t keySessionId, InitDataType initDataType, - const std::vector &initData), + const std::vector &initData, const LimitedDurationLicense &ldlState), (override)); MOCK_METHOD(MediaKeyErrorStatus, loadSession, (int mediaKeysHandle, int32_t keySessionId), (override)); MOCK_METHOD(MediaKeyErrorStatus, updateSession, diff --git a/tests/unittests/media/server/service/cdmService/CdmServiceTests.cpp b/tests/unittests/media/server/service/cdmService/CdmServiceTests.cpp index 2c27cac62..a5f68acf9 100644 --- a/tests/unittests/media/server/service/cdmService/CdmServiceTests.cpp +++ b/tests/unittests/media/server/service/cdmService/CdmServiceTests.cpp @@ -81,7 +81,6 @@ TEST_F(CdmServiceTests, shouldCreateKeySession) triggerSwitchToActiveSuccess(); mediaKeysFactoryWillCreateMediaKeys(); createMediaKeysShouldSucceed(); - mediaKeysWillCheckIfKeySystemIsPlayready(false); mediaKeysWillCreateKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); createKeySessionShouldSucceed(); destroyMediaKeysShouldSucceed(); @@ -108,7 +107,6 @@ TEST_F(CdmServiceTests, shouldFailToCreateKeySessionWhenMediaKeysClientExists) triggerSwitchToActiveSuccess(); mediaKeysFactoryWillCreateMediaKeys(); createMediaKeysShouldSucceed(); - mediaKeysWillCheckIfKeySystemIsPlayready(false); mediaKeysWillCreateKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); createKeySessionShouldSucceed(); mediaKeysWillCreateKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); @@ -123,6 +121,20 @@ TEST_F(CdmServiceTests, shouldGenerateRequest) createMediaKeysShouldSucceed(); mediaKeysWillGenerateRequestWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); generateRequestShouldReturnStatus(firebolt::rialto::MediaKeyErrorStatus::OK); + isExtendedInterfaceUsedShouldReturn(false); + destroyMediaKeysShouldSucceed(); +} + +TEST_F(CdmServiceTests, shouldGenerateRequestWithLdlEnabled) +{ + triggerSwitchToActiveSuccess(); + mediaKeysFactoryWillCreateMediaKeys(); + createMediaKeysShouldSucceed(); + mediaKeysWillCreateKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); + createKeySessionShouldSucceed(); + mediaKeysWillGenerateRequestLdlEnabledWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); + generateRequestWithLdlEnabledShouldReturnStatus(firebolt::rialto::MediaKeyErrorStatus::OK); + isExtendedInterfaceUsedShouldReturn(true); destroyMediaKeysShouldSucceed(); } @@ -199,7 +211,6 @@ TEST_F(CdmServiceTests, shouldCloseKeySession) triggerSwitchToActiveSuccess(); mediaKeysFactoryWillCreateMediaKeys(); createMediaKeysShouldSucceed(); - mediaKeysWillCheckIfKeySystemIsPlayready(false); mediaKeysWillCreateKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); createKeySessionShouldSucceed(); mediaKeysWillCloseKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); @@ -212,7 +223,6 @@ TEST_F(CdmServiceTests, incrementSessionUsage) triggerSwitchToActiveSuccess(); mediaKeysFactoryWillCreateMediaKeys(); createMediaKeysShouldSucceed(); - mediaKeysWillCheckIfKeySystemIsPlayready(false); mediaKeysWillCreateKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); createKeySessionShouldSucceed(); incrementSessionIdUsageCounter(); @@ -224,7 +234,6 @@ TEST_F(CdmServiceTests, deccrementSessionUsage) triggerSwitchToActiveSuccess(); mediaKeysFactoryWillCreateMediaKeys(); createMediaKeysShouldSucceed(); - mediaKeysWillCheckIfKeySystemIsPlayready(false); mediaKeysWillCreateKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); createKeySessionShouldSucceed(); decrementSessionIdUsageCounter(); @@ -260,7 +269,6 @@ TEST_F(CdmServiceTests, shouldFailToCloseKeySessionWhenMediaKeysFails) triggerSwitchToActiveSuccess(); mediaKeysFactoryWillCreateMediaKeys(); createMediaKeysShouldSucceed(); - mediaKeysWillCheckIfKeySystemIsPlayready(false); mediaKeysWillCreateKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); createKeySessionShouldSucceed(); mediaKeysWillCloseKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::INVALID_STATE); @@ -273,7 +281,6 @@ TEST_F(CdmServiceTests, shouldCloseKeySessionDeferred) triggerSwitchToActiveSuccess(); mediaKeysFactoryWillCreateMediaKeys(); createMediaKeysShouldSucceed(); - mediaKeysWillCheckIfKeySystemIsPlayready(false); mediaKeysWillCreateKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); createKeySessionShouldSucceed(); incrementSessionIdUsageCounter(); @@ -288,7 +295,6 @@ TEST_F(CdmServiceTests, shouldCloseKeySessionDeferredWithFailurePrint) triggerSwitchToActiveSuccess(); mediaKeysFactoryWillCreateMediaKeys(); createMediaKeysShouldSucceed(); - mediaKeysWillCheckIfKeySystemIsPlayready(false); mediaKeysWillCreateKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); createKeySessionShouldSucceed(); incrementSessionIdUsageCounter(); @@ -355,7 +361,6 @@ TEST_F(CdmServiceTests, shouldDecrypt) triggerSwitchToActiveSuccess(); mediaKeysFactoryWillCreateMediaKeys(); createMediaKeysShouldSucceed(); - mediaKeysWillCheckIfKeySystemIsPlayready(false); mediaKeysWillCreateKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); createKeySessionShouldSucceed(); mediaKeysWillDecryptWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); @@ -374,7 +379,6 @@ TEST_F(CdmServiceTests, shouldFailToDecryptWhenMediaKeysFails) triggerSwitchToActiveSuccess(); mediaKeysFactoryWillCreateMediaKeys(); createMediaKeysShouldSucceed(); - mediaKeysWillCheckIfKeySystemIsPlayready(false); mediaKeysWillCreateKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); createKeySessionShouldSucceed(); mediaKeysWillDecryptWithStatus(firebolt::rialto::MediaKeyErrorStatus::INVALID_STATE); @@ -396,11 +400,11 @@ TEST_F(CdmServiceTests, shouldSelectKeyId) triggerSwitchToActiveSuccess(); mediaKeysFactoryWillCreateMediaKeys(); createMediaKeysShouldSucceed(); - mediaKeysWillCheckIfKeySystemIsPlayready(false); mediaKeysWillCreateKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); createKeySessionShouldSucceed(); mediaKeysWillSelectKeyIdWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); selectKeyIdShouldReturnStatus(firebolt::rialto::MediaKeyErrorStatus::OK); + isExtendedInterfaceUsedShouldReturn(true); destroyMediaKeysShouldSucceed(); } @@ -415,7 +419,6 @@ TEST_F(CdmServiceTests, shouldFailToSelectKeyIdWhenMediaKeysFails) triggerSwitchToActiveSuccess(); mediaKeysFactoryWillCreateMediaKeys(); createMediaKeysShouldSucceed(); - mediaKeysWillCheckIfKeySystemIsPlayready(false); mediaKeysWillCreateKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); createKeySessionShouldSucceed(); mediaKeysWillSelectKeyIdWithStatus(firebolt::rialto::MediaKeyErrorStatus::INVALID_STATE); @@ -463,8 +466,11 @@ TEST_F(CdmServiceTests, shouldSetDrmHeader) triggerSwitchToActiveSuccess(); mediaKeysFactoryWillCreateMediaKeys(); createMediaKeysShouldSucceed(); + mediaKeysWillCreateKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); + createKeySessionShouldSucceed(); mediaKeysWillSetDrmHeaderWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); setDrmHeaderShouldReturnStatus(firebolt::rialto::MediaKeyErrorStatus::OK); + isExtendedInterfaceUsedShouldReturn(true); destroyMediaKeysShouldSucceed(); } @@ -671,7 +677,6 @@ TEST_F(CdmServiceTests, shouldReleaseKeySession) triggerSwitchToActiveSuccess(); mediaKeysFactoryWillCreateMediaKeys(); createMediaKeysShouldSucceed(); - mediaKeysWillCheckIfKeySystemIsPlayready(false); mediaKeysWillCreateKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); createKeySessionShouldSucceed(); mediaKeysWillReleaseKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); @@ -684,7 +689,6 @@ TEST_F(CdmServiceTests, shouldReleaseKeySessionDeferred) triggerSwitchToActiveSuccess(); mediaKeysFactoryWillCreateMediaKeys(); createMediaKeysShouldSucceed(); - mediaKeysWillCheckIfKeySystemIsPlayready(false); mediaKeysWillCreateKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); createKeySessionShouldSucceed(); incrementSessionIdUsageCounter(); @@ -705,7 +709,6 @@ TEST_F(CdmServiceTests, shouldFailToReleaseKeySessionWhenMediaKeysFails) triggerSwitchToActiveSuccess(); mediaKeysFactoryWillCreateMediaKeys(); createMediaKeysShouldSucceed(); - mediaKeysWillCheckIfKeySystemIsPlayready(false); mediaKeysWillCreateKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); createKeySessionShouldSucceed(); mediaKeysWillReleaseKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::INVALID_STATE); @@ -801,42 +804,29 @@ TEST_F(CdmServiceTests, shouldGetServerCertificateSupportedIfSupportedInActiveSt supportsServerCertificateReturnTrue(); } -TEST_F(CdmServiceTests, shouldCheckThatKeySystemIsPlayready) -{ - triggerSwitchToActiveSuccess(); - mediaKeysFactoryWillCreateMediaKeys(); - createMediaKeysShouldSucceed(); - mediaKeysWillCheckIfKeySystemIsPlayready(true); - mediaKeysWillCreateKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); - createKeySessionShouldSucceed(); - isNetflixPlayreadyKeySystemShouldReturn(true); - destroyMediaKeysShouldSucceed(); -} - -TEST_F(CdmServiceTests, shouldReturnFalseWhenCheckingPlayreadyKeySystemWhenNoMediaKeys) +TEST_F(CdmServiceTests, shouldReturnFalseWhenCheckingExtendedInterfaceWhenNoMediaKeys) { triggerSwitchToActiveSuccess(); - isNetflixPlayreadyKeySystemShouldReturn(false); + isExtendedInterfaceUsedShouldReturn(false); } -TEST_F(CdmServiceTests, shouldReturnFalseWhenCheckingPlayreadyKeySystemWhenMediaKeysFails) +TEST_F(CdmServiceTests, shouldReturnFalseWhenCheckingExtendedInterfaceWhenMediaKeysFails) { triggerSwitchToActiveSuccess(); mediaKeysFactoryWillCreateMediaKeys(); createMediaKeysShouldSucceed(); - mediaKeysWillCheckIfKeySystemIsPlayready(false); mediaKeysWillCreateKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus::OK); createKeySessionShouldSucceed(); - isNetflixPlayreadyKeySystemShouldReturn(false); + isExtendedInterfaceUsedShouldReturn(false); destroyMediaKeysShouldSucceed(); } -TEST_F(CdmServiceTests, shouldReturnFalseWhenCheckingPlayreadyKeySystemWhenMediaKeysIsNotFoundForSession) +TEST_F(CdmServiceTests, shouldReturnFalseWhenCheckingExtendedInterfaceWhenMediaKeysIsNotFoundForSession) { triggerSwitchToActiveSuccess(); mediaKeysFactoryWillCreateMediaKeys(); createMediaKeysShouldSucceed(); - isNetflixPlayreadyKeySystemShouldReturn(false); + isExtendedInterfaceUsedShouldReturn(false); destroyMediaKeysShouldSucceed(); } diff --git a/tests/unittests/media/server/service/cdmService/CdmServiceTestsFixture.cpp b/tests/unittests/media/server/service/cdmService/CdmServiceTestsFixture.cpp index 73e0a7a43..6243ec01d 100644 --- a/tests/unittests/media/server/service/cdmService/CdmServiceTestsFixture.cpp +++ b/tests/unittests/media/server/service/cdmService/CdmServiceTestsFixture.cpp @@ -35,7 +35,6 @@ const std::vector kKeySystems{"expectedKeySystem1", "expectedKeySys const std::string kVersion{"123"}; constexpr int kMediaKeysHandle{2}; constexpr firebolt::rialto::KeySessionType kKeySessionType{firebolt::rialto::KeySessionType::TEMPORARY}; -constexpr bool kIsLDL{false}; constexpr int kKeySessionId{3}; constexpr firebolt::rialto::InitDataType kInitDataType{firebolt::rialto::InitDataType::CENC}; const std::vector kInitData{6, 7, 2}; @@ -44,6 +43,8 @@ const std::vector keyId{1, 4, 7}; const std::vector kDrmHeader{4, 9, 3}; const uint32_t kSubSampleCount{2}; constexpr uint32_t kInitWithLast15{1}; +constexpr firebolt::rialto::LimitedDurationLicense kLdlState{firebolt::rialto::LimitedDurationLicense::NOT_SPECIFIED}; +constexpr firebolt::rialto::LimitedDurationLicense kLdlStateEnabled{firebolt::rialto::LimitedDurationLicense::ENABLED}; } // namespace CdmServiceTests::CdmServiceTests() @@ -126,13 +127,20 @@ void CdmServiceTests::mediaKeysFactoryWillReturnNullptr() void CdmServiceTests::mediaKeysWillCreateKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus status) { - EXPECT_CALL(m_mediaKeysMock, createKeySession(kKeySessionType, _, kIsLDL, _)) - .WillOnce(DoAll(SetArgReferee<3>(kKeySessionId), Return(status))); + EXPECT_CALL(m_mediaKeysMock, createKeySession(kKeySessionType, _, _)) + .WillOnce(DoAll(SetArgReferee<2>(kKeySessionId), Return(status))); } void CdmServiceTests::mediaKeysWillGenerateRequestWithStatus(firebolt::rialto::MediaKeyErrorStatus status) { - EXPECT_CALL(m_mediaKeysMock, generateRequest(kKeySessionId, kInitDataType, kInitData)).WillOnce(Return(status)); + EXPECT_CALL(m_mediaKeysMock, generateRequest(kKeySessionId, kInitDataType, kInitData, kLdlState)) + .WillOnce(Return(status)); +} + +void CdmServiceTests::mediaKeysWillGenerateRequestLdlEnabledWithStatus(firebolt::rialto::MediaKeyErrorStatus status) +{ + EXPECT_CALL(m_mediaKeysMock, generateRequest(kKeySessionId, kInitDataType, kInitData, kLdlStateEnabled)) + .WillOnce(Return(status)); } void CdmServiceTests::mediaKeysWillLoadSessionWithStatus(firebolt::rialto::MediaKeyErrorStatus status) @@ -220,11 +228,6 @@ void CdmServiceTests::mediaKeysWillSelectKeyIdWithStatus(firebolt::rialto::Media EXPECT_CALL(m_mediaKeysMock, selectKeyId(kKeySessionId, keyId)).WillOnce(Return(status)); } -void CdmServiceTests::mediaKeysWillCheckIfKeySystemIsPlayready(bool result) -{ - EXPECT_CALL(m_mediaKeysMock, isNetflixPlayreadyKeySystem()).WillOnce(Return(result)); -} - void CdmServiceTests::mediaKeysWillPing() { EXPECT_CALL(*m_heartbeatProcedureMock, createHandler()); @@ -260,21 +263,25 @@ void CdmServiceTests::createKeySessionShouldSucceed() { int32_t returnKeySessionId = -1; EXPECT_EQ(firebolt::rialto::MediaKeyErrorStatus::OK, - m_sut.createKeySession(kMediaKeysHandle, kKeySessionType, m_mediaKeysClientMock, kIsLDL, - returnKeySessionId)); + m_sut.createKeySession(kMediaKeysHandle, kKeySessionType, m_mediaKeysClientMock, returnKeySessionId)); EXPECT_GE(returnKeySessionId, -1); } void CdmServiceTests::createKeySessionShouldFailWithReturnStatus(firebolt::rialto::MediaKeyErrorStatus status) { int32_t returnKeySessionId = -1; - EXPECT_EQ(status, m_sut.createKeySession(kMediaKeysHandle, kKeySessionType, m_mediaKeysClientMock, kIsLDL, - returnKeySessionId)); + EXPECT_EQ(status, + m_sut.createKeySession(kMediaKeysHandle, kKeySessionType, m_mediaKeysClientMock, returnKeySessionId)); } void CdmServiceTests::generateRequestShouldReturnStatus(firebolt::rialto::MediaKeyErrorStatus status) { - EXPECT_EQ(status, m_sut.generateRequest(kMediaKeysHandle, kKeySessionId, kInitDataType, kInitData)); + EXPECT_EQ(status, m_sut.generateRequest(kMediaKeysHandle, kKeySessionId, kInitDataType, kInitData, kLdlState)); +} + +void CdmServiceTests::generateRequestWithLdlEnabledShouldReturnStatus(firebolt::rialto::MediaKeyErrorStatus status) +{ + EXPECT_EQ(status, m_sut.generateRequest(kMediaKeysHandle, kKeySessionId, kInitDataType, kInitData, kLdlStateEnabled)); } void CdmServiceTests::loadSessionShouldReturnStatus(firebolt::rialto::MediaKeyErrorStatus status) @@ -370,9 +377,9 @@ void CdmServiceTests::releaseKeySessionShouldReturnStatus(firebolt::rialto::Medi EXPECT_EQ(status, m_sut.releaseKeySession(kMediaKeysHandle, kKeySessionId)); } -void CdmServiceTests::isNetflixPlayreadyKeySystemShouldReturn(bool result) +void CdmServiceTests::isExtendedInterfaceUsedShouldReturn(bool result) { - EXPECT_EQ(result, m_sut.isNetflixPlayreadyKeySystem(kKeySessionId)); + EXPECT_EQ(result, m_sut.isExtendedInterfaceUsed(kKeySessionId)); } void CdmServiceTests::getSupportedKeySystemsShouldSucceed() diff --git a/tests/unittests/media/server/service/cdmService/CdmServiceTestsFixture.h b/tests/unittests/media/server/service/cdmService/CdmServiceTestsFixture.h index 77939fd0b..36a8c3676 100644 --- a/tests/unittests/media/server/service/cdmService/CdmServiceTestsFixture.h +++ b/tests/unittests/media/server/service/cdmService/CdmServiceTestsFixture.h @@ -45,6 +45,7 @@ class CdmServiceTests : public testing::Test void mediaKeysFactoryWillReturnNullptr(); void mediaKeysWillCreateKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus status); void mediaKeysWillGenerateRequestWithStatus(firebolt::rialto::MediaKeyErrorStatus status); + void mediaKeysWillGenerateRequestLdlEnabledWithStatus(firebolt::rialto::MediaKeyErrorStatus status); void mediaKeysWillLoadSessionWithStatus(firebolt::rialto::MediaKeyErrorStatus status); void mediaKeysWillUpdateSessionWithStatus(firebolt::rialto::MediaKeyErrorStatus status); void mediaKeysWillCloseKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus status); @@ -62,7 +63,6 @@ class CdmServiceTests : public testing::Test void mediaKeysWillReleaseKeySessionWithStatus(firebolt::rialto::MediaKeyErrorStatus status); void mediaKeysWillDecryptWithStatus(firebolt::rialto::MediaKeyErrorStatus status); void mediaKeysWillSelectKeyIdWithStatus(firebolt::rialto::MediaKeyErrorStatus status); - void mediaKeysWillCheckIfKeySystemIsPlayready(bool result); void mediaKeysWillPing(); void mediaKeysWillGetMetricSystemDataWithStatus(firebolt::rialto::MediaKeyErrorStatus status); @@ -85,6 +85,7 @@ class CdmServiceTests : public testing::Test void createKeySessionShouldSucceed(); void createKeySessionShouldFailWithReturnStatus(firebolt::rialto::MediaKeyErrorStatus status); void generateRequestShouldReturnStatus(firebolt::rialto::MediaKeyErrorStatus status); + void generateRequestWithLdlEnabledShouldReturnStatus(firebolt::rialto::MediaKeyErrorStatus status); void loadSessionShouldReturnStatus(firebolt::rialto::MediaKeyErrorStatus status); void updateSessionShouldReturnStatus(firebolt::rialto::MediaKeyErrorStatus status); void closeKeySessionShouldReturnStatus(firebolt::rialto::MediaKeyErrorStatus status); @@ -102,7 +103,7 @@ class CdmServiceTests : public testing::Test void getLastDrmErrorShouldReturnStatus(firebolt::rialto::MediaKeyErrorStatus status); void getDrmTimeShouldReturnStatus(firebolt::rialto::MediaKeyErrorStatus status); void releaseKeySessionShouldReturnStatus(firebolt::rialto::MediaKeyErrorStatus status); - void isNetflixPlayreadyKeySystemShouldReturn(bool result); + void isExtendedInterfaceUsedShouldReturn(bool result); void incrementSessionIdUsageCounter(); void decrementSessionIdUsageCounter(); void getMetricSystemDataShouldReturnStatus(firebolt::rialto::MediaKeyErrorStatus status); From fc2f3fe0cd5b75a19b91250b839984a931b8617a Mon Sep 17 00:00:00 2001 From: Marcin Wojciechowski <105790697+skywojciechowskim@users.noreply.github.com> Date: Mon, 23 Feb 2026 10:04:47 +0100 Subject: [PATCH 09/29] Re-enabled CT (#450) Summary: Re-enabled CT Type: Fix Test Plan: UT/CT, Fullstack Jira: NO-JIRA --- .../componenttests/server/tests/mediaPipeline/FlushTest.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/componenttests/server/tests/mediaPipeline/FlushTest.cpp b/tests/componenttests/server/tests/mediaPipeline/FlushTest.cpp index 132aebe3a..eefc8c03c 100644 --- a/tests/componenttests/server/tests/mediaPipeline/FlushTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/FlushTest.cpp @@ -34,6 +34,7 @@ constexpr bool kAsync{true}; } // namespace using testing::_; +using testing::AtLeast; using testing::Invoke; using testing::Return; using testing::StrEq; @@ -51,6 +52,8 @@ class FlushTest : public MediaPipelineTest { GstElementFactory *elementFactory = gst_element_factory_find("fakesrc"); m_audioSink = gst_element_factory_create(elementFactory, nullptr); + EXPECT_CALL(*m_glibWrapperMock, gTypeName(G_OBJECT_TYPE(m_audioSink))).WillRepeatedly(Return("audio_sink")); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_audioSink)).Times(AtLeast(0)); gst_object_unref(elementFactory); } @@ -66,7 +69,6 @@ class FlushTest : public MediaPipelineTest gboolean *asyncPtr = reinterpret_cast(element); *asyncPtr = static_cast(kAsync); })); - EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_audioSink)); EXPECT_CALL(*m_gstWrapperMock, gstEventNewFlushStart()).WillOnce(Return(&m_flushStartEvent)); EXPECT_CALL(*m_gstWrapperMock, gstElementSendEvent(GST_ELEMENT(&m_audioAppSrc), &m_flushStartEvent)) .WillOnce(Return(true)); @@ -232,7 +234,7 @@ class FlushTest : public MediaPipelineTest * * Code: */ -TEST_F(FlushTest, DISABLED_flushAudioSourceSuccess) +TEST_F(FlushTest, flushAudioSourceSuccess) { // Step 1: Create a new media session createSession(); From 6325af236930c44eccfc96b9b69728b1ff90371b Mon Sep 17 00:00:00 2001 From: Koky2701 <90915184+Koky2701@users.noreply.github.com> Date: Mon, 2 Mar 2026 14:54:53 +0100 Subject: [PATCH 10/29] Feature/rdkemw 12692 (#438) Summary: 'report-decode-errors' and 'queued-frames' properties missing from RialtoMSEVideoSink Type: Feature Test Plan: UT/CT, Fullstack Jira: RDKEMW-12692 --- media/client/ipc/include/MediaPipelineIpc.h | 4 + .../client/ipc/interface/IMediaPipelineIpc.h | 24 ++++ media/client/ipc/source/MediaPipelineIpc.cpp | 67 +++++++++++ media/client/main/include/MediaPipeline.h | 4 + .../client/main/include/MediaPipelineProxy.h | 8 ++ media/client/main/source/MediaPipeline.cpp | 10 ++ media/public/include/IMediaPipeline.h | 24 ++++ media/server/gstplayer/CMakeLists.txt | 1 + .../gstplayer/include/GenericPlayerContext.h | 5 + .../gstplayer/include/GstGenericPlayer.h | 3 + .../include/IGstGenericPlayerPrivate.h | 7 ++ .../include/tasks/IGenericPlayerTaskFactory.h | 15 +++ .../tasks/generic/GenericPlayerTaskFactory.h | 4 + .../tasks/generic/SetReportDecodeErrors.h | 49 ++++++++ .../gstplayer/interface/IGstGenericPlayer.h | 19 ++++ .../gstplayer/source/GstGenericPlayer.cpp | 80 +++++++++++++ .../generic/GenericPlayerTaskFactory.cpp | 9 ++ .../tasks/generic/SetReportDecodeErrors.cpp | 52 +++++++++ .../source/tasks/generic/SetupElement.cpp | 5 +- .../ipc/include/MediaPipelineModuleService.h | 8 ++ .../ipc/source/MediaPipelineModuleService.cpp | 34 ++++++ .../include/MediaPipelineServerInternal.h | 28 +++++ .../source/MediaPipelineServerInternal.cpp | 57 ++++++++++ .../service/include/IMediaPipelineService.h | 2 + .../service/source/MediaPipelineService.cpp | 28 +++++ .../service/source/MediaPipelineService.h | 2 + proto/mediapipelinemodule.proto | 50 +++++++++ .../MediaPipelineProtoRequestMatchers.h | 14 +++ .../client/mocks/MediaPipelineModuleMock.h | 20 ++++ .../tests/base/MediaPipelineTestMethods.cpp | 28 +++++ .../tests/base/MediaPipelineTestMethods.h | 4 + .../client/tests/mse/PipelinePropertyTest.cpp | 10 ++ .../mediaPipeline/PipelinePropertyTest.cpp | 12 +- .../unittests/media/client/ipc/CMakeLists.txt | 2 + .../mediaPipelineIpc/GetQueuedFramesTest.cpp | 100 +++++++++++++++++ .../SetReportDecodeErrorsTest.cpp | 96 ++++++++++++++++ .../media/client/main/CMakeLists.txt | 2 + .../mediaPipeline/GetQueuedFramesTest.cpp | 63 +++++++++++ .../mediaPipeline/MediaPipelineProxyTest.cpp | 15 +++ .../SetReportDecodeErrorsTest.cpp | 58 ++++++++++ .../client/mocks/ipc/MediaPipelineIpcMock.h | 2 + .../main/MediaPipelineAndControlClientMock.h | 2 + .../media/server/gstplayer/CMakeLists.txt | 1 + .../GstGenericPlayerPrivateTest.cpp | 30 +++++ .../genericPlayer/GstGenericPlayerTest.cpp | 37 ++++++ .../common/GenericTasksTestsBase.cpp | 15 +++ .../common/GenericTasksTestsBase.h | 4 + .../common/GstGenericPlayerTestCommon.cpp | 14 +++ .../common/GstGenericPlayerTestCommon.h | 1 + .../GenericPlayerTaskFactoryTest.cpp | 8 ++ .../tasksTests/SetReportDecodeErrorsTest.cpp | 30 +++++ .../MediaPipelineModuleServiceTests.cpp | 32 ++++++ ...MediaPipelineModuleServiceTestsFixture.cpp | 73 ++++++++++++ .../MediaPipelineModuleServiceTestsFixture.h | 8 ++ .../MiscellaneousFunctionsTest.cpp | 106 ++++++++++++++++++ .../gstplayer/GenericPlayerTaskFactoryMock.h | 4 + .../mocks/gstplayer/GstGenericPlayerMock.h | 3 + .../gstplayer/GstGenericPlayerPrivateMock.h | 1 + .../main/MediaPipelineServerInternalMock.h | 2 + .../mocks/service/MediaPipelineServiceMock.h | 2 + .../MediaPipelineServiceTests.cpp | 40 +++++++ .../MediaPipelineServiceTestsFixture.cpp | 44 ++++++++ .../MediaPipelineServiceTestsFixture.h | 8 ++ 63 files changed, 1488 insertions(+), 2 deletions(-) create mode 100644 media/server/gstplayer/include/tasks/generic/SetReportDecodeErrors.h create mode 100644 media/server/gstplayer/source/tasks/generic/SetReportDecodeErrors.cpp create mode 100644 tests/unittests/media/client/ipc/mediaPipelineIpc/GetQueuedFramesTest.cpp create mode 100644 tests/unittests/media/client/ipc/mediaPipelineIpc/SetReportDecodeErrorsTest.cpp create mode 100644 tests/unittests/media/client/main/mediaPipeline/GetQueuedFramesTest.cpp create mode 100644 tests/unittests/media/client/main/mediaPipeline/SetReportDecodeErrorsTest.cpp create mode 100644 tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/SetReportDecodeErrorsTest.cpp diff --git a/media/client/ipc/include/MediaPipelineIpc.h b/media/client/ipc/include/MediaPipelineIpc.h index db45d00d9..e2e907f68 100644 --- a/media/client/ipc/include/MediaPipelineIpc.h +++ b/media/client/ipc/include/MediaPipelineIpc.h @@ -93,6 +93,10 @@ class MediaPipelineIpc : public IMediaPipelineIpc, public IpcModule bool setImmediateOutput(int32_t sourceId, bool immediateOutput) override; + bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) override; + + bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) override; + bool getImmediateOutput(int32_t sourceId, bool &immediateOutput) override; bool getStats(int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames) override; diff --git a/media/client/ipc/interface/IMediaPipelineIpc.h b/media/client/ipc/interface/IMediaPipelineIpc.h index 782a1931b..bee5f597d 100644 --- a/media/client/ipc/interface/IMediaPipelineIpc.h +++ b/media/client/ipc/interface/IMediaPipelineIpc.h @@ -188,6 +188,30 @@ class IMediaPipelineIpc */ virtual bool setImmediateOutput(int32_t sourceId, bool immediateOutput) = 0; + /** + * @brief Sets the "Report Decode Errors" property for this source. + * + * This method is asynchronous, it will set the "Report Decode Errors" property + * + * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() + * @param[in] reportDecodeErrors : Set Report Decode Errors mode on the sink + * + * @retval true on success. + */ + virtual bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) = 0; + + /** + * @brief Gets the queued frames for this source. + * + * This method is synchronous, it gets the queued frames property + * + * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() + * @param[out] queuedFrames : Get queued frames on the decoder + * + * @retval true on success. + */ + virtual bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) = 0; + /** * @brief Gets the "Immediate Output" property for this source. * diff --git a/media/client/ipc/source/MediaPipelineIpc.cpp b/media/client/ipc/source/MediaPipelineIpc.cpp index ff1b09847..890ab8905 100644 --- a/media/client/ipc/source/MediaPipelineIpc.cpp +++ b/media/client/ipc/source/MediaPipelineIpc.cpp @@ -567,6 +567,73 @@ bool MediaPipelineIpc::setImmediateOutput(int32_t sourceId, bool immediateOutput return true; } +bool MediaPipelineIpc::setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) +{ + if (!reattachChannelIfRequired()) + { + RIALTO_CLIENT_LOG_ERROR("Reattachment of the ipc channel failed, ipc disconnected"); + return false; + } + + firebolt::rialto::SetReportDecodeErrorsRequest request; + + request.set_session_id(m_sessionId); + request.set_source_id(sourceId); + request.set_report_decode_errors(reportDecodeErrors); + + firebolt::rialto::SetReportDecodeErrorsResponse response; + auto ipcController = m_ipc.createRpcController(); + auto blockingClosure = m_ipc.createBlockingClosure(); + m_mediaPipelineStub->setReportDecodeErrors(ipcController.get(), &request, &response, blockingClosure.get()); + + // wait for the call to complete + blockingClosure->wait(); + + // check the result + if (ipcController->Failed()) + { + RIALTO_CLIENT_LOG_ERROR("failed to set report decode error due to '%s'", ipcController->ErrorText().c_str()); + return false; + } + + return true; +} + +bool MediaPipelineIpc::getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) +{ + if (!reattachChannelIfRequired()) + { + RIALTO_CLIENT_LOG_ERROR("Reattachment of the ipc channel failed, ipc disconnected"); + return false; + } + + firebolt::rialto::GetQueuedFramesRequest request; + + request.set_session_id(m_sessionId); + request.set_source_id(sourceId); + + firebolt::rialto::GetQueuedFramesResponse response; + auto ipcController = m_ipc.createRpcController(); + auto blockingClosure = m_ipc.createBlockingClosure(); + m_mediaPipelineStub->getQueuedFrames(ipcController.get(), &request, &response, blockingClosure.get()); + + // wait for the call to complete + blockingClosure->wait(); + + // check the result + if (ipcController->Failed()) + { + RIALTO_CLIENT_LOG_ERROR("failed to get queued frames due to '%s'", ipcController->ErrorText().c_str()); + return false; + } + else + { + queuedFrames = response.queued_frames(); + } + + return true; +} + bool MediaPipelineIpc::getImmediateOutput(int32_t sourceId, bool &immediateOutput) { if (!reattachChannelIfRequired()) diff --git a/media/client/main/include/MediaPipeline.h b/media/client/main/include/MediaPipeline.h index 13075a511..d47f0fe16 100644 --- a/media/client/main/include/MediaPipeline.h +++ b/media/client/main/include/MediaPipeline.h @@ -132,6 +132,10 @@ class MediaPipeline : public IMediaPipelineAndIControlClient, public IMediaPipel bool setImmediateOutput(int32_t sourceId, bool immediateOutput) override; + bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) override; + + bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) override; + bool getImmediateOutput(int32_t sourceId, bool &immediateOutput) override; bool getStats(int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames) override; diff --git a/media/client/main/include/MediaPipelineProxy.h b/media/client/main/include/MediaPipelineProxy.h index b38371963..af45114aa 100644 --- a/media/client/main/include/MediaPipelineProxy.h +++ b/media/client/main/include/MediaPipelineProxy.h @@ -68,6 +68,14 @@ class MediaPipelineProxy : public IMediaPipelineAndIControlClient { return m_mediaPipeline->setImmediateOutput(sourceId, immediateOutput); } + bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) + { + return m_mediaPipeline->setReportDecodeErrors(sourceId, reportDecodeErrors); + } + bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) + { + return m_mediaPipeline->getQueuedFrames(sourceId, queuedFrames); + } bool getImmediateOutput(int32_t sourceId, bool &immediateOutput) { return m_mediaPipeline->getImmediateOutput(sourceId, immediateOutput); diff --git a/media/client/main/source/MediaPipeline.cpp b/media/client/main/source/MediaPipeline.cpp index e529fe3b6..b97367f1d 100644 --- a/media/client/main/source/MediaPipeline.cpp +++ b/media/client/main/source/MediaPipeline.cpp @@ -313,6 +313,16 @@ bool MediaPipeline::setImmediateOutput(int32_t sourceId, bool immediateOutput) return m_mediaPipelineIpc->setImmediateOutput(sourceId, immediateOutput); } +bool MediaPipeline::setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) +{ + return m_mediaPipelineIpc->setReportDecodeErrors(sourceId, reportDecodeErrors); +} + +bool MediaPipeline::getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) +{ + return m_mediaPipelineIpc->getQueuedFrames(sourceId, queuedFrames); +} + bool MediaPipeline::getImmediateOutput(int32_t sourceId, bool &immediateOutput) { return m_mediaPipelineIpc->getImmediateOutput(sourceId, immediateOutput); diff --git a/media/public/include/IMediaPipeline.h b/media/public/include/IMediaPipeline.h index e6c68787e..10b910f20 100644 --- a/media/public/include/IMediaPipeline.h +++ b/media/public/include/IMediaPipeline.h @@ -1227,6 +1227,30 @@ class IMediaPipeline */ virtual bool setImmediateOutput(int32_t sourceId, bool immediateOutput) = 0; + /** + * @brief Sets the "Report Decode Errors" property for this source. + * + * This method is asynchronous, it will set the "Report Decode Errors" property + * + * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() + * @param[in] reportDecodeErrors : Set Report Decode Errors mode on the sink + * + * @retval true on success. + */ + virtual bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) = 0; + + /** + * @brief Gets the queued frames for this source. + * + * This method is synchronous, it gets the queued frames property + * + * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() + * @param[out] queuedFrames : Get queued frames on the decoder + * + * @retval true on success. + */ + virtual bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) = 0; + /** * @brief Gets the "Immediate Output" property for this source. * diff --git a/media/server/gstplayer/CMakeLists.txt b/media/server/gstplayer/CMakeLists.txt index 6756184da..58278418a 100644 --- a/media/server/gstplayer/CMakeLists.txt +++ b/media/server/gstplayer/CMakeLists.txt @@ -57,6 +57,7 @@ add_library( source/tasks/generic/SetMute.cpp source/tasks/generic/SetPlaybackRate.cpp source/tasks/generic/SetPosition.cpp + source/tasks/generic/SetReportDecodeErrors.cpp source/tasks/generic/SetSourcePosition.cpp source/tasks/generic/SetSubtitleOffset.cpp source/tasks/generic/SetStreamSyncMode.cpp diff --git a/media/server/gstplayer/include/GenericPlayerContext.h b/media/server/gstplayer/include/GenericPlayerContext.h index d97deb4b1..237646ca3 100644 --- a/media/server/gstplayer/include/GenericPlayerContext.h +++ b/media/server/gstplayer/include/GenericPlayerContext.h @@ -155,6 +155,11 @@ struct GenericPlayerContext */ std::optional pendingImmediateOutputForVideo{}; + /** + * @brief Pending report decode errors for MediaSourceType::VIDEO + */ + std::optional pendingReportDecodeErrorsForVideo{}; + /** * @brief Pending low latency */ diff --git a/media/server/gstplayer/include/GstGenericPlayer.h b/media/server/gstplayer/include/GstGenericPlayer.h index 123a8df85..c0fc9f6e4 100644 --- a/media/server/gstplayer/include/GstGenericPlayer.h +++ b/media/server/gstplayer/include/GstGenericPlayer.h @@ -119,7 +119,9 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva void setPlaybackRate(double rate) override; bool getPosition(std::int64_t &position) override; bool setImmediateOutput(const MediaSourceType &mediaSourceType, bool immediateOutput) override; + bool setReportDecodeErrors(const MediaSourceType &mediaSourceType, bool reportDecodeErrors) override; bool getImmediateOutput(const MediaSourceType &mediaSourceType, bool &immediateOutput) override; + bool getQueuedFrames(uint32_t &queuedFrames) override; bool getStats(const MediaSourceType &mediaSourceType, uint64_t &renderedFrames, uint64_t &droppedFrames) override; void setVolume(double targetVolume, uint32_t volumeDuration, firebolt::rialto::EaseType easeType) override; bool getVolume(double &volume) override; @@ -153,6 +155,7 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva void scheduleAllSourcesAttached() override; bool setVideoSinkRectangle() override; bool setImmediateOutput() override; + bool setReportDecodeErrors() override; bool setShowVideoWindow() override; bool setLowLatency() override; bool setSync() override; diff --git a/media/server/gstplayer/include/IGstGenericPlayerPrivate.h b/media/server/gstplayer/include/IGstGenericPlayerPrivate.h index fb48a2816..9b6d54af0 100644 --- a/media/server/gstplayer/include/IGstGenericPlayerPrivate.h +++ b/media/server/gstplayer/include/IGstGenericPlayerPrivate.h @@ -80,6 +80,13 @@ class IGstGenericPlayerPrivate */ virtual bool setImmediateOutput() = 0; + /** + * @brief Sets report decode error. Called by the worker thread. + * + * @retval true on success. + */ + virtual bool setReportDecodeErrors() = 0; + /** * @brief Sets the low latency property. Called by the worker thread. * diff --git a/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h b/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h index f8de67cb4..54b8fc621 100644 --- a/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h +++ b/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h @@ -482,6 +482,21 @@ class IGenericPlayerTaskFactory const firebolt::rialto::MediaSourceType &type, bool immediateOutput) const = 0; + /** + * @brief Creates a SetReportDecodeErrors task. + * + * @param[in] context : The GstPlayer context + * @param[in] player : The GstPlayer instance + * @param[in] type : The media source type + * @param[in] reportDecodeErrors : the value to set for report decode error + * + * @retval the new SetReportDecodeErrors task instance. + */ + virtual std::unique_ptr createSetReportDecodeErrors(GenericPlayerContext &context, + IGstGenericPlayerPrivate &player, + const firebolt::rialto::MediaSourceType &type, + bool reportDecodeErrors) const = 0; + /** * @brief Creates a SetBufferingLimit task. * diff --git a/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h b/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h index aa9a06e55..79e31838b 100644 --- a/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h +++ b/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h @@ -120,6 +120,10 @@ class GenericPlayerTaskFactory : public IGenericPlayerTaskFactory std::unique_ptr createSetImmediateOutput(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, const firebolt::rialto::MediaSourceType &type, bool immediateOutput) const override; + std::unique_ptr createSetReportDecodeErrors(GenericPlayerContext &context, + IGstGenericPlayerPrivate &player, + const firebolt::rialto::MediaSourceType &type, + bool reportDecodeErrors) const override; std::unique_ptr createSetBufferingLimit(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, std::uint32_t limit) const override; std::unique_ptr createSetUseBuffering(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, diff --git a/media/server/gstplayer/include/tasks/generic/SetReportDecodeErrors.h b/media/server/gstplayer/include/tasks/generic/SetReportDecodeErrors.h new file mode 100644 index 000000000..9cf30b310 --- /dev/null +++ b/media/server/gstplayer/include/tasks/generic/SetReportDecodeErrors.h @@ -0,0 +1,49 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIREBOLT_RIALTO_SERVER_TASKS_GENERIC_SET_REPORT_DECODE_ERRORS_H_ +#define FIREBOLT_RIALTO_SERVER_TASKS_GENERIC_SET_REPORT_DECODE_ERRORS_H_ + +#include "GenericPlayerContext.h" +#include "IGlibWrapper.h" +#include "IGstGenericPlayerPrivate.h" +#include "IGstWrapper.h" +#include "IPlayerTask.h" + +#include + +namespace firebolt::rialto::server::tasks::generic +{ +class SetReportDecodeErrors : public IPlayerTask +{ +public: + explicit SetReportDecodeErrors(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, + const MediaSourceType &type, bool reportDecodeErrors); + ~SetReportDecodeErrors() override; + void execute() const override; + +private: + GenericPlayerContext &m_context; + IGstGenericPlayerPrivate &m_player; + const MediaSourceType m_type; + bool m_reportDecodeErrors; +}; +} // namespace firebolt::rialto::server::tasks::generic + +#endif // FIREBOLT_RIALTO_SERVER_TASKS_GENERIC_SET_REPORT_DECODE_ERRORS_H_ diff --git a/media/server/gstplayer/interface/IGstGenericPlayer.h b/media/server/gstplayer/interface/IGstGenericPlayer.h index 5e6842926..bb0931513 100644 --- a/media/server/gstplayer/interface/IGstGenericPlayer.h +++ b/media/server/gstplayer/interface/IGstGenericPlayer.h @@ -196,6 +196,25 @@ class IGstGenericPlayer */ virtual bool setImmediateOutput(const MediaSourceType &mediaSourceType, bool immediateOutput) = 0; + /** + * @brief Sets the "Report Decode Error" property for this source. + * + * @param[in] mediaSourceType : The media source type + * @param[in] reportDecodeErrors : Set report decode error + * + * @retval true on success. + */ + virtual bool setReportDecodeErrors(const MediaSourceType &mediaSourceType, bool reportDecodeErrors) = 0; + + /** + * @brief Gets the queued frames for this source. + * + * @param[out] queuedFrames : Get queued frames mode on the decoder + * + * @retval true on success. + */ + virtual bool getQueuedFrames(uint32_t &queuedFrames) = 0; + /** * @brief Gets the "Immediate Output" property for this source. * diff --git a/media/server/gstplayer/source/GstGenericPlayer.cpp b/media/server/gstplayer/source/GstGenericPlayer.cpp index c0e2fad13..9adadd7c9 100644 --- a/media/server/gstplayer/source/GstGenericPlayer.cpp +++ b/media/server/gstplayer/source/GstGenericPlayer.cpp @@ -629,6 +629,41 @@ bool GstGenericPlayer::setImmediateOutput(const MediaSourceType &mediaSourceType return true; } +bool GstGenericPlayer::setReportDecodeErrors(const MediaSourceType &mediaSourceType, bool reportDecodeErrors) +{ + if (!m_workerThread) + return false; + + m_workerThread->enqueueTask( + m_taskFactory->createSetReportDecodeErrors(m_context, *this, mediaSourceType, reportDecodeErrors)); + return true; +} + +bool GstGenericPlayer::getQueuedFrames(uint32_t &queuedFrames) +{ + bool returnValue{false}; + GstElement *decoder{getDecoder(MediaSourceType::VIDEO)}; + if (decoder) + { + if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(decoder), "queued-frames")) + { + m_glibWrapper->gObjectGet(decoder, "queued-frames", &queuedFrames, nullptr); + returnValue = true; + } + else + { + RIALTO_SERVER_LOG_ERROR("queued-frames not supported in element %s", GST_ELEMENT_NAME(decoder)); + } + m_gstWrapper->gstObjectUnref(decoder); + } + else + { + RIALTO_SERVER_LOG_ERROR("Failed to get queued-frames property, decoder is NULL"); + } + + return returnValue; +} + bool GstGenericPlayer::getImmediateOutput(const MediaSourceType &mediaSourceType, bool &immediateOutputRef) { bool returnValue{false}; @@ -1336,6 +1371,51 @@ bool GstGenericPlayer::setImmediateOutput() return result; } +bool GstGenericPlayer::setReportDecodeErrors() +{ + bool result{false}; + bool reportDecodeErrors{false}; + + { + std::unique_lock lock{m_context.propertyMutex}; + if (!m_context.pendingReportDecodeErrorsForVideo.has_value()) + { + return false; + } + reportDecodeErrors = m_context.pendingReportDecodeErrorsForVideo.value(); + } + + GstElement *decoder = getDecoder(MediaSourceType::VIDEO); + if (decoder) + { + RIALTO_SERVER_LOG_DEBUG("Set report decode errors to %s", reportDecodeErrors ? "TRUE" : "FALSE"); + + if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(decoder), "report-decode-errors")) + { + gboolean reportDecodeErrorsGboolean{reportDecodeErrors ? TRUE : FALSE}; + m_glibWrapper->gObjectSet(decoder, "report-decode-errors", reportDecodeErrorsGboolean, nullptr); + result = true; + } + else + { + RIALTO_SERVER_LOG_ERROR("Failed to set report-decode-errors property on decoder '%s'", + GST_ELEMENT_NAME(decoder)); + } + + m_gstWrapper->gstObjectUnref(decoder); + + { + std::unique_lock lock{m_context.propertyMutex}; + m_context.pendingReportDecodeErrorsForVideo.reset(); + } + } + else + { + RIALTO_SERVER_LOG_DEBUG("Pending report-decode-errors, decoder is NULL"); + } + return result; +} + bool GstGenericPlayer::setShowVideoWindow() { if (!m_context.pendingShowVideoWindow.has_value()) diff --git a/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp b/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp index 318f04379..a7886a335 100644 --- a/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp +++ b/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp @@ -41,6 +41,7 @@ #include "tasks/generic/SetMute.h" #include "tasks/generic/SetPlaybackRate.h" #include "tasks/generic/SetPosition.h" +#include "tasks/generic/SetReportDecodeErrors.h" #include "tasks/generic/SetSourcePosition.h" #include "tasks/generic/SetStreamSyncMode.h" #include "tasks/generic/SetSubtitleOffset.h" @@ -326,6 +327,14 @@ GenericPlayerTaskFactory::createSetImmediateOutput(GenericPlayerContext &context return std::make_unique(context, player, type, immediateOutput); } +std::unique_ptr +GenericPlayerTaskFactory::createSetReportDecodeErrors(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, + const firebolt::rialto::MediaSourceType &type, + bool reportDecodeErrors) const +{ + return std::make_unique(context, player, type, reportDecodeErrors); +} + std::unique_ptr GenericPlayerTaskFactory::createSetBufferingLimit(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, std::uint32_t limit) const diff --git a/media/server/gstplayer/source/tasks/generic/SetReportDecodeErrors.cpp b/media/server/gstplayer/source/tasks/generic/SetReportDecodeErrors.cpp new file mode 100644 index 000000000..6815df0ce --- /dev/null +++ b/media/server/gstplayer/source/tasks/generic/SetReportDecodeErrors.cpp @@ -0,0 +1,52 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SetReportDecodeErrors.h" +#include "RialtoServerLogging.h" +#include "TypeConverters.h" + +namespace firebolt::rialto::server::tasks::generic +{ +SetReportDecodeErrors::SetReportDecodeErrors(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, + const MediaSourceType &type, bool reportDecodeErrors) + : m_context{context}, m_player(player), m_type{type}, m_reportDecodeErrors{reportDecodeErrors} +{ + RIALTO_SERVER_LOG_DEBUG("Constructing SetReportDecodeErrors"); +} + +SetReportDecodeErrors::~SetReportDecodeErrors() +{ + RIALTO_SERVER_LOG_DEBUG("SetReportDecodeErrors finished"); +} + +void SetReportDecodeErrors::execute() const +{ + RIALTO_SERVER_LOG_DEBUG("Executing SetReportDecodeErrors for %s source", common::convertMediaSourceType(m_type)); + + m_context.pendingReportDecodeErrorsForVideo = m_reportDecodeErrors; + + if (!m_context.pipeline) + { + RIALTO_SERVER_LOG_WARN("Pipeline not available yet - cannot apply report-decode-errors setting"); + return; + } + + m_player.setReportDecodeErrors(); +} +} // namespace firebolt::rialto::server::tasks::generic diff --git a/media/server/gstplayer/source/tasks/generic/SetupElement.cpp b/media/server/gstplayer/source/tasks/generic/SetupElement.cpp index 99588430a..c7048fbe0 100644 --- a/media/server/gstplayer/source/tasks/generic/SetupElement.cpp +++ b/media/server/gstplayer/source/tasks/generic/SetupElement.cpp @@ -246,6 +246,10 @@ void SetupElement::execute() const RIALTO_SERVER_LOG_INFO("Setting video decoder handle for subtitle sink: %p", m_element); m_context.isVideoHandleSet = true; } + if (m_context.pendingReportDecodeErrorsForVideo.has_value()) + { + m_player.setReportDecodeErrors(); + } } } @@ -333,7 +337,6 @@ void SetupElement::execute() const { m_gstWrapper->gstBaseParseSetPtsInterpolation(GST_BASE_PARSE(m_element), FALSE); } - m_gstWrapper->gstObjectUnref(m_element); } } // namespace firebolt::rialto::server::tasks::generic diff --git a/media/server/ipc/include/MediaPipelineModuleService.h b/media/server/ipc/include/MediaPipelineModuleService.h index f0321827b..61d932c08 100644 --- a/media/server/ipc/include/MediaPipelineModuleService.h +++ b/media/server/ipc/include/MediaPipelineModuleService.h @@ -88,6 +88,14 @@ class MediaPipelineModuleService : public IMediaPipelineModuleService const ::firebolt::rialto::SetImmediateOutputRequest *request, ::firebolt::rialto::SetImmediateOutputResponse *response, ::google::protobuf::Closure *done) override; + void setReportDecodeErrors(::google::protobuf::RpcController *controller, + const ::firebolt::rialto::SetReportDecodeErrorsRequest *request, + ::firebolt::rialto::SetReportDecodeErrorsResponse *response, + ::google::protobuf::Closure *done) override; + void getQueuedFrames(::google::protobuf::RpcController *controller, + const ::firebolt::rialto::GetQueuedFramesRequest *request, + ::firebolt::rialto::GetQueuedFramesResponse *response, + ::google::protobuf::Closure *done) override; void getImmediateOutput(::google::protobuf::RpcController *controller, const ::firebolt::rialto::GetImmediateOutputRequest *request, ::firebolt::rialto::GetImmediateOutputResponse *response, diff --git a/media/server/ipc/source/MediaPipelineModuleService.cpp b/media/server/ipc/source/MediaPipelineModuleService.cpp index 7e08ec2f7..d876c1f9d 100644 --- a/media/server/ipc/source/MediaPipelineModuleService.cpp +++ b/media/server/ipc/source/MediaPipelineModuleService.cpp @@ -679,6 +679,40 @@ void MediaPipelineModuleService::setImmediateOutput(::google::protobuf::RpcContr done->Run(); } +void MediaPipelineModuleService::setReportDecodeErrors(::google::protobuf::RpcController *controller, + const ::firebolt::rialto::SetReportDecodeErrorsRequest *request, + ::firebolt::rialto::SetReportDecodeErrorsResponse *response, + ::google::protobuf::Closure *done) +{ + RIALTO_SERVER_LOG_DEBUG("entry:"); + if (!m_mediaPipelineService.setReportDecodeErrors(request->session_id(), request->source_id(), + request->report_decode_errors())) + { + RIALTO_SERVER_LOG_ERROR("Set Report Decode Error failed"); + controller->SetFailed("Operation failed"); + } + done->Run(); +} + +void MediaPipelineModuleService::getQueuedFrames(::google::protobuf::RpcController *controller, + const ::firebolt::rialto::GetQueuedFramesRequest *request, + ::firebolt::rialto::GetQueuedFramesResponse *response, + ::google::protobuf::Closure *done) +{ + RIALTO_SERVER_LOG_DEBUG("entry:"); + uint32_t queuedFramesNumber; + if (!m_mediaPipelineService.getQueuedFrames(request->session_id(), request->source_id(), queuedFramesNumber)) + { + RIALTO_SERVER_LOG_ERROR("Get queued frames failed"); + controller->SetFailed("Operation failed"); + } + else + { + response->set_queued_frames(queuedFramesNumber); + } + done->Run(); +} + void MediaPipelineModuleService::getImmediateOutput(::google::protobuf::RpcController *controller, const ::firebolt::rialto::GetImmediateOutputRequest *request, ::firebolt::rialto::GetImmediateOutputResponse *response, diff --git a/media/server/main/include/MediaPipelineServerInternal.h b/media/server/main/include/MediaPipelineServerInternal.h index cfd0cd2a7..cae22ebfa 100644 --- a/media/server/main/include/MediaPipelineServerInternal.h +++ b/media/server/main/include/MediaPipelineServerInternal.h @@ -113,6 +113,10 @@ class MediaPipelineServerInternal : public IMediaPipelineServerInternal, public bool setImmediateOutput(int32_t sourceId, bool immediateOutput) override; + bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) override; + + bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) override; + bool getImmediateOutput(int32_t sourceId, bool &immediateOutput) override; bool getStats(int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames) override; @@ -396,6 +400,30 @@ class MediaPipelineServerInternal : public IMediaPipelineServerInternal, public */ bool setImmediateOutputInternal(int32_t sourceId, bool immediateOutput); + /** + * @brief Sets the "Report Decode Errors" property for this source. + * + * This method is asynchronous + * + * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() + * @param[in] reportDecodeErrors : The desired Set Report Decode Errors mode on the sink + * + * @retval true on success. + */ + bool setReportDecodeErrorsInternal(int32_t sourceId, bool reportDecodeErrors); + + /** + * @brief Gets the queued frames for this source. + * + * This method is asynchronous + * + * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() + * @param[in] queuedFrames : Number of queued frames + * + * @retval true on success. + */ + bool getQueuedFramesInternal(int32_t sourceId, uint32_t &queuedFrames); + /** * @brief Gets the "Immediate Output" property for this source. * diff --git a/media/server/main/source/MediaPipelineServerInternal.cpp b/media/server/main/source/MediaPipelineServerInternal.cpp index 3cd73fdfd..2e940bb01 100644 --- a/media/server/main/source/MediaPipelineServerInternal.cpp +++ b/media/server/main/source/MediaPipelineServerInternal.cpp @@ -534,6 +534,63 @@ bool MediaPipelineServerInternal::setImmediateOutputInternal(int32_t sourceId, b return m_gstPlayer->setImmediateOutput(sourceIter->first, immediateOutput); } +bool MediaPipelineServerInternal::setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) +{ + RIALTO_SERVER_LOG_DEBUG("entry:"); + + bool result; + auto task = [&]() { result = setReportDecodeErrorsInternal(sourceId, reportDecodeErrors); }; + + m_mainThread->enqueueTaskAndWait(m_mainThreadClientId, task); + return result; +} + +bool MediaPipelineServerInternal::setReportDecodeErrorsInternal(int32_t sourceId, bool reportDecodeErrors) +{ + if (!m_gstPlayer) + { + RIALTO_SERVER_LOG_ERROR("Failed - Gstreamer player has not been loaded"); + return false; + } + auto sourceIter = std::find_if(m_attachedSources.begin(), m_attachedSources.end(), + [sourceId](const auto &src) { return src.second == sourceId; }); + if (sourceIter == m_attachedSources.end()) + { + RIALTO_SERVER_LOG_ERROR("Failed - Source not found"); + return false; + } + + return m_gstPlayer->setReportDecodeErrors(sourceIter->first, reportDecodeErrors); +} + +bool MediaPipelineServerInternal::getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) +{ + RIALTO_SERVER_LOG_DEBUG("entry:"); + + bool result; + auto task = [&]() { result = getQueuedFramesInternal(sourceId, queuedFrames); }; + + m_mainThread->enqueueTaskAndWait(m_mainThreadClientId, task); + return result; +} + +bool MediaPipelineServerInternal::getQueuedFramesInternal(int32_t sourceId, uint32_t &queuedFrames) +{ + if (!m_gstPlayer) + { + RIALTO_SERVER_LOG_ERROR("Failed - Gstreamer player has not been loaded"); + return false; + } + auto sourceIter = std::find_if(m_attachedSources.begin(), m_attachedSources.end(), + [sourceId](const auto &src) { return src.second == sourceId; }); + if (sourceIter == m_attachedSources.end()) + { + RIALTO_SERVER_LOG_ERROR("Failed - Source not found"); + return false; + } + return m_gstPlayer->getQueuedFrames(queuedFrames); +} + bool MediaPipelineServerInternal::getImmediateOutput(int32_t sourceId, bool &immediateOutput) { RIALTO_SERVER_LOG_DEBUG("entry:"); diff --git a/media/server/service/include/IMediaPipelineService.h b/media/server/service/include/IMediaPipelineService.h index ae1fc5e04..ffe742178 100644 --- a/media/server/service/include/IMediaPipelineService.h +++ b/media/server/service/include/IMediaPipelineService.h @@ -55,6 +55,8 @@ class IMediaPipelineService virtual bool setPosition(int sessionId, std::int64_t position) = 0; virtual bool getPosition(int sessionId, std::int64_t &position) = 0; virtual bool setImmediateOutput(int sessionId, int32_t sourceId, bool immediateOutput) = 0; + virtual bool setReportDecodeErrors(int sessionId, int32_t sourceId, bool reportDecodeErrors) = 0; + virtual bool getQueuedFrames(int sessionId, int32_t sourceId, uint32_t &queuedFrames) = 0; virtual bool getImmediateOutput(int sessionId, int32_t sourceId, bool &immediateOutput) = 0; virtual bool getStats(int sessionId, int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames) = 0; virtual bool setVideoWindow(int sessionId, std::uint32_t x, std::uint32_t y, std::uint32_t width, diff --git a/media/server/service/source/MediaPipelineService.cpp b/media/server/service/source/MediaPipelineService.cpp index 5d2212017..57b884fe1 100644 --- a/media/server/service/source/MediaPipelineService.cpp +++ b/media/server/service/source/MediaPipelineService.cpp @@ -267,6 +267,34 @@ bool MediaPipelineService::setImmediateOutput(int sessionId, int32_t sourceId, b return mediaPipelineIter->second->setImmediateOutput(sourceId, immediateOutput); } +bool MediaPipelineService::setReportDecodeErrors(int sessionId, int32_t sourceId, bool reportDecodeErrors) +{ + RIALTO_SERVER_LOG_INFO("MediaPipelineService requested to setReportDecodeErrors, session id: %d", sessionId); + + std::lock_guard lock{m_mediaPipelineMutex}; + auto mediaPipelineIter = m_mediaPipelines.find(sessionId); + if (mediaPipelineIter == m_mediaPipelines.end()) + { + RIALTO_SERVER_LOG_ERROR("Session with id: %d does not exists", sessionId); + return false; + } + return mediaPipelineIter->second->setReportDecodeErrors(sourceId, reportDecodeErrors); +} + +bool MediaPipelineService::getQueuedFrames(int sessionId, int32_t sourceId, uint32_t &queuedFrames) +{ + RIALTO_SERVER_LOG_INFO("MediaPipelineService requested to getQueuedFrames, session id: %d", sessionId); + + std::lock_guard lock{m_mediaPipelineMutex}; + auto mediaPipelineIter = m_mediaPipelines.find(sessionId); + if (mediaPipelineIter == m_mediaPipelines.end()) + { + RIALTO_SERVER_LOG_ERROR("Session with id: %d does not exists", sessionId); + return false; + } + return mediaPipelineIter->second->getQueuedFrames(sourceId, queuedFrames); +} + bool MediaPipelineService::getImmediateOutput(int sessionId, int32_t sourceId, bool &immediateOutput) { RIALTO_SERVER_LOG_INFO("MediaPipelineService requested to getImmediateOutput, session id: %d", sessionId); diff --git a/media/server/service/source/MediaPipelineService.h b/media/server/service/source/MediaPipelineService.h index bf9ec234f..5290c2475 100644 --- a/media/server/service/source/MediaPipelineService.h +++ b/media/server/service/source/MediaPipelineService.h @@ -66,6 +66,8 @@ class MediaPipelineService : public IMediaPipelineService bool setPosition(int sessionId, std::int64_t position) override; bool getPosition(int sessionId, std::int64_t &position) override; bool setImmediateOutput(int sessionId, int32_t sourceId, bool immediateOutput) override; + bool setReportDecodeErrors(int sessionId, int32_t sourceId, bool reportDecodeErrors) override; + bool getQueuedFrames(int sessionId, int32_t sourceId, uint32_t &queuedFrames) override; bool getImmediateOutput(int sessionId, int32_t sourceId, bool &immediateOutput) override; bool getStats(int sessionId, int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames) override; bool setVideoWindow(int sessionId, std::uint32_t x, std::uint32_t y, std::uint32_t width, diff --git a/proto/mediapipelinemodule.proto b/proto/mediapipelinemodule.proto index 1807810de..b111b3fb9 100644 --- a/proto/mediapipelinemodule.proto +++ b/proto/mediapipelinemodule.proto @@ -678,6 +678,24 @@ message SetImmediateOutputRequest { message SetImmediateOutputResponse { } +/** + * @fn void setReportDecodeErrors(int session_id, int source_id, bool report_decode_errors) + * @brief Enables or disables decode error reporting for this source. + * + * @param[in] session_id The id of the A/V session. + * @param[in] source_id The id of the media source. + * @param[in] report_decode_errors Enable decode error reporting. + */ +message SetReportDecodeErrorsRequest { + optional int32 session_id = 1 [default = -1]; + optional int32 source_id = 2 [default = -1]; + optional bool report_decode_errors = 3 [default = false]; +} + +message SetReportDecodeErrorsResponse { +} + + /** * @fn void getImmediateOutput(int session_id, int source_id, bool &immediate_output) * @brief Gets the "Immediate Output" property for this source. @@ -695,6 +713,23 @@ message GetImmediateOutputResponse { optional bool immediate_output = 1 [default = false]; } +/** + * @fn void getQueuedFrames(int session_id, int source_id, uint32_t &queued_frames) + * @brief Gets the "Queued Frames" property for this source. + * + * @param[in] session_id The id of the A/V session. + * @param[in] source_id The id of the media source. + * @param[out] queued_frames The number of queued frames on the decoder + * + */ +message GetQueuedFramesRequest { + optional int32 session_id = 1 [default = -1]; + optional int32 source_id = 2 [default = -1]; +} +message GetQueuedFramesResponse { + optional uint32 queued_frames = 1 [default = 0]; +} + /** * @fn void getStats(int session_id, int source_id, uint64 &rendered_frames, uint64 &dropped_frames) * @brief Get various stats from the source. @@ -1078,6 +1113,14 @@ service MediaPipelineModule { rpc setImmediateOutput(SetImmediateOutputRequest) returns (SetImmediateOutputResponse) { } + /** + * @brief Sets the "Report Decode Errors" property for this source. + * @see SetReportDecodeErrorsRequest + */ + rpc setReportDecodeErrors(SetReportDecodeErrorsRequest) + returns (SetReportDecodeErrorsResponse) { + } + /** * @brief Gets the "Immediate Output" property for this source. * @see GetImmediateOutputRequest @@ -1085,6 +1128,13 @@ service MediaPipelineModule { rpc getImmediateOutput(GetImmediateOutputRequest) returns (GetImmediateOutputResponse) { } + /** + * @brief Gets the "Queued Frames" property for this source. + * @see GetQueuedFramesRequest + */ + rpc getQueuedFrames(GetQueuedFramesRequest) returns (GetQueuedFramesResponse) { + } + /** * @brief Gets the current stats property * @see GetStatsRequest diff --git a/tests/common/matchers/MediaPipelineProtoRequestMatchers.h b/tests/common/matchers/MediaPipelineProtoRequestMatchers.h index 04c48ad78..1424cf106 100644 --- a/tests/common/matchers/MediaPipelineProtoRequestMatchers.h +++ b/tests/common/matchers/MediaPipelineProtoRequestMatchers.h @@ -366,6 +366,20 @@ MATCHER_P2(getImmediateOutputRequestMatcher, sessionId, sourceId, "") return (kRequest->session_id() == sessionId) && (kRequest->source_id() == sourceId); } +MATCHER_P2(setReportDecodeErrorsRequestMatcher, sessionId, sourceId, "") +{ + const ::firebolt::rialto::SetReportDecodeErrorsRequest *kRequest = + dynamic_cast(arg); + return (kRequest->session_id() == sessionId) && (kRequest->source_id() == sourceId); +} + +MATCHER_P2(getQueuedFramesRequestMatcher, sessionId, sourceId, "") +{ + const ::firebolt::rialto::GetQueuedFramesRequest *kRequest = + dynamic_cast(arg); + return (kRequest->session_id() == sessionId) && (kRequest->source_id() == sourceId); +} + MATCHER_P2(getStatsRequestMatcher, sessionId, sourceId, "") { const ::firebolt::rialto::GetStatsRequest *kRequest = dynamic_cast(arg); diff --git a/tests/componenttests/client/mocks/MediaPipelineModuleMock.h b/tests/componenttests/client/mocks/MediaPipelineModuleMock.h index a6fc772c0..5cf614593 100644 --- a/tests/componenttests/client/mocks/MediaPipelineModuleMock.h +++ b/tests/componenttests/client/mocks/MediaPipelineModuleMock.h @@ -72,6 +72,13 @@ class MediaPipelineModuleMock : public ::firebolt::rialto::MediaPipelineModule (::google::protobuf::RpcController * controller, const ::firebolt::rialto::GetImmediateOutputRequest *request, ::firebolt::rialto::GetImmediateOutputResponse *response, ::google::protobuf::Closure *done)); + MOCK_METHOD(void, setReportDecodeErrors, + (::google::protobuf::RpcController * controller, + const ::firebolt::rialto::SetReportDecodeErrorsRequest *request, + ::firebolt::rialto::SetReportDecodeErrorsResponse *response, ::google::protobuf::Closure *done)); + MOCK_METHOD(void, getQueuedFrames, + (::google::protobuf::RpcController * controller, const ::firebolt::rialto::GetQueuedFramesRequest *request, + ::firebolt::rialto::GetQueuedFramesResponse *response, ::google::protobuf::Closure *done)); MOCK_METHOD(void, getStats, (::google::protobuf::RpcController * controller, const ::firebolt::rialto::GetStatsRequest *request, ::firebolt::rialto::GetStatsResponse *response, ::google::protobuf::Closure *done)); @@ -231,6 +238,19 @@ class MediaPipelineModuleMock : public ::firebolt::rialto::MediaPipelineModule return response; } + ::firebolt::rialto::SetReportDecodeErrorsResponse SetReportDecodeErrorsResponse() + { + firebolt::rialto::SetReportDecodeErrorsResponse response; + return response; + } + + ::firebolt::rialto::GetQueuedFramesResponse getQueuedFramesResponse(uint32_t queuedFramesResponse) + { + firebolt::rialto::GetQueuedFramesResponse response; + response.set_queued_frames(queuedFramesResponse); + return response; + } + ::firebolt::rialto::GetStatsResponse getStatsResponse(const uint64_t renderedFrames, const uint64_t droppedFrames) { firebolt::rialto::GetStatsResponse response; diff --git a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp index e91d67bbb..a7f2f388a 100644 --- a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp +++ b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp @@ -1450,6 +1450,34 @@ void MediaPipelineTestMethods::getImmediateOutput(bool immediateOutput) EXPECT_TRUE(m_mediaPipeline->getImmediateOutput(kVideoSourceId, immediateOutput)); } +void MediaPipelineTestMethods::shouldSetReportDecodeErrors(bool reportDecodeErrors) +{ + EXPECT_CALL(*m_mediaPipelineModuleMock, + setReportDecodeErrors(_, setReportDecodeErrorsRequestMatcher(kSessionId, kVideoSourceId), _, _)) + .WillOnce(DoAll(SetArgPointee<2>(m_mediaPipelineModuleMock->SetReportDecodeErrorsResponse()), + WithArgs<0, 3>(Invoke(&(*m_mediaPipelineModuleMock), &MediaPipelineModuleMock::defaultReturn)))); +} + +void MediaPipelineTestMethods::setReportDecodeErrors(bool reportDecodeErrors) +{ + EXPECT_TRUE(m_mediaPipeline->setReportDecodeErrors(kVideoSourceId, reportDecodeErrors)); +} + +void MediaPipelineTestMethods::shouldGetQueuedFrames(uint32_t queuedFrames) +{ + EXPECT_CALL(*m_mediaPipelineModuleMock, + getQueuedFrames(_, getQueuedFramesRequestMatcher(kSessionId, kVideoSourceId), _, _)) + .WillOnce(DoAll(SetArgPointee<2>(m_mediaPipelineModuleMock->getQueuedFramesResponse(queuedFrames)), + WithArgs<0, 3>(Invoke(&(*m_mediaPipelineModuleMock), &MediaPipelineModuleMock::defaultReturn)))); +} + +void MediaPipelineTestMethods::getQueuedFrames(uint32_t queuedFrames) +{ + uint32_t returnQueuedFrames; + EXPECT_TRUE(m_mediaPipeline->getQueuedFrames(kVideoSourceId, returnQueuedFrames)); + EXPECT_EQ(returnQueuedFrames, queuedFrames); +} + void MediaPipelineTestMethods::shouldGetStats(uint64_t renderedFrames, uint64_t droppedFrames) { EXPECT_CALL(*m_mediaPipelineModuleMock, getStats(_, getStatsRequestMatcher(kSessionId, kVideoSourceId), _, _)) diff --git a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.h b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.h index c6b9d7f74..13425beaa 100644 --- a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.h +++ b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.h @@ -130,6 +130,8 @@ class MediaPipelineTestMethods void shouldGetPosition(const int64_t position); void shouldSetImmediateOutput(bool immediateOutput); void shouldGetImmediateOutput(bool immediateOutput); + void shouldSetReportDecodeErrors(bool reportDecodeErrors); + void shouldGetQueuedFrames(uint32_t queuedFrames); void shouldGetStats(uint64_t renderedFrames, uint64_t droppedFrames); void shouldFlush(); void shouldFailToFlush(); @@ -248,6 +250,8 @@ class MediaPipelineTestMethods void getPosition(const int64_t expectedPosition); void setImmediateOutput(bool immediateOutput); void getImmediateOutput(bool immediateOutput); + void setReportDecodeErrors(bool reportDecodeErrors); + void getQueuedFrames(uint32_t queuedFrames); void getStats(uint64_t expectedFrames, uint64_t expectedDropped); void createMediaPipelineCapabilitiesObject(); void destroyMediaPipelineCapabilitiesObject(); diff --git a/tests/componenttests/client/tests/mse/PipelinePropertyTest.cpp b/tests/componenttests/client/tests/mse/PipelinePropertyTest.cpp index a64aeadd7..db623007c 100644 --- a/tests/componenttests/client/tests/mse/PipelinePropertyTest.cpp +++ b/tests/componenttests/client/tests/mse/PipelinePropertyTest.cpp @@ -174,5 +174,15 @@ TEST_F(PipelinePropertyTest, setAndGetPipelineProperties) // Step 12: Get UseBuffering MediaPipelineTestMethods::shouldGetUseBuffering(useBuffering); MediaPipelineTestMethods::getUseBuffering(useBuffering); + + // Step 13: Set Report Decode Errors + bool reportDecodeErrors{true}; + MediaPipelineTestMethods::shouldSetReportDecodeErrors(reportDecodeErrors); + MediaPipelineTestMethods::setReportDecodeErrors(reportDecodeErrors); + + // Step 14: Get Queued Frames + uint32_t queuedFrames{123}; + MediaPipelineTestMethods::shouldGetQueuedFrames(queuedFrames); + MediaPipelineTestMethods::getQueuedFrames(queuedFrames); } } // namespace firebolt::rialto::client::ct diff --git a/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp b/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp index 2d11ee4f6..a6410d9a5 100644 --- a/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp @@ -159,6 +159,16 @@ class PipelinePropertyTest : public MediaPipelineTest *returnVal = value ? TRUE : FALSE; })); } + else if constexpr (std::is_same_v) + { + EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq(propertyName.c_str()), _)) + .WillOnce(Invoke( + [&](gpointer, const gchar *, void *val) + { + guint *returnVal = reinterpret_cast(val); + *returnVal = value; + })); + } else if constexpr (std::is_same_v) { EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq(propertyName.c_str()), _)) @@ -587,7 +597,7 @@ TEST_F(PipelinePropertyTest, pipelinePropertyGetAndSetSuccess) willStop(); stop(); - // Step 19: Destroy media session + // Step 18: Destroy media session gstPlayerWillBeDestructed(); destroySession(); } diff --git a/tests/unittests/media/client/ipc/CMakeLists.txt b/tests/unittests/media/client/ipc/CMakeLists.txt index c4018151c..205b9e411 100644 --- a/tests/unittests/media/client/ipc/CMakeLists.txt +++ b/tests/unittests/media/client/ipc/CMakeLists.txt @@ -37,6 +37,8 @@ add_gtests ( mediaPipelineIpc/GetPositionTest.cpp mediaPipelineIpc/SetImmediateOutputTest.cpp mediaPipelineIpc/GetImmediateOutputTest.cpp + mediaPipelineIpc/SetReportDecodeErrorsTest.cpp + mediaPipelineIpc/GetQueuedFramesTest.cpp mediaPipelineIpc/GetStatsTest.cpp mediaPipelineIpc/RenderFrameTest.cpp mediaPipelineIpc/GetVolumeTest.cpp diff --git a/tests/unittests/media/client/ipc/mediaPipelineIpc/GetQueuedFramesTest.cpp b/tests/unittests/media/client/ipc/mediaPipelineIpc/GetQueuedFramesTest.cpp new file mode 100644 index 000000000..17bdd5c84 --- /dev/null +++ b/tests/unittests/media/client/ipc/mediaPipelineIpc/GetQueuedFramesTest.cpp @@ -0,0 +1,100 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MediaPipelineIpcTestBase.h" +#include "MediaPipelineProtoRequestMatchers.h" + +class RialtoClientMediaPipelineIpcGetQueuedFramesTest : public MediaPipelineIpcTestBase +{ +protected: + virtual void SetUp() + { + MediaPipelineIpcTestBase::SetUp(); + + createMediaPipelineIpc(); + } + + virtual void TearDown() + { + destroyMediaPipelineIpc(); + + MediaPipelineIpcTestBase::TearDown(); + } + + const int32_t m_kSourceId{1}; +}; + +/** + * Test that getQueuedFrames can be called successfully. + */ +TEST_F(RialtoClientMediaPipelineIpcGetQueuedFramesTest, Success) +{ + expectIpcApiCallSuccess(); + + EXPECT_CALL(*m_channelMock, + CallMethod(methodMatcher("getQueuedFrames"), m_controllerMock.get(), + getQueuedFramesRequestMatcher(m_sessionId, m_kSourceId), _, m_blockingClosureMock.get())); + + uint32_t queuedFrames; + EXPECT_TRUE(m_mediaPipelineIpc->getQueuedFrames(m_kSourceId, queuedFrames)); +} + +/** + * Test that getQueuedFrames fails if the ipc channel disconnected. + */ +TEST_F(RialtoClientMediaPipelineIpcGetQueuedFramesTest, ChannelDisconnected) +{ + expectIpcApiCallDisconnected(); + expectUnsubscribeEvents(); + + uint32_t queuedFrames; + EXPECT_FALSE(m_mediaPipelineIpc->getQueuedFrames(m_kSourceId, queuedFrames)); + + // Reattach channel on destroySession + EXPECT_CALL(*m_ipcClientMock, getChannel()).WillOnce(Return(m_channelMock)).RetiresOnSaturation(); + expectSubscribeEvents(); +} + +/** + * Test that getQueuedFrames fails if the ipc channel disconnected and succeeds if the channel is reconnected. + */ +TEST_F(RialtoClientMediaPipelineIpcGetQueuedFramesTest, ReconnectChannel) +{ + expectIpcApiCallReconnected(); + expectUnsubscribeEvents(); + expectSubscribeEvents(); + + EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("getQueuedFrames"), _, _, _, _)); + + uint32_t queuedFrames; + EXPECT_TRUE(m_mediaPipelineIpc->getQueuedFrames(m_kSourceId, queuedFrames)); +} + +/** + * Test that getQueuedFrames fails when ipc fails. + */ +TEST_F(RialtoClientMediaPipelineIpcGetQueuedFramesTest, GetQueuedFramesFailure) +{ + expectIpcApiCallFailure(); + + EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("getQueuedFrames"), _, _, _, _)); + + uint32_t queuedFrames; + EXPECT_FALSE(m_mediaPipelineIpc->getQueuedFrames(m_kSourceId, queuedFrames)); +} diff --git a/tests/unittests/media/client/ipc/mediaPipelineIpc/SetReportDecodeErrorsTest.cpp b/tests/unittests/media/client/ipc/mediaPipelineIpc/SetReportDecodeErrorsTest.cpp new file mode 100644 index 000000000..d7e5fb8d2 --- /dev/null +++ b/tests/unittests/media/client/ipc/mediaPipelineIpc/SetReportDecodeErrorsTest.cpp @@ -0,0 +1,96 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2025 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MediaPipelineIpcTestBase.h" +#include "MediaPipelineProtoRequestMatchers.h" + +class RialtoClientMediaPipelineIpcSetReportDecodeErrorsTest : public MediaPipelineIpcTestBase +{ +protected: + virtual void SetUp() + { + MediaPipelineIpcTestBase::SetUp(); + + createMediaPipelineIpc(); + } + + virtual void TearDown() + { + destroyMediaPipelineIpc(); + + MediaPipelineIpcTestBase::TearDown(); + } + + const int32_t m_kSourceId{1}; +}; + +/** + * Test that setReportDecodeErrors can be called successfully. + */ +TEST_F(RialtoClientMediaPipelineIpcSetReportDecodeErrorsTest, Success) +{ + expectIpcApiCallSuccess(); + + EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("setReportDecodeErrors"), m_controllerMock.get(), + setReportDecodeErrorsRequestMatcher(m_sessionId, m_kSourceId), _, + m_blockingClosureMock.get())); + + EXPECT_TRUE(m_mediaPipelineIpc->setReportDecodeErrors(m_kSourceId, true)); +} + +/** + * Test that setReportDecodeErrors fails if the ipc channel disconnected. + */ +TEST_F(RialtoClientMediaPipelineIpcSetReportDecodeErrorsTest, ChannelDisconnected) +{ + expectIpcApiCallDisconnected(); + expectUnsubscribeEvents(); + + EXPECT_FALSE(m_mediaPipelineIpc->setReportDecodeErrors(m_kSourceId, true)); + + // Reattach channel on destroySession + EXPECT_CALL(*m_ipcClientMock, getChannel()).WillOnce(Return(m_channelMock)).RetiresOnSaturation(); + expectSubscribeEvents(); +} + +/** + * Test that setReportDecodeErrors fails if the ipc channel disconnected and succeeds if the channel is reconnected. + */ +TEST_F(RialtoClientMediaPipelineIpcSetReportDecodeErrorsTest, ReconnectChannel) +{ + expectIpcApiCallReconnected(); + expectUnsubscribeEvents(); + expectSubscribeEvents(); + + EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("setReportDecodeErrors"), _, _, _, _)); + + EXPECT_TRUE(m_mediaPipelineIpc->setReportDecodeErrors(m_kSourceId, true)); +} + +/** + * Test that setReportDecodeErrors fails when ipc fails. + */ +TEST_F(RialtoClientMediaPipelineIpcSetReportDecodeErrorsTest, SetReportDecodeErrorsFailure) +{ + expectIpcApiCallFailure(); + + EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("setReportDecodeErrors"), _, _, _, _)); + + EXPECT_FALSE(m_mediaPipelineIpc->setReportDecodeErrors(m_kSourceId, true)); +} diff --git a/tests/unittests/media/client/main/CMakeLists.txt b/tests/unittests/media/client/main/CMakeLists.txt index 17a5b0d95..4387fe598 100644 --- a/tests/unittests/media/client/main/CMakeLists.txt +++ b/tests/unittests/media/client/main/CMakeLists.txt @@ -34,6 +34,8 @@ add_gtests ( mediaPipeline/GetPositionTest.cpp mediaPipeline/SetImmediateOutputTest.cpp mediaPipeline/GetImmediateOutputTest.cpp + mediaPipeline/SetReportDecodeErrorsTest.cpp + mediaPipeline/GetQueuedFramesTest.cpp mediaPipeline/GetStatsTest.cpp mediaPipeline/RenderFrameTest.cpp mediaPipeline/SetVolumeTest.cpp diff --git a/tests/unittests/media/client/main/mediaPipeline/GetQueuedFramesTest.cpp b/tests/unittests/media/client/main/mediaPipeline/GetQueuedFramesTest.cpp new file mode 100644 index 000000000..277b46872 --- /dev/null +++ b/tests/unittests/media/client/main/mediaPipeline/GetQueuedFramesTest.cpp @@ -0,0 +1,63 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2025 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MediaPipelineTestBase.h" + +class RialtoClientMediaPipelineGetQueuedFramesTest : public MediaPipelineTestBase +{ +protected: + const int32_t m_kSourceId{1}; + + virtual void SetUp() + { + MediaPipelineTestBase::SetUp(); + + createMediaPipeline(); + } + + virtual void TearDown() + { + destroyMediaPipeline(); + + MediaPipelineTestBase::TearDown(); + } +}; + +/** + * Test that getQueuedFrames returns success if the IPC API succeeds. + */ +TEST_F(RialtoClientMediaPipelineGetQueuedFramesTest, GetQueuedFramesSuccess) +{ + constexpr uint32_t kExpectedQueuedFrames{123}; + EXPECT_CALL(*m_mediaPipelineIpcMock, getQueuedFrames(m_kSourceId, _)) + .WillOnce(DoAll(SetArgReferee<1>(123), Return(true))); + uint32_t queuedFrames; + EXPECT_TRUE(m_mediaPipeline->getQueuedFrames(m_kSourceId, queuedFrames)); + EXPECT_EQ(kExpectedQueuedFrames, queuedFrames); +} + +/** + * Test that getQueuedFrames returns failure if the IPC API fails. + */ +TEST_F(RialtoClientMediaPipelineGetQueuedFramesTest, GetQueuedFramesFailure) +{ + EXPECT_CALL(*m_mediaPipelineIpcMock, getQueuedFrames(m_kSourceId, _)).WillOnce(Return(false)); + uint32_t queuedFrames; + EXPECT_FALSE(m_mediaPipeline->getQueuedFrames(m_kSourceId, queuedFrames)); +} diff --git a/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp b/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp index 6e22bdf4d..7d9b7a2c7 100644 --- a/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp +++ b/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp @@ -163,6 +163,21 @@ TEST_F(RialtoClientMediaPipelineProxyTest, TestPassthrough) ///////////////////////////////////////////// + EXPECT_CALL(*mediaPipelineMock, setReportDecodeErrors(kSourceId, true)).WillOnce(Return(true)); + EXPECT_TRUE(proxy->setReportDecodeErrors(kSourceId, true)); + + ///////////////////////////////////////////// + + { + const uint32_t kQueuedFrames{123}; + uint32_t returnQueuedFrames; + EXPECT_CALL(*mediaPipelineMock, getQueuedFrames(kSourceId, _)) + .WillOnce(DoAll(SetArgReferee<1>(kQueuedFrames), Return(true))); + EXPECT_TRUE(proxy->getQueuedFrames(kSourceId, returnQueuedFrames)); + EXPECT_EQ(returnQueuedFrames, kQueuedFrames); + } + ///////////////////////////////////////////// + EXPECT_CALL(*mediaPipelineMock, setVideoWindow(1, 2, 3, 4)).WillOnce(Return(true)); EXPECT_TRUE(proxy->setVideoWindow(1, 2, 3, 4)); diff --git a/tests/unittests/media/client/main/mediaPipeline/SetReportDecodeErrorsTest.cpp b/tests/unittests/media/client/main/mediaPipeline/SetReportDecodeErrorsTest.cpp new file mode 100644 index 000000000..29f10533d --- /dev/null +++ b/tests/unittests/media/client/main/mediaPipeline/SetReportDecodeErrorsTest.cpp @@ -0,0 +1,58 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2025 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MediaPipelineTestBase.h" + +class RialtoClientMediaPipelineSetReportDecodeErrorsTest : public MediaPipelineTestBase +{ +protected: + const int32_t m_kSourceId{1}; + + virtual void SetUp() + { + MediaPipelineTestBase::SetUp(); + + createMediaPipeline(); + } + + virtual void TearDown() + { + destroyMediaPipeline(); + + MediaPipelineTestBase::TearDown(); + } +}; + +/** + * Test that setReportDecodeErrors returns success if the IPC API succeeds. + */ +TEST_F(RialtoClientMediaPipelineSetReportDecodeErrorsTest, SetReportDecodeErrorsSuccess) +{ + EXPECT_CALL(*m_mediaPipelineIpcMock, setReportDecodeErrors(m_kSourceId, _)).WillOnce(Return(true)); + EXPECT_TRUE(m_mediaPipeline->setReportDecodeErrors(m_kSourceId, true)); +} + +/** + * Test that setReportDecodeErrors returns failure if the IPC API fails. + */ +TEST_F(RialtoClientMediaPipelineSetReportDecodeErrorsTest, SetReportDecodeErrorsFailure) +{ + EXPECT_CALL(*m_mediaPipelineIpcMock, setReportDecodeErrors(m_kSourceId, _)).WillOnce(Return(false)); + EXPECT_FALSE(m_mediaPipeline->setReportDecodeErrors(m_kSourceId, true)); +} diff --git a/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h b/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h index 32e72ba2c..f08f54fef 100644 --- a/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h +++ b/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h @@ -47,6 +47,8 @@ class MediaPipelineIpcMock : public IMediaPipelineIpc MOCK_METHOD(bool, getPosition, (int64_t & position), (override)); MOCK_METHOD(bool, setImmediateOutput, (int32_t sourceId, bool immediateOutput), (override)); MOCK_METHOD(bool, getImmediateOutput, (int32_t sourceId, bool &immediateOutput), (override)); + MOCK_METHOD(bool, setReportDecodeErrors, (int32_t sourceId, bool reportDecodeErrors), (override)); + MOCK_METHOD(bool, getQueuedFrames, (int32_t sourceId, uint32_t &queuedFrames), (override)); MOCK_METHOD(bool, getStats, (int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames), (override)); MOCK_METHOD(bool, setPlaybackRate, (double rate), (override)); MOCK_METHOD(bool, renderFrame, (), (override)); diff --git a/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h b/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h index c69140210..1be860018 100644 --- a/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h +++ b/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h @@ -50,6 +50,8 @@ class MediaPipelineAndControlClientMock : public IMediaPipelineAndIControlClient MOCK_METHOD(bool, getPosition, (int64_t & position), (override)); MOCK_METHOD(bool, setImmediateOutput, (int32_t sourceId, bool immediateOutput), (override)); MOCK_METHOD(bool, getImmediateOutput, (int32_t sourceId, bool &immediateOutput), (override)); + MOCK_METHOD(bool, setReportDecodeErrors, (int32_t sourceId, bool reportDecodeErrors), (override)); + MOCK_METHOD(bool, getQueuedFrames, (int32_t sourceId, uint32_t &queuedFrames), (override)); MOCK_METHOD(bool, getStats, (int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames), (override)); MOCK_METHOD(bool, setVideoWindow, (uint32_t x, uint32_t y, uint32_t width, uint32_t height), (override)); diff --git a/tests/unittests/media/server/gstplayer/CMakeLists.txt b/tests/unittests/media/server/gstplayer/CMakeLists.txt index ed6635e3b..00a647f56 100644 --- a/tests/unittests/media/server/gstplayer/CMakeLists.txt +++ b/tests/unittests/media/server/gstplayer/CMakeLists.txt @@ -49,6 +49,7 @@ add_gtests(RialtoServerGstPlayerUnitTests genericPlayer/tasksTests/SetBufferingLimitTest.cpp genericPlayer/tasksTests/SetLowLatencyTest.cpp genericPlayer/tasksTests/SetImmediateOutputTest.cpp + genericPlayer/tasksTests/SetReportDecodeErrorsTest.cpp genericPlayer/tasksTests/SetMuteTest.cpp genericPlayer/tasksTests/SetPlaybackRateTest.cpp genericPlayer/tasksTests/SetPositionTest.cpp diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp index 20ad4904e..f6514dd39 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp @@ -72,6 +72,7 @@ const std::string kAutoVideoSinkTypeName{"GstAutoVideoSink"}; const std::string kAutoAudioSinkTypeName{"GstAutoAudioSink"}; constexpr bool kResetTime{true}; const std::string kImmediateOutputStr{"immediate-output"}; +const std::string kReportDecodeErrorsStr{"report-decode-errors"}; const std::string kLowLatencyStr{"low-latency"}; const std::string kSyncStr{"sync"}; const std::string kSyncOffStr{"sync-off"}; @@ -355,6 +356,35 @@ TEST_F(GstGenericPlayerPrivateTest, shouldSetImmediateOutput) EXPECT_TRUE(m_sut->setImmediateOutput()); } +TEST_F(GstGenericPlayerPrivateTest, shouldFailToSetReportDecodeErrorsIfPropertyDoesntExist) +{ + modifyContext([&](GenericPlayerContext &context) { context.pendingReportDecodeErrorsForVideo = true; }); + + expectGetVideoDecoder(m_realElement); + + expectPropertyDoesntExist(m_glibWrapperMock, m_gstWrapperMock, m_realElement, kReportDecodeErrorsStr); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_realElement)).Times(1); + EXPECT_FALSE(m_sut->setReportDecodeErrors()); +} + +TEST_F(GstGenericPlayerPrivateTest, shouldSetReportDecodeErrors) +{ + modifyContext([&](GenericPlayerContext &context) { context.pendingReportDecodeErrorsForVideo = true; }); + + expectGetVideoDecoder(m_realElement); + + GParamSpec gParamSpec{}; + + EXPECT_CALL(*m_glibWrapperMock, + gObjectClassFindProperty(G_OBJECT_GET_CLASS(m_realElement), StrEq(kReportDecodeErrorsStr))) + .WillOnce(Return(&gParamSpec)); + + EXPECT_CALL(*m_glibWrapperMock, gObjectSetStub(m_realElement, StrEq(kReportDecodeErrorsStr))).Times(1); + + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_realElement)).Times(1); + EXPECT_TRUE(m_sut->setReportDecodeErrors()); +} + TEST_F(GstGenericPlayerPrivateTest, shouldFailToSetLowLatencyIfSinkIsNull) { modifyContext([&](GenericPlayerContext &context) { context.pendingLowLatency = true; }); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp index f633cb441..5b92065f1 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp @@ -452,6 +452,43 @@ TEST_F(GstGenericPlayerTest, shouldFailToGetImmediateOutputInPlayingStateIfPrope EXPECT_FALSE(m_sut->getImmediateOutput(MediaSourceType::VIDEO, immediateOutputState)); } +TEST_F(GstGenericPlayerTest, shouldSetReportDecodeErrors) +{ + std::unique_ptr task{std::make_unique>()}; + EXPECT_CALL(dynamic_cast &>(*task), execute()); + EXPECT_CALL(m_taskFactoryMock, createSetReportDecodeErrors(_, _, MediaSourceType::VIDEO, true)) + .WillOnce(Return(ByMove(std::move(task)))); + + EXPECT_TRUE(m_sut->setReportDecodeErrors(MediaSourceType::VIDEO, true)); +} + +TEST_F(GstGenericPlayerTest, shouldGetQueuedFramesInPlayingState) +{ + setPipelineState(GST_STATE_PLAYING); + const uint32_t kTestQueuedFramesValue{123}; + const std::string kPropertyStr{"queued-frames"}; + + expectGetVideoDecoder(m_element); + willGetElementProperty(kPropertyStr, kTestQueuedFramesValue); + + uint32_t queuedFrames; + EXPECT_TRUE(m_sut->getQueuedFrames(queuedFrames)); + EXPECT_EQ(queuedFrames, kTestQueuedFramesValue); +} + +TEST_F(GstGenericPlayerTest, shouldFailToGetQueuedFramesInPlayingStateIfPropertyDoesntExist) +{ + setPipelineState(GST_STATE_PLAYING); + + expectGetVideoDecoder(m_element); + + EXPECT_CALL(*m_glibWrapperMock, gObjectClassFindProperty(_, StrEq("queued-frames"))).WillOnce(Return(nullptr)); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_element)).Times(1); + + uint32_t queuedFrames; + EXPECT_FALSE(m_sut->getQueuedFrames(queuedFrames)); +} + TEST_F(GstGenericPlayerTest, shouldGetStatsInPlayingState) { constexpr guint64 kRenderedFrames{1234}; diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp index f379b5884..6d9c80666 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp @@ -46,6 +46,7 @@ #include "tasks/generic/SetMute.h" #include "tasks/generic/SetPlaybackRate.h" #include "tasks/generic/SetPosition.h" +#include "tasks/generic/SetReportDecodeErrors.h" #include "tasks/generic/SetSourcePosition.h" #include "tasks/generic/SetStreamSyncMode.h" #include "tasks/generic/SetSubtitleOffset.h" @@ -3294,6 +3295,20 @@ void GenericTasksTestsBase::triggerSetImmediateOutput() EXPECT_EQ(testContext->m_context.pendingImmediateOutputForVideo, true); } +void GenericTasksTestsBase::shouldSetReportDecodeErrors() +{ + EXPECT_CALL(testContext->m_gstPlayer, setReportDecodeErrors()).WillOnce(Return(true)); +} + +void GenericTasksTestsBase::triggerSetReportDecodeErrors() +{ + firebolt::rialto::server::tasks::generic::SetReportDecodeErrors task{testContext->m_context, testContext->m_gstPlayer, + MediaSourceType::VIDEO, true}; + task.execute(); + + EXPECT_EQ(testContext->m_context.pendingReportDecodeErrorsForVideo, true); +} + void GenericTasksTestsBase::shouldSetLowLatency() { EXPECT_CALL(testContext->m_gstPlayer, setLowLatency()).WillOnce(Return(true)); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h index 29f64e16a..e0b722fa4 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h @@ -277,6 +277,10 @@ class GenericTasksTestsBase : public ::testing::Test void shouldSetVideoMute(); void shouldSetSubtitleMute(); + // report-decode-errors decoder property test method + void shouldSetReportDecodeErrors(); + void triggerSetReportDecodeErrors(); + // immediate-output sink property test methods void shouldSetImmediateOutput(); void triggerSetImmediateOutput(); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp index 3920d589a..b3e5db62f 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp @@ -217,6 +217,20 @@ void GstGenericPlayerTestCommon::expectGetDecoder(GstElement *element) EXPECT_CALL(*m_gstWrapperMock, gstIteratorFree(&m_it)); } +void GstGenericPlayerTestCommon::expectGetVideoDecoder(GstElement *element) +{ + EXPECT_CALL(*m_gstWrapperMock, gstBinIterateRecurse(GST_BIN(&m_pipeline))).WillOnce(Return(&m_it)); + EXPECT_CALL(*m_gstWrapperMock, gstIteratorNext(&m_it, _)).WillOnce(Return(GST_ITERATOR_OK)); + EXPECT_CALL(*m_glibWrapperMock, gValueGetObject(_)).WillOnce(Return(element)); + EXPECT_CALL(*m_gstWrapperMock, gstElementGetFactory(element)).WillOnce(Return(m_factory)); + EXPECT_CALL(*m_gstWrapperMock, gstElementFactoryListIsType(m_factory, (GST_ELEMENT_FACTORY_TYPE_DECODER | + GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO))) + .WillOnce(Return(TRUE)); + EXPECT_CALL(*m_gstWrapperMock, gstObjectRef(element)).WillOnce(Return(element)); + EXPECT_CALL(*m_glibWrapperMock, gValueUnset(_)); + EXPECT_CALL(*m_gstWrapperMock, gstIteratorFree(&m_it)); +} + void GstGenericPlayerTestCommon::expectGetVideoParser(GstElement *element) { EXPECT_CALL(*m_gstWrapperMock, gstBinIterateRecurse(GST_BIN(&m_pipeline))).WillOnce(Return(&m_it)); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h index 8f5e11dff..bc0e07b02 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h @@ -118,6 +118,7 @@ class GstGenericPlayerTestCommon : public ::testing::Test void expectCheckPlaySink(); void expectSetMessageCallback(); void expectGetDecoder(GstElement *element); + void expectGetVideoDecoder(GstElement *element); void expectGetVideoParser(GstElement *element); void expectGetSink(const std::string &sinkName, GstElement *elementObj); void expectNoDecoder(); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp index 70c305e22..d25627cda 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp @@ -53,6 +53,7 @@ #include "tasks/generic/SetMute.h" #include "tasks/generic/SetPlaybackRate.h" #include "tasks/generic/SetPosition.h" +#include "tasks/generic/SetReportDecodeErrors.h" #include "tasks/generic/SetSourcePosition.h" #include "tasks/generic/SetStreamSyncMode.h" #include "tasks/generic/SetSync.h" @@ -344,6 +345,13 @@ TEST_F(GenericPlayerTaskFactoryTest, ShouldCreateSetImmediateOutput) EXPECT_NO_THROW(dynamic_cast(*task)); } +TEST_F(GenericPlayerTaskFactoryTest, ShouldCreateSetReportDecodeErrors) +{ + auto task = m_sut.createSetReportDecodeErrors(m_context, m_gstPlayer, firebolt::rialto::MediaSourceType::VIDEO, true); + EXPECT_NE(task, nullptr); + EXPECT_NO_THROW(dynamic_cast(*task)); +} + TEST_F(GenericPlayerTaskFactoryTest, ShouldCreateSetTextTrackIdentifier) { auto task = m_sut.createSetTextTrackIdentifier(m_context, ""); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/SetReportDecodeErrorsTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/SetReportDecodeErrorsTest.cpp new file mode 100644 index 000000000..3d3f89f96 --- /dev/null +++ b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/SetReportDecodeErrorsTest.cpp @@ -0,0 +1,30 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2025 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GenericTasksTestsBase.h" + +class SetReportDecodeErrorsTest : public GenericTasksTestsBase +{ +}; + +TEST_F(SetReportDecodeErrorsTest, shouldSetReportDecodeErrors) +{ + shouldSetReportDecodeErrors(); + triggerSetReportDecodeErrors(); +} diff --git a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTests.cpp b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTests.cpp index dcad505bb..593a01b1e 100644 --- a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTests.cpp +++ b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTests.cpp @@ -281,6 +281,38 @@ TEST_F(MediaPipelineModuleServiceTests, shouldFailToGetImmediateOutput) sendGetImmediateOutputRequestAndReceiveFail(); } +TEST_F(MediaPipelineModuleServiceTests, shouldSetReportDecodeErrors) +{ + mediaPipelineServiceWillCreateSession(); + sendCreateSessionRequestAndReceiveResponse(); + mediaPipelineServiceWillSetReportDecodeErrors(); + sendSetReportDecodeErrorsRequestAndReceiveResponse(); +} + +TEST_F(MediaPipelineModuleServiceTests, shouldFailToSetReportDecodeErrors) +{ + mediaPipelineServiceWillCreateSession(); + sendCreateSessionRequestAndReceiveResponse(); + mediaPipelineServiceWillFailToSetReportDecodeErrors(); + sendSetReportDecodeErrorsRequestAndReceiveFail(); +} + +TEST_F(MediaPipelineModuleServiceTests, shouldGetQueuedFrames) +{ + mediaPipelineServiceWillCreateSession(); + sendCreateSessionRequestAndReceiveResponse(); + mediaPipelineServiceWillGetQueuedFrames(); + sendGetQueuedFramesRequestAndReceiveResponse(); +} + +TEST_F(MediaPipelineModuleServiceTests, shouldFailToGetQueuedFrames) +{ + mediaPipelineServiceWillCreateSession(); + sendCreateSessionRequestAndReceiveResponse(); + mediaPipelineServiceWillFailToGetQueuedFrames(); + sendGetQueuedFramesRequestAndReceiveFail(); +} + TEST_F(MediaPipelineModuleServiceTests, shouldGetStats) { mediaPipelineServiceWillCreateSession(); diff --git a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp index 31ec98768..5aa608e2e 100644 --- a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp +++ b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp @@ -86,6 +86,8 @@ constexpr uint64_t kDroppedFrames{321}; constexpr uint32_t kDuration{30}; constexpr bool kImmediateOutputVal1{false}; constexpr bool kImmediateOutputVal2{true}; +constexpr bool kReportDecodeErrorsVal{false}; +constexpr uint32_t kQueuedFramesVal{123}; constexpr int64_t kDiscontinuityGap{1}; constexpr bool kIsAudioAac{false}; constexpr uint32_t kBufferingLimit{12341}; @@ -518,6 +520,33 @@ void MediaPipelineModuleServiceTests::mediaPipelineServiceWillFailToGetImmediate EXPECT_CALL(m_mediaPipelineServiceMock, getImmediateOutput(kHardcodedSessionId, _, _)).WillOnce(Return(false)); } +void MediaPipelineModuleServiceTests::mediaPipelineServiceWillSetReportDecodeErrors() +{ + expectRequestSuccess(); + EXPECT_CALL(m_mediaPipelineServiceMock, setReportDecodeErrors(kHardcodedSessionId, _, kReportDecodeErrorsVal)) + .WillOnce(Return(true)); +} + +void MediaPipelineModuleServiceTests::mediaPipelineServiceWillFailToSetReportDecodeErrors() +{ + expectRequestFailure(); + EXPECT_CALL(m_mediaPipelineServiceMock, setReportDecodeErrors(kHardcodedSessionId, _, kReportDecodeErrorsVal)) + .WillOnce(Return(false)); +} + +void MediaPipelineModuleServiceTests::mediaPipelineServiceWillGetQueuedFrames() +{ + expectRequestSuccess(); + EXPECT_CALL(m_mediaPipelineServiceMock, getQueuedFrames(kHardcodedSessionId, _, _)) + .WillOnce(DoAll(SetArgReferee<2>(kQueuedFramesVal), Return(true))); +} + +void MediaPipelineModuleServiceTests::mediaPipelineServiceWillFailToGetQueuedFrames() +{ + expectRequestFailure(); + EXPECT_CALL(m_mediaPipelineServiceMock, getQueuedFrames(kHardcodedSessionId, _, _)).WillOnce(Return(false)); +} + void MediaPipelineModuleServiceTests::mediaPipelineServiceWillGetStats() { expectRequestSuccess(); @@ -1148,6 +1177,50 @@ void MediaPipelineModuleServiceTests::sendGetImmediateOutputRequestAndReceiveFai m_service->getImmediateOutput(m_controllerMock.get(), &request, &response, m_closureMock.get()); } +void MediaPipelineModuleServiceTests::sendSetReportDecodeErrorsRequestAndReceiveResponse() +{ + firebolt::rialto::SetReportDecodeErrorsRequest request; + firebolt::rialto::SetReportDecodeErrorsResponse response; + + request.set_session_id(kHardcodedSessionId); + request.set_report_decode_errors(kReportDecodeErrorsVal); + + m_service->setReportDecodeErrors(m_controllerMock.get(), &request, &response, m_closureMock.get()); +} + +void MediaPipelineModuleServiceTests::sendSetReportDecodeErrorsRequestAndReceiveFail() +{ + firebolt::rialto::SetReportDecodeErrorsRequest request; + firebolt::rialto::SetReportDecodeErrorsResponse response; + + request.set_session_id(kHardcodedSessionId); + request.set_report_decode_errors(kReportDecodeErrorsVal); + + m_service->setReportDecodeErrors(m_controllerMock.get(), &request, &response, m_closureMock.get()); +} + +void MediaPipelineModuleServiceTests::sendGetQueuedFramesRequestAndReceiveResponse() +{ + firebolt::rialto::GetQueuedFramesRequest request; + firebolt::rialto::GetQueuedFramesResponse response; + + request.set_session_id(kHardcodedSessionId); + + m_service->getQueuedFrames(m_controllerMock.get(), &request, &response, m_closureMock.get()); + + EXPECT_EQ(response.queued_frames(), kQueuedFramesVal); +} + +void MediaPipelineModuleServiceTests::sendGetQueuedFramesRequestAndReceiveFail() +{ + firebolt::rialto::GetQueuedFramesRequest request; + firebolt::rialto::GetQueuedFramesResponse response; + + request.set_session_id(kHardcodedSessionId); + + m_service->getQueuedFrames(m_controllerMock.get(), &request, &response, m_closureMock.get()); +} + void MediaPipelineModuleServiceTests::sendGetStatsRequestAndReceiveResponse() { firebolt::rialto::GetStatsRequest request; diff --git a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.h b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.h index 90f02fb4a..b987dd88e 100644 --- a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.h +++ b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.h @@ -78,6 +78,10 @@ class MediaPipelineModuleServiceTests : public testing::Test void mediaPipelineServiceWillFailToSetImmediateOutput(); void mediaPipelineServiceWillGetImmediateOutput(); void mediaPipelineServiceWillFailToGetImmediateOutput(); + void mediaPipelineServiceWillSetReportDecodeErrors(); + void mediaPipelineServiceWillFailToSetReportDecodeErrors(); + void mediaPipelineServiceWillGetQueuedFrames(); + void mediaPipelineServiceWillFailToGetQueuedFrames(); void mediaPipelineServiceWillGetStats(); void mediaPipelineServiceWillFailToGetStats(); void mediaPipelineServiceWillRenderFrame(); @@ -154,6 +158,10 @@ class MediaPipelineModuleServiceTests : public testing::Test void sendSetImmediateOutputRequestAndReceiveFail(); void sendGetImmediateOutputRequestAndReceiveResponse(); void sendGetImmediateOutputRequestAndReceiveFail(); + void sendSetReportDecodeErrorsRequestAndReceiveResponse(); + void sendSetReportDecodeErrorsRequestAndReceiveFail(); + void sendGetQueuedFramesRequestAndReceiveResponse(); + void sendGetQueuedFramesRequestAndReceiveFail(); void sendGetStatsRequestAndReceiveResponse(); void sendGetStatsRequestAndReceiveResponseWithoutStatsMatch(); void sendHaveDataRequestAndReceiveResponse(); diff --git a/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp b/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp index aa4f3e2f3..b1ca63c5b 100644 --- a/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp +++ b/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp @@ -327,6 +327,112 @@ TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetImmediateOutputSu EXPECT_EQ(immediateOutputState, false); } +/** + * Test that SetReportDecodeErrors returns failure if the gstreamer player is not initialized + */ +TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, SetReportDecodeErrorsFailureDueToUninitializedPlayer) +{ + mainThreadWillEnqueueTaskAndWait(); + EXPECT_FALSE(m_mediaPipeline->setReportDecodeErrors(m_kDummySourceId, true)); +} + +/** + * Test that SetReportDecodeErrors returns failure if the gstreamer API fails + */ +TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, SetReportDecodeErrorsFailure) +{ + loadGstPlayer(); + int videoSourceId = attachSource(firebolt::rialto::MediaSourceType::VIDEO, "video/h264"); + mainThreadWillEnqueueTaskAndWait(); + EXPECT_CALL(*m_gstPlayerMock, setReportDecodeErrors(_, _)).WillOnce(Return(false)); + EXPECT_FALSE(m_mediaPipeline->setReportDecodeErrors(videoSourceId, true)); +} + +/** + * Test that SetReportDecodeErrors fails if source is not present. + */ +TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, SetReportDecodeErrorsNoSourcePresent) +{ + loadGstPlayer(); + // No attachment of source + mainThreadWillEnqueueTaskAndWait(); + + EXPECT_FALSE(m_mediaPipeline->setReportDecodeErrors(m_kDummySourceId, true)); +} + +/** + * Test that SetReportDecodeErrors returns success if the gstreamer API succeeds + */ +TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, SetReportDecodeErrorsSuccess) +{ + loadGstPlayer(); + int videoSourceId = attachSource(firebolt::rialto::MediaSourceType::VIDEO, "video/h264"); + + mainThreadWillEnqueueTaskAndWait(); + EXPECT_CALL(*m_gstPlayerMock, setReportDecodeErrors(_, true)).WillOnce(Return(true)); + EXPECT_TRUE(m_mediaPipeline->setReportDecodeErrors(videoSourceId, true)); + + mainThreadWillEnqueueTaskAndWait(); + EXPECT_CALL(*m_gstPlayerMock, setReportDecodeErrors(_, false)).WillOnce(Return(true)); + EXPECT_TRUE(m_mediaPipeline->setReportDecodeErrors(videoSourceId, false)); +} + +/** + * Test that GetQueuedFrames returns failure if the gstreamer player is not initialized + */ +TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetQueuedFramesFailureDueToUninitializedPlayer) +{ + mainThreadWillEnqueueTaskAndWait(); + uint32_t queuedFrames; + EXPECT_FALSE(m_mediaPipeline->getQueuedFrames(m_kDummySourceId, queuedFrames)); +} + +/** + * Test that GetQueuedFrames returns failure if the gstreamer API fails + */ +TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetQueuedFramesFailure) +{ + loadGstPlayer(); + int videoSourceId = attachSource(firebolt::rialto::MediaSourceType::VIDEO, "video/h264"); + mainThreadWillEnqueueTaskAndWait(); + EXPECT_CALL(*m_gstPlayerMock, getQueuedFrames(_)).WillOnce(Return(false)); + uint32_t queuedFrames; + EXPECT_FALSE(m_mediaPipeline->getQueuedFrames(videoSourceId, queuedFrames)); +} + +/** + * Test that GetQueuedFrames fails if source is not present. + */ +TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetQueuedFramesNoSourcePresent) +{ + loadGstPlayer(); + // No attachment of source + mainThreadWillEnqueueTaskAndWait(); + + uint32_t queuedFrames; + EXPECT_FALSE(m_mediaPipeline->getQueuedFrames(m_kDummySourceId, queuedFrames)); +} + +/** + * Test that GetQueuedFrames returns success if the gstreamer API succeeds + */ +TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetQueuedFramesSuccess) +{ + loadGstPlayer(); + int videoSourceId = attachSource(firebolt::rialto::MediaSourceType::VIDEO, "video/h264"); + + uint32_t queuedFrames; + mainThreadWillEnqueueTaskAndWait(); + EXPECT_CALL(*m_gstPlayerMock, getQueuedFrames(_)).WillOnce(DoAll(SetArgReferee<0>(123), Return(true))); + EXPECT_TRUE(m_mediaPipeline->getQueuedFrames(videoSourceId, queuedFrames)); + EXPECT_EQ(queuedFrames, 123); + + mainThreadWillEnqueueTaskAndWait(); + EXPECT_CALL(*m_gstPlayerMock, getQueuedFrames(_)).WillOnce(DoAll(SetArgReferee<0>(456), Return(true))); + EXPECT_TRUE(m_mediaPipeline->getQueuedFrames(videoSourceId, queuedFrames)); + EXPECT_EQ(queuedFrames, 456); +} + /** * Test that GetStats returns failure if the gstreamer player is not initialized */ diff --git a/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h b/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h index 2cb7f0a35..b3fca43e0 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h @@ -133,6 +133,10 @@ class GenericPlayerTaskFactoryMock : public IGenericPlayerTaskFactory (GenericPlayerContext & context, IGstGenericPlayerPrivate &player, const firebolt::rialto::MediaSourceType &type, bool immediateOutput), (const, override)); + MOCK_METHOD(std::unique_ptr, createSetReportDecodeErrors, + (GenericPlayerContext & context, IGstGenericPlayerPrivate &player, + const firebolt::rialto::MediaSourceType &type, bool reportDecodeErrors), + (const, override)); MOCK_METHOD(std::unique_ptr, createSetBufferingLimit, (GenericPlayerContext & context, IGstGenericPlayerPrivate &player, std::uint32_t limit), (const, override)); diff --git a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h index c38c1d9eb..90ab4ebf7 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h @@ -44,6 +44,9 @@ class GstGenericPlayerMock : public IGstGenericPlayer MOCK_METHOD(bool, getPosition, (std::int64_t & position), (override)); MOCK_METHOD(bool, setImmediateOutput, (const MediaSourceType &mediaSourceType, bool immediateOutput), (override)); MOCK_METHOD(bool, getImmediateOutput, (const MediaSourceType &mediaSourceType, bool &immediateOutput), (override)); + MOCK_METHOD(bool, setReportDecodeErrors, (const MediaSourceType &mediaSourceType, bool reportDecodeErrors), + (override)); + MOCK_METHOD(bool, getQueuedFrames, (uint32_t & queuedFrames), (override)); MOCK_METHOD(bool, getStats, (const MediaSourceType &mediaSourceType, uint64_t &renderedFrames, uint64_t &droppedFrames), (override)); MOCK_METHOD(void, setVideoGeometry, (int x, int y, int width, int height), (override)); diff --git a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h index e28c70d8d..d0b018a87 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h @@ -39,6 +39,7 @@ class GstGenericPlayerPrivateMock : public IGstGenericPlayerPrivate MOCK_METHOD(void, scheduleAllSourcesAttached, (), (override)); MOCK_METHOD(bool, setVideoSinkRectangle, (), (override)); MOCK_METHOD(bool, setImmediateOutput, (), (override)); + MOCK_METHOD(bool, setReportDecodeErrors, (), (override)); MOCK_METHOD(bool, setLowLatency, (), (override)); MOCK_METHOD(bool, setSync, (), (override)); MOCK_METHOD(bool, setSyncOff, (), (override)); diff --git a/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h b/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h index 27a12a686..82f56556f 100644 --- a/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h +++ b/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h @@ -44,6 +44,8 @@ class MediaPipelineServerInternalMock : public IMediaPipelineServerInternal MOCK_METHOD(bool, getPosition, (std::int64_t & position), (override)); MOCK_METHOD(bool, setImmediateOutput, (int32_t sourceId, bool immediateOutput), (override)); MOCK_METHOD(bool, getImmediateOutput, (int32_t sourceId, bool &immediateOutput), (override)); + MOCK_METHOD(bool, setReportDecodeErrors, (int32_t sourceId, bool reportDecodeErrors), (override)); + MOCK_METHOD(bool, getQueuedFrames, (int32_t sourceId, uint32_t &queuedFrames), (override)); MOCK_METHOD(bool, getStats, (int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames), (override)); MOCK_METHOD(bool, setVideoWindow, (uint32_t x, uint32_t y, uint32_t width, uint32_t height), (override)); MOCK_METHOD(bool, haveData, (MediaSourceStatus status, uint32_t numFrames, uint32_t needDataRequestId), (override)); diff --git a/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h b/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h index f7b226777..46d46ee4f 100644 --- a/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h +++ b/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h @@ -46,6 +46,8 @@ class MediaPipelineServiceMock : public IMediaPipelineService MOCK_METHOD(bool, getPosition, (int sessionId, int64_t &position), (override)); MOCK_METHOD(bool, setImmediateOutput, (int sessionId, int32_t sourceId, bool immediateOutput), (override)); MOCK_METHOD(bool, getImmediateOutput, (int sessionId, int32_t sourceId, bool &immediateOutput), (override)); + MOCK_METHOD(bool, setReportDecodeErrors, (int sessionId, int32_t sourceId, bool reportDecodeErrors), (override)); + MOCK_METHOD(bool, getQueuedFrames, (int sessionId, int32_t sourceId, uint32_t &queuedFrames), (override)); MOCK_METHOD(bool, getStats, (int sessionId, int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames), (override)); MOCK_METHOD(bool, setVideoWindow, (int, std::uint32_t, std::uint32_t, std::uint32_t, std::uint32_t), (override)); diff --git a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTests.cpp b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTests.cpp index 4cd1f95d1..b922b72fe 100644 --- a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTests.cpp +++ b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTests.cpp @@ -362,6 +362,46 @@ TEST_F(MediaPipelineServiceTests, shouldGetImmediateOutput) getImmediateOutputShouldSucceed(); } +TEST_F(MediaPipelineServiceTests, shouldFailToSetReportDecodeErrorsForNotExistingSession) +{ + createMediaPipelineShouldSuccess(); + setReportDecodeErrorsShouldFail(); +} + +TEST_F(MediaPipelineServiceTests, shouldFailToGetQueuedFramesForNotExistingSession) +{ + createMediaPipelineShouldSuccess(); + getQueuedFramesShouldFail(); +} + +TEST_F(MediaPipelineServiceTests, shouldFailToSetReportDecodeErrors) +{ + initSession(); + mediaPipelineWillFailToSetReportDecodeErrors(); + setReportDecodeErrorsShouldFail(); +} + +TEST_F(MediaPipelineServiceTests, shouldFailToGetQueuedFrames) +{ + initSession(); + mediaPipelineWillFailToGetQueuedFrames(); + getQueuedFramesShouldFail(); +} + +TEST_F(MediaPipelineServiceTests, shouldSetReportDecodeErrors) +{ + initSession(); + mediaPipelineWillSetReportDecodeErrors(); + setReportDecodeErrorsShouldSucceed(); +} + +TEST_F(MediaPipelineServiceTests, shouldGetQueuedFrames) +{ + initSession(); + mediaPipelineWillGetQueuedFrames(); + getQueuedFramesShouldSucceed(); +} + TEST_F(MediaPipelineServiceTests, shouldFailToGetStatsForNotExistingSession) { createMediaPipelineShouldSuccess(); diff --git a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp index 421cb1d8b..6de97e773 100644 --- a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp +++ b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp @@ -69,6 +69,7 @@ const std::string kTextTrackIdentifier{"TextTrackIdentifier"}; constexpr uint32_t kBufferingLimit{4324}; constexpr bool kUseBuffering{true}; constexpr uint64_t kStopPosition{23412}; +constexpr uint32_t kQueuedFrames{123}; } // namespace namespace firebolt::rialto @@ -242,6 +243,26 @@ void MediaPipelineServiceTests::mediaPipelineWillFailToGetImmediateOutput() EXPECT_CALL(m_mediaPipelineMock, getImmediateOutput(_, _)).WillOnce(Return(false)); } +void MediaPipelineServiceTests::mediaPipelineWillSetReportDecodeErrors() +{ + EXPECT_CALL(m_mediaPipelineMock, setReportDecodeErrors(_, _)).WillOnce(Return(true)); +} + +void MediaPipelineServiceTests::mediaPipelineWillFailToSetReportDecodeErrors() +{ + EXPECT_CALL(m_mediaPipelineMock, setReportDecodeErrors(_, _)).WillOnce(Return(false)); +} + +void MediaPipelineServiceTests::mediaPipelineWillGetQueuedFrames() +{ + EXPECT_CALL(m_mediaPipelineMock, getQueuedFrames(_, _)).WillOnce(DoAll(SetArgReferee<1>(123), Return(true))); +} + +void MediaPipelineServiceTests::mediaPipelineWillFailToGetQueuedFrames() +{ + EXPECT_CALL(m_mediaPipelineMock, getQueuedFrames(_, _)).WillOnce(Return(false)); +} + void MediaPipelineServiceTests::mediaPipelineWillGetStats() { EXPECT_CALL(m_mediaPipelineMock, getStats(_, _, _)) @@ -746,6 +767,29 @@ void MediaPipelineServiceTests::getImmediateOutputShouldFail() EXPECT_FALSE(m_sut->getImmediateOutput(kSessionId, kSourceId, immOp)); } +void MediaPipelineServiceTests::setReportDecodeErrorsShouldSucceed() +{ + EXPECT_TRUE(m_sut->setReportDecodeErrors(kSessionId, kSourceId, true)); +} + +void MediaPipelineServiceTests::setReportDecodeErrorsShouldFail() +{ + EXPECT_FALSE(m_sut->setReportDecodeErrors(kSessionId, kSourceId, true)); +} + +void MediaPipelineServiceTests::getQueuedFramesShouldSucceed() +{ + uint32_t queuedFr; + EXPECT_TRUE(m_sut->getQueuedFrames(kSessionId, kSourceId, queuedFr)); + EXPECT_EQ(queuedFr, kQueuedFrames); +} + +void MediaPipelineServiceTests::getQueuedFramesShouldFail() +{ + uint32_t queuedFr; + EXPECT_FALSE(m_sut->getQueuedFrames(kSessionId, kSourceId, queuedFr)); +} + void MediaPipelineServiceTests::getSupportedMimeTypesSucceed() { MediaSourceType type = MediaSourceType::VIDEO; diff --git a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.h b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.h index ead87a642..1a7e28d15 100644 --- a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.h +++ b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.h @@ -66,8 +66,12 @@ class MediaPipelineServiceTests : public testing::Test void mediaPipelineWillFailToGetPosition(); void mediaPipelineWillSetImmediateOutput(); void mediaPipelineWillFailToSetImmediateOutput(); + void mediaPipelineWillSetReportDecodeErrors(); + void mediaPipelineWillFailToSetReportDecodeErrors(); void mediaPipelineWillGetImmediateOutput(); void mediaPipelineWillFailToGetImmediateOutput(); + void mediaPipelineWillGetQueuedFrames(); + void mediaPipelineWillFailToGetQueuedFrames(); void mediaPipelineWillGetStats(); void mediaPipelineWillFailToGetStats(); void mediaPipelineWillRenderFrame(); @@ -156,10 +160,14 @@ class MediaPipelineServiceTests : public testing::Test void haveDataShouldFail(); void getPositionShouldSucceed(); void getPositionShouldFail(); + void setReportDecodeErrorsShouldSucceed(); + void setReportDecodeErrorsShouldFail(); void setImmediateOutputShouldSucceed(); void setImmediateOutputShouldFail(); void getImmediateOutputShouldSucceed(); void getImmediateOutputShouldFail(); + void getQueuedFramesShouldSucceed(); + void getQueuedFramesShouldFail(); void getStatsShouldSucceed(); void getStatsShouldFail(); void getSupportedMimeTypesSucceed(); From 3e621ca7910005c78623a0e60e7cdc7f9ad0c46e Mon Sep 17 00:00:00 2001 From: Adam Czynszak <92790185+aczs@users.noreply.github.com> Date: Wed, 4 Mar 2026 13:01:00 +0000 Subject: [PATCH 11/29] Fixed getting sub sink and its async prop (#456) Summary: Added support for getting text-sink in getSink method Type: Fix Test Plan: UT/CT, Fullstack Jira: VPLAY-12522 --- .../gstplayer/source/GstGenericPlayer.cpp | 5 ++- .../GstGenericPlayerPrivateTest.cpp | 24 +++++----- .../genericPlayer/GstGenericPlayerTest.cpp | 44 ++++++++++++++----- .../common/GstGenericPlayerTestCommon.cpp | 13 +++++- .../common/GstGenericPlayerTestCommon.h | 2 + 5 files changed, 63 insertions(+), 25 deletions(-) diff --git a/media/server/gstplayer/source/GstGenericPlayer.cpp b/media/server/gstplayer/source/GstGenericPlayer.cpp index 9adadd7c9..813ad8fd8 100644 --- a/media/server/gstplayer/source/GstGenericPlayer.cpp +++ b/media/server/gstplayer/source/GstGenericPlayer.cpp @@ -416,6 +416,9 @@ GstElement *GstGenericPlayer::getSink(const MediaSourceType &mediaSourceType) co case MediaSourceType::VIDEO: kSinkName = "video-sink"; break; + case MediaSourceType::SUBTITLE: + kSinkName = "text-sink"; + break; default: break; } @@ -434,7 +437,7 @@ GstElement *GstGenericPlayer::getSink(const MediaSourceType &mediaSourceType) co RIALTO_SERVER_LOG_DEBUG("Pipeline is valid: %p", m_context.pipeline); } m_glibWrapper->gObjectGet(m_context.pipeline, kSinkName, &sink, nullptr); - if (sink) + if (sink && firebolt::rialto::MediaSourceType::SUBTITLE != mediaSourceType) { GstElement *autoSink{sink}; if (firebolt::rialto::MediaSourceType::VIDEO == mediaSourceType) diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp index f6514dd39..2e9c58e67 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp @@ -286,7 +286,7 @@ TEST_F(GstGenericPlayerPrivateTest, shouldNotSetVideoRectangleWhenVideoSinkIsNul TEST_F(GstGenericPlayerPrivateTest, shouldNotSetVideoRectangleWhenVideoSinkDoesNotHaveRectangleProperty) { - expectGetSink(kVideoSinkStr, m_realElement); + expectGetAVSink(kVideoSinkStr, m_realElement); EXPECT_CALL(*m_glibWrapperMock, gObjectClassFindProperty(_, StrEq("rectangle"))).WillOnce(Return(nullptr)); EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_realElement)); EXPECT_FALSE(m_sut->setVideoSinkRectangle()); @@ -294,7 +294,7 @@ TEST_F(GstGenericPlayerPrivateTest, shouldNotSetVideoRectangleWhenVideoSinkDoesN TEST_F(GstGenericPlayerPrivateTest, shouldSetVideoRectangle) { - expectGetSink(kVideoSinkStr, m_realElement); + expectGetAVSink(kVideoSinkStr, m_realElement); EXPECT_CALL(*m_glibWrapperMock, gObjectClassFindProperty(_, StrEq("rectangle"))).WillOnce(Return(&m_rectangleSpec)); EXPECT_CALL(*m_glibWrapperMock, gObjectSetStub(m_realElement, StrEq("rectangle"))); EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_realElement)); @@ -338,7 +338,7 @@ TEST_F(GstGenericPlayerPrivateTest, shouldFailToSetImmediateOutputIfPropertyDoes { modifyContext([&](GenericPlayerContext &context) { context.pendingImmediateOutputForVideo = true; }); - expectGetSink(kVideoSinkStr, m_realElement); + expectGetAVSink(kVideoSinkStr, m_realElement); expectPropertyDoesntExist(m_glibWrapperMock, m_gstWrapperMock, m_realElement, kImmediateOutputStr); EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_realElement)).Times(1); @@ -349,7 +349,7 @@ TEST_F(GstGenericPlayerPrivateTest, shouldSetImmediateOutput) { modifyContext([&](GenericPlayerContext &context) { context.pendingImmediateOutputForVideo = true; }); - expectGetSink(kVideoSinkStr, m_realElement); + expectGetAVSink(kVideoSinkStr, m_realElement); expectSetProperty(m_glibWrapperMock, m_gstWrapperMock, m_realElement, kImmediateOutputStr, true); EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_realElement)).Times(1); @@ -402,7 +402,7 @@ TEST_F(GstGenericPlayerPrivateTest, shouldFailToSetLowLatencyIfPropertyDoesntExi { modifyContext([&](GenericPlayerContext &context) { context.pendingLowLatency = true; }); - expectGetSink(kAudioSinkStr, m_realElement); + expectGetAVSink(kAudioSinkStr, m_realElement); expectPropertyDoesntExist(m_glibWrapperMock, m_gstWrapperMock, m_realElement, kLowLatencyStr); EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_realElement)).Times(1); @@ -413,7 +413,7 @@ TEST_F(GstGenericPlayerPrivateTest, shouldSetLowLatency) { modifyContext([&](GenericPlayerContext &context) { context.pendingLowLatency = true; }); - expectGetSink(kAudioSinkStr, m_realElement); + expectGetAVSink(kAudioSinkStr, m_realElement); expectSetProperty(m_glibWrapperMock, m_gstWrapperMock, m_realElement, kLowLatencyStr, true); EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_realElement)).Times(1); @@ -459,7 +459,7 @@ TEST_F(GstGenericPlayerPrivateTest, shouldFailToSetSyncIfPropertyDoesntExist) { modifyContext([&](GenericPlayerContext &context) { context.pendingSync = true; }); - expectGetSink(kAudioSinkStr, m_realElement); + expectGetAVSink(kAudioSinkStr, m_realElement); expectPropertyDoesntExist(m_glibWrapperMock, m_gstWrapperMock, m_realElement, kSyncStr); EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_realElement)).Times(1); @@ -470,7 +470,7 @@ TEST_F(GstGenericPlayerPrivateTest, shouldSetSync) { modifyContext([&](GenericPlayerContext &context) { context.pendingSync = true; }); - expectGetSink(kAudioSinkStr, m_realElement); + expectGetAVSink(kAudioSinkStr, m_realElement); expectSetProperty(m_glibWrapperMock, m_gstWrapperMock, m_realElement, kSyncStr, true); EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_realElement)).Times(1); @@ -631,7 +631,7 @@ TEST_F(GstGenericPlayerPrivateTest, shouldFailToSetRenderFrameIfPropertyDoesntEx { modifyContext([&](GenericPlayerContext &context) { context.pendingRenderFrame = true; }); - expectGetSink(kVideoSinkStr, m_realElement); + expectGetAVSink(kVideoSinkStr, m_realElement); expectPropertyDoesntExist(m_glibWrapperMock, m_gstWrapperMock, m_realElement, kFrameStepOnPrerollStr); EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_realElement)).Times(1); @@ -642,7 +642,7 @@ TEST_F(GstGenericPlayerPrivateTest, shouldSetRenderFrame) { modifyContext([&](GenericPlayerContext &context) { context.pendingRenderFrame = true; }); - expectGetSink(kVideoSinkStr, m_realElement); + expectGetAVSink(kVideoSinkStr, m_realElement); expectSetProperty(m_glibWrapperMock, m_gstWrapperMock, m_realElement, kFrameStepOnPrerollStr, 1); @@ -2129,7 +2129,7 @@ TEST_F(GstGenericPlayerPrivateTest, failToSetShowVideoWindowNoSink) TEST_F(GstGenericPlayerPrivateTest, failToSetShowVideoWindowNoProperty) { modifyContext([&](GenericPlayerContext &context) { context.pendingShowVideoWindow = true; }); - expectGetSink(kVideoSinkStr, m_realElement); + expectGetAVSink(kVideoSinkStr, m_realElement); EXPECT_CALL(*m_glibWrapperMock, gObjectClassFindProperty(_, StrEq("show-video-window"))).WillOnce(Return(nullptr)); EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_realElement)); EXPECT_FALSE(m_sut->setShowVideoWindow()); @@ -2139,7 +2139,7 @@ TEST_F(GstGenericPlayerPrivateTest, shouldSetShowVideoWindow) { modifyContext([&](GenericPlayerContext &context) { context.pendingShowVideoWindow = true; }); - expectGetSink(kVideoSinkStr, m_realElement); + expectGetAVSink(kVideoSinkStr, m_realElement); EXPECT_CALL(*m_glibWrapperMock, gObjectClassFindProperty(_, StrEq("show-video-window"))) .WillOnce(Return(&m_showVideoWindowSpec)); EXPECT_CALL(*m_glibWrapperMock, gObjectSetStub(m_realElement, StrEq("show-video-window"))); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp index 5b92065f1..108b0e423 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp @@ -398,7 +398,7 @@ TEST_F(GstGenericPlayerTest, shouldGetImmediateOutputInPlayingState) const bool kTestImmediateOutputValue{true}; const std::string kPropertyStr{"immediate-output"}; - expectGetSink(kVideoSinkStr, m_element); + expectGetAVSink(kVideoSinkStr, m_element); willGetElementProperty(kPropertyStr, kTestImmediateOutputValue); bool immediateOutputState; @@ -412,7 +412,7 @@ TEST_F(GstGenericPlayerTest, shouldGetImmediateOutputInPlayingStateForAudio) const bool kTestImmediateOutputValue{true}; const std::string kPropertyStr{"immediate-output"}; - expectGetSink(kAudioSinkStr, m_element); + expectGetAVSink(kAudioSinkStr, m_element); willGetElementProperty(kPropertyStr, kTestImmediateOutputValue); bool immediateOutputState; @@ -443,7 +443,7 @@ TEST_F(GstGenericPlayerTest, shouldFailToGetImmediateOutputInPlayingStateIfPrope { setPipelineState(GST_STATE_PLAYING); - expectGetSink(kVideoSinkStr, m_element); + expectGetAVSink(kVideoSinkStr, m_element); EXPECT_CALL(*m_glibWrapperMock, gObjectClassFindProperty(_, StrEq("immediate-output"))).WillOnce(Return(nullptr)); EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_element)).Times(1); @@ -497,7 +497,7 @@ TEST_F(GstGenericPlayerTest, shouldGetStatsInPlayingState) uint64_t returnedDroppedFrames{}; setPipelineState(GST_STATE_PLAYING); - expectGetSink(kVideoSinkStr, m_element); + expectGetAVSink(kVideoSinkStr, m_element); GstStructure testStructure; EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq("stats"), _)) @@ -545,7 +545,7 @@ TEST_F(GstGenericPlayerTest, shouldFailToGetStatsInPlayingStateIfStructureNull) { setPipelineState(GST_STATE_PLAYING); - expectGetSink(kAudioSinkStr, m_element); + expectGetAVSink(kAudioSinkStr, m_element); // Fail to get GstStructure which should cause the getStats() call to return false EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq("stats"), _)).Times(1); @@ -560,7 +560,7 @@ TEST_F(GstGenericPlayerTest, shouldFailToGetStatsInPlayingStateIfStructIncomplet { setPipelineState(GST_STATE_PLAYING); - expectGetSink(kVideoSinkStr, m_element); + expectGetAVSink(kVideoSinkStr, m_element); GstStructure testStructure; EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq("stats"), _)) @@ -600,7 +600,7 @@ TEST_F(GstGenericPlayerTest, shouldGetVolumeWithNegativeFadeVolume) const gint kNegativeFadeVolume{-100}; const std::string kPropertyStr{"fade-volume"}; - expectGetSink(kAudioSinkStr, m_element); + expectGetAVSink(kAudioSinkStr, m_element); willGetElementProperty(kPropertyStr, kNegativeFadeVolume); constexpr double kVolume{0.5}; @@ -619,7 +619,7 @@ TEST_F(GstGenericPlayerTest, shouldGetVolumeWithPositiveFadeVolume) const gint kFadeVolume{70}; const std::string kPropertyStr{"fade-volume"}; - expectGetSink(kAudioSinkStr, m_element); + expectGetAVSink(kAudioSinkStr, m_element); willGetElementProperty(kPropertyStr, kFadeVolume); @@ -786,7 +786,7 @@ TEST_F(GstGenericPlayerTest, shouldGetSync) const bool kSyncValue{true}; const std::string kPropertyStr{"sync"}; - expectGetSink(kAudioSinkStr, m_element); + expectGetAVSink(kAudioSinkStr, m_element); willGetElementProperty(kPropertyStr, kSyncValue); bool sync; @@ -816,7 +816,7 @@ TEST_F(GstGenericPlayerTest, shouldFailToGetSyncIfStubNull) TEST_F(GstGenericPlayerTest, shouldFailToGetSyncIfPropertyDoesntExist) { - expectGetSink(kAudioSinkStr, m_element); + expectGetAVSink(kAudioSinkStr, m_element); EXPECT_CALL(*m_glibWrapperMock, gObjectClassFindProperty(_, StrEq("sync"))).WillOnce(Return(nullptr)); EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_element)).Times(1); @@ -934,7 +934,7 @@ TEST_F(GstGenericPlayerTest, shouldFlush) bool isAsync{true}; std::unique_ptr task{std::make_unique>()}; EXPECT_CALL(m_flushWatcherMock, setFlushing(MediaSourceType::VIDEO, isAsync)); - expectGetSink(kVideoSinkStr, m_element); + expectGetAVSink(kVideoSinkStr, m_element); EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq("async"), _)) .WillOnce(Invoke( [&](gpointer object, const gchar *first_property_name, void *val) @@ -950,6 +950,28 @@ TEST_F(GstGenericPlayerTest, shouldFlush) m_sut->flush(MediaSourceType::VIDEO, kResetTime, isAsync); } +TEST_F(GstGenericPlayerTest, shouldFlushSubtitles) +{ + constexpr bool kResetTime{true}; + bool isAsync{false}; + std::unique_ptr task{std::make_unique>()}; + EXPECT_CALL(m_flushWatcherMock, setFlushing(MediaSourceType::SUBTITLE, isAsync)); + expectGetSink(kTextSinkStr, m_element); + EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq("async"), _)) + .WillOnce(Invoke( + [&](gpointer object, const gchar *first_property_name, void *val) + { + gboolean *returnVal = reinterpret_cast(val); + *returnVal = FALSE; + })); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_element)); + EXPECT_CALL(dynamic_cast &>(*task), execute()); + EXPECT_CALL(m_taskFactoryMock, createFlush(_, _, MediaSourceType::SUBTITLE, kResetTime, isAsync)) + .WillOnce(Return(ByMove(std::move(task)))); + + m_sut->flush(MediaSourceType::SUBTITLE, kResetTime, isAsync); +} + TEST_F(GstGenericPlayerTest, shouldSetSourcePosition) { constexpr int64_t kPosition{1234}; diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp index b3e5db62f..c4d5d2741 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp @@ -245,7 +245,7 @@ void GstGenericPlayerTestCommon::expectGetVideoParser(GstElement *element) EXPECT_CALL(*m_gstWrapperMock, gstIteratorFree(&m_it)); } -void GstGenericPlayerTestCommon::expectGetSink(const std::string &sinkName, GstElement *elementObj) +void GstGenericPlayerTestCommon::expectGetAVSink(const std::string &sinkName, GstElement *elementObj) { EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq(sinkName.c_str()), _)) .WillOnce(Invoke( @@ -257,6 +257,17 @@ void GstGenericPlayerTestCommon::expectGetSink(const std::string &sinkName, GstE EXPECT_CALL(*m_glibWrapperMock, gTypeName(G_OBJECT_TYPE(elementObj))).WillOnce(Return(kElementTypeName.c_str())); } +void GstGenericPlayerTestCommon::expectGetSink(const std::string &sinkName, GstElement *elementObj) +{ + EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq(sinkName.c_str()), _)) + .WillOnce(Invoke( + [elementObj](gpointer object, const gchar *first_property_name, void *element) + { + GstElement **elementPtr = reinterpret_cast(element); + *elementPtr = elementObj; + })); +} + void GstGenericPlayerTestCommon::expectNoDecoder() { EXPECT_CALL(*m_gstWrapperMock, gstBinIterateRecurse(GST_BIN(&m_pipeline))).WillOnce(Return(&m_it)); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h index bc0e07b02..d8e72efaf 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h @@ -55,6 +55,7 @@ namespace const std::string kElementTypeName{"GenericSink"}; const std::string kAudioSinkStr{"audio-sink"}; const std::string kVideoSinkStr{"video-sink"}; +const std::string kTextSinkStr{"text-sink"}; } // namespace class GstGenericPlayerTestCommon : public ::testing::Test @@ -120,6 +121,7 @@ class GstGenericPlayerTestCommon : public ::testing::Test void expectGetDecoder(GstElement *element); void expectGetVideoDecoder(GstElement *element); void expectGetVideoParser(GstElement *element); + void expectGetAVSink(const std::string &sinkName, GstElement *elementObj); void expectGetSink(const std::string &sinkName, GstElement *elementObj); void expectNoDecoder(); void expectNoParser(); From f38f096e17a4ffd00e34f461e3fa501ed41647ac Mon Sep 17 00:00:00 2001 From: Adam Czynszak <92790185+aczs@users.noreply.github.com> Date: Thu, 12 Mar 2026 16:27:25 +0000 Subject: [PATCH 12/29] Removed blocking get_state calls from aml codec switch (#459) Summary: Removed blocking get_state calls from aml codec switch Type: Fix Test Plan: UT/CT, Fullstack Jira: DELIA-70115 --- .../gstplayer/include/GstGenericPlayer.h | 55 ++ .../gstplayer/source/GstGenericPlayer.cpp | 505 +++++++++++++++++- .../externalLibraryMocks/GstWrapperMock.h | 7 + .../mediaPipeline/AudioSourceSwitchTest.cpp | 14 +- .../GstGenericPlayerPrivateTest.cpp | 205 +++++++ wrappers/include/GstWrapper.h | 16 + wrappers/interface/IGstWrapper.h | 63 +++ 7 files changed, 855 insertions(+), 10 deletions(-) diff --git a/media/server/gstplayer/include/GstGenericPlayer.h b/media/server/gstplayer/include/GstGenericPlayer.h index c0fc9f6e4..8ea963d8d 100644 --- a/media/server/gstplayer/include/GstGenericPlayer.h +++ b/media/server/gstplayer/include/GstGenericPlayer.h @@ -326,6 +326,61 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva std::optional createAudioAttributes(const std::unique_ptr &source) const; + /** + * @brief Configures audio caps based on audio attributes. + * Called by worker thread only! + * + * @param[in] pAttrib : The audio attributes. + * @param[out] audioaac : Set to true if AAC, false otherwise. + * @param[in] svpenabled : Whether SVP is enabled. + * @param[in,out] appsrcCaps : The caps to configure. + */ + void configAudioCap(firebolt::rialto::wrappers::AudioAttributesPrivate *pAttrib, bool *audioaac, bool svpenabled, + GstCaps **appsrcCaps); + + /** + * @brief Halts audio playback by setting playsink to READY and decodebin to PAUSED. + * Called by worker thread only! + */ + void haltAudioPlayback(); + + /** + * @brief Resumes audio playback by syncing playsink and decodebin with parent. + * Called by worker thread only! + */ + void resumeAudioPlayback(); + + /** + * @brief First-time codec switch from AC3 to AAC when no decoder exists yet. + * Called by worker thread only! + * + * @param[in] newAudioCaps : The new audio caps to apply. + */ + void firstTimeSwitchFromAC3toAAC(GstCaps *newAudioCaps); + + /** + * @brief Switches the audio codec by unlinking old parser/decoder and linking new ones. + * Called by worker thread only! + * + * @param[in] isAudioAAC : Whether the new codec is AAC. + * @param[in] newAudioCaps : The new audio caps to apply. + * + * @retval true if codec was switched, false if same codec. + */ + bool switchAudioCodec(bool isAudioAAC, GstCaps *newAudioCaps); + + /** + * @brief Top-level audio track codec channel switch, ported from rdk_gstreamer_utils_soc. + * Called by worker thread only! + */ + bool performAudioTrackCodecChannelSwitch(const void *pSampleAttr, + firebolt::rialto::wrappers::AudioAttributesPrivate *pAudioAttr, + uint32_t *pStatus, unsigned int *pui32Delay, + long long *pAudioChangeTargetPts, // NOLINT(runtime/int) + const long long *pcurrentDispPts, // NOLINT(runtime/int) + unsigned int *audioChangeStage, GstCaps **appsrcCaps, bool *audioaac, + bool svpenabled, GstElement *aSrc, bool *ret); + /** * @brief Sets text track position before pushing data * diff --git a/media/server/gstplayer/source/GstGenericPlayer.cpp b/media/server/gstplayer/source/GstGenericPlayer.cpp index 813ad8fd8..56030d04d 100644 --- a/media/server/gstplayer/source/GstGenericPlayer.cpp +++ b/media/server/gstplayer/source/GstGenericPlayer.cpp @@ -19,6 +19,8 @@ #include #include +#include +#include #include #include "FlushWatcher.h" @@ -622,6 +624,466 @@ GstGenericPlayer::createAudioAttributes(const std::unique_ptrm_codecParam.c_str(), pAttrib->m_samplesPerSecond, pAttrib->m_numberOfChannels, + pAttrib->m_blockAlignment); + if (pAttrib->m_codecParam.compare(0, 4, std::string("mp4a")) == 0) + { + RIALTO_SERVER_LOG_DEBUG("Using AAC"); + capsString = m_glibWrapper->gStrdupPrintf("audio/mpeg, mpegversion=4, enable-svp=(string)%s", + svpenabled ? "true" : "false"); + *audioaac = true; + } + else + { + RIALTO_SERVER_LOG_DEBUG("Using EAC3"); + capsString = m_glibWrapper->gStrdupPrintf("audio/x-eac3, framed=(boolean)true, rate=(int)%u, channels=(int)%u, " + "alignment=(string)frame, enable-svp=(string)%s", + pAttrib->m_samplesPerSecond, pAttrib->m_numberOfChannels, + svpenabled ? "true" : "false"); + *audioaac = false; + } + *appsrcCaps = m_gstWrapper->gstCapsFromString(capsString); + m_glibWrapper->gFree(capsString); +} + +void GstGenericPlayer::haltAudioPlayback() +{ + // this function comes from rdk_gstreamer_utils + if (!m_context.playbackGroup.m_curAudioPlaysinkBin || !m_context.playbackGroup.m_curAudioDecodeBin) + { + RIALTO_SERVER_LOG_ERROR("haltAudioPlayback: audio playsink bin or decode bin is null"); + return; + } + GstState currentState{GST_STATE_VOID_PENDING}, pending{GST_STATE_VOID_PENDING}; + + // Transition Playsink to Ready + if (GST_STATE_CHANGE_FAILURE == + m_gstWrapper->gstElementSetState(m_context.playbackGroup.m_curAudioPlaysinkBin, GST_STATE_READY)) + { + RIALTO_SERVER_LOG_WARN("Failed to set AudioPlaysinkBin to READY"); + return; + } + m_gstWrapper->gstElementGetState(m_context.playbackGroup.m_curAudioPlaysinkBin, ¤tState, &pending, + GST_CLOCK_TIME_NONE); + if (currentState == GST_STATE_PAUSED) + RIALTO_SERVER_LOG_DEBUG("OTF -> Current AudioPlaySinkBin State = %d", currentState); + // Transition Decodebin to Paused + if (GST_STATE_CHANGE_FAILURE == + m_gstWrapper->gstElementSetState(m_context.playbackGroup.m_curAudioDecodeBin, GST_STATE_PAUSED)) + { + RIALTO_SERVER_LOG_WARN("Failed to set AudioDecodeBin to PAUSED"); + return; + } + m_gstWrapper->gstElementGetState(m_context.playbackGroup.m_curAudioDecodeBin, ¤tState, &pending, + GST_CLOCK_TIME_NONE); + if (currentState == GST_STATE_PAUSED) + RIALTO_SERVER_LOG_DEBUG("OTF -> Current DecodeBin State = %d", currentState); +} + +void GstGenericPlayer::resumeAudioPlayback() +{ + // this function comes from rdk_gstreamer_utils + if (!m_context.playbackGroup.m_curAudioPlaysinkBin || !m_context.playbackGroup.m_curAudioDecodeBin) + { + RIALTO_SERVER_LOG_ERROR("resumeAudioPlayback: audio playsink bin or decode bin is null"); + return; + } + GstState currentState{GST_STATE_VOID_PENDING}, pending{GST_STATE_VOID_PENDING}; + m_gstWrapper->gstElementSyncStateWithParent(m_context.playbackGroup.m_curAudioPlaysinkBin); + m_gstWrapper->gstElementGetState(m_context.playbackGroup.m_curAudioPlaysinkBin, ¤tState, &pending, + GST_CLOCK_TIME_NONE); + RIALTO_SERVER_LOG_DEBUG("OTF -> AudioPlaysinkbin State = %d Pending = %d", currentState, pending); + m_gstWrapper->gstElementSyncStateWithParent(m_context.playbackGroup.m_curAudioDecodeBin); + m_gstWrapper->gstElementGetState(m_context.playbackGroup.m_curAudioDecodeBin, ¤tState, &pending, + GST_CLOCK_TIME_NONE); + RIALTO_SERVER_LOG_DEBUG("OTF -> Decodebin State = %d Pending = %d", currentState, pending); +} + +void GstGenericPlayer::firstTimeSwitchFromAC3toAAC(GstCaps *newAudioCaps) +{ + // this function comes from rdk_gstreamer_utils + if (!m_context.playbackGroup.m_curAudioTypefind || !m_context.playbackGroup.m_curAudioDecodeBin) + { + RIALTO_SERVER_LOG_ERROR("firstTimeSwitchFromAC3toAAC: audio typefind or decode bin is null"); + return; + } + GstState currentState{GST_STATE_VOID_PENDING}, pending{GST_STATE_VOID_PENDING}; + GstPad *pTypfdSrcPad = NULL; + GstPad *pTypfdSrcPeerPad = NULL; + GstPad *pNewAudioDecoderSrcPad = NULL; + GstElement *newAudioParse = NULL; + GstElement *newAudioDecoder = NULL; + GstElement *newQueue = NULL; + gboolean linkRet = false; + + /* Get the SinkPad of ASink - pTypfdSrcPeerPad */ + 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"); + // Add new Decoder to Decodebin + if (m_gstWrapper->gstBinAdd(GST_BIN(m_context.playbackGroup.m_curAudioDecodeBin.load()), newAudioDecoder) == TRUE) + { + RIALTO_SERVER_LOG_DEBUG("OTF -> Added New AudioDecoder = %p", newAudioDecoder); + } + // Add new Parser to Decodebin + if (m_gstWrapper->gstBinAdd(GST_BIN(m_context.playbackGroup.m_curAudioDecodeBin.load()), newAudioParse) == TRUE) + { + RIALTO_SERVER_LOG_DEBUG("OTF -> Added New AudioParser = %p", newAudioParse); + } + // Add new Queue to Decodebin + if (m_gstWrapper->gstBinAdd(GST_BIN(m_context.playbackGroup.m_curAudioDecodeBin.load()), newQueue) == TRUE) + { + RIALTO_SERVER_LOG_DEBUG("OTF -> Added New queue = %p", newQueue); + } + if ((pNewAudioDecoderSrcPad = m_gstWrapper->gstElementGetStaticPad(newAudioDecoder, "src")) != NULL) // Unref the Pad + RIALTO_SERVER_LOG_DEBUG("OTF -> New AudioDecoder Src Pad = %p", pNewAudioDecoderSrcPad); + // Connect decoder to ASINK + if (m_gstWrapper->gstPadLink(pNewAudioDecoderSrcPad, pTypfdSrcPeerPad) != GST_PAD_LINK_OK) + RIALTO_SERVER_LOG_DEBUG("OTF -> New AudioDecoder Downstream Link Failed"); + linkRet = m_gstWrapper->gstElementLink(newAudioParse, newQueue) && + m_gstWrapper->gstElementLink(newQueue, newAudioDecoder); + if (!linkRet) + RIALTO_SERVER_LOG_DEBUG("OTF -> Downstream Link Failed for typefind, parser, decoder"); + /* Force Caps */ + RIALTO_SERVER_LOG_DEBUG("OTF -> Typefind Setting to READY"); + if (GST_STATE_CHANGE_FAILURE == + m_gstWrapper->gstElementSetState(m_context.playbackGroup.m_curAudioTypefind, GST_STATE_READY)) + { + RIALTO_SERVER_LOG_WARN("Failed to set Typefind to READY"); + m_gstWrapper->gstObjectUnref(pTypfdSrcPad); + m_gstWrapper->gstObjectUnref(pTypfdSrcPeerPad); + m_gstWrapper->gstObjectUnref(pNewAudioDecoderSrcPad); + return; + } + m_glibWrapper->gObjectSet(G_OBJECT(m_context.playbackGroup.m_curAudioTypefind), "force-caps", newAudioCaps, NULL); + m_gstWrapper->gstElementSyncStateWithParent(m_context.playbackGroup.m_curAudioTypefind); + m_gstWrapper->gstElementGetState(m_context.playbackGroup.m_curAudioTypefind, ¤tState, &pending, + GST_CLOCK_TIME_NONE); + RIALTO_SERVER_LOG_DEBUG("OTF -> New Typefind State = %d Pending = %d", currentState, pending); + RIALTO_SERVER_LOG_DEBUG("OTF -> Typefind Syncing with Parent"); + m_context.playbackGroup.m_linkTypefindParser = true; + /* Update the state */ + m_gstWrapper->gstElementSyncStateWithParent(newAudioDecoder); + m_gstWrapper->gstElementGetState(newAudioDecoder, ¤tState, &pending, GST_CLOCK_TIME_NONE); + RIALTO_SERVER_LOG_DEBUG("OTF -> New AudioDecoder State = %d Pending = %d", currentState, pending); + m_gstWrapper->gstElementSyncStateWithParent(newQueue); + m_gstWrapper->gstElementGetState(newQueue, ¤tState, &pending, GST_CLOCK_TIME_NONE); + RIALTO_SERVER_LOG_DEBUG("OTF -> New queue State = %d Pending = %d", currentState, pending); + m_gstWrapper->gstElementSyncStateWithParent(newAudioParse); + m_gstWrapper->gstElementGetState(newAudioParse, ¤tState, &pending, GST_CLOCK_TIME_NONE); + RIALTO_SERVER_LOG_DEBUG("OTF -> New AudioParser State = %d Pending = %d", currentState, pending); + m_gstWrapper->gstObjectUnref(pTypfdSrcPad); + m_gstWrapper->gstObjectUnref(pTypfdSrcPeerPad); + m_gstWrapper->gstObjectUnref(pNewAudioDecoderSrcPad); + return; +} + +bool GstGenericPlayer::switchAudioCodec(bool isAudioAAC, GstCaps *newAudioCaps) +{ // this function comes from rdk_gstreamer_utils + bool ret = false; + RIALTO_SERVER_LOG_DEBUG("Current Audio Codec AAC = %d Same as Incoming audio Codec AAC = %d", + m_context.playbackGroup.m_isAudioAAC, isAudioAAC); + if (m_context.playbackGroup.m_isAudioAAC == isAudioAAC) + { + return ret; + } + if ((m_context.playbackGroup.m_curAudioDecoder == NULL) && (!(m_context.playbackGroup.m_isAudioAAC)) && (isAudioAAC)) + { + firstTimeSwitchFromAC3toAAC(newAudioCaps); + m_context.playbackGroup.m_isAudioAAC = isAudioAAC; + return true; + } + if (!m_context.playbackGroup.m_curAudioDecoder || !m_context.playbackGroup.m_curAudioParse || + !m_context.playbackGroup.m_curAudioDecodeBin) + { + RIALTO_SERVER_LOG_ERROR("switchAudioCodec: audio decoder, parser or decode bin is null"); + return false; + } + GstElement *newAudioParse = NULL; + GstElement *newAudioDecoder = NULL; + GstPad *newAudioParseSrcPad = NULL; + GstPad *newAudioParseSinkPad = NULL; + GstPad *newAudioDecoderSrcPad = NULL; + GstPad *newAudioDecoderSinkPad = NULL; + GstPad *audioDecSrcPad = NULL; + GstPad *audioDecSinkPad = NULL; + GstPad *audioDecSrcPeerPad = NULL; + GstPad *audioDecSinkPeerPad = NULL; + GstPad *audioParseSrcPad = NULL; + GstPad *audioParseSinkPad = NULL; + GstPad *audioParseSrcPeerPad = NULL; + GstPad *audioParseSinkPeerPad = NULL; + GstState currentState{GST_STATE_VOID_PENDING}, pending{GST_STATE_VOID_PENDING}; + + // 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"); + // Current Audio Decoder NULL + if (GST_STATE_CHANGE_FAILURE == + m_gstWrapper->gstElementSetState(m_context.playbackGroup.m_curAudioDecoder, GST_STATE_NULL)) + { + RIALTO_SERVER_LOG_WARN("Failed to set AudioDecoder to NULL"); + } + m_gstWrapper->gstElementGetState(m_context.playbackGroup.m_curAudioDecoder, ¤tState, &pending, + GST_CLOCK_TIME_NONE); + if (currentState == GST_STATE_NULL) + RIALTO_SERVER_LOG_DEBUG("OTF -> Current AudioDecoder State = %d", currentState); + // Current Audio Parser NULL + if (GST_STATE_CHANGE_FAILURE == + m_gstWrapper->gstElementSetState(m_context.playbackGroup.m_curAudioParse, GST_STATE_NULL)) + { + RIALTO_SERVER_LOG_WARN("Failed to set AudioParser to NULL"); + } + m_gstWrapper->gstElementGetState(m_context.playbackGroup.m_curAudioParse, ¤tState, &pending, + GST_CLOCK_TIME_NONE); + if (currentState == GST_STATE_NULL) + RIALTO_SERVER_LOG_DEBUG("OTF -> Current AudioParser State = %d", currentState); + // Remove Audio Decoder From Decodebin + if (m_gstWrapper->gstBinRemove(GST_BIN(m_context.playbackGroup.m_curAudioDecodeBin.load()), + m_context.playbackGroup.m_curAudioDecoder) == TRUE) + { + RIALTO_SERVER_LOG_DEBUG("OTF -> Removed AudioDecoder = %p", m_context.playbackGroup.m_curAudioDecoder); + m_context.playbackGroup.m_curAudioDecoder = NULL; + } + // Remove Audio Parser From Decodebin + if (m_gstWrapper->gstBinRemove(GST_BIN(m_context.playbackGroup.m_curAudioDecodeBin.load()), + m_context.playbackGroup.m_curAudioParse) == TRUE) + { + RIALTO_SERVER_LOG_DEBUG("OTF -> Removed AudioParser = %p", m_context.playbackGroup.m_curAudioParse); + m_context.playbackGroup.m_curAudioParse = NULL; + } + // Create new Audio Decoder and Parser. The inverse of the current + if (m_context.playbackGroup.m_isAudioAAC) + { + newAudioParse = m_gstWrapper->gstElementFactoryMake("ac3parse", "ac3parse"); + newAudioDecoder = m_gstWrapper->gstElementFactoryMake("identity", "fake_aud_ac3dec"); + } + else + { + newAudioParse = m_gstWrapper->gstElementFactoryMake("aacparse", "aacparse"); + newAudioDecoder = m_gstWrapper->gstElementFactoryMake("avdec_aac", "avdec_aac"); + } + { + GstPadLinkReturn gstPadLinkRet = GST_PAD_LINK_OK; + GstElement *audioParseUpstreamEl = NULL; + // Add new Decoder to Decodebin + if (m_gstWrapper->gstBinAdd(GST_BIN(m_context.playbackGroup.m_curAudioDecodeBin.load()), newAudioDecoder) == TRUE) + { + RIALTO_SERVER_LOG_DEBUG("OTF -> Added New AudioDecoder = %p", newAudioDecoder); + } + // Add new Parser to Decodebin + if (m_gstWrapper->gstBinAdd(GST_BIN(m_context.playbackGroup.m_curAudioDecodeBin.load()), newAudioParse) == TRUE) + { + RIALTO_SERVER_LOG_DEBUG("OTF -> Added New AudioParser = %p", newAudioParse); + } + if ((newAudioDecoderSrcPad = m_gstWrapper->gstElementGetStaticPad(newAudioDecoder, "src")) != + NULL) // Unref the Pad + RIALTO_SERVER_LOG_DEBUG("OTF -> New AudioDecoder Src Pad = %p", newAudioDecoderSrcPad); + if ((newAudioDecoderSinkPad = m_gstWrapper->gstElementGetStaticPad(newAudioDecoder, "sink")) != + NULL) // Unref the Pad + RIALTO_SERVER_LOG_DEBUG("OTF -> New AudioDecoder Sink Pad = %p", newAudioDecoderSinkPad); + // Link New Decoder to Downstream followed by UpStream + if ((gstPadLinkRet = m_gstWrapper->gstPadLink(newAudioDecoderSrcPad, audioDecSrcPeerPad)) != GST_PAD_LINK_OK) + RIALTO_SERVER_LOG_DEBUG("OTF -> New AudioDecoder Downstream Link Failed"); + if ((gstPadLinkRet = m_gstWrapper->gstPadLink(audioDecSinkPeerPad, newAudioDecoderSinkPad)) != GST_PAD_LINK_OK) + RIALTO_SERVER_LOG_DEBUG("OTF -> New AudioDecoder Upstream Link Failed"); + if ((newAudioParseSrcPad = m_gstWrapper->gstElementGetStaticPad(newAudioParse, "src")) != NULL) // Unref the Pad + RIALTO_SERVER_LOG_DEBUG("OTF -> New AudioParser Src Pad = %p", newAudioParseSrcPad); + if ((newAudioParseSinkPad = m_gstWrapper->gstElementGetStaticPad(newAudioParse, "sink")) != NULL) // Unref the Pad + RIALTO_SERVER_LOG_DEBUG("OTF -> New AudioParser Sink Pad = %p", newAudioParseSinkPad); + // Link New Parser to Downstream followed by UpStream + if ((gstPadLinkRet = m_gstWrapper->gstPadLink(newAudioParseSrcPad, audioParseSrcPeerPad)) != GST_PAD_LINK_OK) + RIALTO_SERVER_LOG_DEBUG("OTF -> New AudioParser Downstream Link Failed %d", gstPadLinkRet); + if ((audioParseUpstreamEl = GST_ELEMENT_CAST(m_gstWrapper->gstPadGetParent(audioParseSinkPeerPad))) == + m_context.playbackGroup.m_curAudioTypefind) + { + RIALTO_SERVER_LOG_DEBUG("OTF -> Typefind Setting to READY"); + if (GST_STATE_CHANGE_FAILURE == m_gstWrapper->gstElementSetState(audioParseUpstreamEl, GST_STATE_READY)) + { + RIALTO_SERVER_LOG_WARN("Failed to set Typefind to READY in switchAudioCodec"); + } + m_glibWrapper->gObjectSet(G_OBJECT(audioParseUpstreamEl), "force-caps", newAudioCaps, NULL); + m_gstWrapper->gstElementSyncStateWithParent(audioParseUpstreamEl); + m_gstWrapper->gstElementGetState(audioParseUpstreamEl, ¤tState, &pending, GST_CLOCK_TIME_NONE); + RIALTO_SERVER_LOG_DEBUG("OTF -> New Typefind State = %d Pending = %d", currentState, pending); + RIALTO_SERVER_LOG_DEBUG("OTF -> Typefind Syncing with Parent"); + m_context.playbackGroup.m_linkTypefindParser = true; + m_gstWrapper->gstObjectUnref(audioParseUpstreamEl); + } + m_gstWrapper->gstObjectUnref(newAudioDecoderSrcPad); + m_gstWrapper->gstObjectUnref(newAudioDecoderSinkPad); + m_gstWrapper->gstObjectUnref(newAudioParseSrcPad); + m_gstWrapper->gstObjectUnref(newAudioParseSinkPad); + } + m_gstWrapper->gstObjectUnref(audioParseSinkPeerPad); + m_gstWrapper->gstObjectUnref(audioParseSrcPeerPad); + m_gstWrapper->gstObjectUnref(audioParseSinkPad); + m_gstWrapper->gstObjectUnref(audioParseSrcPad); + m_gstWrapper->gstObjectUnref(audioDecSinkPeerPad); + m_gstWrapper->gstObjectUnref(audioDecSrcPeerPad); + m_gstWrapper->gstObjectUnref(audioDecSinkPad); + m_gstWrapper->gstObjectUnref(audioDecSrcPad); + m_gstWrapper->gstElementSyncStateWithParent(newAudioDecoder); + m_gstWrapper->gstElementGetState(newAudioDecoder, ¤tState, &pending, GST_CLOCK_TIME_NONE); + RIALTO_SERVER_LOG_DEBUG("OTF -> New AudioDecoder State = %d Pending = %d", currentState, pending); + m_gstWrapper->gstElementSyncStateWithParent(newAudioParse); + m_gstWrapper->gstElementGetState(newAudioParse, ¤tState, &pending, GST_CLOCK_TIME_NONE); + RIALTO_SERVER_LOG_DEBUG("OTF -> New AudioParser State = %d Pending = %d", currentState, pending); + m_context.playbackGroup.m_isAudioAAC = isAudioAAC; + return true; +} + +bool GstGenericPlayer::performAudioTrackCodecChannelSwitch(const void *pSampleAttr, + firebolt::rialto::wrappers::AudioAttributesPrivate *pAudioAttr, + uint32_t *pStatus, unsigned int *pui32Delay, + long long *pAudioChangeTargetPts, // NOLINT(runtime/int) + const long long *pcurrentDispPts, // NOLINT(runtime/int) + unsigned int *audioChangeStage, GstCaps **appsrcCaps, + bool *audioaac, bool svpenabled, GstElement *aSrc, bool *ret) +{ + // this function comes from rdk_gstreamer_utils + if (!pStatus || !pui32Delay || !pAudioChangeTargetPts || !pcurrentDispPts || !audioChangeStage || !appsrcCaps || + !audioaac || !aSrc || !ret) + { + RIALTO_SERVER_LOG_ERROR("performAudioTrackCodecChannelSwitch: invalid null parameter"); + return false; + } + + constexpr uint32_t kOk = 0; + constexpr uint32_t kWaitWhileIdling = 100; + constexpr int kAudioChangeGapThresholdMS = 40; + constexpr unsigned int kAudchgAlign = 3; + + struct timespec ts, now; + unsigned int reconfigDelayMs; + clock_gettime(CLOCK_MONOTONIC, &ts); + if (*pStatus != kOk || pSampleAttr == nullptr) + { + RIALTO_SERVER_LOG_DEBUG("No audio data ready yet"); + *pui32Delay = kWaitWhileIdling; + *ret = false; + return true; + } + RIALTO_SERVER_LOG_DEBUG("Received first audio packet after a flush, PTS"); + if (pAudioAttr) + { + const char *pCodecStr = pAudioAttr->m_codecParam.c_str(); + const char *pCodecAcc = strstr(pCodecStr, "mp4a"); + bool isAudioAAC = (pCodecAcc) ? true : false; + bool isCodecSwitch = false; + RIALTO_SERVER_LOG_DEBUG("Audio Attribute format %s channel %d samp %d, bitrate %d blockAlignment %d", pCodecStr, + pAudioAttr->m_numberOfChannels, pAudioAttr->m_samplesPerSecond, pAudioAttr->m_bitrate, + pAudioAttr->m_blockAlignment); + *pAudioChangeTargetPts = *pcurrentDispPts; + *audioChangeStage = kAudchgAlign; + if (*appsrcCaps) + { + m_gstWrapper->gstCapsUnref(*appsrcCaps); + *appsrcCaps = NULL; + } + if (isAudioAAC != *audioaac) + isCodecSwitch = true; + configAudioCap(pAudioAttr, audioaac, svpenabled, appsrcCaps); + { + gboolean sendRet = FALSE; + GstEvent *flushStart = NULL; + GstEvent *flushStop = NULL; + flushStart = m_gstWrapper->gstEventNewFlushStart(); + sendRet = m_gstWrapper->gstElementSendEvent(aSrc, flushStart); + if (!sendRet) + RIALTO_SERVER_LOG_DEBUG("failed to send flush-start event"); + flushStop = m_gstWrapper->gstEventNewFlushStop(TRUE); + sendRet = m_gstWrapper->gstElementSendEvent(aSrc, flushStop); + if (!sendRet) + RIALTO_SERVER_LOG_DEBUG("failed to send flush-stop event"); + } + if (!isCodecSwitch) + { + m_gstWrapper->gstAppSrcSetCaps(GST_APP_SRC(aSrc), *appsrcCaps); + } + else + { + RIALTO_SERVER_LOG_DEBUG("CODEC SWITCH mAudioAAC = %d", *audioaac); + haltAudioPlayback(); + if (switchAudioCodec(*audioaac, *appsrcCaps) == false) + { + RIALTO_SERVER_LOG_DEBUG("CODEC SWITCH FAILED switchAudioCodec mAudioAAC = %d", *audioaac); + } + m_gstWrapper->gstAppSrcSetCaps(GST_APP_SRC(aSrc), *appsrcCaps); + resumeAudioPlayback(); + } + clock_gettime(CLOCK_MONOTONIC, &now); + reconfigDelayMs = now.tv_nsec > ts.tv_nsec ? (now.tv_nsec - ts.tv_nsec) / 1000000 + : (1000 - (ts.tv_nsec - now.tv_nsec) / 1000000); + (*pAudioChangeTargetPts) += (reconfigDelayMs + kAudioChangeGapThresholdMS); + } + else + { + RIALTO_SERVER_LOG_DEBUG("first audio after change no attribute drop!"); + *pui32Delay = 0; + *ret = false; + return true; + } + *ret = true; + return true; +} + bool GstGenericPlayer::setImmediateOutput(const MediaSourceType &mediaSourceType, bool immediateOutputParam) { if (!m_workerThread) @@ -1092,9 +1554,24 @@ bool GstGenericPlayer::reattachSource(const std::unique_ptrgetType()].appSrc)}; GstCaps *oldCaps = m_gstWrapper->gstAppSrcGetCaps(appSrc); + if ((!oldCaps) || (!m_gstWrapper->gstCapsIsEqual(caps, oldCaps))) { RIALTO_SERVER_LOG_DEBUG("Caps not equal. Perform audio track codec channel switch."); + + GstElement *sink = getSink(MediaSourceType::AUDIO); + if (!sink) + { + RIALTO_SERVER_LOG_ERROR("Failed to get audio sink"); + if (caps) + m_gstWrapper->gstCapsUnref(caps); + if (oldCaps) + m_gstWrapper->gstCapsUnref(oldCaps); + return false; + } + std::string sinkName = GST_ELEMENT_NAME(sink); + m_gstWrapper->gstObjectUnref(sink); + int sampleAttributes{ 0}; // rdk_gstreamer_utils::performAudioTrackCodecChannelSwitch checks if this param != NULL only. std::uint32_t status{0}; // must be 0 to make rdk_gstreamer_utils::performAudioTrackCodecChannelSwitch work @@ -1108,14 +1585,26 @@ bool GstGenericPlayer::reattachSource(const std::unique_ptrperformAudioTrackCodecChannelSwitch(&m_context.playbackGroup, &sampleAttributes, &(*audioAttributes), - &status, &ui32Delay, &audioChangeTargetPts, ¤tDispPts, - &audioChangeStage, - &caps, // may fail for amlogic - that implementation changes - // this parameter, it's probably used by Netflix later - &audioAac, svpEnabled, GST_ELEMENT(appSrc), &retVal); + + bool result = false; + if (m_glibWrapper->gStrHasPrefix(sinkName.c_str(), "amlhalasink")) + { + // due to problems audio codec change in prerolling, temporarily moved the code from rdk gstreamer utils to + // Rialto and applied fixes + result = performAudioTrackCodecChannelSwitch(&sampleAttributes, &(*audioAttributes), &status, &ui32Delay, + &audioChangeTargetPts, ¤tDispPts, &audioChangeStage, + &caps, &audioAac, svpEnabled, GST_ELEMENT(appSrc), &retVal); + } + else + { + result = m_rdkGstreamerUtilsWrapper->performAudioTrackCodecChannelSwitch(&m_context.playbackGroup, + &sampleAttributes, + &(*audioAttributes), &status, + &ui32Delay, &audioChangeTargetPts, + ¤tDispPts, &audioChangeStage, + &caps, &audioAac, svpEnabled, + GST_ELEMENT(appSrc), &retVal); + } if (!result || !retVal) { diff --git a/tests/common/externalLibraryMocks/GstWrapperMock.h b/tests/common/externalLibraryMocks/GstWrapperMock.h index 401b8e2f7..c30acf396 100644 --- a/tests/common/externalLibraryMocks/GstWrapperMock.h +++ b/tests/common/externalLibraryMocks/GstWrapperMock.h @@ -220,6 +220,13 @@ class GstWrapperMock : public IGstWrapper MOCK_METHOD(gboolean, gstIsBaseParse, (GstElement * element), (const, override)); MOCK_METHOD(void, gstBaseParseSetPtsInterpolation, (GstBaseParse * parse, gboolean ptsInterpolate), (const, override)); + MOCK_METHOD(GstStateChangeReturn, gstElementGetState, + (GstElement * element, GstState *state, GstState *pending, GstClockTime timeout), (override)); + MOCK_METHOD(GstPad *, gstPadGetPeer, (GstPad * pad), (override)); + MOCK_METHOD(gboolean, gstPadUnlink, (GstPad * srcpad, GstPad *sinkpad), (override)); + MOCK_METHOD(GstPadLinkReturn, gstPadLink, (GstPad * srcpad, GstPad *sinkpad), (override)); + MOCK_METHOD(gboolean, gstBinRemove, (GstBin * bin, GstElement *element), (override)); + MOCK_METHOD(GstObject *, gstPadGetParent, (GstPad * pad), (override)); GstCaps *gstCapsNewSimple(const char *media_type, const char *fieldname, ...) const override { diff --git a/tests/componenttests/server/tests/mediaPipeline/AudioSourceSwitchTest.cpp b/tests/componenttests/server/tests/mediaPipeline/AudioSourceSwitchTest.cpp index b2e694ecd..31f3411d2 100644 --- a/tests/componenttests/server/tests/mediaPipeline/AudioSourceSwitchTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/AudioSourceSwitchTest.cpp @@ -26,6 +26,8 @@ #include "MessageBuilders.h" using testing::_; +using testing::AtLeast; +using testing::Invoke; using testing::Return; using testing::StrEq; @@ -39,8 +41,15 @@ namespace firebolt::rialto::server::ct class AudioSourceSwitchTest : public MediaPipelineTest { public: - AudioSourceSwitchTest() = default; - ~AudioSourceSwitchTest() = default; + AudioSourceSwitchTest() + { + GstElementFactory *elementFactory = gst_element_factory_find("fakesrc"); + m_audioSink = gst_element_factory_create(elementFactory, nullptr); + EXPECT_CALL(*m_glibWrapperMock, gTypeName(G_OBJECT_TYPE(m_audioSink))).WillRepeatedly(Return("audio_sink")); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_audioSink)).Times(AtLeast(0)); + gst_object_unref(elementFactory); + } + ~AudioSourceSwitchTest() override { gst_object_unref(m_audioSink); } void willSwitchAudioSource() { @@ -61,6 +70,7 @@ class AudioSourceSwitchTest : public MediaPipelineTest EXPECT_CALL(*m_gstWrapperMock, gstCapsIsEqual(&m_audioCaps, &m_oldCaps)).WillOnce(Return(FALSE)); EXPECT_CALL(*m_gstWrapperMock, gstCapsToString(&m_oldCaps)).WillOnce(Return(&m_oldCapsStr)); EXPECT_CALL(*m_glibWrapperMock, gFree(&m_oldCapsStr)); + EXPECT_CALL(*m_glibWrapperMock, gStrHasPrefix(_, StrEq("amlhalasink"))).WillOnce(Return(FALSE)); EXPECT_CALL(*m_rdkGstreamerUtilsWrapperMock, performAudioTrackCodecChannelSwitch(_, _, _, _, _, _, _, _, _, _, kSvpEnabled, GST_ELEMENT(&m_audioAppSrc), _)) diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp index 2e9c58e67..255ad8878 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp @@ -2005,6 +2005,7 @@ TEST_F(GstGenericPlayerPrivateTest, shouldReattachMpegAudioSource) GstCaps newGstCaps{}; GstCaps oldGstCaps{}; gchar capsStr[13]{"audio/x-eac3"}; + GstElement *fakeSink = gst_element_factory_make("fakesink", "fakesink"); setPipelineState(GST_STATE_PAUSED); firebolt::rialto::wrappers::PlaybackGroupPrivate *playbackGroup; modifyContext( @@ -2026,6 +2027,12 @@ TEST_F(GstGenericPlayerPrivateTest, shouldReattachMpegAudioSource) EXPECT_CALL(*m_gstWrapperMock, gstStateLock(_)).WillOnce(Return()); EXPECT_CALL(*m_gstWrapperMock, gstElementQueryPosition(_, GST_FORMAT_TIME, _)).WillOnce(Return(TRUE)); EXPECT_CALL(*m_gstWrapperMock, gstStateUnlock(_)).WillOnce(Return()); + EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq("audio-sink"), _)) + .WillOnce(Invoke([&](gpointer object, const gchar *first_property_name, void *element) + { *reinterpret_cast(element) = fakeSink; })); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(fakeSink)); + EXPECT_CALL(*m_glibWrapperMock, gTypeName(G_OBJECT_TYPE(fakeSink))).WillOnce(Return(kElementTypeName.c_str())); + EXPECT_CALL(*m_glibWrapperMock, gStrHasPrefix(StrEq("fakesink"), StrEq("amlhalasink"))).WillOnce(Return(FALSE)); EXPECT_CALL(*m_rdkGstreamerUtilsWrapperMock, performAudioTrackCodecChannelSwitch(playbackGroup, _, _, _, _, _, _, _, _, _, _, _, _)) .WillOnce(Return(true)); @@ -2034,6 +2041,7 @@ TEST_F(GstGenericPlayerPrivateTest, shouldReattachMpegAudioSource) std::unique_ptr source = std::make_unique("audio/aac", false); EXPECT_TRUE(m_sut->reattachSource(source)); + gst_object_unref(fakeSink); } TEST_F(GstGenericPlayerPrivateTest, shouldReattachEac3AudioSource) @@ -2042,6 +2050,7 @@ TEST_F(GstGenericPlayerPrivateTest, shouldReattachEac3AudioSource) GstCaps newGstCaps{}; GstCaps oldGstCaps{}; gchar capsStr[11]{"audio/mpeg"}; + GstElement *fakeSink = gst_element_factory_make("fakesink", "fakesink0"); setPipelineState(GST_STATE_PAUSED); firebolt::rialto::wrappers::PlaybackGroupPrivate *playbackGroup; modifyContext( @@ -2062,6 +2071,12 @@ TEST_F(GstGenericPlayerPrivateTest, shouldReattachEac3AudioSource) EXPECT_CALL(*m_gstWrapperMock, gstStateLock(_)).WillOnce(Return()); EXPECT_CALL(*m_gstWrapperMock, gstElementQueryPosition(_, GST_FORMAT_TIME, _)).WillOnce(Return(TRUE)); EXPECT_CALL(*m_gstWrapperMock, gstStateUnlock(_)).WillOnce(Return()); + EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq("audio-sink"), _)) + .WillOnce(Invoke([&](gpointer object, const gchar *first_property_name, void *element) + { *reinterpret_cast(element) = fakeSink; })); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(fakeSink)); + EXPECT_CALL(*m_glibWrapperMock, gTypeName(G_OBJECT_TYPE(fakeSink))).WillOnce(Return(kElementTypeName.c_str())); + EXPECT_CALL(*m_glibWrapperMock, gStrHasPrefix(_, StrEq("amlhalasink"))).WillOnce(Return(FALSE)); EXPECT_CALL(*m_rdkGstreamerUtilsWrapperMock, performAudioTrackCodecChannelSwitch(playbackGroup, _, _, _, _, _, _, _, _, _, _, _, _)) .WillOnce(Return(true)); @@ -2070,6 +2085,7 @@ TEST_F(GstGenericPlayerPrivateTest, shouldReattachEac3AudioSource) std::unique_ptr source = std::make_unique("audio/x-eac3", false); EXPECT_TRUE(m_sut->reattachSource(source)); + gst_object_unref(fakeSink); } TEST_F(GstGenericPlayerPrivateTest, shouldReattachRawAudioSource) @@ -2078,6 +2094,7 @@ TEST_F(GstGenericPlayerPrivateTest, shouldReattachRawAudioSource) GstCaps newGstCaps{}; GstCaps oldGstCaps{}; gchar capsStr[11]{"audio/mpeg"}; + GstElement *fakeSink = gst_element_factory_make("fakesink", "fakesink1"); setPipelineState(GST_STATE_PAUSED); firebolt::rialto::wrappers::PlaybackGroupPrivate *playbackGroup; modifyContext( @@ -2098,6 +2115,12 @@ TEST_F(GstGenericPlayerPrivateTest, shouldReattachRawAudioSource) EXPECT_CALL(*m_gstWrapperMock, gstStateLock(_)).WillOnce(Return()); EXPECT_CALL(*m_gstWrapperMock, gstElementQueryPosition(_, GST_FORMAT_TIME, _)).WillOnce(Return(TRUE)); EXPECT_CALL(*m_gstWrapperMock, gstStateUnlock(_)).WillOnce(Return()); + EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq("audio-sink"), _)) + .WillOnce(Invoke([&](gpointer object, const gchar *first_property_name, void *element) + { *reinterpret_cast(element) = fakeSink; })); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(fakeSink)); + EXPECT_CALL(*m_glibWrapperMock, gTypeName(G_OBJECT_TYPE(fakeSink))).WillOnce(Return(kElementTypeName.c_str())); + EXPECT_CALL(*m_glibWrapperMock, gStrHasPrefix(_, StrEq("amlhalasink"))).WillOnce(Return(FALSE)); EXPECT_CALL(*m_rdkGstreamerUtilsWrapperMock, performAudioTrackCodecChannelSwitch(playbackGroup, _, _, _, _, _, _, _, _, _, _, _, _)) .WillOnce(Return(true)); @@ -2106,6 +2129,188 @@ TEST_F(GstGenericPlayerPrivateTest, shouldReattachRawAudioSource) std::unique_ptr source = std::make_unique("audio/x-raw", false); EXPECT_TRUE(m_sut->reattachSource(source)); + gst_object_unref(fakeSink); +} + +TEST_F(GstGenericPlayerPrivateTest, shouldReattachAmlhalasinkAudioSourceNoCodecSwitch) +{ + GstAppSrc audioSrc{}; + GstCaps newGstCaps{}; + GstCaps oldGstCaps{}; + GstCaps configCaps{}; + GstEvent flushStartEvent{}; + GstEvent flushStopEvent{}; + gchar capsStr[11]{"audio/mpeg"}; // old caps was AAC + gchar configCapsStr[] = "audio/mpeg, mpegversion=4, enable-svp=(string)true"; + GstElement *fakeSink = gst_element_factory_make("fakesink", "amlhalasink0"); + setPipelineState(GST_STATE_PAUSED); + modifyContext( + [&](GenericPlayerContext &context) + { + context.streamInfo[firebolt::rialto::MediaSourceType::AUDIO].appSrc = GST_ELEMENT(&audioSrc); + context.playbackGroup.m_isAudioAAC = true; // current codec is AAC + }); + + // createCapsFromMediaSource for audio/aac + EXPECT_CALL(*m_gstWrapperMock, gstCapsNewEmptySimple(StrEq("audio/mpeg"))).WillOnce(Return(&newGstCaps)); + EXPECT_CALL(*m_gstWrapperMock, gstCapsSetSimpleIntStub(&newGstCaps, StrEq("mpegversion"), G_TYPE_INT, 4)); + EXPECT_CALL(*m_gstWrapperMock, gstAppSrcGetCaps(GST_APP_SRC(&audioSrc))).WillOnce(Return(&oldGstCaps)); + EXPECT_CALL(*m_gstWrapperMock, gstCapsIsEqual(&newGstCaps, &oldGstCaps)).WillOnce(Return(FALSE)); + EXPECT_CALL(*m_gstWrapperMock, gstCapsToString(&oldGstCaps)).WillOnce(Return(capsStr)); + EXPECT_CALL(*m_glibWrapperMock, gFree(capsStr)); + EXPECT_CALL(*m_gstWrapperMock, gstCapsUnref(&oldGstCaps)); + // getPosition + EXPECT_CALL(*m_gstWrapperMock, gstElementGetState(_)).WillOnce(Return(GST_STATE_PAUSED)); + EXPECT_CALL(*m_gstWrapperMock, gstElementGetStateReturn(_)).WillOnce(Return(GST_STATE_CHANGE_SUCCESS)); + EXPECT_CALL(*m_gstWrapperMock, gstStateLock(_)).WillOnce(Return()); + EXPECT_CALL(*m_gstWrapperMock, gstElementQueryPosition(_, GST_FORMAT_TIME, _)).WillOnce(Return(TRUE)); + EXPECT_CALL(*m_gstWrapperMock, gstStateUnlock(_)).WillOnce(Return()); + // getSink + EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq("audio-sink"), _)) + .WillOnce(Invoke([&](gpointer, const gchar *, void *element) + { *reinterpret_cast(element) = fakeSink; })); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(fakeSink)); + // getSinkChildIfAutoAudioSink + EXPECT_CALL(*m_glibWrapperMock, gTypeName(G_OBJECT_TYPE(fakeSink))).WillOnce(Return(kElementTypeName.c_str())); + // amlhalasink path + EXPECT_CALL(*m_glibWrapperMock, gStrHasPrefix(StrEq("amlhalasink0"), StrEq("amlhalasink"))).WillOnce(Return(TRUE)); + // performAudioTrackCodecChannelSwitch (AAC->AAC, no codec switch): + // configAudioCap unrefs original caps and creates new ones + EXPECT_CALL(*m_gstWrapperMock, gstCapsUnref(&newGstCaps)); + EXPECT_CALL(*m_glibWrapperMock, gStrdupPrintfStub(_)).WillOnce(Return(configCapsStr)); + EXPECT_CALL(*m_gstWrapperMock, gstCapsFromString(configCapsStr)).WillOnce(Return(&configCaps)); + EXPECT_CALL(*m_glibWrapperMock, gFree(configCapsStr)); + // flush events + EXPECT_CALL(*m_gstWrapperMock, gstEventNewFlushStart()).WillOnce(Return(&flushStartEvent)); + EXPECT_CALL(*m_gstWrapperMock, gstElementSendEvent(GST_ELEMENT(&audioSrc), &flushStartEvent)).WillOnce(Return(TRUE)); + EXPECT_CALL(*m_gstWrapperMock, gstEventNewFlushStop(kResetTime)).WillOnce(Return(&flushStopEvent)); + EXPECT_CALL(*m_gstWrapperMock, gstElementSendEvent(GST_ELEMENT(&audioSrc), &flushStopEvent)).WillOnce(Return(TRUE)); + // no codec switch - just set new caps + EXPECT_CALL(*m_gstWrapperMock, gstAppSrcSetCaps(GST_APP_SRC(&audioSrc), &configCaps)); + // end of reattachSource: caps was updated to configCaps + EXPECT_CALL(*m_gstWrapperMock, gstCapsUnref(&configCaps)); + + std::unique_ptr source = + std::make_unique("audio/aac", false); + EXPECT_TRUE(m_sut->reattachSource(source)); + gst_object_unref(fakeSink); +} + +TEST_F(GstGenericPlayerPrivateTest, shouldReattachAmlhalasinkAudioSourceWithFirstTimeCodecSwitch) +{ + GstAppSrc audioSrc{}; + GstCaps newGstCaps{}; + GstCaps oldGstCaps{}; + GstCaps configCaps{}; + GstEvent flushStartEvent{}; + GstEvent flushStopEvent{}; + GstPad typefindSrcPad{}; + GstPad typefindSrcPeerPad{}; + GstElement newAudioDecoder{}; + GstElement newAudioParse{}; + GstElement newQueue{}; + GstPad newAudioDecoderSrcPad{}; + GstElement typefind{}; + GstElement decodeBin{}; + GstElement playsinkBin{}; + gchar capsStr[13]{"audio/x-eac3"}; // old caps was EAC3 (no "audio/mpeg" → audioAac=false) + gchar configCapsStr[] = "audio/mpeg, mpegversion=4, enable-svp=(string)true"; + GstElement *fakeSink = gst_element_factory_make("fakesink", "amlhalasink1"); + setPipelineState(GST_STATE_PAUSED); + modifyContext( + [&](GenericPlayerContext &context) + { + context.streamInfo[firebolt::rialto::MediaSourceType::AUDIO].appSrc = GST_ELEMENT(&audioSrc); + context.playbackGroup.m_isAudioAAC = false; // current codec is EAC3 + context.playbackGroup.m_curAudioDecoder = nullptr; // first time switch: no existing decoder + context.playbackGroup.m_curAudioTypefind = &typefind; + context.playbackGroup.m_curAudioDecodeBin = &decodeBin; + context.playbackGroup.m_curAudioPlaysinkBin = &playsinkBin; + }); + + // createCapsFromMediaSource for audio/aac + EXPECT_CALL(*m_gstWrapperMock, gstCapsNewEmptySimple(StrEq("audio/mpeg"))).WillOnce(Return(&newGstCaps)); + EXPECT_CALL(*m_gstWrapperMock, gstCapsSetSimpleIntStub(&newGstCaps, StrEq("mpegversion"), G_TYPE_INT, 4)); + EXPECT_CALL(*m_gstWrapperMock, gstAppSrcGetCaps(GST_APP_SRC(&audioSrc))).WillOnce(Return(&oldGstCaps)); + EXPECT_CALL(*m_gstWrapperMock, gstCapsIsEqual(&newGstCaps, &oldGstCaps)).WillOnce(Return(FALSE)); + EXPECT_CALL(*m_gstWrapperMock, gstCapsToString(&oldGstCaps)).WillOnce(Return(capsStr)); + EXPECT_CALL(*m_glibWrapperMock, gFree(capsStr)); + EXPECT_CALL(*m_gstWrapperMock, gstCapsUnref(&oldGstCaps)); + // getPosition + EXPECT_CALL(*m_gstWrapperMock, gstElementGetState(_)).WillOnce(Return(GST_STATE_PAUSED)); + EXPECT_CALL(*m_gstWrapperMock, gstElementGetStateReturn(_)).WillOnce(Return(GST_STATE_CHANGE_SUCCESS)); + EXPECT_CALL(*m_gstWrapperMock, gstStateLock(_)).WillOnce(Return()); + EXPECT_CALL(*m_gstWrapperMock, gstElementQueryPosition(_, GST_FORMAT_TIME, _)).WillOnce(Return(TRUE)); + EXPECT_CALL(*m_gstWrapperMock, gstStateUnlock(_)).WillOnce(Return()); + // getSink + EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq("audio-sink"), _)) + .WillOnce(Invoke([&](gpointer, const gchar *, void *element) + { *reinterpret_cast(element) = fakeSink; })); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(fakeSink)); + // getSinkChildIfAutoAudioSink + EXPECT_CALL(*m_glibWrapperMock, gTypeName(G_OBJECT_TYPE(fakeSink))).WillOnce(Return(kElementTypeName.c_str())); + // amlhalasink path + EXPECT_CALL(*m_glibWrapperMock, gStrHasPrefix(StrEq("amlhalasink1"), StrEq("amlhalasink"))).WillOnce(Return(TRUE)); + // performAudioTrackCodecChannelSwitch (EAC3->AAC, codec switch): + // configAudioCap unrefs original caps and creates new AAC caps + EXPECT_CALL(*m_gstWrapperMock, gstCapsUnref(&newGstCaps)); + EXPECT_CALL(*m_glibWrapperMock, gStrdupPrintfStub(_)).WillOnce(Return(configCapsStr)); + EXPECT_CALL(*m_gstWrapperMock, gstCapsFromString(configCapsStr)).WillOnce(Return(&configCaps)); + EXPECT_CALL(*m_glibWrapperMock, gFree(configCapsStr)); + // flush events + EXPECT_CALL(*m_gstWrapperMock, gstEventNewFlushStart()).WillOnce(Return(&flushStartEvent)); + EXPECT_CALL(*m_gstWrapperMock, gstElementSendEvent(GST_ELEMENT(&audioSrc), &flushStartEvent)).WillOnce(Return(TRUE)); + EXPECT_CALL(*m_gstWrapperMock, gstEventNewFlushStop(kResetTime)).WillOnce(Return(&flushStopEvent)); + EXPECT_CALL(*m_gstWrapperMock, gstElementSendEvent(GST_ELEMENT(&audioSrc), &flushStopEvent)).WillOnce(Return(TRUE)); + // haltAudioPlayback + resumeAudioPlayback: playsinkBin and decodeBin each called twice + EXPECT_CALL(*m_gstWrapperMock, gstElementSetState(&playsinkBin, GST_STATE_READY)) + .WillOnce(Return(GST_STATE_CHANGE_SUCCESS)); + EXPECT_CALL(*m_gstWrapperMock, gstElementGetState(&playsinkBin, _, _, GST_CLOCK_TIME_NONE)).Times(2); + EXPECT_CALL(*m_gstWrapperMock, gstElementSetState(&decodeBin, GST_STATE_PAUSED)) + .WillOnce(Return(GST_STATE_CHANGE_SUCCESS)); + EXPECT_CALL(*m_gstWrapperMock, gstElementGetState(&decodeBin, _, _, GST_CLOCK_TIME_NONE)).Times(2); + // switchAudioCodec -> firstTimeSwitchFromAC3toAAC + EXPECT_CALL(*m_gstWrapperMock, gstElementGetStaticPad(&typefind, StrEq("src"))).WillOnce(Return(&typefindSrcPad)); + EXPECT_CALL(*m_gstWrapperMock, gstPadGetPeer(&typefindSrcPad)).WillOnce(Return(&typefindSrcPeerPad)); + EXPECT_CALL(*m_gstWrapperMock, gstPadUnlink(&typefindSrcPad, &typefindSrcPeerPad)).WillOnce(Return(TRUE)); + EXPECT_CALL(*m_gstWrapperMock, gstElementFactoryMake(StrEq("aacparse"), StrEq("aacparse"))) + .WillOnce(Return(&newAudioParse)); + EXPECT_CALL(*m_gstWrapperMock, gstElementFactoryMake(StrEq("avdec_aac"), StrEq("avdec_aac"))) + .WillOnce(Return(&newAudioDecoder)); + EXPECT_CALL(*m_gstWrapperMock, gstElementFactoryMake(StrEq("queue"), StrEq("aqueue"))).WillOnce(Return(&newQueue)); + EXPECT_CALL(*m_gstWrapperMock, gstBinAdd(_, &newAudioDecoder)).WillOnce(Return(TRUE)); + EXPECT_CALL(*m_gstWrapperMock, gstBinAdd(_, &newAudioParse)).WillOnce(Return(TRUE)); + EXPECT_CALL(*m_gstWrapperMock, gstBinAdd(_, &newQueue)).WillOnce(Return(TRUE)); + EXPECT_CALL(*m_gstWrapperMock, gstElementGetStaticPad(&newAudioDecoder, StrEq("src"))) + .WillOnce(Return(&newAudioDecoderSrcPad)); + EXPECT_CALL(*m_gstWrapperMock, gstPadLink(&newAudioDecoderSrcPad, &typefindSrcPeerPad)).WillOnce(Return(GST_PAD_LINK_OK)); + EXPECT_CALL(*m_gstWrapperMock, gstElementLink(&newAudioParse, &newQueue)).WillOnce(Return(TRUE)); + EXPECT_CALL(*m_gstWrapperMock, gstElementLink(&newQueue, &newAudioDecoder)).WillOnce(Return(TRUE)); + EXPECT_CALL(*m_gstWrapperMock, gstElementSetState(&typefind, GST_STATE_READY)).WillOnce(Return(GST_STATE_CHANGE_SUCCESS)); + EXPECT_CALL(*m_glibWrapperMock, gObjectSetStub(&typefind, StrEq("force-caps"))); + EXPECT_CALL(*m_gstWrapperMock, gstElementSyncStateWithParent(&typefind)).WillOnce(Return(TRUE)); + EXPECT_CALL(*m_gstWrapperMock, gstElementGetState(&typefind, _, _, GST_CLOCK_TIME_NONE)); + EXPECT_CALL(*m_gstWrapperMock, gstElementSyncStateWithParent(&newAudioDecoder)).WillOnce(Return(TRUE)); + EXPECT_CALL(*m_gstWrapperMock, gstElementGetState(&newAudioDecoder, _, _, GST_CLOCK_TIME_NONE)); + EXPECT_CALL(*m_gstWrapperMock, gstElementSyncStateWithParent(&newQueue)).WillOnce(Return(TRUE)); + EXPECT_CALL(*m_gstWrapperMock, gstElementGetState(&newQueue, _, _, GST_CLOCK_TIME_NONE)); + EXPECT_CALL(*m_gstWrapperMock, gstElementSyncStateWithParent(&newAudioParse)).WillOnce(Return(TRUE)); + EXPECT_CALL(*m_gstWrapperMock, gstElementGetState(&newAudioParse, _, _, GST_CLOCK_TIME_NONE)); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(&typefindSrcPad)); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(&typefindSrcPeerPad)); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(&newAudioDecoderSrcPad)); + // gstAppSrcSetCaps after codec switch + EXPECT_CALL(*m_gstWrapperMock, gstAppSrcSetCaps(GST_APP_SRC(&audioSrc), &configCaps)); + // resumeAudioPlayback + EXPECT_CALL(*m_gstWrapperMock, gstElementSyncStateWithParent(&playsinkBin)).WillOnce(Return(TRUE)); + EXPECT_CALL(*m_gstWrapperMock, gstElementSyncStateWithParent(&decodeBin)).WillOnce(Return(TRUE)); + // end of reattachSource: caps was updated to configCaps inside performAudioTrackCodecChannelSwitch + EXPECT_CALL(*m_gstWrapperMock, gstCapsUnref(&configCaps)); + + std::unique_ptr source = + std::make_unique("audio/aac", false); + EXPECT_TRUE(m_sut->reattachSource(source)); + gst_object_unref(fakeSink); } TEST_F(GstGenericPlayerPrivateTest, shouldSetSourceFlushed) diff --git a/wrappers/include/GstWrapper.h b/wrappers/include/GstWrapper.h index 055484c43..1c109aa74 100644 --- a/wrappers/include/GstWrapper.h +++ b/wrappers/include/GstWrapper.h @@ -595,6 +595,22 @@ class GstWrapper : public IGstWrapper { gst_base_parse_set_pts_interpolation(parse, ptsInterpolate); } + + GstStateChangeReturn gstElementGetState(GstElement *element, GstState *state, GstState *pending, + GstClockTime timeout) override + { + return gst_element_get_state(element, state, pending, timeout); + } + + GstPad *gstPadGetPeer(GstPad *pad) override { return gst_pad_get_peer(pad); } + + gboolean gstPadUnlink(GstPad *srcpad, GstPad *sinkpad) override { return gst_pad_unlink(srcpad, sinkpad); } + + GstPadLinkReturn gstPadLink(GstPad *srcpad, GstPad *sinkpad) override { return gst_pad_link(srcpad, sinkpad); } + + gboolean gstBinRemove(GstBin *bin, GstElement *element) override { return gst_bin_remove(bin, element); } + + GstObject *gstPadGetParent(GstPad *pad) override { return gst_pad_get_parent(pad); } }; }; // namespace firebolt::rialto::wrappers diff --git a/wrappers/interface/IGstWrapper.h b/wrappers/interface/IGstWrapper.h index 0eca162f7..a7a184624 100644 --- a/wrappers/interface/IGstWrapper.h +++ b/wrappers/interface/IGstWrapper.h @@ -1402,6 +1402,69 @@ class IGstWrapper * @param ptsInterpolate : TRUE if parser should interpolate PTS timestamps */ virtual void gstBaseParseSetPtsInterpolation(GstBaseParse *parse, gboolean ptsInterpolate) const = 0; + + /** + * @brief Gets the state of the element (blocking version with timeout). + * + * @param[in] element : A GstElement to get state of. + * @param[out] state : A pointer to GstState to hold the state, or NULL. + * @param[out] pending : A pointer to GstState to hold the pending state, or NULL. + * @param[in] timeout : A GstClockTime to specify the timeout. + * + * @retval GST_STATE_CHANGE_SUCCESS if the element has no more pending state + * and the last state change succeeded, GST_STATE_CHANGE_ASYNC if the + * element is still performing a state change, or other values on failure. + */ + virtual GstStateChangeReturn gstElementGetState(GstElement *element, GstState *state, GstState *pending, + GstClockTime timeout) = 0; + + /** + * @brief Gets the peer pad of the given pad. + * + * @param[in] pad : A GstPad to get the peer of. + * + * @retval The peer GstPad. Unref after usage. NULL if pad has no peer. + */ + virtual GstPad *gstPadGetPeer(GstPad *pad) = 0; + + /** + * @brief Unlinks the source pad from the sink pad. + * + * @param[in] srcpad : The source GstPad to unlink. + * @param[in] sinkpad : The sink GstPad to unlink. + * + * @retval TRUE if the pads were unlinked, FALSE otherwise. + */ + virtual gboolean gstPadUnlink(GstPad *srcpad, GstPad *sinkpad) = 0; + + /** + * @brief Links the source pad to the sink pad. + * + * @param[in] srcpad : The source GstPad to link. + * @param[in] sinkpad : The sink GstPad to link. + * + * @retval A result code indicating if the connection worked or what went wrong. + */ + virtual GstPadLinkReturn gstPadLink(GstPad *srcpad, GstPad *sinkpad) = 0; + + /** + * @brief Removes an element from the bin. + * + * @param[in] bin : The bin to remove the element from. + * @param[in] element : The element to remove. + * + * @retval TRUE if the element could be removed, FALSE otherwise. + */ + virtual gboolean gstBinRemove(GstBin *bin, GstElement *element) = 0; + + /** + * @brief Gets the parent of a pad. + * + * @param[in] pad : The pad to get the parent of. + * + * @retval The parent GstObject. Unref after usage. NULL if pad has no parent. + */ + virtual GstObject *gstPadGetParent(GstPad *pad) = 0; }; }; // namespace firebolt::rialto::wrappers From 6688f0aea9e132d1f37e79202b7fd73d0980de48 Mon Sep 17 00:00:00 2001 From: Adam Czynszak <92790185+aczs@users.noreply.github.com> Date: Fri, 13 Mar 2026 12:40:14 +0000 Subject: [PATCH 13/29] Revert 'report-decode-errors' and 'queued-frames' properties missing from RialtoMSEVideoSink (#462) Summary: Temporary revert of 'report-decode-errors' and 'queued-frames' properties missing from RialtoMSEVideoSink. It revealed issues in some apps. Type: Feature Test Plan: UT/CT, Fullstack Jira: RDKEMW-12692 --- media/client/ipc/include/MediaPipelineIpc.h | 4 - .../client/ipc/interface/IMediaPipelineIpc.h | 24 ---- media/client/ipc/source/MediaPipelineIpc.cpp | 67 ----------- media/client/main/include/MediaPipeline.h | 4 - .../client/main/include/MediaPipelineProxy.h | 8 -- media/client/main/source/MediaPipeline.cpp | 10 -- media/public/include/IMediaPipeline.h | 24 ---- media/server/gstplayer/CMakeLists.txt | 1 - .../gstplayer/include/GenericPlayerContext.h | 5 - .../gstplayer/include/GstGenericPlayer.h | 3 - .../include/IGstGenericPlayerPrivate.h | 7 -- .../include/tasks/IGenericPlayerTaskFactory.h | 15 --- .../tasks/generic/GenericPlayerTaskFactory.h | 4 - .../tasks/generic/SetReportDecodeErrors.h | 49 -------- .../gstplayer/interface/IGstGenericPlayer.h | 19 ---- .../gstplayer/source/GstGenericPlayer.cpp | 80 ------------- .../generic/GenericPlayerTaskFactory.cpp | 9 -- .../tasks/generic/SetReportDecodeErrors.cpp | 52 --------- .../source/tasks/generic/SetupElement.cpp | 5 +- .../ipc/include/MediaPipelineModuleService.h | 8 -- .../ipc/source/MediaPipelineModuleService.cpp | 34 ------ .../include/MediaPipelineServerInternal.h | 28 ----- .../source/MediaPipelineServerInternal.cpp | 57 ---------- .../service/include/IMediaPipelineService.h | 2 - .../service/source/MediaPipelineService.cpp | 28 ----- .../service/source/MediaPipelineService.h | 2 - proto/mediapipelinemodule.proto | 50 --------- .../MediaPipelineProtoRequestMatchers.h | 14 --- .../client/mocks/MediaPipelineModuleMock.h | 20 ---- .../tests/base/MediaPipelineTestMethods.cpp | 28 ----- .../tests/base/MediaPipelineTestMethods.h | 4 - .../client/tests/mse/PipelinePropertyTest.cpp | 10 -- .../mediaPipeline/PipelinePropertyTest.cpp | 12 +- .../unittests/media/client/ipc/CMakeLists.txt | 2 - .../mediaPipelineIpc/GetQueuedFramesTest.cpp | 100 ----------------- .../SetReportDecodeErrorsTest.cpp | 96 ---------------- .../media/client/main/CMakeLists.txt | 2 - .../mediaPipeline/GetQueuedFramesTest.cpp | 63 ----------- .../mediaPipeline/MediaPipelineProxyTest.cpp | 15 --- .../SetReportDecodeErrorsTest.cpp | 58 ---------- .../client/mocks/ipc/MediaPipelineIpcMock.h | 2 - .../main/MediaPipelineAndControlClientMock.h | 2 - .../media/server/gstplayer/CMakeLists.txt | 1 - .../GstGenericPlayerPrivateTest.cpp | 30 ----- .../genericPlayer/GstGenericPlayerTest.cpp | 37 ------ .../common/GenericTasksTestsBase.cpp | 15 --- .../common/GenericTasksTestsBase.h | 4 - .../common/GstGenericPlayerTestCommon.cpp | 14 --- .../common/GstGenericPlayerTestCommon.h | 1 - .../GenericPlayerTaskFactoryTest.cpp | 8 -- .../tasksTests/SetReportDecodeErrorsTest.cpp | 30 ----- .../MediaPipelineModuleServiceTests.cpp | 32 ------ ...MediaPipelineModuleServiceTestsFixture.cpp | 73 ------------ .../MediaPipelineModuleServiceTestsFixture.h | 8 -- .../MiscellaneousFunctionsTest.cpp | 106 ------------------ .../gstplayer/GenericPlayerTaskFactoryMock.h | 4 - .../mocks/gstplayer/GstGenericPlayerMock.h | 3 - .../gstplayer/GstGenericPlayerPrivateMock.h | 1 - .../main/MediaPipelineServerInternalMock.h | 2 - .../mocks/service/MediaPipelineServiceMock.h | 2 - .../MediaPipelineServiceTests.cpp | 40 ------- .../MediaPipelineServiceTestsFixture.cpp | 44 -------- .../MediaPipelineServiceTestsFixture.h | 8 -- 63 files changed, 2 insertions(+), 1488 deletions(-) delete mode 100644 media/server/gstplayer/include/tasks/generic/SetReportDecodeErrors.h delete mode 100644 media/server/gstplayer/source/tasks/generic/SetReportDecodeErrors.cpp delete mode 100644 tests/unittests/media/client/ipc/mediaPipelineIpc/GetQueuedFramesTest.cpp delete mode 100644 tests/unittests/media/client/ipc/mediaPipelineIpc/SetReportDecodeErrorsTest.cpp delete mode 100644 tests/unittests/media/client/main/mediaPipeline/GetQueuedFramesTest.cpp delete mode 100644 tests/unittests/media/client/main/mediaPipeline/SetReportDecodeErrorsTest.cpp delete mode 100644 tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/SetReportDecodeErrorsTest.cpp diff --git a/media/client/ipc/include/MediaPipelineIpc.h b/media/client/ipc/include/MediaPipelineIpc.h index e2e907f68..db45d00d9 100644 --- a/media/client/ipc/include/MediaPipelineIpc.h +++ b/media/client/ipc/include/MediaPipelineIpc.h @@ -93,10 +93,6 @@ class MediaPipelineIpc : public IMediaPipelineIpc, public IpcModule bool setImmediateOutput(int32_t sourceId, bool immediateOutput) override; - bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) override; - - bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) override; - bool getImmediateOutput(int32_t sourceId, bool &immediateOutput) override; bool getStats(int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames) override; diff --git a/media/client/ipc/interface/IMediaPipelineIpc.h b/media/client/ipc/interface/IMediaPipelineIpc.h index bee5f597d..782a1931b 100644 --- a/media/client/ipc/interface/IMediaPipelineIpc.h +++ b/media/client/ipc/interface/IMediaPipelineIpc.h @@ -188,30 +188,6 @@ class IMediaPipelineIpc */ virtual bool setImmediateOutput(int32_t sourceId, bool immediateOutput) = 0; - /** - * @brief Sets the "Report Decode Errors" property for this source. - * - * This method is asynchronous, it will set the "Report Decode Errors" property - * - * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() - * @param[in] reportDecodeErrors : Set Report Decode Errors mode on the sink - * - * @retval true on success. - */ - virtual bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) = 0; - - /** - * @brief Gets the queued frames for this source. - * - * This method is synchronous, it gets the queued frames property - * - * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() - * @param[out] queuedFrames : Get queued frames on the decoder - * - * @retval true on success. - */ - virtual bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) = 0; - /** * @brief Gets the "Immediate Output" property for this source. * diff --git a/media/client/ipc/source/MediaPipelineIpc.cpp b/media/client/ipc/source/MediaPipelineIpc.cpp index 890ab8905..ff1b09847 100644 --- a/media/client/ipc/source/MediaPipelineIpc.cpp +++ b/media/client/ipc/source/MediaPipelineIpc.cpp @@ -567,73 +567,6 @@ bool MediaPipelineIpc::setImmediateOutput(int32_t sourceId, bool immediateOutput return true; } -bool MediaPipelineIpc::setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) -{ - if (!reattachChannelIfRequired()) - { - RIALTO_CLIENT_LOG_ERROR("Reattachment of the ipc channel failed, ipc disconnected"); - return false; - } - - firebolt::rialto::SetReportDecodeErrorsRequest request; - - request.set_session_id(m_sessionId); - request.set_source_id(sourceId); - request.set_report_decode_errors(reportDecodeErrors); - - firebolt::rialto::SetReportDecodeErrorsResponse response; - auto ipcController = m_ipc.createRpcController(); - auto blockingClosure = m_ipc.createBlockingClosure(); - m_mediaPipelineStub->setReportDecodeErrors(ipcController.get(), &request, &response, blockingClosure.get()); - - // wait for the call to complete - blockingClosure->wait(); - - // check the result - if (ipcController->Failed()) - { - RIALTO_CLIENT_LOG_ERROR("failed to set report decode error due to '%s'", ipcController->ErrorText().c_str()); - return false; - } - - return true; -} - -bool MediaPipelineIpc::getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) -{ - if (!reattachChannelIfRequired()) - { - RIALTO_CLIENT_LOG_ERROR("Reattachment of the ipc channel failed, ipc disconnected"); - return false; - } - - firebolt::rialto::GetQueuedFramesRequest request; - - request.set_session_id(m_sessionId); - request.set_source_id(sourceId); - - firebolt::rialto::GetQueuedFramesResponse response; - auto ipcController = m_ipc.createRpcController(); - auto blockingClosure = m_ipc.createBlockingClosure(); - m_mediaPipelineStub->getQueuedFrames(ipcController.get(), &request, &response, blockingClosure.get()); - - // wait for the call to complete - blockingClosure->wait(); - - // check the result - if (ipcController->Failed()) - { - RIALTO_CLIENT_LOG_ERROR("failed to get queued frames due to '%s'", ipcController->ErrorText().c_str()); - return false; - } - else - { - queuedFrames = response.queued_frames(); - } - - return true; -} - bool MediaPipelineIpc::getImmediateOutput(int32_t sourceId, bool &immediateOutput) { if (!reattachChannelIfRequired()) diff --git a/media/client/main/include/MediaPipeline.h b/media/client/main/include/MediaPipeline.h index d47f0fe16..13075a511 100644 --- a/media/client/main/include/MediaPipeline.h +++ b/media/client/main/include/MediaPipeline.h @@ -132,10 +132,6 @@ class MediaPipeline : public IMediaPipelineAndIControlClient, public IMediaPipel bool setImmediateOutput(int32_t sourceId, bool immediateOutput) override; - bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) override; - - bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) override; - bool getImmediateOutput(int32_t sourceId, bool &immediateOutput) override; bool getStats(int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames) override; diff --git a/media/client/main/include/MediaPipelineProxy.h b/media/client/main/include/MediaPipelineProxy.h index af45114aa..b38371963 100644 --- a/media/client/main/include/MediaPipelineProxy.h +++ b/media/client/main/include/MediaPipelineProxy.h @@ -68,14 +68,6 @@ class MediaPipelineProxy : public IMediaPipelineAndIControlClient { return m_mediaPipeline->setImmediateOutput(sourceId, immediateOutput); } - bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) - { - return m_mediaPipeline->setReportDecodeErrors(sourceId, reportDecodeErrors); - } - bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) - { - return m_mediaPipeline->getQueuedFrames(sourceId, queuedFrames); - } bool getImmediateOutput(int32_t sourceId, bool &immediateOutput) { return m_mediaPipeline->getImmediateOutput(sourceId, immediateOutput); diff --git a/media/client/main/source/MediaPipeline.cpp b/media/client/main/source/MediaPipeline.cpp index b97367f1d..e529fe3b6 100644 --- a/media/client/main/source/MediaPipeline.cpp +++ b/media/client/main/source/MediaPipeline.cpp @@ -313,16 +313,6 @@ bool MediaPipeline::setImmediateOutput(int32_t sourceId, bool immediateOutput) return m_mediaPipelineIpc->setImmediateOutput(sourceId, immediateOutput); } -bool MediaPipeline::setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) -{ - return m_mediaPipelineIpc->setReportDecodeErrors(sourceId, reportDecodeErrors); -} - -bool MediaPipeline::getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) -{ - return m_mediaPipelineIpc->getQueuedFrames(sourceId, queuedFrames); -} - bool MediaPipeline::getImmediateOutput(int32_t sourceId, bool &immediateOutput) { return m_mediaPipelineIpc->getImmediateOutput(sourceId, immediateOutput); diff --git a/media/public/include/IMediaPipeline.h b/media/public/include/IMediaPipeline.h index 10b910f20..e6c68787e 100644 --- a/media/public/include/IMediaPipeline.h +++ b/media/public/include/IMediaPipeline.h @@ -1227,30 +1227,6 @@ class IMediaPipeline */ virtual bool setImmediateOutput(int32_t sourceId, bool immediateOutput) = 0; - /** - * @brief Sets the "Report Decode Errors" property for this source. - * - * This method is asynchronous, it will set the "Report Decode Errors" property - * - * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() - * @param[in] reportDecodeErrors : Set Report Decode Errors mode on the sink - * - * @retval true on success. - */ - virtual bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) = 0; - - /** - * @brief Gets the queued frames for this source. - * - * This method is synchronous, it gets the queued frames property - * - * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() - * @param[out] queuedFrames : Get queued frames on the decoder - * - * @retval true on success. - */ - virtual bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) = 0; - /** * @brief Gets the "Immediate Output" property for this source. * diff --git a/media/server/gstplayer/CMakeLists.txt b/media/server/gstplayer/CMakeLists.txt index 58278418a..6756184da 100644 --- a/media/server/gstplayer/CMakeLists.txt +++ b/media/server/gstplayer/CMakeLists.txt @@ -57,7 +57,6 @@ add_library( source/tasks/generic/SetMute.cpp source/tasks/generic/SetPlaybackRate.cpp source/tasks/generic/SetPosition.cpp - source/tasks/generic/SetReportDecodeErrors.cpp source/tasks/generic/SetSourcePosition.cpp source/tasks/generic/SetSubtitleOffset.cpp source/tasks/generic/SetStreamSyncMode.cpp diff --git a/media/server/gstplayer/include/GenericPlayerContext.h b/media/server/gstplayer/include/GenericPlayerContext.h index 237646ca3..d97deb4b1 100644 --- a/media/server/gstplayer/include/GenericPlayerContext.h +++ b/media/server/gstplayer/include/GenericPlayerContext.h @@ -155,11 +155,6 @@ struct GenericPlayerContext */ std::optional pendingImmediateOutputForVideo{}; - /** - * @brief Pending report decode errors for MediaSourceType::VIDEO - */ - std::optional pendingReportDecodeErrorsForVideo{}; - /** * @brief Pending low latency */ diff --git a/media/server/gstplayer/include/GstGenericPlayer.h b/media/server/gstplayer/include/GstGenericPlayer.h index 8ea963d8d..54476f2ca 100644 --- a/media/server/gstplayer/include/GstGenericPlayer.h +++ b/media/server/gstplayer/include/GstGenericPlayer.h @@ -119,9 +119,7 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva void setPlaybackRate(double rate) override; bool getPosition(std::int64_t &position) override; bool setImmediateOutput(const MediaSourceType &mediaSourceType, bool immediateOutput) override; - bool setReportDecodeErrors(const MediaSourceType &mediaSourceType, bool reportDecodeErrors) override; bool getImmediateOutput(const MediaSourceType &mediaSourceType, bool &immediateOutput) override; - bool getQueuedFrames(uint32_t &queuedFrames) override; bool getStats(const MediaSourceType &mediaSourceType, uint64_t &renderedFrames, uint64_t &droppedFrames) override; void setVolume(double targetVolume, uint32_t volumeDuration, firebolt::rialto::EaseType easeType) override; bool getVolume(double &volume) override; @@ -155,7 +153,6 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva void scheduleAllSourcesAttached() override; bool setVideoSinkRectangle() override; bool setImmediateOutput() override; - bool setReportDecodeErrors() override; bool setShowVideoWindow() override; bool setLowLatency() override; bool setSync() override; diff --git a/media/server/gstplayer/include/IGstGenericPlayerPrivate.h b/media/server/gstplayer/include/IGstGenericPlayerPrivate.h index 9b6d54af0..fb48a2816 100644 --- a/media/server/gstplayer/include/IGstGenericPlayerPrivate.h +++ b/media/server/gstplayer/include/IGstGenericPlayerPrivate.h @@ -80,13 +80,6 @@ class IGstGenericPlayerPrivate */ virtual bool setImmediateOutput() = 0; - /** - * @brief Sets report decode error. Called by the worker thread. - * - * @retval true on success. - */ - virtual bool setReportDecodeErrors() = 0; - /** * @brief Sets the low latency property. Called by the worker thread. * diff --git a/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h b/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h index 54b8fc621..f8de67cb4 100644 --- a/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h +++ b/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h @@ -482,21 +482,6 @@ class IGenericPlayerTaskFactory const firebolt::rialto::MediaSourceType &type, bool immediateOutput) const = 0; - /** - * @brief Creates a SetReportDecodeErrors task. - * - * @param[in] context : The GstPlayer context - * @param[in] player : The GstPlayer instance - * @param[in] type : The media source type - * @param[in] reportDecodeErrors : the value to set for report decode error - * - * @retval the new SetReportDecodeErrors task instance. - */ - virtual std::unique_ptr createSetReportDecodeErrors(GenericPlayerContext &context, - IGstGenericPlayerPrivate &player, - const firebolt::rialto::MediaSourceType &type, - bool reportDecodeErrors) const = 0; - /** * @brief Creates a SetBufferingLimit task. * diff --git a/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h b/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h index 79e31838b..aa9a06e55 100644 --- a/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h +++ b/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h @@ -120,10 +120,6 @@ class GenericPlayerTaskFactory : public IGenericPlayerTaskFactory std::unique_ptr createSetImmediateOutput(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, const firebolt::rialto::MediaSourceType &type, bool immediateOutput) const override; - std::unique_ptr createSetReportDecodeErrors(GenericPlayerContext &context, - IGstGenericPlayerPrivate &player, - const firebolt::rialto::MediaSourceType &type, - bool reportDecodeErrors) const override; std::unique_ptr createSetBufferingLimit(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, std::uint32_t limit) const override; std::unique_ptr createSetUseBuffering(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, diff --git a/media/server/gstplayer/include/tasks/generic/SetReportDecodeErrors.h b/media/server/gstplayer/include/tasks/generic/SetReportDecodeErrors.h deleted file mode 100644 index 9cf30b310..000000000 --- a/media/server/gstplayer/include/tasks/generic/SetReportDecodeErrors.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2026 Sky UK - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef FIREBOLT_RIALTO_SERVER_TASKS_GENERIC_SET_REPORT_DECODE_ERRORS_H_ -#define FIREBOLT_RIALTO_SERVER_TASKS_GENERIC_SET_REPORT_DECODE_ERRORS_H_ - -#include "GenericPlayerContext.h" -#include "IGlibWrapper.h" -#include "IGstGenericPlayerPrivate.h" -#include "IGstWrapper.h" -#include "IPlayerTask.h" - -#include - -namespace firebolt::rialto::server::tasks::generic -{ -class SetReportDecodeErrors : public IPlayerTask -{ -public: - explicit SetReportDecodeErrors(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, - const MediaSourceType &type, bool reportDecodeErrors); - ~SetReportDecodeErrors() override; - void execute() const override; - -private: - GenericPlayerContext &m_context; - IGstGenericPlayerPrivate &m_player; - const MediaSourceType m_type; - bool m_reportDecodeErrors; -}; -} // namespace firebolt::rialto::server::tasks::generic - -#endif // FIREBOLT_RIALTO_SERVER_TASKS_GENERIC_SET_REPORT_DECODE_ERRORS_H_ diff --git a/media/server/gstplayer/interface/IGstGenericPlayer.h b/media/server/gstplayer/interface/IGstGenericPlayer.h index bb0931513..5e6842926 100644 --- a/media/server/gstplayer/interface/IGstGenericPlayer.h +++ b/media/server/gstplayer/interface/IGstGenericPlayer.h @@ -196,25 +196,6 @@ class IGstGenericPlayer */ virtual bool setImmediateOutput(const MediaSourceType &mediaSourceType, bool immediateOutput) = 0; - /** - * @brief Sets the "Report Decode Error" property for this source. - * - * @param[in] mediaSourceType : The media source type - * @param[in] reportDecodeErrors : Set report decode error - * - * @retval true on success. - */ - virtual bool setReportDecodeErrors(const MediaSourceType &mediaSourceType, bool reportDecodeErrors) = 0; - - /** - * @brief Gets the queued frames for this source. - * - * @param[out] queuedFrames : Get queued frames mode on the decoder - * - * @retval true on success. - */ - virtual bool getQueuedFrames(uint32_t &queuedFrames) = 0; - /** * @brief Gets the "Immediate Output" property for this source. * diff --git a/media/server/gstplayer/source/GstGenericPlayer.cpp b/media/server/gstplayer/source/GstGenericPlayer.cpp index 56030d04d..ef9e985b8 100644 --- a/media/server/gstplayer/source/GstGenericPlayer.cpp +++ b/media/server/gstplayer/source/GstGenericPlayer.cpp @@ -1094,41 +1094,6 @@ bool GstGenericPlayer::setImmediateOutput(const MediaSourceType &mediaSourceType return true; } -bool GstGenericPlayer::setReportDecodeErrors(const MediaSourceType &mediaSourceType, bool reportDecodeErrors) -{ - if (!m_workerThread) - return false; - - m_workerThread->enqueueTask( - m_taskFactory->createSetReportDecodeErrors(m_context, *this, mediaSourceType, reportDecodeErrors)); - return true; -} - -bool GstGenericPlayer::getQueuedFrames(uint32_t &queuedFrames) -{ - bool returnValue{false}; - GstElement *decoder{getDecoder(MediaSourceType::VIDEO)}; - if (decoder) - { - if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(decoder), "queued-frames")) - { - m_glibWrapper->gObjectGet(decoder, "queued-frames", &queuedFrames, nullptr); - returnValue = true; - } - else - { - RIALTO_SERVER_LOG_ERROR("queued-frames not supported in element %s", GST_ELEMENT_NAME(decoder)); - } - m_gstWrapper->gstObjectUnref(decoder); - } - else - { - RIALTO_SERVER_LOG_ERROR("Failed to get queued-frames property, decoder is NULL"); - } - - return returnValue; -} - bool GstGenericPlayer::getImmediateOutput(const MediaSourceType &mediaSourceType, bool &immediateOutputRef) { bool returnValue{false}; @@ -1863,51 +1828,6 @@ bool GstGenericPlayer::setImmediateOutput() return result; } -bool GstGenericPlayer::setReportDecodeErrors() -{ - bool result{false}; - bool reportDecodeErrors{false}; - - { - std::unique_lock lock{m_context.propertyMutex}; - if (!m_context.pendingReportDecodeErrorsForVideo.has_value()) - { - return false; - } - reportDecodeErrors = m_context.pendingReportDecodeErrorsForVideo.value(); - } - - GstElement *decoder = getDecoder(MediaSourceType::VIDEO); - if (decoder) - { - RIALTO_SERVER_LOG_DEBUG("Set report decode errors to %s", reportDecodeErrors ? "TRUE" : "FALSE"); - - if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(decoder), "report-decode-errors")) - { - gboolean reportDecodeErrorsGboolean{reportDecodeErrors ? TRUE : FALSE}; - m_glibWrapper->gObjectSet(decoder, "report-decode-errors", reportDecodeErrorsGboolean, nullptr); - result = true; - } - else - { - RIALTO_SERVER_LOG_ERROR("Failed to set report-decode-errors property on decoder '%s'", - GST_ELEMENT_NAME(decoder)); - } - - m_gstWrapper->gstObjectUnref(decoder); - - { - std::unique_lock lock{m_context.propertyMutex}; - m_context.pendingReportDecodeErrorsForVideo.reset(); - } - } - else - { - RIALTO_SERVER_LOG_DEBUG("Pending report-decode-errors, decoder is NULL"); - } - return result; -} - bool GstGenericPlayer::setShowVideoWindow() { if (!m_context.pendingShowVideoWindow.has_value()) diff --git a/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp b/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp index a7886a335..318f04379 100644 --- a/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp +++ b/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp @@ -41,7 +41,6 @@ #include "tasks/generic/SetMute.h" #include "tasks/generic/SetPlaybackRate.h" #include "tasks/generic/SetPosition.h" -#include "tasks/generic/SetReportDecodeErrors.h" #include "tasks/generic/SetSourcePosition.h" #include "tasks/generic/SetStreamSyncMode.h" #include "tasks/generic/SetSubtitleOffset.h" @@ -327,14 +326,6 @@ GenericPlayerTaskFactory::createSetImmediateOutput(GenericPlayerContext &context return std::make_unique(context, player, type, immediateOutput); } -std::unique_ptr -GenericPlayerTaskFactory::createSetReportDecodeErrors(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, - const firebolt::rialto::MediaSourceType &type, - bool reportDecodeErrors) const -{ - return std::make_unique(context, player, type, reportDecodeErrors); -} - std::unique_ptr GenericPlayerTaskFactory::createSetBufferingLimit(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, std::uint32_t limit) const diff --git a/media/server/gstplayer/source/tasks/generic/SetReportDecodeErrors.cpp b/media/server/gstplayer/source/tasks/generic/SetReportDecodeErrors.cpp deleted file mode 100644 index 6815df0ce..000000000 --- a/media/server/gstplayer/source/tasks/generic/SetReportDecodeErrors.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2024 Sky UK - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SetReportDecodeErrors.h" -#include "RialtoServerLogging.h" -#include "TypeConverters.h" - -namespace firebolt::rialto::server::tasks::generic -{ -SetReportDecodeErrors::SetReportDecodeErrors(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, - const MediaSourceType &type, bool reportDecodeErrors) - : m_context{context}, m_player(player), m_type{type}, m_reportDecodeErrors{reportDecodeErrors} -{ - RIALTO_SERVER_LOG_DEBUG("Constructing SetReportDecodeErrors"); -} - -SetReportDecodeErrors::~SetReportDecodeErrors() -{ - RIALTO_SERVER_LOG_DEBUG("SetReportDecodeErrors finished"); -} - -void SetReportDecodeErrors::execute() const -{ - RIALTO_SERVER_LOG_DEBUG("Executing SetReportDecodeErrors for %s source", common::convertMediaSourceType(m_type)); - - m_context.pendingReportDecodeErrorsForVideo = m_reportDecodeErrors; - - if (!m_context.pipeline) - { - RIALTO_SERVER_LOG_WARN("Pipeline not available yet - cannot apply report-decode-errors setting"); - return; - } - - m_player.setReportDecodeErrors(); -} -} // namespace firebolt::rialto::server::tasks::generic diff --git a/media/server/gstplayer/source/tasks/generic/SetupElement.cpp b/media/server/gstplayer/source/tasks/generic/SetupElement.cpp index c7048fbe0..99588430a 100644 --- a/media/server/gstplayer/source/tasks/generic/SetupElement.cpp +++ b/media/server/gstplayer/source/tasks/generic/SetupElement.cpp @@ -246,10 +246,6 @@ void SetupElement::execute() const RIALTO_SERVER_LOG_INFO("Setting video decoder handle for subtitle sink: %p", m_element); m_context.isVideoHandleSet = true; } - if (m_context.pendingReportDecodeErrorsForVideo.has_value()) - { - m_player.setReportDecodeErrors(); - } } } @@ -337,6 +333,7 @@ void SetupElement::execute() const { m_gstWrapper->gstBaseParseSetPtsInterpolation(GST_BASE_PARSE(m_element), FALSE); } + m_gstWrapper->gstObjectUnref(m_element); } } // namespace firebolt::rialto::server::tasks::generic diff --git a/media/server/ipc/include/MediaPipelineModuleService.h b/media/server/ipc/include/MediaPipelineModuleService.h index 61d932c08..f0321827b 100644 --- a/media/server/ipc/include/MediaPipelineModuleService.h +++ b/media/server/ipc/include/MediaPipelineModuleService.h @@ -88,14 +88,6 @@ class MediaPipelineModuleService : public IMediaPipelineModuleService const ::firebolt::rialto::SetImmediateOutputRequest *request, ::firebolt::rialto::SetImmediateOutputResponse *response, ::google::protobuf::Closure *done) override; - void setReportDecodeErrors(::google::protobuf::RpcController *controller, - const ::firebolt::rialto::SetReportDecodeErrorsRequest *request, - ::firebolt::rialto::SetReportDecodeErrorsResponse *response, - ::google::protobuf::Closure *done) override; - void getQueuedFrames(::google::protobuf::RpcController *controller, - const ::firebolt::rialto::GetQueuedFramesRequest *request, - ::firebolt::rialto::GetQueuedFramesResponse *response, - ::google::protobuf::Closure *done) override; void getImmediateOutput(::google::protobuf::RpcController *controller, const ::firebolt::rialto::GetImmediateOutputRequest *request, ::firebolt::rialto::GetImmediateOutputResponse *response, diff --git a/media/server/ipc/source/MediaPipelineModuleService.cpp b/media/server/ipc/source/MediaPipelineModuleService.cpp index d876c1f9d..7e08ec2f7 100644 --- a/media/server/ipc/source/MediaPipelineModuleService.cpp +++ b/media/server/ipc/source/MediaPipelineModuleService.cpp @@ -679,40 +679,6 @@ void MediaPipelineModuleService::setImmediateOutput(::google::protobuf::RpcContr done->Run(); } -void MediaPipelineModuleService::setReportDecodeErrors(::google::protobuf::RpcController *controller, - const ::firebolt::rialto::SetReportDecodeErrorsRequest *request, - ::firebolt::rialto::SetReportDecodeErrorsResponse *response, - ::google::protobuf::Closure *done) -{ - RIALTO_SERVER_LOG_DEBUG("entry:"); - if (!m_mediaPipelineService.setReportDecodeErrors(request->session_id(), request->source_id(), - request->report_decode_errors())) - { - RIALTO_SERVER_LOG_ERROR("Set Report Decode Error failed"); - controller->SetFailed("Operation failed"); - } - done->Run(); -} - -void MediaPipelineModuleService::getQueuedFrames(::google::protobuf::RpcController *controller, - const ::firebolt::rialto::GetQueuedFramesRequest *request, - ::firebolt::rialto::GetQueuedFramesResponse *response, - ::google::protobuf::Closure *done) -{ - RIALTO_SERVER_LOG_DEBUG("entry:"); - uint32_t queuedFramesNumber; - if (!m_mediaPipelineService.getQueuedFrames(request->session_id(), request->source_id(), queuedFramesNumber)) - { - RIALTO_SERVER_LOG_ERROR("Get queued frames failed"); - controller->SetFailed("Operation failed"); - } - else - { - response->set_queued_frames(queuedFramesNumber); - } - done->Run(); -} - void MediaPipelineModuleService::getImmediateOutput(::google::protobuf::RpcController *controller, const ::firebolt::rialto::GetImmediateOutputRequest *request, ::firebolt::rialto::GetImmediateOutputResponse *response, diff --git a/media/server/main/include/MediaPipelineServerInternal.h b/media/server/main/include/MediaPipelineServerInternal.h index cae22ebfa..cfd0cd2a7 100644 --- a/media/server/main/include/MediaPipelineServerInternal.h +++ b/media/server/main/include/MediaPipelineServerInternal.h @@ -113,10 +113,6 @@ class MediaPipelineServerInternal : public IMediaPipelineServerInternal, public bool setImmediateOutput(int32_t sourceId, bool immediateOutput) override; - bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) override; - - bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) override; - bool getImmediateOutput(int32_t sourceId, bool &immediateOutput) override; bool getStats(int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames) override; @@ -400,30 +396,6 @@ class MediaPipelineServerInternal : public IMediaPipelineServerInternal, public */ bool setImmediateOutputInternal(int32_t sourceId, bool immediateOutput); - /** - * @brief Sets the "Report Decode Errors" property for this source. - * - * This method is asynchronous - * - * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() - * @param[in] reportDecodeErrors : The desired Set Report Decode Errors mode on the sink - * - * @retval true on success. - */ - bool setReportDecodeErrorsInternal(int32_t sourceId, bool reportDecodeErrors); - - /** - * @brief Gets the queued frames for this source. - * - * This method is asynchronous - * - * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() - * @param[in] queuedFrames : Number of queued frames - * - * @retval true on success. - */ - bool getQueuedFramesInternal(int32_t sourceId, uint32_t &queuedFrames); - /** * @brief Gets the "Immediate Output" property for this source. * diff --git a/media/server/main/source/MediaPipelineServerInternal.cpp b/media/server/main/source/MediaPipelineServerInternal.cpp index 2e940bb01..3cd73fdfd 100644 --- a/media/server/main/source/MediaPipelineServerInternal.cpp +++ b/media/server/main/source/MediaPipelineServerInternal.cpp @@ -534,63 +534,6 @@ bool MediaPipelineServerInternal::setImmediateOutputInternal(int32_t sourceId, b return m_gstPlayer->setImmediateOutput(sourceIter->first, immediateOutput); } -bool MediaPipelineServerInternal::setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) -{ - RIALTO_SERVER_LOG_DEBUG("entry:"); - - bool result; - auto task = [&]() { result = setReportDecodeErrorsInternal(sourceId, reportDecodeErrors); }; - - m_mainThread->enqueueTaskAndWait(m_mainThreadClientId, task); - return result; -} - -bool MediaPipelineServerInternal::setReportDecodeErrorsInternal(int32_t sourceId, bool reportDecodeErrors) -{ - if (!m_gstPlayer) - { - RIALTO_SERVER_LOG_ERROR("Failed - Gstreamer player has not been loaded"); - return false; - } - auto sourceIter = std::find_if(m_attachedSources.begin(), m_attachedSources.end(), - [sourceId](const auto &src) { return src.second == sourceId; }); - if (sourceIter == m_attachedSources.end()) - { - RIALTO_SERVER_LOG_ERROR("Failed - Source not found"); - return false; - } - - return m_gstPlayer->setReportDecodeErrors(sourceIter->first, reportDecodeErrors); -} - -bool MediaPipelineServerInternal::getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) -{ - RIALTO_SERVER_LOG_DEBUG("entry:"); - - bool result; - auto task = [&]() { result = getQueuedFramesInternal(sourceId, queuedFrames); }; - - m_mainThread->enqueueTaskAndWait(m_mainThreadClientId, task); - return result; -} - -bool MediaPipelineServerInternal::getQueuedFramesInternal(int32_t sourceId, uint32_t &queuedFrames) -{ - if (!m_gstPlayer) - { - RIALTO_SERVER_LOG_ERROR("Failed - Gstreamer player has not been loaded"); - return false; - } - auto sourceIter = std::find_if(m_attachedSources.begin(), m_attachedSources.end(), - [sourceId](const auto &src) { return src.second == sourceId; }); - if (sourceIter == m_attachedSources.end()) - { - RIALTO_SERVER_LOG_ERROR("Failed - Source not found"); - return false; - } - return m_gstPlayer->getQueuedFrames(queuedFrames); -} - bool MediaPipelineServerInternal::getImmediateOutput(int32_t sourceId, bool &immediateOutput) { RIALTO_SERVER_LOG_DEBUG("entry:"); diff --git a/media/server/service/include/IMediaPipelineService.h b/media/server/service/include/IMediaPipelineService.h index ffe742178..ae1fc5e04 100644 --- a/media/server/service/include/IMediaPipelineService.h +++ b/media/server/service/include/IMediaPipelineService.h @@ -55,8 +55,6 @@ class IMediaPipelineService virtual bool setPosition(int sessionId, std::int64_t position) = 0; virtual bool getPosition(int sessionId, std::int64_t &position) = 0; virtual bool setImmediateOutput(int sessionId, int32_t sourceId, bool immediateOutput) = 0; - virtual bool setReportDecodeErrors(int sessionId, int32_t sourceId, bool reportDecodeErrors) = 0; - virtual bool getQueuedFrames(int sessionId, int32_t sourceId, uint32_t &queuedFrames) = 0; virtual bool getImmediateOutput(int sessionId, int32_t sourceId, bool &immediateOutput) = 0; virtual bool getStats(int sessionId, int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames) = 0; virtual bool setVideoWindow(int sessionId, std::uint32_t x, std::uint32_t y, std::uint32_t width, diff --git a/media/server/service/source/MediaPipelineService.cpp b/media/server/service/source/MediaPipelineService.cpp index 57b884fe1..5d2212017 100644 --- a/media/server/service/source/MediaPipelineService.cpp +++ b/media/server/service/source/MediaPipelineService.cpp @@ -267,34 +267,6 @@ bool MediaPipelineService::setImmediateOutput(int sessionId, int32_t sourceId, b return mediaPipelineIter->second->setImmediateOutput(sourceId, immediateOutput); } -bool MediaPipelineService::setReportDecodeErrors(int sessionId, int32_t sourceId, bool reportDecodeErrors) -{ - RIALTO_SERVER_LOG_INFO("MediaPipelineService requested to setReportDecodeErrors, session id: %d", sessionId); - - std::lock_guard lock{m_mediaPipelineMutex}; - auto mediaPipelineIter = m_mediaPipelines.find(sessionId); - if (mediaPipelineIter == m_mediaPipelines.end()) - { - RIALTO_SERVER_LOG_ERROR("Session with id: %d does not exists", sessionId); - return false; - } - return mediaPipelineIter->second->setReportDecodeErrors(sourceId, reportDecodeErrors); -} - -bool MediaPipelineService::getQueuedFrames(int sessionId, int32_t sourceId, uint32_t &queuedFrames) -{ - RIALTO_SERVER_LOG_INFO("MediaPipelineService requested to getQueuedFrames, session id: %d", sessionId); - - std::lock_guard lock{m_mediaPipelineMutex}; - auto mediaPipelineIter = m_mediaPipelines.find(sessionId); - if (mediaPipelineIter == m_mediaPipelines.end()) - { - RIALTO_SERVER_LOG_ERROR("Session with id: %d does not exists", sessionId); - return false; - } - return mediaPipelineIter->second->getQueuedFrames(sourceId, queuedFrames); -} - bool MediaPipelineService::getImmediateOutput(int sessionId, int32_t sourceId, bool &immediateOutput) { RIALTO_SERVER_LOG_INFO("MediaPipelineService requested to getImmediateOutput, session id: %d", sessionId); diff --git a/media/server/service/source/MediaPipelineService.h b/media/server/service/source/MediaPipelineService.h index 5290c2475..bf9ec234f 100644 --- a/media/server/service/source/MediaPipelineService.h +++ b/media/server/service/source/MediaPipelineService.h @@ -66,8 +66,6 @@ class MediaPipelineService : public IMediaPipelineService bool setPosition(int sessionId, std::int64_t position) override; bool getPosition(int sessionId, std::int64_t &position) override; bool setImmediateOutput(int sessionId, int32_t sourceId, bool immediateOutput) override; - bool setReportDecodeErrors(int sessionId, int32_t sourceId, bool reportDecodeErrors) override; - bool getQueuedFrames(int sessionId, int32_t sourceId, uint32_t &queuedFrames) override; bool getImmediateOutput(int sessionId, int32_t sourceId, bool &immediateOutput) override; bool getStats(int sessionId, int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames) override; bool setVideoWindow(int sessionId, std::uint32_t x, std::uint32_t y, std::uint32_t width, diff --git a/proto/mediapipelinemodule.proto b/proto/mediapipelinemodule.proto index b111b3fb9..1807810de 100644 --- a/proto/mediapipelinemodule.proto +++ b/proto/mediapipelinemodule.proto @@ -678,24 +678,6 @@ message SetImmediateOutputRequest { message SetImmediateOutputResponse { } -/** - * @fn void setReportDecodeErrors(int session_id, int source_id, bool report_decode_errors) - * @brief Enables or disables decode error reporting for this source. - * - * @param[in] session_id The id of the A/V session. - * @param[in] source_id The id of the media source. - * @param[in] report_decode_errors Enable decode error reporting. - */ -message SetReportDecodeErrorsRequest { - optional int32 session_id = 1 [default = -1]; - optional int32 source_id = 2 [default = -1]; - optional bool report_decode_errors = 3 [default = false]; -} - -message SetReportDecodeErrorsResponse { -} - - /** * @fn void getImmediateOutput(int session_id, int source_id, bool &immediate_output) * @brief Gets the "Immediate Output" property for this source. @@ -713,23 +695,6 @@ message GetImmediateOutputResponse { optional bool immediate_output = 1 [default = false]; } -/** - * @fn void getQueuedFrames(int session_id, int source_id, uint32_t &queued_frames) - * @brief Gets the "Queued Frames" property for this source. - * - * @param[in] session_id The id of the A/V session. - * @param[in] source_id The id of the media source. - * @param[out] queued_frames The number of queued frames on the decoder - * - */ -message GetQueuedFramesRequest { - optional int32 session_id = 1 [default = -1]; - optional int32 source_id = 2 [default = -1]; -} -message GetQueuedFramesResponse { - optional uint32 queued_frames = 1 [default = 0]; -} - /** * @fn void getStats(int session_id, int source_id, uint64 &rendered_frames, uint64 &dropped_frames) * @brief Get various stats from the source. @@ -1113,14 +1078,6 @@ service MediaPipelineModule { rpc setImmediateOutput(SetImmediateOutputRequest) returns (SetImmediateOutputResponse) { } - /** - * @brief Sets the "Report Decode Errors" property for this source. - * @see SetReportDecodeErrorsRequest - */ - rpc setReportDecodeErrors(SetReportDecodeErrorsRequest) - returns (SetReportDecodeErrorsResponse) { - } - /** * @brief Gets the "Immediate Output" property for this source. * @see GetImmediateOutputRequest @@ -1128,13 +1085,6 @@ service MediaPipelineModule { rpc getImmediateOutput(GetImmediateOutputRequest) returns (GetImmediateOutputResponse) { } - /** - * @brief Gets the "Queued Frames" property for this source. - * @see GetQueuedFramesRequest - */ - rpc getQueuedFrames(GetQueuedFramesRequest) returns (GetQueuedFramesResponse) { - } - /** * @brief Gets the current stats property * @see GetStatsRequest diff --git a/tests/common/matchers/MediaPipelineProtoRequestMatchers.h b/tests/common/matchers/MediaPipelineProtoRequestMatchers.h index 1424cf106..04c48ad78 100644 --- a/tests/common/matchers/MediaPipelineProtoRequestMatchers.h +++ b/tests/common/matchers/MediaPipelineProtoRequestMatchers.h @@ -366,20 +366,6 @@ MATCHER_P2(getImmediateOutputRequestMatcher, sessionId, sourceId, "") return (kRequest->session_id() == sessionId) && (kRequest->source_id() == sourceId); } -MATCHER_P2(setReportDecodeErrorsRequestMatcher, sessionId, sourceId, "") -{ - const ::firebolt::rialto::SetReportDecodeErrorsRequest *kRequest = - dynamic_cast(arg); - return (kRequest->session_id() == sessionId) && (kRequest->source_id() == sourceId); -} - -MATCHER_P2(getQueuedFramesRequestMatcher, sessionId, sourceId, "") -{ - const ::firebolt::rialto::GetQueuedFramesRequest *kRequest = - dynamic_cast(arg); - return (kRequest->session_id() == sessionId) && (kRequest->source_id() == sourceId); -} - MATCHER_P2(getStatsRequestMatcher, sessionId, sourceId, "") { const ::firebolt::rialto::GetStatsRequest *kRequest = dynamic_cast(arg); diff --git a/tests/componenttests/client/mocks/MediaPipelineModuleMock.h b/tests/componenttests/client/mocks/MediaPipelineModuleMock.h index 5cf614593..a6fc772c0 100644 --- a/tests/componenttests/client/mocks/MediaPipelineModuleMock.h +++ b/tests/componenttests/client/mocks/MediaPipelineModuleMock.h @@ -72,13 +72,6 @@ class MediaPipelineModuleMock : public ::firebolt::rialto::MediaPipelineModule (::google::protobuf::RpcController * controller, const ::firebolt::rialto::GetImmediateOutputRequest *request, ::firebolt::rialto::GetImmediateOutputResponse *response, ::google::protobuf::Closure *done)); - MOCK_METHOD(void, setReportDecodeErrors, - (::google::protobuf::RpcController * controller, - const ::firebolt::rialto::SetReportDecodeErrorsRequest *request, - ::firebolt::rialto::SetReportDecodeErrorsResponse *response, ::google::protobuf::Closure *done)); - MOCK_METHOD(void, getQueuedFrames, - (::google::protobuf::RpcController * controller, const ::firebolt::rialto::GetQueuedFramesRequest *request, - ::firebolt::rialto::GetQueuedFramesResponse *response, ::google::protobuf::Closure *done)); MOCK_METHOD(void, getStats, (::google::protobuf::RpcController * controller, const ::firebolt::rialto::GetStatsRequest *request, ::firebolt::rialto::GetStatsResponse *response, ::google::protobuf::Closure *done)); @@ -238,19 +231,6 @@ class MediaPipelineModuleMock : public ::firebolt::rialto::MediaPipelineModule return response; } - ::firebolt::rialto::SetReportDecodeErrorsResponse SetReportDecodeErrorsResponse() - { - firebolt::rialto::SetReportDecodeErrorsResponse response; - return response; - } - - ::firebolt::rialto::GetQueuedFramesResponse getQueuedFramesResponse(uint32_t queuedFramesResponse) - { - firebolt::rialto::GetQueuedFramesResponse response; - response.set_queued_frames(queuedFramesResponse); - return response; - } - ::firebolt::rialto::GetStatsResponse getStatsResponse(const uint64_t renderedFrames, const uint64_t droppedFrames) { firebolt::rialto::GetStatsResponse response; diff --git a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp index a7f2f388a..e91d67bbb 100644 --- a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp +++ b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp @@ -1450,34 +1450,6 @@ void MediaPipelineTestMethods::getImmediateOutput(bool immediateOutput) EXPECT_TRUE(m_mediaPipeline->getImmediateOutput(kVideoSourceId, immediateOutput)); } -void MediaPipelineTestMethods::shouldSetReportDecodeErrors(bool reportDecodeErrors) -{ - EXPECT_CALL(*m_mediaPipelineModuleMock, - setReportDecodeErrors(_, setReportDecodeErrorsRequestMatcher(kSessionId, kVideoSourceId), _, _)) - .WillOnce(DoAll(SetArgPointee<2>(m_mediaPipelineModuleMock->SetReportDecodeErrorsResponse()), - WithArgs<0, 3>(Invoke(&(*m_mediaPipelineModuleMock), &MediaPipelineModuleMock::defaultReturn)))); -} - -void MediaPipelineTestMethods::setReportDecodeErrors(bool reportDecodeErrors) -{ - EXPECT_TRUE(m_mediaPipeline->setReportDecodeErrors(kVideoSourceId, reportDecodeErrors)); -} - -void MediaPipelineTestMethods::shouldGetQueuedFrames(uint32_t queuedFrames) -{ - EXPECT_CALL(*m_mediaPipelineModuleMock, - getQueuedFrames(_, getQueuedFramesRequestMatcher(kSessionId, kVideoSourceId), _, _)) - .WillOnce(DoAll(SetArgPointee<2>(m_mediaPipelineModuleMock->getQueuedFramesResponse(queuedFrames)), - WithArgs<0, 3>(Invoke(&(*m_mediaPipelineModuleMock), &MediaPipelineModuleMock::defaultReturn)))); -} - -void MediaPipelineTestMethods::getQueuedFrames(uint32_t queuedFrames) -{ - uint32_t returnQueuedFrames; - EXPECT_TRUE(m_mediaPipeline->getQueuedFrames(kVideoSourceId, returnQueuedFrames)); - EXPECT_EQ(returnQueuedFrames, queuedFrames); -} - void MediaPipelineTestMethods::shouldGetStats(uint64_t renderedFrames, uint64_t droppedFrames) { EXPECT_CALL(*m_mediaPipelineModuleMock, getStats(_, getStatsRequestMatcher(kSessionId, kVideoSourceId), _, _)) diff --git a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.h b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.h index 13425beaa..c6b9d7f74 100644 --- a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.h +++ b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.h @@ -130,8 +130,6 @@ class MediaPipelineTestMethods void shouldGetPosition(const int64_t position); void shouldSetImmediateOutput(bool immediateOutput); void shouldGetImmediateOutput(bool immediateOutput); - void shouldSetReportDecodeErrors(bool reportDecodeErrors); - void shouldGetQueuedFrames(uint32_t queuedFrames); void shouldGetStats(uint64_t renderedFrames, uint64_t droppedFrames); void shouldFlush(); void shouldFailToFlush(); @@ -250,8 +248,6 @@ class MediaPipelineTestMethods void getPosition(const int64_t expectedPosition); void setImmediateOutput(bool immediateOutput); void getImmediateOutput(bool immediateOutput); - void setReportDecodeErrors(bool reportDecodeErrors); - void getQueuedFrames(uint32_t queuedFrames); void getStats(uint64_t expectedFrames, uint64_t expectedDropped); void createMediaPipelineCapabilitiesObject(); void destroyMediaPipelineCapabilitiesObject(); diff --git a/tests/componenttests/client/tests/mse/PipelinePropertyTest.cpp b/tests/componenttests/client/tests/mse/PipelinePropertyTest.cpp index db623007c..a64aeadd7 100644 --- a/tests/componenttests/client/tests/mse/PipelinePropertyTest.cpp +++ b/tests/componenttests/client/tests/mse/PipelinePropertyTest.cpp @@ -174,15 +174,5 @@ TEST_F(PipelinePropertyTest, setAndGetPipelineProperties) // Step 12: Get UseBuffering MediaPipelineTestMethods::shouldGetUseBuffering(useBuffering); MediaPipelineTestMethods::getUseBuffering(useBuffering); - - // Step 13: Set Report Decode Errors - bool reportDecodeErrors{true}; - MediaPipelineTestMethods::shouldSetReportDecodeErrors(reportDecodeErrors); - MediaPipelineTestMethods::setReportDecodeErrors(reportDecodeErrors); - - // Step 14: Get Queued Frames - uint32_t queuedFrames{123}; - MediaPipelineTestMethods::shouldGetQueuedFrames(queuedFrames); - MediaPipelineTestMethods::getQueuedFrames(queuedFrames); } } // namespace firebolt::rialto::client::ct diff --git a/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp b/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp index a6410d9a5..2d11ee4f6 100644 --- a/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp @@ -159,16 +159,6 @@ class PipelinePropertyTest : public MediaPipelineTest *returnVal = value ? TRUE : FALSE; })); } - else if constexpr (std::is_same_v) - { - EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq(propertyName.c_str()), _)) - .WillOnce(Invoke( - [&](gpointer, const gchar *, void *val) - { - guint *returnVal = reinterpret_cast(val); - *returnVal = value; - })); - } else if constexpr (std::is_same_v) { EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq(propertyName.c_str()), _)) @@ -597,7 +587,7 @@ TEST_F(PipelinePropertyTest, pipelinePropertyGetAndSetSuccess) willStop(); stop(); - // Step 18: Destroy media session + // Step 19: Destroy media session gstPlayerWillBeDestructed(); destroySession(); } diff --git a/tests/unittests/media/client/ipc/CMakeLists.txt b/tests/unittests/media/client/ipc/CMakeLists.txt index 205b9e411..c4018151c 100644 --- a/tests/unittests/media/client/ipc/CMakeLists.txt +++ b/tests/unittests/media/client/ipc/CMakeLists.txt @@ -37,8 +37,6 @@ add_gtests ( mediaPipelineIpc/GetPositionTest.cpp mediaPipelineIpc/SetImmediateOutputTest.cpp mediaPipelineIpc/GetImmediateOutputTest.cpp - mediaPipelineIpc/SetReportDecodeErrorsTest.cpp - mediaPipelineIpc/GetQueuedFramesTest.cpp mediaPipelineIpc/GetStatsTest.cpp mediaPipelineIpc/RenderFrameTest.cpp mediaPipelineIpc/GetVolumeTest.cpp diff --git a/tests/unittests/media/client/ipc/mediaPipelineIpc/GetQueuedFramesTest.cpp b/tests/unittests/media/client/ipc/mediaPipelineIpc/GetQueuedFramesTest.cpp deleted file mode 100644 index 17bdd5c84..000000000 --- a/tests/unittests/media/client/ipc/mediaPipelineIpc/GetQueuedFramesTest.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2026 Sky UK - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "MediaPipelineIpcTestBase.h" -#include "MediaPipelineProtoRequestMatchers.h" - -class RialtoClientMediaPipelineIpcGetQueuedFramesTest : public MediaPipelineIpcTestBase -{ -protected: - virtual void SetUp() - { - MediaPipelineIpcTestBase::SetUp(); - - createMediaPipelineIpc(); - } - - virtual void TearDown() - { - destroyMediaPipelineIpc(); - - MediaPipelineIpcTestBase::TearDown(); - } - - const int32_t m_kSourceId{1}; -}; - -/** - * Test that getQueuedFrames can be called successfully. - */ -TEST_F(RialtoClientMediaPipelineIpcGetQueuedFramesTest, Success) -{ - expectIpcApiCallSuccess(); - - EXPECT_CALL(*m_channelMock, - CallMethod(methodMatcher("getQueuedFrames"), m_controllerMock.get(), - getQueuedFramesRequestMatcher(m_sessionId, m_kSourceId), _, m_blockingClosureMock.get())); - - uint32_t queuedFrames; - EXPECT_TRUE(m_mediaPipelineIpc->getQueuedFrames(m_kSourceId, queuedFrames)); -} - -/** - * Test that getQueuedFrames fails if the ipc channel disconnected. - */ -TEST_F(RialtoClientMediaPipelineIpcGetQueuedFramesTest, ChannelDisconnected) -{ - expectIpcApiCallDisconnected(); - expectUnsubscribeEvents(); - - uint32_t queuedFrames; - EXPECT_FALSE(m_mediaPipelineIpc->getQueuedFrames(m_kSourceId, queuedFrames)); - - // Reattach channel on destroySession - EXPECT_CALL(*m_ipcClientMock, getChannel()).WillOnce(Return(m_channelMock)).RetiresOnSaturation(); - expectSubscribeEvents(); -} - -/** - * Test that getQueuedFrames fails if the ipc channel disconnected and succeeds if the channel is reconnected. - */ -TEST_F(RialtoClientMediaPipelineIpcGetQueuedFramesTest, ReconnectChannel) -{ - expectIpcApiCallReconnected(); - expectUnsubscribeEvents(); - expectSubscribeEvents(); - - EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("getQueuedFrames"), _, _, _, _)); - - uint32_t queuedFrames; - EXPECT_TRUE(m_mediaPipelineIpc->getQueuedFrames(m_kSourceId, queuedFrames)); -} - -/** - * Test that getQueuedFrames fails when ipc fails. - */ -TEST_F(RialtoClientMediaPipelineIpcGetQueuedFramesTest, GetQueuedFramesFailure) -{ - expectIpcApiCallFailure(); - - EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("getQueuedFrames"), _, _, _, _)); - - uint32_t queuedFrames; - EXPECT_FALSE(m_mediaPipelineIpc->getQueuedFrames(m_kSourceId, queuedFrames)); -} diff --git a/tests/unittests/media/client/ipc/mediaPipelineIpc/SetReportDecodeErrorsTest.cpp b/tests/unittests/media/client/ipc/mediaPipelineIpc/SetReportDecodeErrorsTest.cpp deleted file mode 100644 index d7e5fb8d2..000000000 --- a/tests/unittests/media/client/ipc/mediaPipelineIpc/SetReportDecodeErrorsTest.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2025 Sky UK - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "MediaPipelineIpcTestBase.h" -#include "MediaPipelineProtoRequestMatchers.h" - -class RialtoClientMediaPipelineIpcSetReportDecodeErrorsTest : public MediaPipelineIpcTestBase -{ -protected: - virtual void SetUp() - { - MediaPipelineIpcTestBase::SetUp(); - - createMediaPipelineIpc(); - } - - virtual void TearDown() - { - destroyMediaPipelineIpc(); - - MediaPipelineIpcTestBase::TearDown(); - } - - const int32_t m_kSourceId{1}; -}; - -/** - * Test that setReportDecodeErrors can be called successfully. - */ -TEST_F(RialtoClientMediaPipelineIpcSetReportDecodeErrorsTest, Success) -{ - expectIpcApiCallSuccess(); - - EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("setReportDecodeErrors"), m_controllerMock.get(), - setReportDecodeErrorsRequestMatcher(m_sessionId, m_kSourceId), _, - m_blockingClosureMock.get())); - - EXPECT_TRUE(m_mediaPipelineIpc->setReportDecodeErrors(m_kSourceId, true)); -} - -/** - * Test that setReportDecodeErrors fails if the ipc channel disconnected. - */ -TEST_F(RialtoClientMediaPipelineIpcSetReportDecodeErrorsTest, ChannelDisconnected) -{ - expectIpcApiCallDisconnected(); - expectUnsubscribeEvents(); - - EXPECT_FALSE(m_mediaPipelineIpc->setReportDecodeErrors(m_kSourceId, true)); - - // Reattach channel on destroySession - EXPECT_CALL(*m_ipcClientMock, getChannel()).WillOnce(Return(m_channelMock)).RetiresOnSaturation(); - expectSubscribeEvents(); -} - -/** - * Test that setReportDecodeErrors fails if the ipc channel disconnected and succeeds if the channel is reconnected. - */ -TEST_F(RialtoClientMediaPipelineIpcSetReportDecodeErrorsTest, ReconnectChannel) -{ - expectIpcApiCallReconnected(); - expectUnsubscribeEvents(); - expectSubscribeEvents(); - - EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("setReportDecodeErrors"), _, _, _, _)); - - EXPECT_TRUE(m_mediaPipelineIpc->setReportDecodeErrors(m_kSourceId, true)); -} - -/** - * Test that setReportDecodeErrors fails when ipc fails. - */ -TEST_F(RialtoClientMediaPipelineIpcSetReportDecodeErrorsTest, SetReportDecodeErrorsFailure) -{ - expectIpcApiCallFailure(); - - EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("setReportDecodeErrors"), _, _, _, _)); - - EXPECT_FALSE(m_mediaPipelineIpc->setReportDecodeErrors(m_kSourceId, true)); -} diff --git a/tests/unittests/media/client/main/CMakeLists.txt b/tests/unittests/media/client/main/CMakeLists.txt index 4387fe598..17a5b0d95 100644 --- a/tests/unittests/media/client/main/CMakeLists.txt +++ b/tests/unittests/media/client/main/CMakeLists.txt @@ -34,8 +34,6 @@ add_gtests ( mediaPipeline/GetPositionTest.cpp mediaPipeline/SetImmediateOutputTest.cpp mediaPipeline/GetImmediateOutputTest.cpp - mediaPipeline/SetReportDecodeErrorsTest.cpp - mediaPipeline/GetQueuedFramesTest.cpp mediaPipeline/GetStatsTest.cpp mediaPipeline/RenderFrameTest.cpp mediaPipeline/SetVolumeTest.cpp diff --git a/tests/unittests/media/client/main/mediaPipeline/GetQueuedFramesTest.cpp b/tests/unittests/media/client/main/mediaPipeline/GetQueuedFramesTest.cpp deleted file mode 100644 index 277b46872..000000000 --- a/tests/unittests/media/client/main/mediaPipeline/GetQueuedFramesTest.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2025 Sky UK - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "MediaPipelineTestBase.h" - -class RialtoClientMediaPipelineGetQueuedFramesTest : public MediaPipelineTestBase -{ -protected: - const int32_t m_kSourceId{1}; - - virtual void SetUp() - { - MediaPipelineTestBase::SetUp(); - - createMediaPipeline(); - } - - virtual void TearDown() - { - destroyMediaPipeline(); - - MediaPipelineTestBase::TearDown(); - } -}; - -/** - * Test that getQueuedFrames returns success if the IPC API succeeds. - */ -TEST_F(RialtoClientMediaPipelineGetQueuedFramesTest, GetQueuedFramesSuccess) -{ - constexpr uint32_t kExpectedQueuedFrames{123}; - EXPECT_CALL(*m_mediaPipelineIpcMock, getQueuedFrames(m_kSourceId, _)) - .WillOnce(DoAll(SetArgReferee<1>(123), Return(true))); - uint32_t queuedFrames; - EXPECT_TRUE(m_mediaPipeline->getQueuedFrames(m_kSourceId, queuedFrames)); - EXPECT_EQ(kExpectedQueuedFrames, queuedFrames); -} - -/** - * Test that getQueuedFrames returns failure if the IPC API fails. - */ -TEST_F(RialtoClientMediaPipelineGetQueuedFramesTest, GetQueuedFramesFailure) -{ - EXPECT_CALL(*m_mediaPipelineIpcMock, getQueuedFrames(m_kSourceId, _)).WillOnce(Return(false)); - uint32_t queuedFrames; - EXPECT_FALSE(m_mediaPipeline->getQueuedFrames(m_kSourceId, queuedFrames)); -} diff --git a/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp b/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp index 7d9b7a2c7..6e22bdf4d 100644 --- a/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp +++ b/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp @@ -163,21 +163,6 @@ TEST_F(RialtoClientMediaPipelineProxyTest, TestPassthrough) ///////////////////////////////////////////// - EXPECT_CALL(*mediaPipelineMock, setReportDecodeErrors(kSourceId, true)).WillOnce(Return(true)); - EXPECT_TRUE(proxy->setReportDecodeErrors(kSourceId, true)); - - ///////////////////////////////////////////// - - { - const uint32_t kQueuedFrames{123}; - uint32_t returnQueuedFrames; - EXPECT_CALL(*mediaPipelineMock, getQueuedFrames(kSourceId, _)) - .WillOnce(DoAll(SetArgReferee<1>(kQueuedFrames), Return(true))); - EXPECT_TRUE(proxy->getQueuedFrames(kSourceId, returnQueuedFrames)); - EXPECT_EQ(returnQueuedFrames, kQueuedFrames); - } - ///////////////////////////////////////////// - EXPECT_CALL(*mediaPipelineMock, setVideoWindow(1, 2, 3, 4)).WillOnce(Return(true)); EXPECT_TRUE(proxy->setVideoWindow(1, 2, 3, 4)); diff --git a/tests/unittests/media/client/main/mediaPipeline/SetReportDecodeErrorsTest.cpp b/tests/unittests/media/client/main/mediaPipeline/SetReportDecodeErrorsTest.cpp deleted file mode 100644 index 29f10533d..000000000 --- a/tests/unittests/media/client/main/mediaPipeline/SetReportDecodeErrorsTest.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2025 Sky UK - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "MediaPipelineTestBase.h" - -class RialtoClientMediaPipelineSetReportDecodeErrorsTest : public MediaPipelineTestBase -{ -protected: - const int32_t m_kSourceId{1}; - - virtual void SetUp() - { - MediaPipelineTestBase::SetUp(); - - createMediaPipeline(); - } - - virtual void TearDown() - { - destroyMediaPipeline(); - - MediaPipelineTestBase::TearDown(); - } -}; - -/** - * Test that setReportDecodeErrors returns success if the IPC API succeeds. - */ -TEST_F(RialtoClientMediaPipelineSetReportDecodeErrorsTest, SetReportDecodeErrorsSuccess) -{ - EXPECT_CALL(*m_mediaPipelineIpcMock, setReportDecodeErrors(m_kSourceId, _)).WillOnce(Return(true)); - EXPECT_TRUE(m_mediaPipeline->setReportDecodeErrors(m_kSourceId, true)); -} - -/** - * Test that setReportDecodeErrors returns failure if the IPC API fails. - */ -TEST_F(RialtoClientMediaPipelineSetReportDecodeErrorsTest, SetReportDecodeErrorsFailure) -{ - EXPECT_CALL(*m_mediaPipelineIpcMock, setReportDecodeErrors(m_kSourceId, _)).WillOnce(Return(false)); - EXPECT_FALSE(m_mediaPipeline->setReportDecodeErrors(m_kSourceId, true)); -} diff --git a/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h b/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h index f08f54fef..32e72ba2c 100644 --- a/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h +++ b/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h @@ -47,8 +47,6 @@ class MediaPipelineIpcMock : public IMediaPipelineIpc MOCK_METHOD(bool, getPosition, (int64_t & position), (override)); MOCK_METHOD(bool, setImmediateOutput, (int32_t sourceId, bool immediateOutput), (override)); MOCK_METHOD(bool, getImmediateOutput, (int32_t sourceId, bool &immediateOutput), (override)); - MOCK_METHOD(bool, setReportDecodeErrors, (int32_t sourceId, bool reportDecodeErrors), (override)); - MOCK_METHOD(bool, getQueuedFrames, (int32_t sourceId, uint32_t &queuedFrames), (override)); MOCK_METHOD(bool, getStats, (int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames), (override)); MOCK_METHOD(bool, setPlaybackRate, (double rate), (override)); MOCK_METHOD(bool, renderFrame, (), (override)); diff --git a/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h b/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h index 1be860018..c69140210 100644 --- a/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h +++ b/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h @@ -50,8 +50,6 @@ class MediaPipelineAndControlClientMock : public IMediaPipelineAndIControlClient MOCK_METHOD(bool, getPosition, (int64_t & position), (override)); MOCK_METHOD(bool, setImmediateOutput, (int32_t sourceId, bool immediateOutput), (override)); MOCK_METHOD(bool, getImmediateOutput, (int32_t sourceId, bool &immediateOutput), (override)); - MOCK_METHOD(bool, setReportDecodeErrors, (int32_t sourceId, bool reportDecodeErrors), (override)); - MOCK_METHOD(bool, getQueuedFrames, (int32_t sourceId, uint32_t &queuedFrames), (override)); MOCK_METHOD(bool, getStats, (int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames), (override)); MOCK_METHOD(bool, setVideoWindow, (uint32_t x, uint32_t y, uint32_t width, uint32_t height), (override)); diff --git a/tests/unittests/media/server/gstplayer/CMakeLists.txt b/tests/unittests/media/server/gstplayer/CMakeLists.txt index 00a647f56..ed6635e3b 100644 --- a/tests/unittests/media/server/gstplayer/CMakeLists.txt +++ b/tests/unittests/media/server/gstplayer/CMakeLists.txt @@ -49,7 +49,6 @@ add_gtests(RialtoServerGstPlayerUnitTests genericPlayer/tasksTests/SetBufferingLimitTest.cpp genericPlayer/tasksTests/SetLowLatencyTest.cpp genericPlayer/tasksTests/SetImmediateOutputTest.cpp - genericPlayer/tasksTests/SetReportDecodeErrorsTest.cpp genericPlayer/tasksTests/SetMuteTest.cpp genericPlayer/tasksTests/SetPlaybackRateTest.cpp genericPlayer/tasksTests/SetPositionTest.cpp diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp index 255ad8878..5f079f930 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp @@ -72,7 +72,6 @@ const std::string kAutoVideoSinkTypeName{"GstAutoVideoSink"}; const std::string kAutoAudioSinkTypeName{"GstAutoAudioSink"}; constexpr bool kResetTime{true}; const std::string kImmediateOutputStr{"immediate-output"}; -const std::string kReportDecodeErrorsStr{"report-decode-errors"}; const std::string kLowLatencyStr{"low-latency"}; const std::string kSyncStr{"sync"}; const std::string kSyncOffStr{"sync-off"}; @@ -356,35 +355,6 @@ TEST_F(GstGenericPlayerPrivateTest, shouldSetImmediateOutput) EXPECT_TRUE(m_sut->setImmediateOutput()); } -TEST_F(GstGenericPlayerPrivateTest, shouldFailToSetReportDecodeErrorsIfPropertyDoesntExist) -{ - modifyContext([&](GenericPlayerContext &context) { context.pendingReportDecodeErrorsForVideo = true; }); - - expectGetVideoDecoder(m_realElement); - - expectPropertyDoesntExist(m_glibWrapperMock, m_gstWrapperMock, m_realElement, kReportDecodeErrorsStr); - EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_realElement)).Times(1); - EXPECT_FALSE(m_sut->setReportDecodeErrors()); -} - -TEST_F(GstGenericPlayerPrivateTest, shouldSetReportDecodeErrors) -{ - modifyContext([&](GenericPlayerContext &context) { context.pendingReportDecodeErrorsForVideo = true; }); - - expectGetVideoDecoder(m_realElement); - - GParamSpec gParamSpec{}; - - EXPECT_CALL(*m_glibWrapperMock, - gObjectClassFindProperty(G_OBJECT_GET_CLASS(m_realElement), StrEq(kReportDecodeErrorsStr))) - .WillOnce(Return(&gParamSpec)); - - EXPECT_CALL(*m_glibWrapperMock, gObjectSetStub(m_realElement, StrEq(kReportDecodeErrorsStr))).Times(1); - - EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_realElement)).Times(1); - EXPECT_TRUE(m_sut->setReportDecodeErrors()); -} - TEST_F(GstGenericPlayerPrivateTest, shouldFailToSetLowLatencyIfSinkIsNull) { modifyContext([&](GenericPlayerContext &context) { context.pendingLowLatency = true; }); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp index 108b0e423..138fbcecf 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp @@ -452,43 +452,6 @@ TEST_F(GstGenericPlayerTest, shouldFailToGetImmediateOutputInPlayingStateIfPrope EXPECT_FALSE(m_sut->getImmediateOutput(MediaSourceType::VIDEO, immediateOutputState)); } -TEST_F(GstGenericPlayerTest, shouldSetReportDecodeErrors) -{ - std::unique_ptr task{std::make_unique>()}; - EXPECT_CALL(dynamic_cast &>(*task), execute()); - EXPECT_CALL(m_taskFactoryMock, createSetReportDecodeErrors(_, _, MediaSourceType::VIDEO, true)) - .WillOnce(Return(ByMove(std::move(task)))); - - EXPECT_TRUE(m_sut->setReportDecodeErrors(MediaSourceType::VIDEO, true)); -} - -TEST_F(GstGenericPlayerTest, shouldGetQueuedFramesInPlayingState) -{ - setPipelineState(GST_STATE_PLAYING); - const uint32_t kTestQueuedFramesValue{123}; - const std::string kPropertyStr{"queued-frames"}; - - expectGetVideoDecoder(m_element); - willGetElementProperty(kPropertyStr, kTestQueuedFramesValue); - - uint32_t queuedFrames; - EXPECT_TRUE(m_sut->getQueuedFrames(queuedFrames)); - EXPECT_EQ(queuedFrames, kTestQueuedFramesValue); -} - -TEST_F(GstGenericPlayerTest, shouldFailToGetQueuedFramesInPlayingStateIfPropertyDoesntExist) -{ - setPipelineState(GST_STATE_PLAYING); - - expectGetVideoDecoder(m_element); - - EXPECT_CALL(*m_glibWrapperMock, gObjectClassFindProperty(_, StrEq("queued-frames"))).WillOnce(Return(nullptr)); - EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_element)).Times(1); - - uint32_t queuedFrames; - EXPECT_FALSE(m_sut->getQueuedFrames(queuedFrames)); -} - TEST_F(GstGenericPlayerTest, shouldGetStatsInPlayingState) { constexpr guint64 kRenderedFrames{1234}; diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp index 6d9c80666..f379b5884 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp @@ -46,7 +46,6 @@ #include "tasks/generic/SetMute.h" #include "tasks/generic/SetPlaybackRate.h" #include "tasks/generic/SetPosition.h" -#include "tasks/generic/SetReportDecodeErrors.h" #include "tasks/generic/SetSourcePosition.h" #include "tasks/generic/SetStreamSyncMode.h" #include "tasks/generic/SetSubtitleOffset.h" @@ -3295,20 +3294,6 @@ void GenericTasksTestsBase::triggerSetImmediateOutput() EXPECT_EQ(testContext->m_context.pendingImmediateOutputForVideo, true); } -void GenericTasksTestsBase::shouldSetReportDecodeErrors() -{ - EXPECT_CALL(testContext->m_gstPlayer, setReportDecodeErrors()).WillOnce(Return(true)); -} - -void GenericTasksTestsBase::triggerSetReportDecodeErrors() -{ - firebolt::rialto::server::tasks::generic::SetReportDecodeErrors task{testContext->m_context, testContext->m_gstPlayer, - MediaSourceType::VIDEO, true}; - task.execute(); - - EXPECT_EQ(testContext->m_context.pendingReportDecodeErrorsForVideo, true); -} - void GenericTasksTestsBase::shouldSetLowLatency() { EXPECT_CALL(testContext->m_gstPlayer, setLowLatency()).WillOnce(Return(true)); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h index e0b722fa4..29f64e16a 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h @@ -277,10 +277,6 @@ class GenericTasksTestsBase : public ::testing::Test void shouldSetVideoMute(); void shouldSetSubtitleMute(); - // report-decode-errors decoder property test method - void shouldSetReportDecodeErrors(); - void triggerSetReportDecodeErrors(); - // immediate-output sink property test methods void shouldSetImmediateOutput(); void triggerSetImmediateOutput(); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp index c4d5d2741..ef78956a1 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp @@ -217,20 +217,6 @@ void GstGenericPlayerTestCommon::expectGetDecoder(GstElement *element) EXPECT_CALL(*m_gstWrapperMock, gstIteratorFree(&m_it)); } -void GstGenericPlayerTestCommon::expectGetVideoDecoder(GstElement *element) -{ - EXPECT_CALL(*m_gstWrapperMock, gstBinIterateRecurse(GST_BIN(&m_pipeline))).WillOnce(Return(&m_it)); - EXPECT_CALL(*m_gstWrapperMock, gstIteratorNext(&m_it, _)).WillOnce(Return(GST_ITERATOR_OK)); - EXPECT_CALL(*m_glibWrapperMock, gValueGetObject(_)).WillOnce(Return(element)); - EXPECT_CALL(*m_gstWrapperMock, gstElementGetFactory(element)).WillOnce(Return(m_factory)); - EXPECT_CALL(*m_gstWrapperMock, gstElementFactoryListIsType(m_factory, (GST_ELEMENT_FACTORY_TYPE_DECODER | - GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO))) - .WillOnce(Return(TRUE)); - EXPECT_CALL(*m_gstWrapperMock, gstObjectRef(element)).WillOnce(Return(element)); - EXPECT_CALL(*m_glibWrapperMock, gValueUnset(_)); - EXPECT_CALL(*m_gstWrapperMock, gstIteratorFree(&m_it)); -} - void GstGenericPlayerTestCommon::expectGetVideoParser(GstElement *element) { EXPECT_CALL(*m_gstWrapperMock, gstBinIterateRecurse(GST_BIN(&m_pipeline))).WillOnce(Return(&m_it)); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h index d8e72efaf..aaae1fc5c 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h @@ -119,7 +119,6 @@ class GstGenericPlayerTestCommon : public ::testing::Test void expectCheckPlaySink(); void expectSetMessageCallback(); void expectGetDecoder(GstElement *element); - void expectGetVideoDecoder(GstElement *element); void expectGetVideoParser(GstElement *element); void expectGetAVSink(const std::string &sinkName, GstElement *elementObj); void expectGetSink(const std::string &sinkName, GstElement *elementObj); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp index d25627cda..70c305e22 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp @@ -53,7 +53,6 @@ #include "tasks/generic/SetMute.h" #include "tasks/generic/SetPlaybackRate.h" #include "tasks/generic/SetPosition.h" -#include "tasks/generic/SetReportDecodeErrors.h" #include "tasks/generic/SetSourcePosition.h" #include "tasks/generic/SetStreamSyncMode.h" #include "tasks/generic/SetSync.h" @@ -345,13 +344,6 @@ TEST_F(GenericPlayerTaskFactoryTest, ShouldCreateSetImmediateOutput) EXPECT_NO_THROW(dynamic_cast(*task)); } -TEST_F(GenericPlayerTaskFactoryTest, ShouldCreateSetReportDecodeErrors) -{ - auto task = m_sut.createSetReportDecodeErrors(m_context, m_gstPlayer, firebolt::rialto::MediaSourceType::VIDEO, true); - EXPECT_NE(task, nullptr); - EXPECT_NO_THROW(dynamic_cast(*task)); -} - TEST_F(GenericPlayerTaskFactoryTest, ShouldCreateSetTextTrackIdentifier) { auto task = m_sut.createSetTextTrackIdentifier(m_context, ""); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/SetReportDecodeErrorsTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/SetReportDecodeErrorsTest.cpp deleted file mode 100644 index 3d3f89f96..000000000 --- a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/SetReportDecodeErrorsTest.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2025 Sky UK - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "GenericTasksTestsBase.h" - -class SetReportDecodeErrorsTest : public GenericTasksTestsBase -{ -}; - -TEST_F(SetReportDecodeErrorsTest, shouldSetReportDecodeErrors) -{ - shouldSetReportDecodeErrors(); - triggerSetReportDecodeErrors(); -} diff --git a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTests.cpp b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTests.cpp index 593a01b1e..dcad505bb 100644 --- a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTests.cpp +++ b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTests.cpp @@ -281,38 +281,6 @@ TEST_F(MediaPipelineModuleServiceTests, shouldFailToGetImmediateOutput) sendGetImmediateOutputRequestAndReceiveFail(); } -TEST_F(MediaPipelineModuleServiceTests, shouldSetReportDecodeErrors) -{ - mediaPipelineServiceWillCreateSession(); - sendCreateSessionRequestAndReceiveResponse(); - mediaPipelineServiceWillSetReportDecodeErrors(); - sendSetReportDecodeErrorsRequestAndReceiveResponse(); -} - -TEST_F(MediaPipelineModuleServiceTests, shouldFailToSetReportDecodeErrors) -{ - mediaPipelineServiceWillCreateSession(); - sendCreateSessionRequestAndReceiveResponse(); - mediaPipelineServiceWillFailToSetReportDecodeErrors(); - sendSetReportDecodeErrorsRequestAndReceiveFail(); -} - -TEST_F(MediaPipelineModuleServiceTests, shouldGetQueuedFrames) -{ - mediaPipelineServiceWillCreateSession(); - sendCreateSessionRequestAndReceiveResponse(); - mediaPipelineServiceWillGetQueuedFrames(); - sendGetQueuedFramesRequestAndReceiveResponse(); -} - -TEST_F(MediaPipelineModuleServiceTests, shouldFailToGetQueuedFrames) -{ - mediaPipelineServiceWillCreateSession(); - sendCreateSessionRequestAndReceiveResponse(); - mediaPipelineServiceWillFailToGetQueuedFrames(); - sendGetQueuedFramesRequestAndReceiveFail(); -} - TEST_F(MediaPipelineModuleServiceTests, shouldGetStats) { mediaPipelineServiceWillCreateSession(); diff --git a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp index 5aa608e2e..31ec98768 100644 --- a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp +++ b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp @@ -86,8 +86,6 @@ constexpr uint64_t kDroppedFrames{321}; constexpr uint32_t kDuration{30}; constexpr bool kImmediateOutputVal1{false}; constexpr bool kImmediateOutputVal2{true}; -constexpr bool kReportDecodeErrorsVal{false}; -constexpr uint32_t kQueuedFramesVal{123}; constexpr int64_t kDiscontinuityGap{1}; constexpr bool kIsAudioAac{false}; constexpr uint32_t kBufferingLimit{12341}; @@ -520,33 +518,6 @@ void MediaPipelineModuleServiceTests::mediaPipelineServiceWillFailToGetImmediate EXPECT_CALL(m_mediaPipelineServiceMock, getImmediateOutput(kHardcodedSessionId, _, _)).WillOnce(Return(false)); } -void MediaPipelineModuleServiceTests::mediaPipelineServiceWillSetReportDecodeErrors() -{ - expectRequestSuccess(); - EXPECT_CALL(m_mediaPipelineServiceMock, setReportDecodeErrors(kHardcodedSessionId, _, kReportDecodeErrorsVal)) - .WillOnce(Return(true)); -} - -void MediaPipelineModuleServiceTests::mediaPipelineServiceWillFailToSetReportDecodeErrors() -{ - expectRequestFailure(); - EXPECT_CALL(m_mediaPipelineServiceMock, setReportDecodeErrors(kHardcodedSessionId, _, kReportDecodeErrorsVal)) - .WillOnce(Return(false)); -} - -void MediaPipelineModuleServiceTests::mediaPipelineServiceWillGetQueuedFrames() -{ - expectRequestSuccess(); - EXPECT_CALL(m_mediaPipelineServiceMock, getQueuedFrames(kHardcodedSessionId, _, _)) - .WillOnce(DoAll(SetArgReferee<2>(kQueuedFramesVal), Return(true))); -} - -void MediaPipelineModuleServiceTests::mediaPipelineServiceWillFailToGetQueuedFrames() -{ - expectRequestFailure(); - EXPECT_CALL(m_mediaPipelineServiceMock, getQueuedFrames(kHardcodedSessionId, _, _)).WillOnce(Return(false)); -} - void MediaPipelineModuleServiceTests::mediaPipelineServiceWillGetStats() { expectRequestSuccess(); @@ -1177,50 +1148,6 @@ void MediaPipelineModuleServiceTests::sendGetImmediateOutputRequestAndReceiveFai m_service->getImmediateOutput(m_controllerMock.get(), &request, &response, m_closureMock.get()); } -void MediaPipelineModuleServiceTests::sendSetReportDecodeErrorsRequestAndReceiveResponse() -{ - firebolt::rialto::SetReportDecodeErrorsRequest request; - firebolt::rialto::SetReportDecodeErrorsResponse response; - - request.set_session_id(kHardcodedSessionId); - request.set_report_decode_errors(kReportDecodeErrorsVal); - - m_service->setReportDecodeErrors(m_controllerMock.get(), &request, &response, m_closureMock.get()); -} - -void MediaPipelineModuleServiceTests::sendSetReportDecodeErrorsRequestAndReceiveFail() -{ - firebolt::rialto::SetReportDecodeErrorsRequest request; - firebolt::rialto::SetReportDecodeErrorsResponse response; - - request.set_session_id(kHardcodedSessionId); - request.set_report_decode_errors(kReportDecodeErrorsVal); - - m_service->setReportDecodeErrors(m_controllerMock.get(), &request, &response, m_closureMock.get()); -} - -void MediaPipelineModuleServiceTests::sendGetQueuedFramesRequestAndReceiveResponse() -{ - firebolt::rialto::GetQueuedFramesRequest request; - firebolt::rialto::GetQueuedFramesResponse response; - - request.set_session_id(kHardcodedSessionId); - - m_service->getQueuedFrames(m_controllerMock.get(), &request, &response, m_closureMock.get()); - - EXPECT_EQ(response.queued_frames(), kQueuedFramesVal); -} - -void MediaPipelineModuleServiceTests::sendGetQueuedFramesRequestAndReceiveFail() -{ - firebolt::rialto::GetQueuedFramesRequest request; - firebolt::rialto::GetQueuedFramesResponse response; - - request.set_session_id(kHardcodedSessionId); - - m_service->getQueuedFrames(m_controllerMock.get(), &request, &response, m_closureMock.get()); -} - void MediaPipelineModuleServiceTests::sendGetStatsRequestAndReceiveResponse() { firebolt::rialto::GetStatsRequest request; diff --git a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.h b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.h index b987dd88e..90f02fb4a 100644 --- a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.h +++ b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.h @@ -78,10 +78,6 @@ class MediaPipelineModuleServiceTests : public testing::Test void mediaPipelineServiceWillFailToSetImmediateOutput(); void mediaPipelineServiceWillGetImmediateOutput(); void mediaPipelineServiceWillFailToGetImmediateOutput(); - void mediaPipelineServiceWillSetReportDecodeErrors(); - void mediaPipelineServiceWillFailToSetReportDecodeErrors(); - void mediaPipelineServiceWillGetQueuedFrames(); - void mediaPipelineServiceWillFailToGetQueuedFrames(); void mediaPipelineServiceWillGetStats(); void mediaPipelineServiceWillFailToGetStats(); void mediaPipelineServiceWillRenderFrame(); @@ -158,10 +154,6 @@ class MediaPipelineModuleServiceTests : public testing::Test void sendSetImmediateOutputRequestAndReceiveFail(); void sendGetImmediateOutputRequestAndReceiveResponse(); void sendGetImmediateOutputRequestAndReceiveFail(); - void sendSetReportDecodeErrorsRequestAndReceiveResponse(); - void sendSetReportDecodeErrorsRequestAndReceiveFail(); - void sendGetQueuedFramesRequestAndReceiveResponse(); - void sendGetQueuedFramesRequestAndReceiveFail(); void sendGetStatsRequestAndReceiveResponse(); void sendGetStatsRequestAndReceiveResponseWithoutStatsMatch(); void sendHaveDataRequestAndReceiveResponse(); diff --git a/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp b/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp index b1ca63c5b..aa4f3e2f3 100644 --- a/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp +++ b/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp @@ -327,112 +327,6 @@ TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetImmediateOutputSu EXPECT_EQ(immediateOutputState, false); } -/** - * Test that SetReportDecodeErrors returns failure if the gstreamer player is not initialized - */ -TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, SetReportDecodeErrorsFailureDueToUninitializedPlayer) -{ - mainThreadWillEnqueueTaskAndWait(); - EXPECT_FALSE(m_mediaPipeline->setReportDecodeErrors(m_kDummySourceId, true)); -} - -/** - * Test that SetReportDecodeErrors returns failure if the gstreamer API fails - */ -TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, SetReportDecodeErrorsFailure) -{ - loadGstPlayer(); - int videoSourceId = attachSource(firebolt::rialto::MediaSourceType::VIDEO, "video/h264"); - mainThreadWillEnqueueTaskAndWait(); - EXPECT_CALL(*m_gstPlayerMock, setReportDecodeErrors(_, _)).WillOnce(Return(false)); - EXPECT_FALSE(m_mediaPipeline->setReportDecodeErrors(videoSourceId, true)); -} - -/** - * Test that SetReportDecodeErrors fails if source is not present. - */ -TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, SetReportDecodeErrorsNoSourcePresent) -{ - loadGstPlayer(); - // No attachment of source - mainThreadWillEnqueueTaskAndWait(); - - EXPECT_FALSE(m_mediaPipeline->setReportDecodeErrors(m_kDummySourceId, true)); -} - -/** - * Test that SetReportDecodeErrors returns success if the gstreamer API succeeds - */ -TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, SetReportDecodeErrorsSuccess) -{ - loadGstPlayer(); - int videoSourceId = attachSource(firebolt::rialto::MediaSourceType::VIDEO, "video/h264"); - - mainThreadWillEnqueueTaskAndWait(); - EXPECT_CALL(*m_gstPlayerMock, setReportDecodeErrors(_, true)).WillOnce(Return(true)); - EXPECT_TRUE(m_mediaPipeline->setReportDecodeErrors(videoSourceId, true)); - - mainThreadWillEnqueueTaskAndWait(); - EXPECT_CALL(*m_gstPlayerMock, setReportDecodeErrors(_, false)).WillOnce(Return(true)); - EXPECT_TRUE(m_mediaPipeline->setReportDecodeErrors(videoSourceId, false)); -} - -/** - * Test that GetQueuedFrames returns failure if the gstreamer player is not initialized - */ -TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetQueuedFramesFailureDueToUninitializedPlayer) -{ - mainThreadWillEnqueueTaskAndWait(); - uint32_t queuedFrames; - EXPECT_FALSE(m_mediaPipeline->getQueuedFrames(m_kDummySourceId, queuedFrames)); -} - -/** - * Test that GetQueuedFrames returns failure if the gstreamer API fails - */ -TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetQueuedFramesFailure) -{ - loadGstPlayer(); - int videoSourceId = attachSource(firebolt::rialto::MediaSourceType::VIDEO, "video/h264"); - mainThreadWillEnqueueTaskAndWait(); - EXPECT_CALL(*m_gstPlayerMock, getQueuedFrames(_)).WillOnce(Return(false)); - uint32_t queuedFrames; - EXPECT_FALSE(m_mediaPipeline->getQueuedFrames(videoSourceId, queuedFrames)); -} - -/** - * Test that GetQueuedFrames fails if source is not present. - */ -TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetQueuedFramesNoSourcePresent) -{ - loadGstPlayer(); - // No attachment of source - mainThreadWillEnqueueTaskAndWait(); - - uint32_t queuedFrames; - EXPECT_FALSE(m_mediaPipeline->getQueuedFrames(m_kDummySourceId, queuedFrames)); -} - -/** - * Test that GetQueuedFrames returns success if the gstreamer API succeeds - */ -TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetQueuedFramesSuccess) -{ - loadGstPlayer(); - int videoSourceId = attachSource(firebolt::rialto::MediaSourceType::VIDEO, "video/h264"); - - uint32_t queuedFrames; - mainThreadWillEnqueueTaskAndWait(); - EXPECT_CALL(*m_gstPlayerMock, getQueuedFrames(_)).WillOnce(DoAll(SetArgReferee<0>(123), Return(true))); - EXPECT_TRUE(m_mediaPipeline->getQueuedFrames(videoSourceId, queuedFrames)); - EXPECT_EQ(queuedFrames, 123); - - mainThreadWillEnqueueTaskAndWait(); - EXPECT_CALL(*m_gstPlayerMock, getQueuedFrames(_)).WillOnce(DoAll(SetArgReferee<0>(456), Return(true))); - EXPECT_TRUE(m_mediaPipeline->getQueuedFrames(videoSourceId, queuedFrames)); - EXPECT_EQ(queuedFrames, 456); -} - /** * Test that GetStats returns failure if the gstreamer player is not initialized */ diff --git a/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h b/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h index b3fca43e0..2cb7f0a35 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h @@ -133,10 +133,6 @@ class GenericPlayerTaskFactoryMock : public IGenericPlayerTaskFactory (GenericPlayerContext & context, IGstGenericPlayerPrivate &player, const firebolt::rialto::MediaSourceType &type, bool immediateOutput), (const, override)); - MOCK_METHOD(std::unique_ptr, createSetReportDecodeErrors, - (GenericPlayerContext & context, IGstGenericPlayerPrivate &player, - const firebolt::rialto::MediaSourceType &type, bool reportDecodeErrors), - (const, override)); MOCK_METHOD(std::unique_ptr, createSetBufferingLimit, (GenericPlayerContext & context, IGstGenericPlayerPrivate &player, std::uint32_t limit), (const, override)); diff --git a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h index 90ab4ebf7..c38c1d9eb 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h @@ -44,9 +44,6 @@ class GstGenericPlayerMock : public IGstGenericPlayer MOCK_METHOD(bool, getPosition, (std::int64_t & position), (override)); MOCK_METHOD(bool, setImmediateOutput, (const MediaSourceType &mediaSourceType, bool immediateOutput), (override)); MOCK_METHOD(bool, getImmediateOutput, (const MediaSourceType &mediaSourceType, bool &immediateOutput), (override)); - MOCK_METHOD(bool, setReportDecodeErrors, (const MediaSourceType &mediaSourceType, bool reportDecodeErrors), - (override)); - MOCK_METHOD(bool, getQueuedFrames, (uint32_t & queuedFrames), (override)); MOCK_METHOD(bool, getStats, (const MediaSourceType &mediaSourceType, uint64_t &renderedFrames, uint64_t &droppedFrames), (override)); MOCK_METHOD(void, setVideoGeometry, (int x, int y, int width, int height), (override)); diff --git a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h index d0b018a87..e28c70d8d 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h @@ -39,7 +39,6 @@ class GstGenericPlayerPrivateMock : public IGstGenericPlayerPrivate MOCK_METHOD(void, scheduleAllSourcesAttached, (), (override)); MOCK_METHOD(bool, setVideoSinkRectangle, (), (override)); MOCK_METHOD(bool, setImmediateOutput, (), (override)); - MOCK_METHOD(bool, setReportDecodeErrors, (), (override)); MOCK_METHOD(bool, setLowLatency, (), (override)); MOCK_METHOD(bool, setSync, (), (override)); MOCK_METHOD(bool, setSyncOff, (), (override)); diff --git a/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h b/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h index 82f56556f..27a12a686 100644 --- a/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h +++ b/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h @@ -44,8 +44,6 @@ class MediaPipelineServerInternalMock : public IMediaPipelineServerInternal MOCK_METHOD(bool, getPosition, (std::int64_t & position), (override)); MOCK_METHOD(bool, setImmediateOutput, (int32_t sourceId, bool immediateOutput), (override)); MOCK_METHOD(bool, getImmediateOutput, (int32_t sourceId, bool &immediateOutput), (override)); - MOCK_METHOD(bool, setReportDecodeErrors, (int32_t sourceId, bool reportDecodeErrors), (override)); - MOCK_METHOD(bool, getQueuedFrames, (int32_t sourceId, uint32_t &queuedFrames), (override)); MOCK_METHOD(bool, getStats, (int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames), (override)); MOCK_METHOD(bool, setVideoWindow, (uint32_t x, uint32_t y, uint32_t width, uint32_t height), (override)); MOCK_METHOD(bool, haveData, (MediaSourceStatus status, uint32_t numFrames, uint32_t needDataRequestId), (override)); diff --git a/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h b/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h index 46d46ee4f..f7b226777 100644 --- a/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h +++ b/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h @@ -46,8 +46,6 @@ class MediaPipelineServiceMock : public IMediaPipelineService MOCK_METHOD(bool, getPosition, (int sessionId, int64_t &position), (override)); MOCK_METHOD(bool, setImmediateOutput, (int sessionId, int32_t sourceId, bool immediateOutput), (override)); MOCK_METHOD(bool, getImmediateOutput, (int sessionId, int32_t sourceId, bool &immediateOutput), (override)); - MOCK_METHOD(bool, setReportDecodeErrors, (int sessionId, int32_t sourceId, bool reportDecodeErrors), (override)); - MOCK_METHOD(bool, getQueuedFrames, (int sessionId, int32_t sourceId, uint32_t &queuedFrames), (override)); MOCK_METHOD(bool, getStats, (int sessionId, int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames), (override)); MOCK_METHOD(bool, setVideoWindow, (int, std::uint32_t, std::uint32_t, std::uint32_t, std::uint32_t), (override)); diff --git a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTests.cpp b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTests.cpp index b922b72fe..4cd1f95d1 100644 --- a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTests.cpp +++ b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTests.cpp @@ -362,46 +362,6 @@ TEST_F(MediaPipelineServiceTests, shouldGetImmediateOutput) getImmediateOutputShouldSucceed(); } -TEST_F(MediaPipelineServiceTests, shouldFailToSetReportDecodeErrorsForNotExistingSession) -{ - createMediaPipelineShouldSuccess(); - setReportDecodeErrorsShouldFail(); -} - -TEST_F(MediaPipelineServiceTests, shouldFailToGetQueuedFramesForNotExistingSession) -{ - createMediaPipelineShouldSuccess(); - getQueuedFramesShouldFail(); -} - -TEST_F(MediaPipelineServiceTests, shouldFailToSetReportDecodeErrors) -{ - initSession(); - mediaPipelineWillFailToSetReportDecodeErrors(); - setReportDecodeErrorsShouldFail(); -} - -TEST_F(MediaPipelineServiceTests, shouldFailToGetQueuedFrames) -{ - initSession(); - mediaPipelineWillFailToGetQueuedFrames(); - getQueuedFramesShouldFail(); -} - -TEST_F(MediaPipelineServiceTests, shouldSetReportDecodeErrors) -{ - initSession(); - mediaPipelineWillSetReportDecodeErrors(); - setReportDecodeErrorsShouldSucceed(); -} - -TEST_F(MediaPipelineServiceTests, shouldGetQueuedFrames) -{ - initSession(); - mediaPipelineWillGetQueuedFrames(); - getQueuedFramesShouldSucceed(); -} - TEST_F(MediaPipelineServiceTests, shouldFailToGetStatsForNotExistingSession) { createMediaPipelineShouldSuccess(); diff --git a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp index 6de97e773..421cb1d8b 100644 --- a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp +++ b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp @@ -69,7 +69,6 @@ const std::string kTextTrackIdentifier{"TextTrackIdentifier"}; constexpr uint32_t kBufferingLimit{4324}; constexpr bool kUseBuffering{true}; constexpr uint64_t kStopPosition{23412}; -constexpr uint32_t kQueuedFrames{123}; } // namespace namespace firebolt::rialto @@ -243,26 +242,6 @@ void MediaPipelineServiceTests::mediaPipelineWillFailToGetImmediateOutput() EXPECT_CALL(m_mediaPipelineMock, getImmediateOutput(_, _)).WillOnce(Return(false)); } -void MediaPipelineServiceTests::mediaPipelineWillSetReportDecodeErrors() -{ - EXPECT_CALL(m_mediaPipelineMock, setReportDecodeErrors(_, _)).WillOnce(Return(true)); -} - -void MediaPipelineServiceTests::mediaPipelineWillFailToSetReportDecodeErrors() -{ - EXPECT_CALL(m_mediaPipelineMock, setReportDecodeErrors(_, _)).WillOnce(Return(false)); -} - -void MediaPipelineServiceTests::mediaPipelineWillGetQueuedFrames() -{ - EXPECT_CALL(m_mediaPipelineMock, getQueuedFrames(_, _)).WillOnce(DoAll(SetArgReferee<1>(123), Return(true))); -} - -void MediaPipelineServiceTests::mediaPipelineWillFailToGetQueuedFrames() -{ - EXPECT_CALL(m_mediaPipelineMock, getQueuedFrames(_, _)).WillOnce(Return(false)); -} - void MediaPipelineServiceTests::mediaPipelineWillGetStats() { EXPECT_CALL(m_mediaPipelineMock, getStats(_, _, _)) @@ -767,29 +746,6 @@ void MediaPipelineServiceTests::getImmediateOutputShouldFail() EXPECT_FALSE(m_sut->getImmediateOutput(kSessionId, kSourceId, immOp)); } -void MediaPipelineServiceTests::setReportDecodeErrorsShouldSucceed() -{ - EXPECT_TRUE(m_sut->setReportDecodeErrors(kSessionId, kSourceId, true)); -} - -void MediaPipelineServiceTests::setReportDecodeErrorsShouldFail() -{ - EXPECT_FALSE(m_sut->setReportDecodeErrors(kSessionId, kSourceId, true)); -} - -void MediaPipelineServiceTests::getQueuedFramesShouldSucceed() -{ - uint32_t queuedFr; - EXPECT_TRUE(m_sut->getQueuedFrames(kSessionId, kSourceId, queuedFr)); - EXPECT_EQ(queuedFr, kQueuedFrames); -} - -void MediaPipelineServiceTests::getQueuedFramesShouldFail() -{ - uint32_t queuedFr; - EXPECT_FALSE(m_sut->getQueuedFrames(kSessionId, kSourceId, queuedFr)); -} - void MediaPipelineServiceTests::getSupportedMimeTypesSucceed() { MediaSourceType type = MediaSourceType::VIDEO; diff --git a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.h b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.h index 1a7e28d15..ead87a642 100644 --- a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.h +++ b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.h @@ -66,12 +66,8 @@ class MediaPipelineServiceTests : public testing::Test void mediaPipelineWillFailToGetPosition(); void mediaPipelineWillSetImmediateOutput(); void mediaPipelineWillFailToSetImmediateOutput(); - void mediaPipelineWillSetReportDecodeErrors(); - void mediaPipelineWillFailToSetReportDecodeErrors(); void mediaPipelineWillGetImmediateOutput(); void mediaPipelineWillFailToGetImmediateOutput(); - void mediaPipelineWillGetQueuedFrames(); - void mediaPipelineWillFailToGetQueuedFrames(); void mediaPipelineWillGetStats(); void mediaPipelineWillFailToGetStats(); void mediaPipelineWillRenderFrame(); @@ -160,14 +156,10 @@ class MediaPipelineServiceTests : public testing::Test void haveDataShouldFail(); void getPositionShouldSucceed(); void getPositionShouldFail(); - void setReportDecodeErrorsShouldSucceed(); - void setReportDecodeErrorsShouldFail(); void setImmediateOutputShouldSucceed(); void setImmediateOutputShouldFail(); void getImmediateOutputShouldSucceed(); void getImmediateOutputShouldFail(); - void getQueuedFramesShouldSucceed(); - void getQueuedFramesShouldFail(); void getStatsShouldSucceed(); void getStatsShouldFail(); void getSupportedMimeTypesSucceed(); From 0ae80e62bfeb3a892e620eab548790c3eda9e12e Mon Sep 17 00:00:00 2001 From: Koky2701 <90915184+Koky2701@users.noreply.github.com> Date: Tue, 17 Mar 2026 14:10:57 +0100 Subject: [PATCH 14/29] Fix Coverity issues in Rialto master (#457) Summary: Switch unnecessary copy constructors with move semantics Type: Fix Test Plan: UT/CT, Fullstack Jira: RDKEMW-12966 --- ipc/server/include/IIpcServer.h | 8 ++++---- ipc/server/source/IpcServerImpl.cpp | 8 ++++---- ipc/server/source/IpcServerImpl.h | 9 +++++---- logging/source/EnvVariableParser.cpp | 3 ++- media/client/ipc/source/IpcClient.cpp | 3 ++- media/client/main/source/ClientController.cpp | 3 ++- media/client/main/source/Control.cpp | 3 ++- media/client/main/source/MediaPipeline.cpp | 2 +- media/server/gstplayer/include/CapsBuilder.h | 16 ++++++++-------- .../tasks/generic/CheckAudioUnderflow.h | 2 +- .../gstplayer/include/tasks/generic/Eos.h | 2 +- .../include/tasks/generic/ProcessAudioGap.h | 2 +- .../gstplayer/include/tasks/generic/SetMute.h | 4 ++-- .../include/tasks/generic/SetPlaybackRate.h | 5 +++-- .../include/tasks/generic/SetPosition.h | 2 +- .../tasks/generic/SetTextTrackIdentifier.h | 2 +- .../include/tasks/generic/SetVolume.h | 6 +++--- .../include/tasks/generic/SetupElement.h | 5 +++-- .../tasks/generic/UpdatePlaybackGroup.h | 6 +++--- .../gstplayer/include/tasks/webAudio/Eos.h | 2 +- .../include/tasks/webAudio/HandleBusMessage.h | 4 ++-- .../gstplayer/include/tasks/webAudio/SetCaps.h | 6 +++--- .../include/tasks/webAudio/SetVolume.h | 4 ++-- .../include/tasks/webAudio/WriteBuffer.h | 5 +++-- media/server/gstplayer/source/CapsBuilder.cpp | 18 ++++++++++-------- .../gstplayer/source/GstCapabilities.cpp | 9 +++++---- .../source/tasks/generic/AttachSamples.cpp | 5 +++-- .../tasks/generic/CheckAudioUnderflow.cpp | 2 +- .../gstplayer/source/tasks/generic/Eos.cpp | 2 +- .../source/tasks/generic/ProcessAudioGap.cpp | 2 +- .../gstplayer/source/tasks/generic/SetMute.cpp | 4 ++-- .../source/tasks/generic/SetPlaybackRate.cpp | 5 +++-- .../source/tasks/generic/SetPosition.cpp | 3 ++- .../tasks/generic/SetTextTrackIdentifier.cpp | 2 +- .../source/tasks/generic/SetVolume.cpp | 6 +++--- .../source/tasks/generic/SetupElement.cpp | 4 ++-- .../tasks/generic/UpdatePlaybackGroup.cpp | 4 ++-- .../gstplayer/source/tasks/webAudio/Eos.cpp | 2 +- .../source/tasks/webAudio/HandleBusMessage.cpp | 4 ++-- .../source/tasks/webAudio/SetCaps.cpp | 13 +++++++------ .../source/tasks/webAudio/SetVolume.cpp | 2 +- .../source/tasks/webAudio/WriteBuffer.cpp | 2 +- .../ipc/source/MediaPipelineModuleService.cpp | 5 +++-- media/server/main/include/MainThread.h | 7 ++++--- .../main/include/MediaKeysCapabilities.h | 4 ++-- .../main/include/MediaKeysServerInternal.h | 4 ++-- .../main/include/MediaPipelineCapabilities.h | 2 +- .../main/include/MediaPipelineServerInternal.h | 5 +++-- media/server/main/interface/IMainThread.h | 6 +++--- media/server/main/source/MainThread.cpp | 15 ++++++++------- media/server/main/source/MediaKeySession.cpp | 5 +++-- .../main/source/MediaKeysCapabilities.cpp | 5 +++-- .../main/source/MediaKeysServerInternal.cpp | 4 ++-- .../main/source/MediaPipelineCapabilities.cpp | 2 +- .../source/MediaPipelineServerInternal.cpp | 4 ++-- media/server/service/source/CdmService.cpp | 4 ++-- .../service/source/MediaPipelineService.cpp | 2 +- .../service/source/WebAudioPlayerService.cpp | 2 +- serverManager/serverManagerSim/HttpRequest.cpp | 7 ++++--- tests/unittests/ipc/mocks/IpcServerMock.h | 9 +++++---- .../media/server/mocks/main/MainThreadMock.h | 6 +++--- 61 files changed, 161 insertions(+), 138 deletions(-) diff --git a/ipc/server/include/IIpcServer.h b/ipc/server/include/IIpcServer.h index 10a2a8936..b7c9bc26d 100644 --- a/ipc/server/include/IIpcServer.h +++ b/ipc/server/include/IIpcServer.h @@ -162,10 +162,10 @@ class IServer return addSocket(socketPath, std::move(clientConnectedCb), nullptr); } virtual bool addSocket(const std::string &socketPath, - std::function &)> clientConnectedCb, - std::function &)> clientDisconnectedCb) = 0; - virtual bool addSocket(int fd, std::function &)> clientConnectedCb, - std::function &)> clientDisconnectedCb) = 0; + const std::function &)> &clientConnectedCb, + const std::function &)> &clientDisconnectedCb) = 0; + virtual bool addSocket(int fd, const std::function &)> &clientConnectedCb, + const std::function &)> &clientDisconnectedCb) = 0; /** * @brief Create a client. diff --git a/ipc/server/source/IpcServerImpl.cpp b/ipc/server/source/IpcServerImpl.cpp index f6cd2da73..6536c704a 100644 --- a/ipc/server/source/IpcServerImpl.cpp +++ b/ipc/server/source/IpcServerImpl.cpp @@ -209,8 +209,8 @@ void ServerImpl::closeListeningSocket(Socket *socket) } bool ServerImpl::addSocket(const std::string &socketPath, - std::function &)> clientConnectedCb, - std::function &)> clientDisconnectedCb) + const std::function &)> &clientConnectedCb, + const std::function &)> &clientDisconnectedCb) { // store the path Socket socket; @@ -289,8 +289,8 @@ bool ServerImpl::addSocket(const std::string &socketPath, return true; } -bool ServerImpl::addSocket(int fd, std::function &)> clientConnectedCb, - std::function &)> clientDisconnectedCb) +bool ServerImpl::addSocket(int fd, const std::function &)> &clientConnectedCb, + const std::function &)> &clientDisconnectedCb) { // store the path Socket socket; diff --git a/ipc/server/source/IpcServerImpl.h b/ipc/server/source/IpcServerImpl.h index 591f2a80b..758dc89e1 100644 --- a/ipc/server/source/IpcServerImpl.h +++ b/ipc/server/source/IpcServerImpl.h @@ -64,10 +64,11 @@ class ServerImpl final : public ::firebolt::rialto::ipc::IServer, public std::en ~ServerImpl() final; public: - bool addSocket(const std::string &socketPath, std::function &)> clientConnectedCb, - std::function &)> clientDisconnectedCb) override; - bool addSocket(int fd, std::function &)> clientConnectedCb, - std::function &)> clientDisconnectedCb) override; + bool addSocket(const std::string &socketPath, + const std::function &)> &clientConnectedCb, + const std::function &)> &clientDisconnectedCb) override; + bool addSocket(int fd, const std::function &)> &clientConnectedCb, + const std::function &)> &clientDisconnectedCb) override; std::shared_ptr addClient(int socketFd, std::function &)> clientDisconnectedCb) override; diff --git a/logging/source/EnvVariableParser.cpp b/logging/source/EnvVariableParser.cpp index 5ef89d89c..0253891fb 100644 --- a/logging/source/EnvVariableParser.cpp +++ b/logging/source/EnvVariableParser.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include namespace @@ -70,7 +71,7 @@ std::vector split(std::string s, const std::string &delimiter) result.push_back(s.substr(0, pos)); s.erase(0, pos + delimiter.length()); } - result.push_back(s); + result.push_back(std::move(s)); return result; } diff --git a/media/client/ipc/source/IpcClient.cpp b/media/client/ipc/source/IpcClient.cpp index 53c5b8d6a..57f2f5c60 100644 --- a/media/client/ipc/source/IpcClient.cpp +++ b/media/client/ipc/source/IpcClient.cpp @@ -19,6 +19,7 @@ #include "IpcClient.h" #include "RialtoClientLogging.h" +#include namespace firebolt::rialto::client { @@ -191,7 +192,7 @@ std::shared_ptr IpcClient::createBlockingClosure() // check which thread we're being called from, this determines if we pump // event loop from within the wait() method or not if (m_ipcThread.get_id() == std::this_thread::get_id()) - return m_blockingClosureFactory->createBlockingClosurePoll(ipcChannel); + return m_blockingClosureFactory->createBlockingClosurePoll(std::move(ipcChannel)); else return m_blockingClosureFactory->createBlockingClosureSemaphore(); } diff --git a/media/client/main/source/ClientController.cpp b/media/client/main/source/ClientController.cpp index eac82154c..5f9fe6b6f 100644 --- a/media/client/main/source/ClientController.cpp +++ b/media/client/main/source/ClientController.cpp @@ -25,6 +25,7 @@ #include #include #include +#include namespace { @@ -265,7 +266,7 @@ void ClientController::changeStateAndNotifyClients(ApplicationState state) std::shared_ptr clientLocked{client.lock()}; if (clientLocked) { - currentClients.push_back(clientLocked); + currentClients.push_back(std::move(clientLocked)); } else { diff --git a/media/client/main/source/Control.cpp b/media/client/main/source/Control.cpp index 215558fe8..2526694eb 100644 --- a/media/client/main/source/Control.cpp +++ b/media/client/main/source/Control.cpp @@ -20,6 +20,7 @@ #include "Control.h" #include "IControlIpc.h" #include "RialtoClientLogging.h" +#include namespace firebolt::rialto { @@ -76,7 +77,7 @@ bool Control::registerClient(std::weak_ptr client, ApplicationSt std::shared_ptr lockedClient = client.lock(); if (lockedClient && m_clientController.registerClient(lockedClient, appState)) { - m_clientsToUnregister.push_back(lockedClient); + m_clientsToUnregister.push_back(std::move(lockedClient)); return true; } RIALTO_CLIENT_LOG_WARN("Unable to register client"); diff --git a/media/client/main/source/MediaPipeline.cpp b/media/client/main/source/MediaPipeline.cpp index e529fe3b6..8e26a036f 100644 --- a/media/client/main/source/MediaPipeline.cpp +++ b/media/client/main/source/MediaPipeline.cpp @@ -808,7 +808,7 @@ void MediaPipeline::notifyNeedMediaData(int32_t sourceId, size_t frameCount, uin RIALTO_CLIENT_LOG_INFO("NeedMediaData received in state != RUNNING, ignoring request id %u", requestId); break; } - m_needDataRequestMap[requestId] = needDataRequest; + m_needDataRequestMap[requestId] = std::move(needDataRequest); } std::shared_ptr client = m_mediaPipelineClient.lock(); diff --git a/media/server/gstplayer/include/CapsBuilder.h b/media/server/gstplayer/include/CapsBuilder.h index 283f7ac04..0c9452ab9 100644 --- a/media/server/gstplayer/include/CapsBuilder.h +++ b/media/server/gstplayer/include/CapsBuilder.h @@ -31,8 +31,8 @@ namespace firebolt::rialto::server class MediaSourceCapsBuilder { public: - MediaSourceCapsBuilder(std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, + MediaSourceCapsBuilder(const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, const firebolt::rialto::IMediaPipeline::MediaSourceAV &source); virtual ~MediaSourceCapsBuilder() = default; virtual GstCaps *buildCaps(); @@ -51,8 +51,8 @@ class MediaSourceCapsBuilder class MediaSourceAudioCapsBuilder : public MediaSourceCapsBuilder { public: - MediaSourceAudioCapsBuilder(std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, + MediaSourceAudioCapsBuilder(const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, const IMediaPipeline::MediaSourceAudio &source); ~MediaSourceAudioCapsBuilder() override = default; GstCaps *buildCaps() override; @@ -72,8 +72,8 @@ class MediaSourceAudioCapsBuilder : public MediaSourceCapsBuilder class MediaSourceVideoCapsBuilder : public MediaSourceCapsBuilder { public: - MediaSourceVideoCapsBuilder(std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, + MediaSourceVideoCapsBuilder(const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, const IMediaPipeline::MediaSourceVideo &source); ~MediaSourceVideoCapsBuilder() override = default; GstCaps *buildCaps() override; @@ -85,8 +85,8 @@ class MediaSourceVideoCapsBuilder : public MediaSourceCapsBuilder class MediaSourceVideoDolbyVisionCapsBuilder : public MediaSourceVideoCapsBuilder { public: - MediaSourceVideoDolbyVisionCapsBuilder(std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, + MediaSourceVideoDolbyVisionCapsBuilder(const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, const IMediaPipeline::MediaSourceVideoDolbyVision &source); ~MediaSourceVideoDolbyVisionCapsBuilder() override = default; GstCaps *buildCaps() override; diff --git a/media/server/gstplayer/include/tasks/generic/CheckAudioUnderflow.h b/media/server/gstplayer/include/tasks/generic/CheckAudioUnderflow.h index 3f0122320..919ee1de4 100644 --- a/media/server/gstplayer/include/tasks/generic/CheckAudioUnderflow.h +++ b/media/server/gstplayer/include/tasks/generic/CheckAudioUnderflow.h @@ -33,7 +33,7 @@ class CheckAudioUnderflow : public IPlayerTask { public: CheckAudioUnderflow(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, IGstGenericPlayerClient *client, - std::shared_ptr gstWrapper); + const std::shared_ptr &gstWrapper); ~CheckAudioUnderflow() override = default; void execute() const override; diff --git a/media/server/gstplayer/include/tasks/generic/Eos.h b/media/server/gstplayer/include/tasks/generic/Eos.h index da51c6741..b8176c7dc 100644 --- a/media/server/gstplayer/include/tasks/generic/Eos.h +++ b/media/server/gstplayer/include/tasks/generic/Eos.h @@ -33,7 +33,7 @@ class Eos : public IPlayerTask { public: Eos(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, - std::shared_ptr gstWrapper, + const std::shared_ptr &gstWrapper, const firebolt::rialto::MediaSourceType &type); ~Eos() override; void execute() const override; diff --git a/media/server/gstplayer/include/tasks/generic/ProcessAudioGap.h b/media/server/gstplayer/include/tasks/generic/ProcessAudioGap.h index 4f614172a..b21285edd 100644 --- a/media/server/gstplayer/include/tasks/generic/ProcessAudioGap.h +++ b/media/server/gstplayer/include/tasks/generic/ProcessAudioGap.h @@ -36,7 +36,7 @@ class ProcessAudioGap : public IPlayerTask ProcessAudioGap(GenericPlayerContext &context, const std::shared_ptr &gstWrapper, const std::shared_ptr &glibWrapper, - const std::shared_ptr rdkGstreamerUtilsWrapper, + const std::shared_ptr &rdkGstreamerUtilsWrapper, std::int64_t position, std::uint32_t duration, std::int64_t discontinuityGap, bool audioAac); ~ProcessAudioGap() override; void execute() const override; diff --git a/media/server/gstplayer/include/tasks/generic/SetMute.h b/media/server/gstplayer/include/tasks/generic/SetMute.h index 17892dcc1..f2662d819 100644 --- a/media/server/gstplayer/include/tasks/generic/SetMute.h +++ b/media/server/gstplayer/include/tasks/generic/SetMute.h @@ -33,8 +33,8 @@ class SetMute : public IPlayerTask { public: SetMute(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, - std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, + const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, const MediaSourceType &mediaSourceType, bool mute); ~SetMute() override; void execute() const override; diff --git a/media/server/gstplayer/include/tasks/generic/SetPlaybackRate.h b/media/server/gstplayer/include/tasks/generic/SetPlaybackRate.h index 8adbddcf7..7f121fc7f 100644 --- a/media/server/gstplayer/include/tasks/generic/SetPlaybackRate.h +++ b/media/server/gstplayer/include/tasks/generic/SetPlaybackRate.h @@ -31,8 +31,9 @@ namespace firebolt::rialto::server::tasks::generic class SetPlaybackRate : public IPlayerTask { public: - SetPlaybackRate(GenericPlayerContext &context, std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, double rate); + SetPlaybackRate(GenericPlayerContext &context, + const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, double rate); ~SetPlaybackRate() override; void execute() const override; diff --git a/media/server/gstplayer/include/tasks/generic/SetPosition.h b/media/server/gstplayer/include/tasks/generic/SetPosition.h index 0be5a265f..ec90760e2 100644 --- a/media/server/gstplayer/include/tasks/generic/SetPosition.h +++ b/media/server/gstplayer/include/tasks/generic/SetPosition.h @@ -34,7 +34,7 @@ class SetPosition : public IPlayerTask { public: SetPosition(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, IGstGenericPlayerClient *client, - std::shared_ptr gstWrapper, std::int64_t position); + const std::shared_ptr &gstWrapper, std::int64_t position); ~SetPosition() override; void execute() const override; diff --git a/media/server/gstplayer/include/tasks/generic/SetTextTrackIdentifier.h b/media/server/gstplayer/include/tasks/generic/SetTextTrackIdentifier.h index b0554d310..bcb866fb1 100644 --- a/media/server/gstplayer/include/tasks/generic/SetTextTrackIdentifier.h +++ b/media/server/gstplayer/include/tasks/generic/SetTextTrackIdentifier.h @@ -33,7 +33,7 @@ class SetTextTrackIdentifier : public IPlayerTask { public: SetTextTrackIdentifier(GenericPlayerContext &context, - std::shared_ptr glibWrapper, + const std::shared_ptr &glibWrapper, const std::string &textTrackIdentifier); ~SetTextTrackIdentifier() override; void execute() const override; diff --git a/media/server/gstplayer/include/tasks/generic/SetVolume.h b/media/server/gstplayer/include/tasks/generic/SetVolume.h index 202dac828..416cbfa19 100644 --- a/media/server/gstplayer/include/tasks/generic/SetVolume.h +++ b/media/server/gstplayer/include/tasks/generic/SetVolume.h @@ -33,9 +33,9 @@ class SetVolume : public IPlayerTask { public: SetVolume(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, - std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, - std::shared_ptr rdkGstreamerUtilsWrapper, + const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, + const std::shared_ptr &rdkGstreamerUtilsWrapper, double targetVolume, uint32_t volumeDuration, firebolt::rialto::EaseType easeType); ~SetVolume() override; void execute() const override; diff --git a/media/server/gstplayer/include/tasks/generic/SetupElement.h b/media/server/gstplayer/include/tasks/generic/SetupElement.h index c50beddcf..c41e46274 100644 --- a/media/server/gstplayer/include/tasks/generic/SetupElement.h +++ b/media/server/gstplayer/include/tasks/generic/SetupElement.h @@ -33,8 +33,9 @@ namespace firebolt::rialto::server::tasks::generic class SetupElement : public IPlayerTask { public: - SetupElement(GenericPlayerContext &context, std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, + SetupElement(GenericPlayerContext &context, + const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, IGstGenericPlayerPrivate &player, GstElement *element); ~SetupElement() override; void execute() const override; diff --git a/media/server/gstplayer/include/tasks/generic/UpdatePlaybackGroup.h b/media/server/gstplayer/include/tasks/generic/UpdatePlaybackGroup.h index f1cb25000..7b4a9c557 100644 --- a/media/server/gstplayer/include/tasks/generic/UpdatePlaybackGroup.h +++ b/media/server/gstplayer/include/tasks/generic/UpdatePlaybackGroup.h @@ -34,9 +34,9 @@ class UpdatePlaybackGroup : public IPlayerTask { public: UpdatePlaybackGroup(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, - std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, GstElement *typefind, - const GstCaps *caps); + const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, + GstElement *typefind, const GstCaps *caps); ~UpdatePlaybackGroup() override; void execute() const override; diff --git a/media/server/gstplayer/include/tasks/webAudio/Eos.h b/media/server/gstplayer/include/tasks/webAudio/Eos.h index 230628fa4..2d7605bc6 100644 --- a/media/server/gstplayer/include/tasks/webAudio/Eos.h +++ b/media/server/gstplayer/include/tasks/webAudio/Eos.h @@ -32,7 +32,7 @@ namespace firebolt::rialto::server::tasks::webaudio class Eos : public IPlayerTask { public: - Eos(WebAudioPlayerContext &context, std::shared_ptr gstWrapper); + Eos(WebAudioPlayerContext &context, const std::shared_ptr &gstWrapper); ~Eos() override; void execute() const override; diff --git a/media/server/gstplayer/include/tasks/webAudio/HandleBusMessage.h b/media/server/gstplayer/include/tasks/webAudio/HandleBusMessage.h index a45ccc97f..2820523de 100644 --- a/media/server/gstplayer/include/tasks/webAudio/HandleBusMessage.h +++ b/media/server/gstplayer/include/tasks/webAudio/HandleBusMessage.h @@ -35,8 +35,8 @@ class HandleBusMessage : public IPlayerTask { public: HandleBusMessage(WebAudioPlayerContext &context, IGstWebAudioPlayerPrivate &player, IGstWebAudioPlayerClient *client, - std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, GstMessage *message); + const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, GstMessage *message); ~HandleBusMessage() override; void execute() const override; diff --git a/media/server/gstplayer/include/tasks/webAudio/SetCaps.h b/media/server/gstplayer/include/tasks/webAudio/SetCaps.h index d4b7e76b1..0d74701de 100644 --- a/media/server/gstplayer/include/tasks/webAudio/SetCaps.h +++ b/media/server/gstplayer/include/tasks/webAudio/SetCaps.h @@ -32,9 +32,9 @@ namespace firebolt::rialto::server::tasks::webaudio class SetCaps : public IPlayerTask { public: - SetCaps(WebAudioPlayerContext &context, std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, const std::string &audioMimeType, - std::weak_ptr config); + SetCaps(WebAudioPlayerContext &context, const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, + const std::string &audioMimeType, std::weak_ptr config); ~SetCaps() override; void execute() const override; diff --git a/media/server/gstplayer/include/tasks/webAudio/SetVolume.h b/media/server/gstplayer/include/tasks/webAudio/SetVolume.h index eddf127d7..42d34cfe5 100644 --- a/media/server/gstplayer/include/tasks/webAudio/SetVolume.h +++ b/media/server/gstplayer/include/tasks/webAudio/SetVolume.h @@ -30,8 +30,8 @@ namespace firebolt::rialto::server::tasks::webaudio class SetVolume : public IPlayerTask { public: - SetVolume(WebAudioPlayerContext &context, std::shared_ptr gstWrapper, - double volume); + SetVolume(WebAudioPlayerContext &context, + const std::shared_ptr &gstWrapper, double volume); ~SetVolume() override; void execute() const override; diff --git a/media/server/gstplayer/include/tasks/webAudio/WriteBuffer.h b/media/server/gstplayer/include/tasks/webAudio/WriteBuffer.h index c87e0a2ea..8588d811e 100644 --- a/media/server/gstplayer/include/tasks/webAudio/WriteBuffer.h +++ b/media/server/gstplayer/include/tasks/webAudio/WriteBuffer.h @@ -32,8 +32,9 @@ namespace firebolt::rialto::server::tasks::webaudio class WriteBuffer : public IPlayerTask { public: - WriteBuffer(WebAudioPlayerContext &context, std::shared_ptr gstWrapper, - uint8_t *mainPtr, uint32_t mainLength, uint8_t *wrapPtr, uint32_t wrapLength); + WriteBuffer(WebAudioPlayerContext &context, + const std::shared_ptr &gstWrapper, uint8_t *mainPtr, + uint32_t mainLength, uint8_t *wrapPtr, uint32_t wrapLength); ~WriteBuffer() override; void execute() const override; diff --git a/media/server/gstplayer/source/CapsBuilder.cpp b/media/server/gstplayer/source/CapsBuilder.cpp index 4491b3656..647b0353b 100644 --- a/media/server/gstplayer/source/CapsBuilder.cpp +++ b/media/server/gstplayer/source/CapsBuilder.cpp @@ -24,8 +24,8 @@ namespace firebolt::rialto::server { -MediaSourceCapsBuilder::MediaSourceCapsBuilder(std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, +MediaSourceCapsBuilder::MediaSourceCapsBuilder(const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, const firebolt::rialto::IMediaPipeline::MediaSourceAV &source) : m_gstWrapper(gstWrapper), m_glibWrapper(glibWrapper), m_attachedSource(source) { @@ -92,8 +92,9 @@ void MediaSourceCapsBuilder::addStreamFormatToCaps(GstCaps *caps) const } MediaSourceAudioCapsBuilder::MediaSourceAudioCapsBuilder( - std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, const IMediaPipeline::MediaSourceAudio &source) + const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, + const IMediaPipeline::MediaSourceAudio &source) : MediaSourceCapsBuilder(gstWrapper, glibWrapper, source), m_attachedAudioSource(source) { } @@ -226,8 +227,9 @@ void MediaSourceAudioCapsBuilder::addFlacSpecificData(GstCaps *caps) const } MediaSourceVideoCapsBuilder::MediaSourceVideoCapsBuilder( - std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, const IMediaPipeline::MediaSourceVideo &source) + const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, + const IMediaPipeline::MediaSourceVideo &source) : MediaSourceCapsBuilder(gstWrapper, glibWrapper, source), m_attachedVideoSource(source) { } @@ -244,8 +246,8 @@ GstCaps *MediaSourceVideoCapsBuilder::buildCaps() } MediaSourceVideoDolbyVisionCapsBuilder::MediaSourceVideoDolbyVisionCapsBuilder( - std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, + const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, const IMediaPipeline::MediaSourceVideoDolbyVision &source) : MediaSourceVideoCapsBuilder(gstWrapper, glibWrapper, source), m_attachedDolbySource(source) { diff --git a/media/server/gstplayer/source/GstCapabilities.cpp b/media/server/gstplayer/source/GstCapabilities.cpp index 13173441d..c7604d171 100644 --- a/media/server/gstplayer/source/GstCapabilities.cpp +++ b/media/server/gstplayer/source/GstCapabilities.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "GstCapabilities.h" #include "GstMimeMapping.h" @@ -274,12 +275,12 @@ std::vector GstCapabilities::getSupportedProperties(MediaSourceType { for (guint j = 0; j < nProps && !propertiesToLookFor.empty(); ++j) { - const std::string kPropName{props[j]->name}; - auto it = propertiesToLookFor.find(kPropName); + std::string propName{props[j]->name}; + auto it = propertiesToLookFor.find(propName); if (it != propertiesToLookFor.end()) { - RIALTO_SERVER_LOG_DEBUG("Found property '%s'", kPropName.c_str()); - propertiesFound.push_back(kPropName); + RIALTO_SERVER_LOG_DEBUG("Found property '%s'", propName.c_str()); + propertiesFound.push_back(std::move(propName)); propertiesToLookFor.erase(it); } } diff --git a/media/server/gstplayer/source/tasks/generic/AttachSamples.cpp b/media/server/gstplayer/source/tasks/generic/AttachSamples.cpp index ef4605d4e..a9049f44b 100644 --- a/media/server/gstplayer/source/tasks/generic/AttachSamples.cpp +++ b/media/server/gstplayer/source/tasks/generic/AttachSamples.cpp @@ -22,6 +22,7 @@ #include "IGstGenericPlayerPrivate.h" #include "RialtoServerLogging.h" #include "TypeConverters.h" +#include namespace firebolt::rialto::server::tasks::generic { @@ -42,7 +43,7 @@ AttachSamples::AttachSamples(GenericPlayerContext &context, dynamic_cast(*mediaSegment); VideoData videoData = {gstBuffer, videoSegment.getWidth(), videoSegment.getHeight(), videoSegment.getFrameRate(), videoSegment.getCodecData()}; - m_videoData.push_back(videoData); + m_videoData.push_back(std::move(videoData)); } catch (const std::exception &e) { @@ -62,7 +63,7 @@ AttachSamples::AttachSamples(GenericPlayerContext &context, audioSegment.getCodecData(), audioSegment.getClippingStart(), audioSegment.getClippingEnd()}; - m_audioData.push_back(audioData); + m_audioData.push_back(std::move(audioData)); } catch (const std::exception &e) { diff --git a/media/server/gstplayer/source/tasks/generic/CheckAudioUnderflow.cpp b/media/server/gstplayer/source/tasks/generic/CheckAudioUnderflow.cpp index 6cbeec528..55e641304 100644 --- a/media/server/gstplayer/source/tasks/generic/CheckAudioUnderflow.cpp +++ b/media/server/gstplayer/source/tasks/generic/CheckAudioUnderflow.cpp @@ -31,7 +31,7 @@ namespace firebolt::rialto::server::tasks::generic { CheckAudioUnderflow::CheckAudioUnderflow(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, IGstGenericPlayerClient *client, - std::shared_ptr gstWrapper) + const std::shared_ptr &gstWrapper) : m_context{context}, m_player(player), m_gstPlayerClient{client}, m_gstWrapper{gstWrapper} { } diff --git a/media/server/gstplayer/source/tasks/generic/Eos.cpp b/media/server/gstplayer/source/tasks/generic/Eos.cpp index 371487570..af2a8193e 100644 --- a/media/server/gstplayer/source/tasks/generic/Eos.cpp +++ b/media/server/gstplayer/source/tasks/generic/Eos.cpp @@ -26,7 +26,7 @@ namespace firebolt::rialto::server::tasks::generic { Eos::Eos(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, - std::shared_ptr gstWrapper, + const std::shared_ptr &gstWrapper, const firebolt::rialto::MediaSourceType &type) : m_context{context}, m_player{player}, m_gstWrapper{gstWrapper}, m_type{type} { diff --git a/media/server/gstplayer/source/tasks/generic/ProcessAudioGap.cpp b/media/server/gstplayer/source/tasks/generic/ProcessAudioGap.cpp index 13f5b870b..049f8b89a 100644 --- a/media/server/gstplayer/source/tasks/generic/ProcessAudioGap.cpp +++ b/media/server/gstplayer/source/tasks/generic/ProcessAudioGap.cpp @@ -25,7 +25,7 @@ namespace firebolt::rialto::server::tasks::generic ProcessAudioGap::ProcessAudioGap( GenericPlayerContext &context, const std::shared_ptr &gstWrapper, const std::shared_ptr &glibWrapper, - const std::shared_ptr rdkGstreamerUtilsWrapper, + const std::shared_ptr &rdkGstreamerUtilsWrapper, std::int64_t position, std::uint32_t duration, std::int64_t discontinuityGap, bool audioAac) : m_context{context}, m_gstWrapper{gstWrapper}, m_glibWrapper{glibWrapper}, m_rdkGstreamerUtilsWrapper{rdkGstreamerUtilsWrapper}, m_position{position}, m_duration{duration}, diff --git a/media/server/gstplayer/source/tasks/generic/SetMute.cpp b/media/server/gstplayer/source/tasks/generic/SetMute.cpp index da66764a8..5bd4743e1 100644 --- a/media/server/gstplayer/source/tasks/generic/SetMute.cpp +++ b/media/server/gstplayer/source/tasks/generic/SetMute.cpp @@ -25,8 +25,8 @@ namespace firebolt::rialto::server::tasks::generic { SetMute::SetMute(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, - std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, + const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, const MediaSourceType &mediaSourceType, bool mute) : m_context{context}, m_player{player}, m_gstWrapper{gstWrapper}, m_glibWrapper{glibWrapper}, m_mediaSourceType{mediaSourceType}, m_mute{mute} diff --git a/media/server/gstplayer/source/tasks/generic/SetPlaybackRate.cpp b/media/server/gstplayer/source/tasks/generic/SetPlaybackRate.cpp index f5f8f609c..dc7bb9ffb 100644 --- a/media/server/gstplayer/source/tasks/generic/SetPlaybackRate.cpp +++ b/media/server/gstplayer/source/tasks/generic/SetPlaybackRate.cpp @@ -31,8 +31,9 @@ const char kCustomInstantRateChangeEventName[] = "custom-instant-rate-change"; namespace firebolt::rialto::server::tasks::generic { SetPlaybackRate::SetPlaybackRate(GenericPlayerContext &context, - std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, double rate) + const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, + double rate) : m_context{context}, m_gstWrapper{gstWrapper}, m_glibWrapper{glibWrapper}, m_rate{rate} { RIALTO_SERVER_LOG_DEBUG("Constructing SetPlaybackRate"); diff --git a/media/server/gstplayer/source/tasks/generic/SetPosition.cpp b/media/server/gstplayer/source/tasks/generic/SetPosition.cpp index df9d1b72b..db485ddad 100644 --- a/media/server/gstplayer/source/tasks/generic/SetPosition.cpp +++ b/media/server/gstplayer/source/tasks/generic/SetPosition.cpp @@ -28,7 +28,8 @@ namespace firebolt::rialto::server::tasks::generic { SetPosition::SetPosition(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, IGstGenericPlayerClient *client, - std::shared_ptr gstWrapper, std::int64_t position) + const std::shared_ptr &gstWrapper, + std::int64_t position) : m_context{context}, m_player{player}, m_gstPlayerClient{client}, m_gstWrapper{gstWrapper}, m_position{position} { RIALTO_SERVER_LOG_DEBUG("Constructing SetPosition"); diff --git a/media/server/gstplayer/source/tasks/generic/SetTextTrackIdentifier.cpp b/media/server/gstplayer/source/tasks/generic/SetTextTrackIdentifier.cpp index b29ab0572..7b72b08d4 100644 --- a/media/server/gstplayer/source/tasks/generic/SetTextTrackIdentifier.cpp +++ b/media/server/gstplayer/source/tasks/generic/SetTextTrackIdentifier.cpp @@ -24,7 +24,7 @@ namespace firebolt::rialto::server::tasks::generic { SetTextTrackIdentifier::SetTextTrackIdentifier(GenericPlayerContext &context, - std::shared_ptr glibWrapper, + const std::shared_ptr &glibWrapper, const std::string &textTrackIdentifier) : m_context{context}, m_glibWrapper{glibWrapper}, m_textTrackIdentifier{textTrackIdentifier} { diff --git a/media/server/gstplayer/source/tasks/generic/SetVolume.cpp b/media/server/gstplayer/source/tasks/generic/SetVolume.cpp index 0ceeae94f..e904392b2 100644 --- a/media/server/gstplayer/source/tasks/generic/SetVolume.cpp +++ b/media/server/gstplayer/source/tasks/generic/SetVolume.cpp @@ -27,9 +27,9 @@ namespace firebolt::rialto::server::tasks::generic { SetVolume::SetVolume(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, - std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, - std::shared_ptr rdkGstreamerUtilsWrapper, + const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, + const std::shared_ptr &rdkGstreamerUtilsWrapper, double targetVolume, uint32_t volumeDuration, firebolt::rialto::EaseType easeType) : m_context{context}, m_player{player}, m_gstWrapper{gstWrapper}, m_glibWrapper{glibWrapper}, m_rdkGstreamerUtilsWrapper{rdkGstreamerUtilsWrapper}, m_targetVolume{targetVolume}, diff --git a/media/server/gstplayer/source/tasks/generic/SetupElement.cpp b/media/server/gstplayer/source/tasks/generic/SetupElement.cpp index 99588430a..16484c9a6 100644 --- a/media/server/gstplayer/source/tasks/generic/SetupElement.cpp +++ b/media/server/gstplayer/source/tasks/generic/SetupElement.cpp @@ -129,8 +129,8 @@ void autoAudioSinkChildRemovedCallback(GstChildProxy *obj, GObject *object, gcha namespace firebolt::rialto::server::tasks::generic { SetupElement::SetupElement(GenericPlayerContext &context, - std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, + const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, IGstGenericPlayerPrivate &player, GstElement *element) : m_context{context}, m_gstWrapper{gstWrapper}, m_glibWrapper{glibWrapper}, m_player{player}, m_element{element} { diff --git a/media/server/gstplayer/source/tasks/generic/UpdatePlaybackGroup.cpp b/media/server/gstplayer/source/tasks/generic/UpdatePlaybackGroup.cpp index 36f3f79b7..5946d6899 100644 --- a/media/server/gstplayer/source/tasks/generic/UpdatePlaybackGroup.cpp +++ b/media/server/gstplayer/source/tasks/generic/UpdatePlaybackGroup.cpp @@ -23,8 +23,8 @@ namespace firebolt::rialto::server::tasks::generic { UpdatePlaybackGroup::UpdatePlaybackGroup(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, - std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, + const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, GstElement *typefind, const GstCaps *caps) : m_context{context}, m_player{player}, m_gstWrapper{gstWrapper}, m_glibWrapper{glibWrapper}, m_typefind{typefind}, m_caps{caps} diff --git a/media/server/gstplayer/source/tasks/webAudio/Eos.cpp b/media/server/gstplayer/source/tasks/webAudio/Eos.cpp index be11e5405..33e2d91e4 100644 --- a/media/server/gstplayer/source/tasks/webAudio/Eos.cpp +++ b/media/server/gstplayer/source/tasks/webAudio/Eos.cpp @@ -24,7 +24,7 @@ namespace firebolt::rialto::server::tasks::webaudio { -Eos::Eos(WebAudioPlayerContext &context, std::shared_ptr gstWrapper) +Eos::Eos(WebAudioPlayerContext &context, const std::shared_ptr &gstWrapper) : m_context{context}, m_gstWrapper{gstWrapper} { RIALTO_SERVER_LOG_DEBUG("Constructing Eos"); diff --git a/media/server/gstplayer/source/tasks/webAudio/HandleBusMessage.cpp b/media/server/gstplayer/source/tasks/webAudio/HandleBusMessage.cpp index e8ffad051..12de30447 100644 --- a/media/server/gstplayer/source/tasks/webAudio/HandleBusMessage.cpp +++ b/media/server/gstplayer/source/tasks/webAudio/HandleBusMessage.cpp @@ -27,8 +27,8 @@ namespace firebolt::rialto::server::tasks::webaudio { HandleBusMessage::HandleBusMessage(WebAudioPlayerContext &context, IGstWebAudioPlayerPrivate &player, IGstWebAudioPlayerClient *client, - std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, + const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, GstMessage *message) : m_context{context}, m_player{player}, m_gstPlayerClient{client}, m_gstWrapper{gstWrapper}, m_glibWrapper{glibWrapper}, m_message{message} diff --git a/media/server/gstplayer/source/tasks/webAudio/SetCaps.cpp b/media/server/gstplayer/source/tasks/webAudio/SetCaps.cpp index 5d42d4c31..533158f02 100644 --- a/media/server/gstplayer/source/tasks/webAudio/SetCaps.cpp +++ b/media/server/gstplayer/source/tasks/webAudio/SetCaps.cpp @@ -33,8 +33,8 @@ namespace class WebAudioCapsBuilder { public: - WebAudioCapsBuilder(std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper) + WebAudioCapsBuilder(const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper) : m_gstWrapper(gstWrapper), m_glibWrapper(glibWrapper) { } @@ -49,8 +49,8 @@ class WebAudioCapsBuilder class WebAudioPcmCapsBuilder : public WebAudioCapsBuilder { public: - WebAudioPcmCapsBuilder(std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, + WebAudioPcmCapsBuilder(const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, const WebAudioPcmConfig &pcmConfig) : WebAudioCapsBuilder(gstWrapper, glibWrapper), m_pcmConfig(pcmConfig) { @@ -105,8 +105,9 @@ class WebAudioPcmCapsBuilder : public WebAudioCapsBuilder }; }; // namespace -SetCaps::SetCaps(WebAudioPlayerContext &context, std::shared_ptr gstWrapper, - std::shared_ptr glibWrapper, +SetCaps::SetCaps(WebAudioPlayerContext &context, + const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper, const std::string &audioMimeType, std::weak_ptr webAudioConfig) : m_context{context}, m_gstWrapper{gstWrapper}, m_glibWrapper{glibWrapper}, m_audioMimeType{audioMimeType} { diff --git a/media/server/gstplayer/source/tasks/webAudio/SetVolume.cpp b/media/server/gstplayer/source/tasks/webAudio/SetVolume.cpp index 88c94bfe6..84b2605a6 100644 --- a/media/server/gstplayer/source/tasks/webAudio/SetVolume.cpp +++ b/media/server/gstplayer/source/tasks/webAudio/SetVolume.cpp @@ -25,7 +25,7 @@ namespace firebolt::rialto::server::tasks::webaudio { SetVolume::SetVolume(WebAudioPlayerContext &context, - std::shared_ptr gstWrapper, double volume) + const std::shared_ptr &gstWrapper, double volume) : m_context{context}, m_gstWrapper{gstWrapper}, m_volume{volume} { RIALTO_SERVER_LOG_DEBUG("Constructing SetVolume"); diff --git a/media/server/gstplayer/source/tasks/webAudio/WriteBuffer.cpp b/media/server/gstplayer/source/tasks/webAudio/WriteBuffer.cpp index 8d7aebd32..bda9b5685 100644 --- a/media/server/gstplayer/source/tasks/webAudio/WriteBuffer.cpp +++ b/media/server/gstplayer/source/tasks/webAudio/WriteBuffer.cpp @@ -27,7 +27,7 @@ namespace firebolt::rialto::server::tasks::webaudio { WriteBuffer::WriteBuffer(WebAudioPlayerContext &context, - std::shared_ptr gstWrapper, uint8_t *mainPtr, + const std::shared_ptr &gstWrapper, uint8_t *mainPtr, uint32_t mainLength, uint8_t *wrapPtr, uint32_t wrapLength) : m_context{context}, m_gstWrapper{gstWrapper}, m_mainPtr{mainPtr}, m_mainLength{mainLength}, m_wrapPtr{wrapPtr}, m_wrapLength{wrapLength} diff --git a/media/server/ipc/source/MediaPipelineModuleService.cpp b/media/server/ipc/source/MediaPipelineModuleService.cpp index 7e08ec2f7..3853f6605 100644 --- a/media/server/ipc/source/MediaPipelineModuleService.cpp +++ b/media/server/ipc/source/MediaPipelineModuleService.cpp @@ -26,6 +26,7 @@ #include #include #include +#include namespace { @@ -471,8 +472,8 @@ void MediaPipelineModuleService::attachSource(::google::protobuf::RpcController { framed = kConfig.framed(); } - AudioConfig audioConfig{numberofchannels, sampleRate, codecSpecificConfig, format, - layout, channelMask, streamHeaders, framed}; + AudioConfig audioConfig{numberofchannels, sampleRate, std::move(codecSpecificConfig), format, + layout, channelMask, std::move(streamHeaders), framed}; mediaSource = std::make_unique(request->mime_type(), hasDrm, audioConfig, diff --git a/media/server/main/include/MainThread.h b/media/server/main/include/MainThread.h index b7fef969a..afb55ec62 100644 --- a/media/server/main/include/MainThread.h +++ b/media/server/main/include/MainThread.h @@ -68,9 +68,9 @@ class MainThread : public IMainThread int32_t registerClient() override; void unregisterClient(uint32_t clientId) override; - void enqueueTask(uint32_t clientId, Task task) override; - void enqueueTaskAndWait(uint32_t clientId, Task task) override; - void enqueuePriorityTaskAndWait(uint32_t clientId, Task task) override; + void enqueueTask(uint32_t clientId, const Task &task) override; + void enqueueTaskAndWait(uint32_t clientId, const Task &task) override; + void enqueuePriorityTaskAndWait(uint32_t clientId, const Task &task) override; private: /** @@ -78,6 +78,7 @@ class MainThread : public IMainThread */ struct TaskInfo { + bool done{false}; /**< A flag indicating whether the task has completed. */ uint32_t clientId; /**< The id of the client creating the task. */ Task task; /**< The task to execute. */ std::unique_ptr mutex; /**< Mutex for the task condition variable. */ diff --git a/media/server/main/include/MediaKeysCapabilities.h b/media/server/main/include/MediaKeysCapabilities.h index 524d1d664..92cff1e41 100644 --- a/media/server/main/include/MediaKeysCapabilities.h +++ b/media/server/main/include/MediaKeysCapabilities.h @@ -57,8 +57,8 @@ class MediaKeysCapabilities : public IMediaKeysCapabilities * @param[in] ocdmFactory : The ocdm factory. * @param[in] ocdmSystemFactory : The ocdmSystem factory. */ - MediaKeysCapabilities(std::shared_ptr ocdmFactory, - std::shared_ptr ocdmSystemFactory); + MediaKeysCapabilities(const std::shared_ptr &ocdmFactory, + const std::shared_ptr &ocdmSystemFactory); /** * @brief Virtual destructor. diff --git a/media/server/main/include/MediaKeysServerInternal.h b/media/server/main/include/MediaKeysServerInternal.h index bf9680651..12924decd 100644 --- a/media/server/main/include/MediaKeysServerInternal.h +++ b/media/server/main/include/MediaKeysServerInternal.h @@ -64,8 +64,8 @@ class MediaKeysServerInternal : public IMediaKeysServerInternal * */ MediaKeysServerInternal(const std::string &keySystem, const std::shared_ptr &mainThreadFactory, - std::shared_ptr ocdmSystemFactory, - std::shared_ptr mediaKeySessionFactory); + const std::shared_ptr &ocdmSystemFactory, + const std::shared_ptr &mediaKeySessionFactory); /** * @brief Virtual destructor. diff --git a/media/server/main/include/MediaPipelineCapabilities.h b/media/server/main/include/MediaPipelineCapabilities.h index be5a34451..da5ec541b 100644 --- a/media/server/main/include/MediaPipelineCapabilities.h +++ b/media/server/main/include/MediaPipelineCapabilities.h @@ -55,7 +55,7 @@ class MediaPipelineCapabilities : public IMediaPipelineCapabilities * * @param[in] gstCapabilitiesFactory : The gstreamer capabilities factory. */ - explicit MediaPipelineCapabilities(std::shared_ptr gstCapabilitiesFactory); + explicit MediaPipelineCapabilities(const std::shared_ptr &gstCapabilitiesFactory); /** * @brief Virtual destructor. diff --git a/media/server/main/include/MediaPipelineServerInternal.h b/media/server/main/include/MediaPipelineServerInternal.h index cfd0cd2a7..0c92f63c0 100644 --- a/media/server/main/include/MediaPipelineServerInternal.h +++ b/media/server/main/include/MediaPipelineServerInternal.h @@ -78,11 +78,12 @@ class MediaPipelineServerInternal : public IMediaPipelineServerInternal, public * @param[in] activeRequests : The active requests * @param[in] decryptionService : The decryption service */ - MediaPipelineServerInternal(std::shared_ptr client, const VideoRequirements &videoRequirements, + MediaPipelineServerInternal(const std::shared_ptr &client, + const VideoRequirements &videoRequirements, const std::shared_ptr &gstPlayerFactory, int sessionId, const std::shared_ptr &shmBuffer, const std::shared_ptr &mainThreadFactory, - std::shared_ptr timerFactory, + const std::shared_ptr &timerFactory, std::unique_ptr &&dataReaderFactory, std::unique_ptr &&activeRequests, IDecryptionService &decryptionService); diff --git a/media/server/main/interface/IMainThread.h b/media/server/main/interface/IMainThread.h index 6a4ac3490..7e9a12f2a 100644 --- a/media/server/main/interface/IMainThread.h +++ b/media/server/main/interface/IMainThread.h @@ -94,7 +94,7 @@ class IMainThread * @param[in] clientId : The id of the registered client. * @param[in] task : Task to queue. */ - virtual void enqueueTask(uint32_t clientId, Task task) = 0; + virtual void enqueueTask(uint32_t clientId, const Task &task) = 0; /** * @brief Enqueue a task on the main thread and wait for it to finish before returning. @@ -102,7 +102,7 @@ class IMainThread * @param[in] clientId : The id of the registered client. * @param[in] task : Task to queue. */ - virtual void enqueueTaskAndWait(uint32_t clientId, Task task) = 0; + virtual void enqueueTaskAndWait(uint32_t clientId, const Task &task) = 0; /** * @brief Enqueue a priority task on the main thread and wait for it to finish before returning. @@ -110,7 +110,7 @@ class IMainThread * @param[in] clientId : The id of the registered client. * @param[in] task : Task to queue. */ - virtual void enqueuePriorityTaskAndWait(uint32_t clientId, Task task) = 0; + virtual void enqueuePriorityTaskAndWait(uint32_t clientId, const Task &task) = 0; }; } // namespace firebolt::rialto::server diff --git a/media/server/main/source/MainThread.cpp b/media/server/main/source/MainThread.cpp index 30c2661d0..38aa64746 100644 --- a/media/server/main/source/MainThread.cpp +++ b/media/server/main/source/MainThread.cpp @@ -99,6 +99,7 @@ void MainThread::mainThreadLoop() if (nullptr != kTaskInfo->cv) { std::unique_lock lockTask(*(kTaskInfo->mutex)); + kTaskInfo->done = true; kTaskInfo->cv->notify_one(); } } @@ -111,7 +112,7 @@ const std::shared_ptr MainThread::waitForTask() { m_taskQueueCv.wait(lock, [this] { return !m_taskQueue.empty(); }); } - const std::shared_ptr kTaskInfo = m_taskQueue.front(); + const auto kTaskInfo = m_taskQueue.front(); m_taskQueue.pop_front(); return kTaskInfo; } @@ -136,19 +137,19 @@ void MainThread::unregisterClient(uint32_t clientId) m_registeredClients.erase(clientId); } -void MainThread::enqueueTask(uint32_t clientId, Task task) +void MainThread::enqueueTask(uint32_t clientId, const Task &task) { std::shared_ptr newTask = std::make_shared(); newTask->clientId = clientId; newTask->task = task; { std::unique_lock lock(m_taskQueueMutex); - m_taskQueue.push_back(newTask); + m_taskQueue.push_back(std::move(newTask)); } m_taskQueueCv.notify_one(); } -void MainThread::enqueueTaskAndWait(uint32_t clientId, Task task) +void MainThread::enqueueTaskAndWait(uint32_t clientId, const Task &task) { std::shared_ptr newTask = std::make_shared(); newTask->clientId = clientId; @@ -164,11 +165,11 @@ void MainThread::enqueueTaskAndWait(uint32_t clientId, Task task) } m_taskQueueCv.notify_one(); - newTask->cv->wait(lockTask); + newTask->cv->wait(lockTask, [&] { return newTask->done; }); } } -void MainThread::enqueuePriorityTaskAndWait(uint32_t clientId, Task task) +void MainThread::enqueuePriorityTaskAndWait(uint32_t clientId, const Task &task) { std::shared_ptr newTask = std::make_shared(); newTask->clientId = clientId; @@ -184,7 +185,7 @@ void MainThread::enqueuePriorityTaskAndWait(uint32_t clientId, Task task) } m_taskQueueCv.notify_one(); - newTask->cv->wait(lockTask); + newTask->cv->wait(lockTask, [&] { return newTask->done; }); } } } // namespace firebolt::rialto::server diff --git a/media/server/main/source/MediaKeySession.cpp b/media/server/main/source/MediaKeySession.cpp index ab23b05a4..e6cc38355 100644 --- a/media/server/main/source/MediaKeySession.cpp +++ b/media/server/main/source/MediaKeySession.cpp @@ -18,6 +18,7 @@ */ #include +#include #include "MediaKeySession.h" #include "MediaKeysCommon.h" @@ -408,7 +409,7 @@ void MediaKeySession::onProcessChallenge(const char url[], const uint8_t challen { std::string urlStr = url; std::vector challengeVec = std::vector{challenge, challenge + challengeLength}; - auto task = [&, urlStr, challengeVec]() + auto task = [&, urlStr = std::move(urlStr), challengeVec = std::move(challengeVec)]() { std::shared_ptr client = m_mediaKeysClient.lock(); if (client) @@ -430,7 +431,7 @@ void MediaKeySession::onProcessChallenge(const char url[], const uint8_t challen void MediaKeySession::onKeyUpdated(const uint8_t keyId[], const uint8_t keyIdLength) { std::vector keyIdVec = std::vector{keyId, keyId + keyIdLength}; - auto task = [&, keyIdVec]() + auto task = [&, keyIdVec = std::move(keyIdVec)]() { std::shared_ptr client = m_mediaKeysClient.lock(); if (client) diff --git a/media/server/main/source/MediaKeysCapabilities.cpp b/media/server/main/source/MediaKeysCapabilities.cpp index f0ad87a81..b22836fb5 100644 --- a/media/server/main/source/MediaKeysCapabilities.cpp +++ b/media/server/main/source/MediaKeysCapabilities.cpp @@ -91,8 +91,9 @@ std::shared_ptr MediaKeysCapabilitiesFactory::getMediaKe namespace firebolt::rialto::server { -MediaKeysCapabilities::MediaKeysCapabilities(std::shared_ptr ocdmFactory, - std::shared_ptr ocdmSystemFactory) +MediaKeysCapabilities::MediaKeysCapabilities( + const std::shared_ptr &ocdmFactory, + const std::shared_ptr &ocdmSystemFactory) : m_ocdmSystemFactory{ocdmSystemFactory} { RIALTO_SERVER_LOG_DEBUG("entry:"); diff --git a/media/server/main/source/MediaKeysServerInternal.cpp b/media/server/main/source/MediaKeysServerInternal.cpp index aa74faa74..72240c3fc 100644 --- a/media/server/main/source/MediaKeysServerInternal.cpp +++ b/media/server/main/source/MediaKeysServerInternal.cpp @@ -103,8 +103,8 @@ namespace firebolt::rialto::server { MediaKeysServerInternal::MediaKeysServerInternal( const std::string &keySystem, const std::shared_ptr &mainThreadFactory, - std::shared_ptr ocdmSystemFactory, - std::shared_ptr mediaKeySessionFactory) + const std::shared_ptr &ocdmSystemFactory, + const std::shared_ptr &mediaKeySessionFactory) : m_mediaKeySessionFactory(mediaKeySessionFactory), m_kKeySystem(keySystem) { RIALTO_SERVER_LOG_DEBUG("entry:"); diff --git a/media/server/main/source/MediaPipelineCapabilities.cpp b/media/server/main/source/MediaPipelineCapabilities.cpp index 67cd7c19a..ffd5ec585 100644 --- a/media/server/main/source/MediaPipelineCapabilities.cpp +++ b/media/server/main/source/MediaPipelineCapabilities.cpp @@ -61,7 +61,7 @@ std::unique_ptr MediaPipelineCapabilitiesFactory::cr namespace firebolt::rialto::server { -MediaPipelineCapabilities::MediaPipelineCapabilities(std::shared_ptr gstCapabilitiesFactory) +MediaPipelineCapabilities::MediaPipelineCapabilities(const std::shared_ptr &gstCapabilitiesFactory) : m_kGstCapabilitiesFactory{gstCapabilitiesFactory} { RIALTO_SERVER_LOG_DEBUG("entry:"); diff --git a/media/server/main/source/MediaPipelineServerInternal.cpp b/media/server/main/source/MediaPipelineServerInternal.cpp index 3cd73fdfd..22fa5ca53 100644 --- a/media/server/main/source/MediaPipelineServerInternal.cpp +++ b/media/server/main/source/MediaPipelineServerInternal.cpp @@ -129,10 +129,10 @@ std::unique_ptr MediaPipelineServerInterna } MediaPipelineServerInternal::MediaPipelineServerInternal( - std::shared_ptr client, const VideoRequirements &videoRequirements, + const std::shared_ptr &client, const VideoRequirements &videoRequirements, const std::shared_ptr &gstPlayerFactory, int sessionId, const std::shared_ptr &shmBuffer, const std::shared_ptr &mainThreadFactory, - std::shared_ptr timerFactory, std::unique_ptr &&dataReaderFactory, + const std::shared_ptr &timerFactory, std::unique_ptr &&dataReaderFactory, std::unique_ptr &&activeRequests, IDecryptionService &decryptionService) : m_mediaPipelineClient(client), m_kGstPlayerFactory(gstPlayerFactory), m_kVideoRequirements(videoRequirements), m_sessionId{sessionId}, m_shmBuffer{shmBuffer}, m_dataReaderFactory{std::move(dataReaderFactory)}, diff --git a/media/server/service/source/CdmService.cpp b/media/server/service/source/CdmService.cpp index 78ff7eebc..01dbad6d4 100644 --- a/media/server/service/source/CdmService.cpp +++ b/media/server/service/source/CdmService.cpp @@ -31,8 +31,8 @@ namespace firebolt::rialto::server::service { CdmService::CdmService(std::shared_ptr &&mediaKeysFactory, std::shared_ptr &&mediaKeysCapabilitiesFactory) - : m_mediaKeysFactory{mediaKeysFactory}, m_mediaKeysCapabilitiesFactory{mediaKeysCapabilitiesFactory}, - m_isActive{false} + : m_mediaKeysFactory{std::move(mediaKeysFactory)}, + m_mediaKeysCapabilitiesFactory{std::move(mediaKeysCapabilitiesFactory)}, m_isActive{false} { RIALTO_SERVER_LOG_DEBUG("CdmService is constructed"); } diff --git a/media/server/service/source/MediaPipelineService.cpp b/media/server/service/source/MediaPipelineService.cpp index 5d2212017..e977c296f 100644 --- a/media/server/service/source/MediaPipelineService.cpp +++ b/media/server/service/source/MediaPipelineService.cpp @@ -32,7 +32,7 @@ MediaPipelineService::MediaPipelineService( IPlaybackService &playbackService, std::shared_ptr &&mediaPipelineFactory, std::shared_ptr &&mediaPipelineCapabilitiesFactory, IDecryptionService &decryptionService) - : m_playbackService{playbackService}, m_mediaPipelineFactory{mediaPipelineFactory}, + : m_playbackService{playbackService}, m_mediaPipelineFactory{std::move(mediaPipelineFactory)}, m_mediaPipelineCapabilities{mediaPipelineCapabilitiesFactory->createMediaPipelineCapabilities()}, m_decryptionService{decryptionService} { diff --git a/media/server/service/source/WebAudioPlayerService.cpp b/media/server/service/source/WebAudioPlayerService.cpp index e2182c201..b3831e63e 100644 --- a/media/server/service/source/WebAudioPlayerService.cpp +++ b/media/server/service/source/WebAudioPlayerService.cpp @@ -34,7 +34,7 @@ namespace firebolt::rialto::server::service { WebAudioPlayerService::WebAudioPlayerService(IPlaybackService &playbackService, std::shared_ptr &&webAudioPlayerFactory) - : m_playbackService{playbackService}, m_webAudioPlayerFactory{webAudioPlayerFactory} + : m_playbackService{playbackService}, m_webAudioPlayerFactory{std::move(webAudioPlayerFactory)} { RIALTO_SERVER_LOG_DEBUG("WebAudioPlayerService is constructed"); } diff --git a/serverManager/serverManagerSim/HttpRequest.cpp b/serverManager/serverManagerSim/HttpRequest.cpp index ba2bfb50c..154cff654 100644 --- a/serverManager/serverManagerSim/HttpRequest.cpp +++ b/serverManager/serverManagerSim/HttpRequest.cpp @@ -19,6 +19,7 @@ #include "HttpRequest.h" #include +#include namespace { @@ -28,16 +29,16 @@ std::vector splitUri(std::string uri) size_t pos = 0; while ((pos = uri.find("/")) != std::string::npos) { - const std::string token = uri.substr(0, pos); + std::string token = uri.substr(0, pos); if (!token.empty()) { - result.push_back(token); + result.push_back(std::move(token)); } uri.erase(0, pos + 1); } if (!uri.empty()) { - result.push_back(uri); + result.push_back(std::move(uri)); } return result; } diff --git a/tests/unittests/ipc/mocks/IpcServerMock.h b/tests/unittests/ipc/mocks/IpcServerMock.h index d0509ea1b..140ac9339 100644 --- a/tests/unittests/ipc/mocks/IpcServerMock.h +++ b/tests/unittests/ipc/mocks/IpcServerMock.h @@ -34,12 +34,13 @@ class ServerMock : public IServer virtual ~ServerMock() = default; MOCK_METHOD(bool, addSocket, - (const std::string &socketPath, std::function &)> clientConnectedCb, - std::function &)> clientDisconnectedCb), + (const std::string &socketPath, + const std::function &)> &clientConnectedCb, + const std::function &)> &clientDisconnectedCb), (override)); MOCK_METHOD(bool, addSocket, - (int fd, std::function &)> clientConnectedCb, - std::function &)> clientDisconnectedCb), + (int fd, const std::function &)> &clientConnectedCb, + const std::function &)> &clientDisconnectedCb), (override)); MOCK_METHOD(std::shared_ptr, addClient, (int socketFd, std::function &)> clientDisconnectedCb), (override)); diff --git a/tests/unittests/media/server/mocks/main/MainThreadMock.h b/tests/unittests/media/server/mocks/main/MainThreadMock.h index acf9dc5e2..d8359bfde 100644 --- a/tests/unittests/media/server/mocks/main/MainThreadMock.h +++ b/tests/unittests/media/server/mocks/main/MainThreadMock.h @@ -32,9 +32,9 @@ class MainThreadMock : public IMainThread public: MOCK_METHOD(int32_t, registerClient, (), (override)); MOCK_METHOD(void, unregisterClient, (uint32_t clientId), (override)); - MOCK_METHOD(void, enqueueTask, (uint32_t clientId, Task task), (override)); - MOCK_METHOD(void, enqueueTaskAndWait, (uint32_t clientId, Task task), (override)); - MOCK_METHOD(void, enqueuePriorityTaskAndWait, (uint32_t clientId, Task task), (override)); + MOCK_METHOD(void, enqueueTask, (uint32_t clientId, const Task &task), (override)); + MOCK_METHOD(void, enqueueTaskAndWait, (uint32_t clientId, const Task &task), (override)); + MOCK_METHOD(void, enqueuePriorityTaskAndWait, (uint32_t clientId, const Task &task), (override)); }; } // namespace firebolt::rialto::server::mock From 5a092bb70723b2a493b1c7489687d23aad9e52f2 Mon Sep 17 00:00:00 2001 From: Marcin Wojciechowski <105790697+skywojciechowskim@users.noreply.github.com> Date: Tue, 31 Mar 2026 13:34:41 +0200 Subject: [PATCH 15/29] Dedicated script to install dependencies and build native build (#470) Summary: Dedicated script to install dependencies and build native build Type: Feature Test Plan: UT/CT, Fullstack Jira: RDKEMW-15078 --- .github/workflows/native_rialto_build.yml | 18 ++------------ build_native.sh | 29 +++++++++++++++++++++++ install_dependencies_for_native_build.sh | 27 +++++++++++++++++++++ 3 files changed, 58 insertions(+), 16 deletions(-) create mode 100644 build_native.sh create mode 100644 install_dependencies_for_native_build.sh diff --git a/.github/workflows/native_rialto_build.yml b/.github/workflows/native_rialto_build.yml index 7ee8cf52a..580779f6c 100644 --- a/.github/workflows/native_rialto_build.yml +++ b/.github/workflows/native_rialto_build.yml @@ -36,25 +36,11 @@ jobs: - name: Install Dependencies run: | - sudo apt-get update - sudo apt-get install build-essential - sudo apt-get install cmake - sudo apt-get install libunwind-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev libgstreamer1.0-dev - - - name: Install protobuf - run: | - sudo apt-get install protobuf-compiler + sudo sh -x install_dependencies_for_native_build.sh - name: Build Rialto run: | - cmake . -B build -DNATIVE_BUILD=ON -DRIALTO_BUILD_TYPE="Debug" &> output_file.txt - if [ $? -eq 0 ] - then - make -C build &>> output_file.txt - else - exit 1 - fi - + sh -x build_native.sh &> output_file.txt - name: Report Build Status Success if: success() diff --git a/build_native.sh b/build_native.sh new file mode 100644 index 000000000..bcd2870c9 --- /dev/null +++ b/build_native.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2026 Sky UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Script for building the Rialto Native. + +cmake . -B build -DNATIVE_BUILD=ON -DRIALTO_BUILD_TYPE="Debug" +if [ $? -eq 0 ] +then + make -C build -j$(nproc) +else + exit 1 +fi \ No newline at end of file diff --git a/install_dependencies_for_native_build.sh b/install_dependencies_for_native_build.sh new file mode 100644 index 000000000..679fb67ff --- /dev/null +++ b/install_dependencies_for_native_build.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2026 Sky UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Entry script for building the dependencies for Rialto Native Build. + +apt-get update +apt-get install -y build-essential +apt-get install -y cmake +apt-get install -y libunwind-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev libgstreamer1.0-dev libyaml-cpp-dev +apt-get install -y protobuf-compiler \ No newline at end of file From 83f7a331827ef056728571b1bd540185f4a72e28 Mon Sep 17 00:00:00 2001 From: Koky2701 <90915184+Koky2701@users.noreply.github.com> Date: Fri, 17 Apr 2026 15:17:03 +0200 Subject: [PATCH 16/29] Fix memory leaks in Rialto master (#463) Fix memory leaks in Rialto master Summary: Finding potential memory leak points and adding fixes Type: Fix Test Plan: UT/CT, Fullstack Jira: RDKEMW-15495 --- common/source/EventThread.cpp | 12 ++-- common/source/Timer.cpp | 30 ++++++-- ipc/client/source/IpcChannelImpl.cpp | 70 +++++++++---------- ipc/server/source/IpcServerImpl.cpp | 7 +- media/server/main/include/ActiveRequests.h | 2 + media/server/main/source/ActiveRequests.cpp | 34 ++++++--- .../source/MediaPipelineServerInternal.cpp | 7 +- .../unittests/common/unittests/TimerTests.cpp | 28 ++++++++ .../ControlModuleServiceTests.cpp | 9 +++ .../activeRequests/ActiveRequestsTests.cpp | 23 ++++++ 10 files changed, 156 insertions(+), 66 deletions(-) diff --git a/common/source/EventThread.cpp b/common/source/EventThread.cpp index 1ad60188b..be4bc6d0e 100644 --- a/common/source/EventThread.cpp +++ b/common/source/EventThread.cpp @@ -54,13 +54,11 @@ EventThread::EventThread(std::string threadName) : m_kThreadName(std::move(threa EventThread::~EventThread() { - std::unique_lock locker(m_lock); - - m_shutdown = true; - - m_cond.notify_all(); - - locker.unlock(); + { + std::lock_guard locker(m_lock); + m_shutdown = true; + m_cond.notify_all(); + } if (m_thread.joinable()) m_thread.join(); diff --git a/common/source/Timer.cpp b/common/source/Timer.cpp index ae109a692..8469a4737 100644 --- a/common/source/Timer.cpp +++ b/common/source/Timer.cpp @@ -59,15 +59,22 @@ Timer::Timer(const std::chrono::milliseconds &timeout, const std::function lock{m_mutex}; - if (!m_cv.wait_for(lock, m_timeout, [this]() { return !m_active; })) + bool shouldExecuteCallback = false; { - if (m_active && m_callback) + std::unique_lock lock{m_mutex}; + if (!m_cv.wait_for(lock, m_timeout, [this]() { return !m_active; })) { - lock.unlock(); - m_callback(); + if (m_active && m_callback) + { + shouldExecuteCallback = true; + } } } + + if (shouldExecuteCallback) + { + m_callback(); + } } while (timerType == TimerType::PERIODIC && m_active); m_active = false; }); @@ -81,10 +88,19 @@ Timer::~Timer() void Timer::cancel() { m_active = false; + m_cv.notify_one(); + + if (std::this_thread::get_id() == m_thread.get_id()) + { + if (m_thread.joinable()) + { + m_thread.detach(); + } + return; + } - if (std::this_thread::get_id() != m_thread.get_id() && m_thread.joinable()) + if (m_thread.joinable()) { - m_cv.notify_one(); m_thread.join(); } } diff --git a/ipc/client/source/IpcChannelImpl.cpp b/ipc/client/source/IpcChannelImpl.cpp index 85c6fea52..f81a01003 100644 --- a/ipc/client/source/IpcChannelImpl.cpp +++ b/ipc/client/source/IpcChannelImpl.cpp @@ -562,37 +562,36 @@ void ChannelImpl::processTimeoutEvent() return; } - // check if any method call has no expired - std::unique_lock locker(m_lock); - // stores the timed-out method calls std::vector timedOuts; - // remove the method calls that have expired - const auto kNow = std::chrono::steady_clock::now(); - auto it = m_methodCalls.begin(); - while (it != m_methodCalls.end()) { - if (kNow >= it->second.timeoutDeadline) + // check if any method call has now expired + std::unique_lock locker(m_lock); + + // remove the method calls that have expired + const auto kNow = std::chrono::steady_clock::now(); + auto it = m_methodCalls.begin(); + while (it != m_methodCalls.end()) { - timedOuts.emplace_back(it->second); - it = m_methodCalls.erase(it); + if (kNow >= it->second.timeoutDeadline) + { + timedOuts.emplace_back(it->second); + it = m_methodCalls.erase(it); + } + else + { + ++it; + } } - else + + // if we still have method calls available, then re-calculate the timer for the next timeout + if (!m_methodCalls.empty()) { - ++it; + updateTimeoutTimer(); } } - // if we still have method calls available, then re-calculate the timer for the next timeout - if (!m_methodCalls.empty()) - { - updateTimeoutTimer(); - } - - // drop the lock and now terminate the timed out method calls - locker.unlock(); - for (auto &call : timedOuts) { completeWithError(&call, "Timed out"); @@ -718,28 +717,25 @@ void ChannelImpl::processReplyFromServer(const transport::MethodCallReply &reply { RIALTO_IPC_LOG_DEBUG("processing reply from server"); - std::unique_lock locker(m_lock); - - // find the original request + MethodCall methodCall; const uint64_t kSerialId = reply.reply_id(); - auto it = m_methodCalls.find(kSerialId); - if (it == m_methodCalls.end()) + { - RIALTO_IPC_LOG_ERROR("failed to find request for received reply with id %" PRIu64 "", reply.reply_id()); - return; - } + std::lock_guard locker(m_lock); - // take the method call and remove from the map of outstanding calls - MethodCall methodCall = it->second; - m_methodCalls.erase(it); + auto it = m_methodCalls.find(kSerialId); + if (it == m_methodCalls.end()) + { + RIALTO_IPC_LOG_ERROR("failed to find request for received reply with id %" PRIu64 "", reply.reply_id()); + return; + } - // update the timeout timer now a method call has been processed - updateTimeoutTimer(); + methodCall = it->second; + m_methodCalls.erase(it); - // can now drop the lock - locker.unlock(); + updateTimeoutTimer(); + } - // this is an actual reply so try and read it if (!methodCall.response->ParseFromString(reply.reply_message())) { RIALTO_IPC_LOG_ERROR("failed to parse method reply from server"); diff --git a/ipc/server/source/IpcServerImpl.cpp b/ipc/server/source/IpcServerImpl.cpp index 6536c704a..dea990eb9 100644 --- a/ipc/server/source/IpcServerImpl.cpp +++ b/ipc/server/source/IpcServerImpl.cpp @@ -1303,9 +1303,10 @@ bool ServerImpl::isClientConnected(uint64_t clientId) const */ void ServerImpl::disconnectClient(uint64_t clientId) { - std::unique_lock locker(m_clientsLock); - m_condemnedClients.insert(clientId); - locker.unlock(); + { + std::lock_guard locker(m_clientsLock); + m_condemnedClients.insert(clientId); + } wakeEventLoop(); } diff --git a/media/server/main/include/ActiveRequests.h b/media/server/main/include/ActiveRequests.h index b90ef73a7..8e4af1644 100644 --- a/media/server/main/include/ActiveRequests.h +++ b/media/server/main/include/ActiveRequests.h @@ -24,6 +24,7 @@ #include #include #include +#include namespace firebolt::rialto::server { @@ -54,6 +55,7 @@ class ActiveRequests : public IActiveRequests std::uint32_t m_bytesWritten; std::uint32_t m_maxMediaBytes; IMediaPipeline::MediaSegmentVector m_segments; + std::vector> m_segmentBuffers; }; ActiveRequests(); diff --git a/media/server/main/source/ActiveRequests.cpp b/media/server/main/source/ActiveRequests.cpp index 3431fc672..343adcd1c 100644 --- a/media/server/main/source/ActiveRequests.cpp +++ b/media/server/main/source/ActiveRequests.cpp @@ -18,18 +18,14 @@ */ #include "ActiveRequests.h" +#include "RialtoServerLogging.h" #include +#include #include namespace firebolt::rialto::server { -ActiveRequests::ActiveRequestsData::~ActiveRequestsData() -{ - for (std::unique_ptr &segment : m_segments) - { - delete[] segment->getData(); - } -} +ActiveRequests::ActiveRequestsData::~ActiveRequestsData() {} AddSegmentStatus ActiveRequests::ActiveRequestsData::addSegment(const std::unique_ptr &segment) { @@ -38,10 +34,10 @@ AddSegmentStatus ActiveRequests::ActiveRequestsData::addSegment(const std::uniqu std::unique_ptr copiedSegment = segment->copy(); - uint8_t *data = new uint8_t[segment->getDataLength()]; - std::memcpy(data, segment->getData(), segment->getDataLength()); + m_segmentBuffers.emplace_back(segment->getDataLength()); + std::memcpy(m_segmentBuffers.back().data(), segment->getData(), segment->getDataLength()); - copiedSegment->setData(segment->getDataLength(), data); + copiedSegment->setData(m_segmentBuffers.back().size(), m_segmentBuffers.back().data()); m_segments.push_back(std::move(copiedSegment)); m_bytesWritten += segment->getDataLength(); @@ -53,7 +49,23 @@ ActiveRequests::ActiveRequests() : m_currentId{0} {} std::uint32_t ActiveRequests::insert(const MediaSourceType &mediaSourceType, std::uint32_t maxMediaBytes) { std::unique_lock lock{m_mutex}; - m_requestMap.insert(std::make_pair(m_currentId, ActiveRequestsData(mediaSourceType, maxMediaBytes))); + + if (m_currentId == std::numeric_limits::max()) + { + m_currentId = 1; + } + + auto [it, inserted] = m_requestMap.emplace(m_currentId, ActiveRequestsData(mediaSourceType, maxMediaBytes)); + if (!inserted) + { + do + { + ++m_currentId; + } while (m_requestMap.find(m_currentId) != m_requestMap.end()); + + m_requestMap.emplace(m_currentId, ActiveRequestsData(mediaSourceType, maxMediaBytes)); + } + return m_currentId++; } diff --git a/media/server/main/source/MediaPipelineServerInternal.cpp b/media/server/main/source/MediaPipelineServerInternal.cpp index 22fa5ca53..c9bbe84b1 100644 --- a/media/server/main/source/MediaPipelineServerInternal.cpp +++ b/media/server/main/source/MediaPipelineServerInternal.cpp @@ -299,7 +299,12 @@ bool MediaPipelineServerInternal::removeSourceInternal(int32_t id) return false; } - m_needMediaDataTimers.erase(sourceIter->first); + MediaSourceType type = sourceIter->first; + + m_needMediaDataTimers.erase(type); + m_noAvailableSamplesCounter.erase(type); + m_isMediaTypeEosMap.erase(type); + m_attachedSources.erase(sourceIter); return true; } diff --git a/tests/unittests/common/unittests/TimerTests.cpp b/tests/unittests/common/unittests/TimerTests.cpp index f6f8aa1bb..dfc86c8f2 100644 --- a/tests/unittests/common/unittests/TimerTests.cpp +++ b/tests/unittests/common/unittests/TimerTests.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "ITimer.h" @@ -64,6 +65,33 @@ TEST(TimerTests, ShouldCancelTimer) EXPECT_FALSE(callFlag); } +TEST(TimerTests, ShouldCancelFromSameThread) +{ + std::mutex mtx; + std::condition_variable cv; + bool cancelled = false; + + std::shared_ptr timer; + timer = ITimerFactory::getFactory()->createTimer( + std::chrono::milliseconds{50}, + [&]() + { + timer->cancel(); + { + std::lock_guard lock{mtx}; + cancelled = true; + } + cv.notify_one(); + }, + TimerType::ONE_SHOT); + + std::unique_lock lock{mtx}; + cv.wait_for(lock, kEnoughTimeForTestToComplete, [&] { return cancelled; }); + + EXPECT_TRUE(cancelled); + EXPECT_FALSE(timer->isActive()); +} + TEST(TimerTests, ShouldTimeoutPeriodicTimer) { std::mutex mtx; diff --git a/tests/unittests/media/server/ipc/controlModuleService/ControlModuleServiceTests.cpp b/tests/unittests/media/server/ipc/controlModuleService/ControlModuleServiceTests.cpp index 89d61c1e7..d1b79780f 100644 --- a/tests/unittests/media/server/ipc/controlModuleService/ControlModuleServiceTests.cpp +++ b/tests/unittests/media/server/ipc/controlModuleService/ControlModuleServiceTests.cpp @@ -106,3 +106,12 @@ TEST_F(ControlModuleServiceTests, FactoryCreatesObject) { testFactoryCreatesObject(); } + +TEST_F(ControlModuleServiceTests, shouldRegisterClientWithoutSchemaVersion) +{ + clientWillConnect(); + sendClientConnected(); + controlServiceWillRegisterClient(); + sendRegisterClientRequestAndReceiveResponse(std::nullopt); + controlServiceWillRemoveControl(); +} diff --git a/tests/unittests/media/server/main/activeRequests/ActiveRequestsTests.cpp b/tests/unittests/media/server/main/activeRequests/ActiveRequestsTests.cpp index 171480c5c..89188d929 100644 --- a/tests/unittests/media/server/main/activeRequests/ActiveRequestsTests.cpp +++ b/tests/unittests/media/server/main/activeRequests/ActiveRequestsTests.cpp @@ -149,6 +149,29 @@ TEST_F(ActiveRequestsTests, shouldAddAndGetSegments) EXPECT_EQ(kSegments[0]->getType(), firebolt::rialto::MediaSourceType::AUDIO); } +TEST_F(ActiveRequestsTests, insertShouldHandleDuplicateId) +{ + EXPECT_EQ(0, m_sut.insert(firebolt::rialto::MediaSourceType::AUDIO, 100)); + EXPECT_EQ(firebolt::rialto::MediaSourceType::AUDIO, m_sut.getType(0)); + + EXPECT_EQ(1, m_sut.insert(firebolt::rialto::MediaSourceType::VIDEO, 100)); + EXPECT_EQ(firebolt::rialto::MediaSourceType::VIDEO, m_sut.getType(1)); + + EXPECT_EQ(2, m_sut.insert(firebolt::rialto::MediaSourceType::AUDIO, 100)); + EXPECT_EQ(firebolt::rialto::MediaSourceType::AUDIO, m_sut.getType(2)); +} + +TEST_F(ActiveRequestsTests, insertShouldResolveDuplicateIdCollision) +{ + EXPECT_EQ(0, m_sut.insert(firebolt::rialto::MediaSourceType::AUDIO, 100)); + EXPECT_EQ(firebolt::rialto::MediaSourceType::AUDIO, m_sut.getType(0)); + + m_sut.insert(firebolt::rialto::MediaSourceType::VIDEO, 100); + + EXPECT_EQ(2, m_sut.insert(firebolt::rialto::MediaSourceType::AUDIO, 100)); + EXPECT_EQ(firebolt::rialto::MediaSourceType::AUDIO, m_sut.getType(2)); +} + TEST_F(ActiveRequestsTests, shouldAddAndRemoveSegments) { std::vector data{'T', 'E', 'S', 'T'}; From 73dc08ca1194975cebe550d55003d4d5e906468c Mon Sep 17 00:00:00 2001 From: Marcin Wojciechowski <105790697+skywojciechowskim@users.noreply.github.com> Date: Mon, 20 Apr 2026 10:10:56 +0200 Subject: [PATCH 17/29] Fixed initialisation of playbackGroup elements in GenericPlayerContext. Added linking of typefind and the parser after the audio switch (#481) 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 --- media/server/gstplayer/include/Utils.h | 1 + .../gstplayer/source/GstGenericPlayer.cpp | 5 ++ media/server/gstplayer/source/Utils.cpp | 5 ++ .../source/tasks/generic/DeepElementAdded.cpp | 41 ++++++---- .../tasks/generic/UpdatePlaybackGroup.cpp | 14 ++++ .../GstGenericPlayerPrivateTest.cpp | 1 + .../common/GenericTasksTestsBase.cpp | 78 +++++++++++++++---- .../common/GenericTasksTestsBase.h | 2 + .../tasksTests/UpdatePlaybackGroupTest.cpp | 16 ++++ 9 files changed, 131 insertions(+), 32 deletions(-) diff --git a/media/server/gstplayer/include/Utils.h b/media/server/gstplayer/include/Utils.h index 81037266d..7351ec903 100644 --- a/media/server/gstplayer/include/Utils.h +++ b/media/server/gstplayer/include/Utils.h @@ -32,6 +32,7 @@ namespace firebolt::rialto::server bool isVideoDecoder(const firebolt::rialto::wrappers::IGstWrapper &gstWrapper, GstElement *element); bool isAudioDecoder(const firebolt::rialto::wrappers::IGstWrapper &gstWrapper, GstElement *element); bool isVideoParser(const firebolt::rialto::wrappers::IGstWrapper &gstWrapper, GstElement *element); +bool isAudioParser(const firebolt::rialto::wrappers::IGstWrapper &gstWrapper, GstElement *element); bool isVideoSink(const firebolt::rialto::wrappers::IGstWrapper &gstWrapper, GstElement *element); bool isAudioSink(const firebolt::rialto::wrappers::IGstWrapper &gstWrapper, GstElement *element); bool isSink(const firebolt::rialto::wrappers::IGstWrapper &gstWrapper, GstElement *element); diff --git a/media/server/gstplayer/source/GstGenericPlayer.cpp b/media/server/gstplayer/source/GstGenericPlayer.cpp index ef9e985b8..1db10cc15 100644 --- a/media/server/gstplayer/source/GstGenericPlayer.cpp +++ b/media/server/gstplayer/source/GstGenericPlayer.cpp @@ -301,6 +301,11 @@ void GstGenericPlayer::termPipeline() m_gstWrapper->gstObjectUnref(m_context.videoSink); m_context.videoSink = nullptr; } + if (m_context.playbackGroup.m_curAudioPlaysinkBin) + { + m_gstWrapper->gstObjectUnref(m_context.playbackGroup.m_curAudioPlaysinkBin); + m_context.playbackGroup.m_curAudioPlaysinkBin = nullptr; + } // Delete the pipeline m_gstWrapper->gstObjectUnref(m_context.pipeline); diff --git a/media/server/gstplayer/source/Utils.cpp b/media/server/gstplayer/source/Utils.cpp index 243aa060d..520778de4 100644 --- a/media/server/gstplayer/source/Utils.cpp +++ b/media/server/gstplayer/source/Utils.cpp @@ -64,6 +64,11 @@ bool isVideoParser(const firebolt::rialto::wrappers::IGstWrapper &gstWrapper, Gs return isType(gstWrapper, element, GST_ELEMENT_FACTORY_TYPE_PARSER | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO); } +bool isAudioParser(const firebolt::rialto::wrappers::IGstWrapper &gstWrapper, GstElement *element) +{ + return isType(gstWrapper, element, GST_ELEMENT_FACTORY_TYPE_PARSER | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO); +} + bool isVideoSink(const firebolt::rialto::wrappers::IGstWrapper &gstWrapper, GstElement *element) { return isType(gstWrapper, element, GST_ELEMENT_FACTORY_TYPE_SINK | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO); diff --git a/media/server/gstplayer/source/tasks/generic/DeepElementAdded.cpp b/media/server/gstplayer/source/tasks/generic/DeepElementAdded.cpp index 83e6bef12..4eb337ead 100644 --- a/media/server/gstplayer/source/tasks/generic/DeepElementAdded.cpp +++ b/media/server/gstplayer/source/tasks/generic/DeepElementAdded.cpp @@ -19,6 +19,7 @@ #include "tasks/generic/DeepElementAdded.h" #include "RialtoServerLogging.h" +#include "Utils.h" namespace { @@ -69,42 +70,48 @@ void DeepElementAdded::execute() const m_context.playbackGroup.m_gstPipeline = GST_ELEMENT(m_pipeline); RIALTO_SERVER_LOG_DEBUG("Element = %p Bin = %p Pipeline = %p", m_element, m_bin, m_pipeline); - if (m_callbackRegistered) - { - m_context.playbackGroup.m_curAudioTypefind = m_element; - } if (m_elementName) { if (m_gstWrapper->gstObjectCast(m_bin) == m_gstWrapper->gstObjectCast(m_context.playbackGroup.m_curAudioDecodeBin)) { - if (m_glibWrapper->gStrrstr(m_elementName, "parse")) + if (isAudioParser(*m_gstWrapper, m_element)) { RIALTO_SERVER_LOG_DEBUG("curAudioParse = %s", m_elementName); m_context.playbackGroup.m_curAudioParse = m_element; } - else if (m_glibWrapper->gStrrstr(m_elementName, "dec")) + else if (isAudioDecoder(*m_gstWrapper, m_element)) { RIALTO_SERVER_LOG_DEBUG("curAudioDecoder = %s", m_elementName); m_context.playbackGroup.m_curAudioDecoder = m_element; } + else if (m_callbackRegistered && m_glibWrapper->gStrrstr(m_elementName, "typefind")) + { + RIALTO_SERVER_LOG_DEBUG("curAudioTypefind = %s", m_elementName); + m_context.playbackGroup.m_curAudioTypefind = m_element; + } } - else + else if (isAudioSink(*m_gstWrapper, m_element)) { - if (m_glibWrapper->gStrrstr(m_elementName, "audiosink")) + GstElement *audioSinkParent = reinterpret_cast(m_gstWrapper->gstElementGetParent(m_element)); + if (audioSinkParent) { - GstElement *audioSinkParent = - reinterpret_cast(m_gstWrapper->gstElementGetParent(m_element)); - if (audioSinkParent) + gchar *audioSinkParentName = m_gstWrapper->gstElementGetName(audioSinkParent); + RIALTO_SERVER_LOG_DEBUG("audioSinkParentName = %s", audioSinkParentName); + if (audioSinkParentName && m_glibWrapper->gStrrstr(audioSinkParentName, "bin")) { - gchar *audioSinkParentName = m_gstWrapper->gstElementGetName(audioSinkParent); - RIALTO_SERVER_LOG_DEBUG("audioSinkParentName = %s", audioSinkParentName); - if (audioSinkParentName && m_glibWrapper->gStrrstr(audioSinkParentName, "bin")) + RIALTO_SERVER_LOG_DEBUG("curAudioPlaysinkBin = %s", audioSinkParentName); + if (m_context.playbackGroup.m_curAudioPlaysinkBin) { - RIALTO_SERVER_LOG_DEBUG("curAudioPlaysinkBin = %s", audioSinkParentName); - m_context.playbackGroup.m_curAudioPlaysinkBin = audioSinkParent; + // Unref previous audio playsink bin, if exists + m_gstWrapper->gstObjectUnref(m_context.playbackGroup.m_curAudioPlaysinkBin); } - m_glibWrapper->gFree(audioSinkParentName); + m_context.playbackGroup.m_curAudioPlaysinkBin = audioSinkParent; + } + else + { + m_gstWrapper->gstObjectUnref(audioSinkParent); } + m_glibWrapper->gFree(audioSinkParentName); } } } diff --git a/media/server/gstplayer/source/tasks/generic/UpdatePlaybackGroup.cpp b/media/server/gstplayer/source/tasks/generic/UpdatePlaybackGroup.cpp index 5946d6899..489ccf047 100644 --- a/media/server/gstplayer/source/tasks/generic/UpdatePlaybackGroup.cpp +++ b/media/server/gstplayer/source/tasks/generic/UpdatePlaybackGroup.cpp @@ -68,6 +68,20 @@ void UpdatePlaybackGroup::execute() const { m_player.setUseBuffering(); } + if (m_context.playbackGroup.m_linkTypefindParser && m_context.playbackGroup.m_curAudioTypefind && + m_context.playbackGroup.m_curAudioParse) + { + if (m_gstWrapper->gstElementLink(m_context.playbackGroup.m_curAudioTypefind, + m_context.playbackGroup.m_curAudioParse)) + { + RIALTO_SERVER_LOG_DEBUG("Linked typefind to parser"); + m_context.playbackGroup.m_linkTypefindParser = false; + } + else + { + RIALTO_SERVER_LOG_DEBUG("Failed to link typefind to parser"); + } + } } m_glibWrapper->gFree(elementName); m_gstWrapper->gstObjectUnref(typeFindParent); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp index 5f079f930..744507881 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp @@ -2276,6 +2276,7 @@ TEST_F(GstGenericPlayerPrivateTest, shouldReattachAmlhalasinkAudioSourceWithFirs EXPECT_CALL(*m_gstWrapperMock, gstElementSyncStateWithParent(&decodeBin)).WillOnce(Return(TRUE)); // end of reattachSource: caps was updated to configCaps inside performAudioTrackCodecChannelSwitch EXPECT_CALL(*m_gstWrapperMock, gstCapsUnref(&configCaps)); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(&playsinkBin)); std::unique_ptr source = std::make_unique("audio/aac", false); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp index f379b5884..37f814afe 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp @@ -1941,7 +1941,7 @@ void GenericTasksTestsBase::shouldNotRegisterCallbackWhenElementNameIsNotTypefin EXPECT_CALL(*testContext->m_gstWrapper, gstElementGetName(testContext->m_element)) .WillOnce(Return(testContext->m_elementName)); EXPECT_CALL(*testContext->m_glibWrapper, gStrrstr(testContext->m_elementName, StrEq("typefind"))) - .WillOnce(Return(nullptr)); + .WillRepeatedly(Return(nullptr)); EXPECT_CALL(*testContext->m_glibWrapper, gFree(testContext->m_elementName)); } @@ -1952,7 +1952,7 @@ void GenericTasksTestsBase::shouldRegisterCallbackForTypefindElement() EXPECT_CALL(*testContext->m_gstWrapper, gstElementGetName(testContext->m_element)) .WillOnce(Return(testContext->m_typefindElementName)); EXPECT_CALL(*testContext->m_glibWrapper, gStrrstr(testContext->m_typefindElementName, StrEq("typefind"))) - .WillOnce(Return(testContext->m_typefindElementName)); + .WillRepeatedly(Return(testContext->m_typefindElementName)); EXPECT_CALL(*testContext->m_glibWrapper, gSignalConnect(G_OBJECT(testContext->m_element), StrEq("have-type"), _, &testContext->m_gstPlayer)) .WillOnce(Return(kSignalId)); @@ -1982,9 +1982,19 @@ void GenericTasksTestsBase::shouldUpdatePlaybackGroupWhenCallbackIsCalled() void GenericTasksTestsBase::shouldSetTypefindElement() { - EXPECT_CALL(*testContext->m_gstWrapper, gstObjectCast(nullptr)).WillOnce(Return(nullptr)); - EXPECT_CALL(*testContext->m_glibWrapper, gStrrstr(testContext->m_typefindElementName, StrEq("audiosink"))) - .WillOnce(Return(nullptr)); + testContext->m_context.playbackGroup.m_curAudioDecodeBin = &testContext->m_audioDecodeBin; + EXPECT_CALL(*testContext->m_gstWrapper, gstObjectCast(&testContext->m_audioDecodeBin)) + .WillOnce(Return(&testContext->m_obj1)); + + EXPECT_CALL(*testContext->m_gstWrapper, gstElementGetFactory(_)).WillRepeatedly(Return(testContext->m_elementFactory)); + EXPECT_CALL(*testContext->m_gstWrapper, + gstElementFactoryListIsType(testContext->m_elementFactory, + GST_ELEMENT_FACTORY_TYPE_PARSER | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) + .WillOnce(Return(FALSE)); + EXPECT_CALL(*testContext->m_gstWrapper, + gstElementFactoryListIsType(testContext->m_elementFactory, + GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) + .WillOnce(Return(FALSE)); } void GenericTasksTestsBase::triggerDeepElementAdded() @@ -2030,8 +2040,11 @@ void GenericTasksTestsBase::shouldSetParseElement() testContext->m_context.playbackGroup.m_curAudioDecodeBin = &testContext->m_audioDecodeBin; EXPECT_CALL(*testContext->m_gstWrapper, gstObjectCast(&testContext->m_audioDecodeBin)) .WillOnce(Return(&testContext->m_obj1)); - EXPECT_CALL(*testContext->m_glibWrapper, gStrrstr(testContext->m_parseElementName, StrEq("parse"))) - .WillOnce(Return(testContext->m_parseElementName)); + EXPECT_CALL(*testContext->m_gstWrapper, gstElementGetFactory(_)).WillRepeatedly(Return(testContext->m_elementFactory)); + EXPECT_CALL(*testContext->m_gstWrapper, + gstElementFactoryListIsType(testContext->m_elementFactory, + GST_ELEMENT_FACTORY_TYPE_PARSER | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) + .WillOnce(Return(TRUE)); } void GenericTasksTestsBase::checkParsePlaybackGroupAdded() @@ -2056,10 +2069,15 @@ void GenericTasksTestsBase::shouldSetDecoderElement() testContext->m_context.playbackGroup.m_curAudioDecodeBin = &testContext->m_audioDecodeBin; EXPECT_CALL(*testContext->m_gstWrapper, gstObjectCast(&testContext->m_audioDecodeBin)) .WillOnce(Return(&testContext->m_obj1)); - EXPECT_CALL(*testContext->m_glibWrapper, gStrrstr(testContext->m_decoderElementName, StrEq("parse"))) - .WillOnce(Return(nullptr)); - EXPECT_CALL(*testContext->m_glibWrapper, gStrrstr(testContext->m_decoderElementName, StrEq("dec"))) - .WillOnce(Return(testContext->m_decoderElementName)); + EXPECT_CALL(*testContext->m_gstWrapper, gstElementGetFactory(_)).WillRepeatedly(Return(testContext->m_elementFactory)); + EXPECT_CALL(*testContext->m_gstWrapper, + gstElementFactoryListIsType(testContext->m_elementFactory, + GST_ELEMENT_FACTORY_TYPE_PARSER | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) + .WillOnce(Return(FALSE)); + EXPECT_CALL(*testContext->m_gstWrapper, + gstElementFactoryListIsType(testContext->m_elementFactory, + GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) + .WillOnce(Return(TRUE)); } void GenericTasksTestsBase::checkDecoderPlaybackGroupAdded() @@ -2074,8 +2092,11 @@ void GenericTasksTestsBase::checkDecoderPlaybackGroupAdded() void GenericTasksTestsBase::shouldSetGenericElement() { EXPECT_CALL(*testContext->m_gstWrapper, gstObjectCast(nullptr)).WillOnce(Return(nullptr)); - EXPECT_CALL(*testContext->m_glibWrapper, gStrrstr(testContext->m_elementName, StrEq("audiosink"))) - .WillOnce(Return(nullptr)); + EXPECT_CALL(*testContext->m_gstWrapper, gstElementGetFactory(_)).WillRepeatedly(Return(testContext->m_elementFactory)); + EXPECT_CALL(*testContext->m_gstWrapper, + gstElementFactoryListIsType(testContext->m_elementFactory, + GST_ELEMENT_FACTORY_TYPE_SINK | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) + .WillOnce(Return(FALSE)); } void GenericTasksTestsBase::shouldSetAudioSinkElement() @@ -2089,8 +2110,11 @@ void GenericTasksTestsBase::shouldSetAudioSinkElement() EXPECT_CALL(*testContext->m_glibWrapper, gFree(testContext->m_audioSinkElementName)); EXPECT_CALL(*testContext->m_gstWrapper, gstObjectCast(nullptr)).WillOnce(Return(nullptr)); - EXPECT_CALL(*testContext->m_glibWrapper, gStrrstr(testContext->m_audioSinkElementName, StrEq("audiosink"))) - .WillOnce(Return(testContext->m_audioSinkElementName)); + EXPECT_CALL(*testContext->m_gstWrapper, gstElementGetFactory(_)).WillRepeatedly(Return(testContext->m_elementFactory)); + EXPECT_CALL(*testContext->m_gstWrapper, + gstElementFactoryListIsType(testContext->m_elementFactory, + GST_ELEMENT_FACTORY_TYPE_SINK | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) + .WillOnce(Return(TRUE)); } void GenericTasksTestsBase::shouldHaveNullParentSink() @@ -2098,6 +2122,7 @@ void GenericTasksTestsBase::shouldHaveNullParentSink() EXPECT_CALL(*testContext->m_gstWrapper, gstElementGetParent(testContext->m_element)) .WillOnce(Return(GST_OBJECT(&testContext->m_audioParentSink))); EXPECT_CALL(*testContext->m_gstWrapper, gstElementGetName(&testContext->m_audioParentSink)).WillOnce(Return(nullptr)); + EXPECT_CALL(*testContext->m_gstWrapper, gstObjectUnref(&testContext->m_audioParentSink)); EXPECT_CALL(*testContext->m_glibWrapper, gFree(nullptr)); } @@ -2108,11 +2133,16 @@ void GenericTasksTestsBase::shouldHaveNonBinParentSink() EXPECT_CALL(*testContext->m_gstWrapper, gstElementGetName(&testContext->m_audioParentSink)) .WillOnce(Return(testContext->m_elementName)); EXPECT_CALL(*testContext->m_glibWrapper, gStrrstr(testContext->m_elementName, StrEq("bin"))).WillOnce(Return(nullptr)); + EXPECT_CALL(*testContext->m_gstWrapper, gstObjectUnref(&testContext->m_audioParentSink)); EXPECT_CALL(*testContext->m_glibWrapper, gFree(testContext->m_elementName)); } void GenericTasksTestsBase::shouldHaveBinParentSink() { + // Previous bin should be unrefed + testContext->m_context.playbackGroup.m_curAudioPlaysinkBin = &testContext->m_childElement; + EXPECT_CALL(*testContext->m_gstWrapper, gstObjectUnref(&testContext->m_childElement)); + EXPECT_CALL(*testContext->m_gstWrapper, gstElementGetParent(testContext->m_element)) .WillOnce(Return(GST_OBJECT(&testContext->m_audioParentSink))); EXPECT_CALL(*testContext->m_gstWrapper, gstElementGetName(&testContext->m_audioParentSink)) @@ -2235,6 +2265,24 @@ void GenericTasksTestsBase::shouldTriggerSetUseBuffering() EXPECT_CALL(testContext->m_gstPlayer, setUseBuffering()).WillOnce(Return(true)); } +void GenericTasksTestsBase::shouldLinkTypefindAndParser() +{ + testContext->m_context.playbackGroup.m_linkTypefindParser = true; + testContext->m_context.playbackGroup.m_curAudioParse = &testContext->m_childElement; + EXPECT_CALL(*testContext->m_gstWrapper, + gstElementLink(testContext->m_element, testContext->m_context.playbackGroup.m_curAudioParse)) + .WillOnce(Return(TRUE)); +} + +void GenericTasksTestsBase::shouldFailToLinkTypefindAndParser() +{ + testContext->m_context.playbackGroup.m_linkTypefindParser = true; + testContext->m_context.playbackGroup.m_curAudioParse = &testContext->m_childElement; + EXPECT_CALL(*testContext->m_gstWrapper, + gstElementLink(testContext->m_element, testContext->m_context.playbackGroup.m_curAudioParse)) + .WillOnce(Return(FALSE)); +} + void GenericTasksTestsBase::shouldStopGstPlayer() { auto audioStreamIt{testContext->m_context.streamInfo.find(firebolt::rialto::MediaSourceType::AUDIO)}; diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h index 29f64e16a..b1ee082db 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h @@ -235,6 +235,8 @@ class GenericTasksTestsBase : public ::testing::Test void checkPlaybackGroupAdded(); void setUseBufferingPending(); void shouldTriggerSetUseBuffering(); + void shouldLinkTypefindAndParser(); + void shouldFailToLinkTypefindAndParser(); // Stop test methods void shouldStopGstPlayer(); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/UpdatePlaybackGroupTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/UpdatePlaybackGroupTest.cpp index dfed351b4..74312e50f 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/UpdatePlaybackGroupTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/UpdatePlaybackGroupTest.cpp @@ -72,3 +72,19 @@ TEST_F(UpdatePlaybackGroupTest, shouldTriggerUseBuffering) triggerUpdatePlaybackGroup(); checkPlaybackGroupAdded(); } + +TEST_F(UpdatePlaybackGroupTest, shouldLinkTypefindWithParser) +{ + shouldSuccessfullyFindTypefindAndParent(); + shouldLinkTypefindAndParser(); + triggerUpdatePlaybackGroup(); + checkPlaybackGroupAdded(); +} + +TEST_F(UpdatePlaybackGroupTest, shouldFailToLinkTypefindWithParser) +{ + shouldSuccessfullyFindTypefindAndParent(); + shouldFailToLinkTypefindAndParser(); + triggerUpdatePlaybackGroup(); + checkPlaybackGroupAdded(); +} From c1e7f20f5069ae44fa5755907c12bec77bcd9388 Mon Sep 17 00:00:00 2001 From: Koky2701 <90915184+Koky2701@users.noreply.github.com> Date: Mon, 20 Apr 2026 12:37:37 +0200 Subject: [PATCH 18/29] Bringing back support for "queued-frames" and "report-decode-errors" properties (#485) Summary: 'report-decode-errors' and 'queued-frames' properties missing from RialtoMSEVideoSink Type: Feature Test Plan: UT/CT, Fullstack Jira: RDKEMW-12692 --- media/client/ipc/include/MediaPipelineIpc.h | 4 + .../client/ipc/interface/IMediaPipelineIpc.h | 24 ++++ media/client/ipc/source/MediaPipelineIpc.cpp | 67 +++++++++++ media/client/main/include/MediaPipeline.h | 4 + .../client/main/include/MediaPipelineProxy.h | 8 ++ media/client/main/source/MediaPipeline.cpp | 10 ++ media/public/include/IMediaPipeline.h | 24 ++++ media/server/gstplayer/CMakeLists.txt | 1 + .../gstplayer/include/GenericPlayerContext.h | 5 + .../gstplayer/include/GstGenericPlayer.h | 3 + .../include/IGstGenericPlayerPrivate.h | 7 ++ .../include/tasks/IGenericPlayerTaskFactory.h | 15 +++ .../tasks/generic/GenericPlayerTaskFactory.h | 4 + .../tasks/generic/SetReportDecodeErrors.h | 49 ++++++++ .../gstplayer/interface/IGstGenericPlayer.h | 19 ++++ .../gstplayer/source/GstGenericPlayer.cpp | 80 +++++++++++++ .../generic/GenericPlayerTaskFactory.cpp | 9 ++ .../tasks/generic/SetReportDecodeErrors.cpp | 52 +++++++++ .../source/tasks/generic/SetupElement.cpp | 5 +- .../ipc/include/MediaPipelineModuleService.h | 8 ++ .../ipc/source/MediaPipelineModuleService.cpp | 34 ++++++ .../include/MediaPipelineServerInternal.h | 28 +++++ .../source/MediaPipelineServerInternal.cpp | 57 ++++++++++ .../service/include/IMediaPipelineService.h | 2 + .../service/source/MediaPipelineService.cpp | 28 +++++ .../service/source/MediaPipelineService.h | 2 + proto/mediapipelinemodule.proto | 50 +++++++++ .../MediaPipelineProtoRequestMatchers.h | 14 +++ .../client/mocks/MediaPipelineModuleMock.h | 20 ++++ .../tests/base/MediaPipelineTestMethods.cpp | 28 +++++ .../tests/base/MediaPipelineTestMethods.h | 4 + .../client/tests/mse/PipelinePropertyTest.cpp | 10 ++ .../mediaPipeline/PipelinePropertyTest.cpp | 12 +- .../unittests/media/client/ipc/CMakeLists.txt | 2 + .../mediaPipelineIpc/GetQueuedFramesTest.cpp | 100 +++++++++++++++++ .../SetReportDecodeErrorsTest.cpp | 96 ++++++++++++++++ .../media/client/main/CMakeLists.txt | 2 + .../mediaPipeline/GetQueuedFramesTest.cpp | 63 +++++++++++ .../mediaPipeline/MediaPipelineProxyTest.cpp | 15 +++ .../SetReportDecodeErrorsTest.cpp | 58 ++++++++++ .../client/mocks/ipc/MediaPipelineIpcMock.h | 2 + .../main/MediaPipelineAndControlClientMock.h | 2 + .../media/server/gstplayer/CMakeLists.txt | 1 + .../GstGenericPlayerPrivateTest.cpp | 30 +++++ .../genericPlayer/GstGenericPlayerTest.cpp | 37 ++++++ .../common/GenericTasksTestsBase.cpp | 15 +++ .../common/GenericTasksTestsBase.h | 4 + .../common/GstGenericPlayerTestCommon.cpp | 14 +++ .../common/GstGenericPlayerTestCommon.h | 1 + .../GenericPlayerTaskFactoryTest.cpp | 8 ++ .../tasksTests/SetReportDecodeErrorsTest.cpp | 30 +++++ .../MediaPipelineModuleServiceTests.cpp | 32 ++++++ ...MediaPipelineModuleServiceTestsFixture.cpp | 73 ++++++++++++ .../MediaPipelineModuleServiceTestsFixture.h | 8 ++ .../MiscellaneousFunctionsTest.cpp | 106 ++++++++++++++++++ .../gstplayer/GenericPlayerTaskFactoryMock.h | 4 + .../mocks/gstplayer/GstGenericPlayerMock.h | 3 + .../gstplayer/GstGenericPlayerPrivateMock.h | 1 + .../main/MediaPipelineServerInternalMock.h | 2 + .../mocks/service/MediaPipelineServiceMock.h | 2 + .../MediaPipelineServiceTests.cpp | 40 +++++++ .../MediaPipelineServiceTestsFixture.cpp | 44 ++++++++ .../MediaPipelineServiceTestsFixture.h | 8 ++ 63 files changed, 1488 insertions(+), 2 deletions(-) create mode 100644 media/server/gstplayer/include/tasks/generic/SetReportDecodeErrors.h create mode 100644 media/server/gstplayer/source/tasks/generic/SetReportDecodeErrors.cpp create mode 100644 tests/unittests/media/client/ipc/mediaPipelineIpc/GetQueuedFramesTest.cpp create mode 100644 tests/unittests/media/client/ipc/mediaPipelineIpc/SetReportDecodeErrorsTest.cpp create mode 100644 tests/unittests/media/client/main/mediaPipeline/GetQueuedFramesTest.cpp create mode 100644 tests/unittests/media/client/main/mediaPipeline/SetReportDecodeErrorsTest.cpp create mode 100644 tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/SetReportDecodeErrorsTest.cpp diff --git a/media/client/ipc/include/MediaPipelineIpc.h b/media/client/ipc/include/MediaPipelineIpc.h index db45d00d9..e2e907f68 100644 --- a/media/client/ipc/include/MediaPipelineIpc.h +++ b/media/client/ipc/include/MediaPipelineIpc.h @@ -93,6 +93,10 @@ class MediaPipelineIpc : public IMediaPipelineIpc, public IpcModule bool setImmediateOutput(int32_t sourceId, bool immediateOutput) override; + bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) override; + + bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) override; + bool getImmediateOutput(int32_t sourceId, bool &immediateOutput) override; bool getStats(int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames) override; diff --git a/media/client/ipc/interface/IMediaPipelineIpc.h b/media/client/ipc/interface/IMediaPipelineIpc.h index 782a1931b..bee5f597d 100644 --- a/media/client/ipc/interface/IMediaPipelineIpc.h +++ b/media/client/ipc/interface/IMediaPipelineIpc.h @@ -188,6 +188,30 @@ class IMediaPipelineIpc */ virtual bool setImmediateOutput(int32_t sourceId, bool immediateOutput) = 0; + /** + * @brief Sets the "Report Decode Errors" property for this source. + * + * This method is asynchronous, it will set the "Report Decode Errors" property + * + * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() + * @param[in] reportDecodeErrors : Set Report Decode Errors mode on the sink + * + * @retval true on success. + */ + virtual bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) = 0; + + /** + * @brief Gets the queued frames for this source. + * + * This method is synchronous, it gets the queued frames property + * + * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() + * @param[out] queuedFrames : Get queued frames on the decoder + * + * @retval true on success. + */ + virtual bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) = 0; + /** * @brief Gets the "Immediate Output" property for this source. * diff --git a/media/client/ipc/source/MediaPipelineIpc.cpp b/media/client/ipc/source/MediaPipelineIpc.cpp index ff1b09847..890ab8905 100644 --- a/media/client/ipc/source/MediaPipelineIpc.cpp +++ b/media/client/ipc/source/MediaPipelineIpc.cpp @@ -567,6 +567,73 @@ bool MediaPipelineIpc::setImmediateOutput(int32_t sourceId, bool immediateOutput return true; } +bool MediaPipelineIpc::setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) +{ + if (!reattachChannelIfRequired()) + { + RIALTO_CLIENT_LOG_ERROR("Reattachment of the ipc channel failed, ipc disconnected"); + return false; + } + + firebolt::rialto::SetReportDecodeErrorsRequest request; + + request.set_session_id(m_sessionId); + request.set_source_id(sourceId); + request.set_report_decode_errors(reportDecodeErrors); + + firebolt::rialto::SetReportDecodeErrorsResponse response; + auto ipcController = m_ipc.createRpcController(); + auto blockingClosure = m_ipc.createBlockingClosure(); + m_mediaPipelineStub->setReportDecodeErrors(ipcController.get(), &request, &response, blockingClosure.get()); + + // wait for the call to complete + blockingClosure->wait(); + + // check the result + if (ipcController->Failed()) + { + RIALTO_CLIENT_LOG_ERROR("failed to set report decode error due to '%s'", ipcController->ErrorText().c_str()); + return false; + } + + return true; +} + +bool MediaPipelineIpc::getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) +{ + if (!reattachChannelIfRequired()) + { + RIALTO_CLIENT_LOG_ERROR("Reattachment of the ipc channel failed, ipc disconnected"); + return false; + } + + firebolt::rialto::GetQueuedFramesRequest request; + + request.set_session_id(m_sessionId); + request.set_source_id(sourceId); + + firebolt::rialto::GetQueuedFramesResponse response; + auto ipcController = m_ipc.createRpcController(); + auto blockingClosure = m_ipc.createBlockingClosure(); + m_mediaPipelineStub->getQueuedFrames(ipcController.get(), &request, &response, blockingClosure.get()); + + // wait for the call to complete + blockingClosure->wait(); + + // check the result + if (ipcController->Failed()) + { + RIALTO_CLIENT_LOG_ERROR("failed to get queued frames due to '%s'", ipcController->ErrorText().c_str()); + return false; + } + else + { + queuedFrames = response.queued_frames(); + } + + return true; +} + bool MediaPipelineIpc::getImmediateOutput(int32_t sourceId, bool &immediateOutput) { if (!reattachChannelIfRequired()) diff --git a/media/client/main/include/MediaPipeline.h b/media/client/main/include/MediaPipeline.h index 13075a511..d47f0fe16 100644 --- a/media/client/main/include/MediaPipeline.h +++ b/media/client/main/include/MediaPipeline.h @@ -132,6 +132,10 @@ class MediaPipeline : public IMediaPipelineAndIControlClient, public IMediaPipel bool setImmediateOutput(int32_t sourceId, bool immediateOutput) override; + bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) override; + + bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) override; + bool getImmediateOutput(int32_t sourceId, bool &immediateOutput) override; bool getStats(int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames) override; diff --git a/media/client/main/include/MediaPipelineProxy.h b/media/client/main/include/MediaPipelineProxy.h index b38371963..af45114aa 100644 --- a/media/client/main/include/MediaPipelineProxy.h +++ b/media/client/main/include/MediaPipelineProxy.h @@ -68,6 +68,14 @@ class MediaPipelineProxy : public IMediaPipelineAndIControlClient { return m_mediaPipeline->setImmediateOutput(sourceId, immediateOutput); } + bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) + { + return m_mediaPipeline->setReportDecodeErrors(sourceId, reportDecodeErrors); + } + bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) + { + return m_mediaPipeline->getQueuedFrames(sourceId, queuedFrames); + } bool getImmediateOutput(int32_t sourceId, bool &immediateOutput) { return m_mediaPipeline->getImmediateOutput(sourceId, immediateOutput); diff --git a/media/client/main/source/MediaPipeline.cpp b/media/client/main/source/MediaPipeline.cpp index 8e26a036f..ff123515e 100644 --- a/media/client/main/source/MediaPipeline.cpp +++ b/media/client/main/source/MediaPipeline.cpp @@ -313,6 +313,16 @@ bool MediaPipeline::setImmediateOutput(int32_t sourceId, bool immediateOutput) return m_mediaPipelineIpc->setImmediateOutput(sourceId, immediateOutput); } +bool MediaPipeline::setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) +{ + return m_mediaPipelineIpc->setReportDecodeErrors(sourceId, reportDecodeErrors); +} + +bool MediaPipeline::getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) +{ + return m_mediaPipelineIpc->getQueuedFrames(sourceId, queuedFrames); +} + bool MediaPipeline::getImmediateOutput(int32_t sourceId, bool &immediateOutput) { return m_mediaPipelineIpc->getImmediateOutput(sourceId, immediateOutput); diff --git a/media/public/include/IMediaPipeline.h b/media/public/include/IMediaPipeline.h index e6c68787e..10b910f20 100644 --- a/media/public/include/IMediaPipeline.h +++ b/media/public/include/IMediaPipeline.h @@ -1227,6 +1227,30 @@ class IMediaPipeline */ virtual bool setImmediateOutput(int32_t sourceId, bool immediateOutput) = 0; + /** + * @brief Sets the "Report Decode Errors" property for this source. + * + * This method is asynchronous, it will set the "Report Decode Errors" property + * + * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() + * @param[in] reportDecodeErrors : Set Report Decode Errors mode on the sink + * + * @retval true on success. + */ + virtual bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) = 0; + + /** + * @brief Gets the queued frames for this source. + * + * This method is synchronous, it gets the queued frames property + * + * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() + * @param[out] queuedFrames : Get queued frames on the decoder + * + * @retval true on success. + */ + virtual bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) = 0; + /** * @brief Gets the "Immediate Output" property for this source. * diff --git a/media/server/gstplayer/CMakeLists.txt b/media/server/gstplayer/CMakeLists.txt index 6756184da..58278418a 100644 --- a/media/server/gstplayer/CMakeLists.txt +++ b/media/server/gstplayer/CMakeLists.txt @@ -57,6 +57,7 @@ add_library( source/tasks/generic/SetMute.cpp source/tasks/generic/SetPlaybackRate.cpp source/tasks/generic/SetPosition.cpp + source/tasks/generic/SetReportDecodeErrors.cpp source/tasks/generic/SetSourcePosition.cpp source/tasks/generic/SetSubtitleOffset.cpp source/tasks/generic/SetStreamSyncMode.cpp diff --git a/media/server/gstplayer/include/GenericPlayerContext.h b/media/server/gstplayer/include/GenericPlayerContext.h index d97deb4b1..237646ca3 100644 --- a/media/server/gstplayer/include/GenericPlayerContext.h +++ b/media/server/gstplayer/include/GenericPlayerContext.h @@ -155,6 +155,11 @@ struct GenericPlayerContext */ std::optional pendingImmediateOutputForVideo{}; + /** + * @brief Pending report decode errors for MediaSourceType::VIDEO + */ + std::optional pendingReportDecodeErrorsForVideo{}; + /** * @brief Pending low latency */ diff --git a/media/server/gstplayer/include/GstGenericPlayer.h b/media/server/gstplayer/include/GstGenericPlayer.h index 54476f2ca..8ea963d8d 100644 --- a/media/server/gstplayer/include/GstGenericPlayer.h +++ b/media/server/gstplayer/include/GstGenericPlayer.h @@ -119,7 +119,9 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva void setPlaybackRate(double rate) override; bool getPosition(std::int64_t &position) override; bool setImmediateOutput(const MediaSourceType &mediaSourceType, bool immediateOutput) override; + bool setReportDecodeErrors(const MediaSourceType &mediaSourceType, bool reportDecodeErrors) override; bool getImmediateOutput(const MediaSourceType &mediaSourceType, bool &immediateOutput) override; + bool getQueuedFrames(uint32_t &queuedFrames) override; bool getStats(const MediaSourceType &mediaSourceType, uint64_t &renderedFrames, uint64_t &droppedFrames) override; void setVolume(double targetVolume, uint32_t volumeDuration, firebolt::rialto::EaseType easeType) override; bool getVolume(double &volume) override; @@ -153,6 +155,7 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva void scheduleAllSourcesAttached() override; bool setVideoSinkRectangle() override; bool setImmediateOutput() override; + bool setReportDecodeErrors() override; bool setShowVideoWindow() override; bool setLowLatency() override; bool setSync() override; diff --git a/media/server/gstplayer/include/IGstGenericPlayerPrivate.h b/media/server/gstplayer/include/IGstGenericPlayerPrivate.h index fb48a2816..9b6d54af0 100644 --- a/media/server/gstplayer/include/IGstGenericPlayerPrivate.h +++ b/media/server/gstplayer/include/IGstGenericPlayerPrivate.h @@ -80,6 +80,13 @@ class IGstGenericPlayerPrivate */ virtual bool setImmediateOutput() = 0; + /** + * @brief Sets report decode error. Called by the worker thread. + * + * @retval true on success. + */ + virtual bool setReportDecodeErrors() = 0; + /** * @brief Sets the low latency property. Called by the worker thread. * diff --git a/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h b/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h index f8de67cb4..54b8fc621 100644 --- a/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h +++ b/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h @@ -482,6 +482,21 @@ class IGenericPlayerTaskFactory const firebolt::rialto::MediaSourceType &type, bool immediateOutput) const = 0; + /** + * @brief Creates a SetReportDecodeErrors task. + * + * @param[in] context : The GstPlayer context + * @param[in] player : The GstPlayer instance + * @param[in] type : The media source type + * @param[in] reportDecodeErrors : the value to set for report decode error + * + * @retval the new SetReportDecodeErrors task instance. + */ + virtual std::unique_ptr createSetReportDecodeErrors(GenericPlayerContext &context, + IGstGenericPlayerPrivate &player, + const firebolt::rialto::MediaSourceType &type, + bool reportDecodeErrors) const = 0; + /** * @brief Creates a SetBufferingLimit task. * diff --git a/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h b/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h index aa9a06e55..79e31838b 100644 --- a/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h +++ b/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h @@ -120,6 +120,10 @@ class GenericPlayerTaskFactory : public IGenericPlayerTaskFactory std::unique_ptr createSetImmediateOutput(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, const firebolt::rialto::MediaSourceType &type, bool immediateOutput) const override; + std::unique_ptr createSetReportDecodeErrors(GenericPlayerContext &context, + IGstGenericPlayerPrivate &player, + const firebolt::rialto::MediaSourceType &type, + bool reportDecodeErrors) const override; std::unique_ptr createSetBufferingLimit(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, std::uint32_t limit) const override; std::unique_ptr createSetUseBuffering(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, diff --git a/media/server/gstplayer/include/tasks/generic/SetReportDecodeErrors.h b/media/server/gstplayer/include/tasks/generic/SetReportDecodeErrors.h new file mode 100644 index 000000000..9cf30b310 --- /dev/null +++ b/media/server/gstplayer/include/tasks/generic/SetReportDecodeErrors.h @@ -0,0 +1,49 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIREBOLT_RIALTO_SERVER_TASKS_GENERIC_SET_REPORT_DECODE_ERRORS_H_ +#define FIREBOLT_RIALTO_SERVER_TASKS_GENERIC_SET_REPORT_DECODE_ERRORS_H_ + +#include "GenericPlayerContext.h" +#include "IGlibWrapper.h" +#include "IGstGenericPlayerPrivate.h" +#include "IGstWrapper.h" +#include "IPlayerTask.h" + +#include + +namespace firebolt::rialto::server::tasks::generic +{ +class SetReportDecodeErrors : public IPlayerTask +{ +public: + explicit SetReportDecodeErrors(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, + const MediaSourceType &type, bool reportDecodeErrors); + ~SetReportDecodeErrors() override; + void execute() const override; + +private: + GenericPlayerContext &m_context; + IGstGenericPlayerPrivate &m_player; + const MediaSourceType m_type; + bool m_reportDecodeErrors; +}; +} // namespace firebolt::rialto::server::tasks::generic + +#endif // FIREBOLT_RIALTO_SERVER_TASKS_GENERIC_SET_REPORT_DECODE_ERRORS_H_ diff --git a/media/server/gstplayer/interface/IGstGenericPlayer.h b/media/server/gstplayer/interface/IGstGenericPlayer.h index 5e6842926..bb0931513 100644 --- a/media/server/gstplayer/interface/IGstGenericPlayer.h +++ b/media/server/gstplayer/interface/IGstGenericPlayer.h @@ -196,6 +196,25 @@ class IGstGenericPlayer */ virtual bool setImmediateOutput(const MediaSourceType &mediaSourceType, bool immediateOutput) = 0; + /** + * @brief Sets the "Report Decode Error" property for this source. + * + * @param[in] mediaSourceType : The media source type + * @param[in] reportDecodeErrors : Set report decode error + * + * @retval true on success. + */ + virtual bool setReportDecodeErrors(const MediaSourceType &mediaSourceType, bool reportDecodeErrors) = 0; + + /** + * @brief Gets the queued frames for this source. + * + * @param[out] queuedFrames : Get queued frames mode on the decoder + * + * @retval true on success. + */ + virtual bool getQueuedFrames(uint32_t &queuedFrames) = 0; + /** * @brief Gets the "Immediate Output" property for this source. * diff --git a/media/server/gstplayer/source/GstGenericPlayer.cpp b/media/server/gstplayer/source/GstGenericPlayer.cpp index 1db10cc15..e227e648a 100644 --- a/media/server/gstplayer/source/GstGenericPlayer.cpp +++ b/media/server/gstplayer/source/GstGenericPlayer.cpp @@ -1099,6 +1099,41 @@ bool GstGenericPlayer::setImmediateOutput(const MediaSourceType &mediaSourceType return true; } +bool GstGenericPlayer::setReportDecodeErrors(const MediaSourceType &mediaSourceType, bool reportDecodeErrors) +{ + if (!m_workerThread) + return false; + + m_workerThread->enqueueTask( + m_taskFactory->createSetReportDecodeErrors(m_context, *this, mediaSourceType, reportDecodeErrors)); + return true; +} + +bool GstGenericPlayer::getQueuedFrames(uint32_t &queuedFrames) +{ + bool returnValue{false}; + GstElement *decoder{getDecoder(MediaSourceType::VIDEO)}; + if (decoder) + { + if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(decoder), "queued-frames")) + { + m_glibWrapper->gObjectGet(decoder, "queued-frames", &queuedFrames, nullptr); + returnValue = true; + } + else + { + RIALTO_SERVER_LOG_ERROR("queued-frames not supported in element %s", GST_ELEMENT_NAME(decoder)); + } + m_gstWrapper->gstObjectUnref(decoder); + } + else + { + RIALTO_SERVER_LOG_ERROR("Failed to get queued-frames property, decoder is NULL"); + } + + return returnValue; +} + bool GstGenericPlayer::getImmediateOutput(const MediaSourceType &mediaSourceType, bool &immediateOutputRef) { bool returnValue{false}; @@ -1833,6 +1868,51 @@ bool GstGenericPlayer::setImmediateOutput() return result; } +bool GstGenericPlayer::setReportDecodeErrors() +{ + bool result{false}; + bool reportDecodeErrors{false}; + + { + std::unique_lock lock{m_context.propertyMutex}; + if (!m_context.pendingReportDecodeErrorsForVideo.has_value()) + { + return false; + } + reportDecodeErrors = m_context.pendingReportDecodeErrorsForVideo.value(); + } + + GstElement *decoder = getDecoder(MediaSourceType::VIDEO); + if (decoder) + { + RIALTO_SERVER_LOG_DEBUG("Set report decode errors to %s", reportDecodeErrors ? "TRUE" : "FALSE"); + + if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(decoder), "report-decode-errors")) + { + gboolean reportDecodeErrorsGboolean{reportDecodeErrors ? TRUE : FALSE}; + m_glibWrapper->gObjectSet(decoder, "report-decode-errors", reportDecodeErrorsGboolean, nullptr); + result = true; + } + else + { + RIALTO_SERVER_LOG_ERROR("Failed to set report-decode-errors property on decoder '%s'", + GST_ELEMENT_NAME(decoder)); + } + + m_gstWrapper->gstObjectUnref(decoder); + + { + std::unique_lock lock{m_context.propertyMutex}; + m_context.pendingReportDecodeErrorsForVideo.reset(); + } + } + else + { + RIALTO_SERVER_LOG_DEBUG("Pending report-decode-errors, decoder is NULL"); + } + return result; +} + bool GstGenericPlayer::setShowVideoWindow() { if (!m_context.pendingShowVideoWindow.has_value()) diff --git a/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp b/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp index 318f04379..a7886a335 100644 --- a/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp +++ b/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp @@ -41,6 +41,7 @@ #include "tasks/generic/SetMute.h" #include "tasks/generic/SetPlaybackRate.h" #include "tasks/generic/SetPosition.h" +#include "tasks/generic/SetReportDecodeErrors.h" #include "tasks/generic/SetSourcePosition.h" #include "tasks/generic/SetStreamSyncMode.h" #include "tasks/generic/SetSubtitleOffset.h" @@ -326,6 +327,14 @@ GenericPlayerTaskFactory::createSetImmediateOutput(GenericPlayerContext &context return std::make_unique(context, player, type, immediateOutput); } +std::unique_ptr +GenericPlayerTaskFactory::createSetReportDecodeErrors(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, + const firebolt::rialto::MediaSourceType &type, + bool reportDecodeErrors) const +{ + return std::make_unique(context, player, type, reportDecodeErrors); +} + std::unique_ptr GenericPlayerTaskFactory::createSetBufferingLimit(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, std::uint32_t limit) const diff --git a/media/server/gstplayer/source/tasks/generic/SetReportDecodeErrors.cpp b/media/server/gstplayer/source/tasks/generic/SetReportDecodeErrors.cpp new file mode 100644 index 000000000..6815df0ce --- /dev/null +++ b/media/server/gstplayer/source/tasks/generic/SetReportDecodeErrors.cpp @@ -0,0 +1,52 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SetReportDecodeErrors.h" +#include "RialtoServerLogging.h" +#include "TypeConverters.h" + +namespace firebolt::rialto::server::tasks::generic +{ +SetReportDecodeErrors::SetReportDecodeErrors(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, + const MediaSourceType &type, bool reportDecodeErrors) + : m_context{context}, m_player(player), m_type{type}, m_reportDecodeErrors{reportDecodeErrors} +{ + RIALTO_SERVER_LOG_DEBUG("Constructing SetReportDecodeErrors"); +} + +SetReportDecodeErrors::~SetReportDecodeErrors() +{ + RIALTO_SERVER_LOG_DEBUG("SetReportDecodeErrors finished"); +} + +void SetReportDecodeErrors::execute() const +{ + RIALTO_SERVER_LOG_DEBUG("Executing SetReportDecodeErrors for %s source", common::convertMediaSourceType(m_type)); + + m_context.pendingReportDecodeErrorsForVideo = m_reportDecodeErrors; + + if (!m_context.pipeline) + { + RIALTO_SERVER_LOG_WARN("Pipeline not available yet - cannot apply report-decode-errors setting"); + return; + } + + m_player.setReportDecodeErrors(); +} +} // namespace firebolt::rialto::server::tasks::generic diff --git a/media/server/gstplayer/source/tasks/generic/SetupElement.cpp b/media/server/gstplayer/source/tasks/generic/SetupElement.cpp index 16484c9a6..00541a00e 100644 --- a/media/server/gstplayer/source/tasks/generic/SetupElement.cpp +++ b/media/server/gstplayer/source/tasks/generic/SetupElement.cpp @@ -246,6 +246,10 @@ void SetupElement::execute() const RIALTO_SERVER_LOG_INFO("Setting video decoder handle for subtitle sink: %p", m_element); m_context.isVideoHandleSet = true; } + if (m_context.pendingReportDecodeErrorsForVideo.has_value()) + { + m_player.setReportDecodeErrors(); + } } } @@ -333,7 +337,6 @@ void SetupElement::execute() const { m_gstWrapper->gstBaseParseSetPtsInterpolation(GST_BASE_PARSE(m_element), FALSE); } - m_gstWrapper->gstObjectUnref(m_element); } } // namespace firebolt::rialto::server::tasks::generic diff --git a/media/server/ipc/include/MediaPipelineModuleService.h b/media/server/ipc/include/MediaPipelineModuleService.h index f0321827b..61d932c08 100644 --- a/media/server/ipc/include/MediaPipelineModuleService.h +++ b/media/server/ipc/include/MediaPipelineModuleService.h @@ -88,6 +88,14 @@ class MediaPipelineModuleService : public IMediaPipelineModuleService const ::firebolt::rialto::SetImmediateOutputRequest *request, ::firebolt::rialto::SetImmediateOutputResponse *response, ::google::protobuf::Closure *done) override; + void setReportDecodeErrors(::google::protobuf::RpcController *controller, + const ::firebolt::rialto::SetReportDecodeErrorsRequest *request, + ::firebolt::rialto::SetReportDecodeErrorsResponse *response, + ::google::protobuf::Closure *done) override; + void getQueuedFrames(::google::protobuf::RpcController *controller, + const ::firebolt::rialto::GetQueuedFramesRequest *request, + ::firebolt::rialto::GetQueuedFramesResponse *response, + ::google::protobuf::Closure *done) override; void getImmediateOutput(::google::protobuf::RpcController *controller, const ::firebolt::rialto::GetImmediateOutputRequest *request, ::firebolt::rialto::GetImmediateOutputResponse *response, diff --git a/media/server/ipc/source/MediaPipelineModuleService.cpp b/media/server/ipc/source/MediaPipelineModuleService.cpp index 3853f6605..3bd0f2948 100644 --- a/media/server/ipc/source/MediaPipelineModuleService.cpp +++ b/media/server/ipc/source/MediaPipelineModuleService.cpp @@ -680,6 +680,40 @@ void MediaPipelineModuleService::setImmediateOutput(::google::protobuf::RpcContr done->Run(); } +void MediaPipelineModuleService::setReportDecodeErrors(::google::protobuf::RpcController *controller, + const ::firebolt::rialto::SetReportDecodeErrorsRequest *request, + ::firebolt::rialto::SetReportDecodeErrorsResponse *response, + ::google::protobuf::Closure *done) +{ + RIALTO_SERVER_LOG_DEBUG("entry:"); + if (!m_mediaPipelineService.setReportDecodeErrors(request->session_id(), request->source_id(), + request->report_decode_errors())) + { + RIALTO_SERVER_LOG_ERROR("Set Report Decode Error failed"); + controller->SetFailed("Operation failed"); + } + done->Run(); +} + +void MediaPipelineModuleService::getQueuedFrames(::google::protobuf::RpcController *controller, + const ::firebolt::rialto::GetQueuedFramesRequest *request, + ::firebolt::rialto::GetQueuedFramesResponse *response, + ::google::protobuf::Closure *done) +{ + RIALTO_SERVER_LOG_DEBUG("entry:"); + uint32_t queuedFramesNumber; + if (!m_mediaPipelineService.getQueuedFrames(request->session_id(), request->source_id(), queuedFramesNumber)) + { + RIALTO_SERVER_LOG_ERROR("Get queued frames failed"); + controller->SetFailed("Operation failed"); + } + else + { + response->set_queued_frames(queuedFramesNumber); + } + done->Run(); +} + void MediaPipelineModuleService::getImmediateOutput(::google::protobuf::RpcController *controller, const ::firebolt::rialto::GetImmediateOutputRequest *request, ::firebolt::rialto::GetImmediateOutputResponse *response, diff --git a/media/server/main/include/MediaPipelineServerInternal.h b/media/server/main/include/MediaPipelineServerInternal.h index 0c92f63c0..8b1eff28c 100644 --- a/media/server/main/include/MediaPipelineServerInternal.h +++ b/media/server/main/include/MediaPipelineServerInternal.h @@ -114,6 +114,10 @@ class MediaPipelineServerInternal : public IMediaPipelineServerInternal, public bool setImmediateOutput(int32_t sourceId, bool immediateOutput) override; + bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) override; + + bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) override; + bool getImmediateOutput(int32_t sourceId, bool &immediateOutput) override; bool getStats(int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames) override; @@ -397,6 +401,30 @@ class MediaPipelineServerInternal : public IMediaPipelineServerInternal, public */ bool setImmediateOutputInternal(int32_t sourceId, bool immediateOutput); + /** + * @brief Sets the "Report Decode Errors" property for this source. + * + * This method is asynchronous + * + * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() + * @param[in] reportDecodeErrors : The desired Set Report Decode Errors mode on the sink + * + * @retval true on success. + */ + bool setReportDecodeErrorsInternal(int32_t sourceId, bool reportDecodeErrors); + + /** + * @brief Gets the queued frames for this source. + * + * This method is asynchronous + * + * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() + * @param[in] queuedFrames : Number of queued frames + * + * @retval true on success. + */ + bool getQueuedFramesInternal(int32_t sourceId, uint32_t &queuedFrames); + /** * @brief Gets the "Immediate Output" property for this source. * diff --git a/media/server/main/source/MediaPipelineServerInternal.cpp b/media/server/main/source/MediaPipelineServerInternal.cpp index c9bbe84b1..e09804541 100644 --- a/media/server/main/source/MediaPipelineServerInternal.cpp +++ b/media/server/main/source/MediaPipelineServerInternal.cpp @@ -539,6 +539,63 @@ bool MediaPipelineServerInternal::setImmediateOutputInternal(int32_t sourceId, b return m_gstPlayer->setImmediateOutput(sourceIter->first, immediateOutput); } +bool MediaPipelineServerInternal::setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) +{ + RIALTO_SERVER_LOG_DEBUG("entry:"); + + bool result; + auto task = [&]() { result = setReportDecodeErrorsInternal(sourceId, reportDecodeErrors); }; + + m_mainThread->enqueueTaskAndWait(m_mainThreadClientId, task); + return result; +} + +bool MediaPipelineServerInternal::setReportDecodeErrorsInternal(int32_t sourceId, bool reportDecodeErrors) +{ + if (!m_gstPlayer) + { + RIALTO_SERVER_LOG_ERROR("Failed - Gstreamer player has not been loaded"); + return false; + } + auto sourceIter = std::find_if(m_attachedSources.begin(), m_attachedSources.end(), + [sourceId](const auto &src) { return src.second == sourceId; }); + if (sourceIter == m_attachedSources.end()) + { + RIALTO_SERVER_LOG_ERROR("Failed - Source not found"); + return false; + } + + return m_gstPlayer->setReportDecodeErrors(sourceIter->first, reportDecodeErrors); +} + +bool MediaPipelineServerInternal::getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) +{ + RIALTO_SERVER_LOG_DEBUG("entry:"); + + bool result; + auto task = [&]() { result = getQueuedFramesInternal(sourceId, queuedFrames); }; + + m_mainThread->enqueueTaskAndWait(m_mainThreadClientId, task); + return result; +} + +bool MediaPipelineServerInternal::getQueuedFramesInternal(int32_t sourceId, uint32_t &queuedFrames) +{ + if (!m_gstPlayer) + { + RIALTO_SERVER_LOG_ERROR("Failed - Gstreamer player has not been loaded"); + return false; + } + auto sourceIter = std::find_if(m_attachedSources.begin(), m_attachedSources.end(), + [sourceId](const auto &src) { return src.second == sourceId; }); + if (sourceIter == m_attachedSources.end()) + { + RIALTO_SERVER_LOG_ERROR("Failed - Source not found"); + return false; + } + return m_gstPlayer->getQueuedFrames(queuedFrames); +} + bool MediaPipelineServerInternal::getImmediateOutput(int32_t sourceId, bool &immediateOutput) { RIALTO_SERVER_LOG_DEBUG("entry:"); diff --git a/media/server/service/include/IMediaPipelineService.h b/media/server/service/include/IMediaPipelineService.h index ae1fc5e04..ffe742178 100644 --- a/media/server/service/include/IMediaPipelineService.h +++ b/media/server/service/include/IMediaPipelineService.h @@ -55,6 +55,8 @@ class IMediaPipelineService virtual bool setPosition(int sessionId, std::int64_t position) = 0; virtual bool getPosition(int sessionId, std::int64_t &position) = 0; virtual bool setImmediateOutput(int sessionId, int32_t sourceId, bool immediateOutput) = 0; + virtual bool setReportDecodeErrors(int sessionId, int32_t sourceId, bool reportDecodeErrors) = 0; + virtual bool getQueuedFrames(int sessionId, int32_t sourceId, uint32_t &queuedFrames) = 0; virtual bool getImmediateOutput(int sessionId, int32_t sourceId, bool &immediateOutput) = 0; virtual bool getStats(int sessionId, int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames) = 0; virtual bool setVideoWindow(int sessionId, std::uint32_t x, std::uint32_t y, std::uint32_t width, diff --git a/media/server/service/source/MediaPipelineService.cpp b/media/server/service/source/MediaPipelineService.cpp index e977c296f..79571867a 100644 --- a/media/server/service/source/MediaPipelineService.cpp +++ b/media/server/service/source/MediaPipelineService.cpp @@ -267,6 +267,34 @@ bool MediaPipelineService::setImmediateOutput(int sessionId, int32_t sourceId, b return mediaPipelineIter->second->setImmediateOutput(sourceId, immediateOutput); } +bool MediaPipelineService::setReportDecodeErrors(int sessionId, int32_t sourceId, bool reportDecodeErrors) +{ + RIALTO_SERVER_LOG_INFO("MediaPipelineService requested to setReportDecodeErrors, session id: %d", sessionId); + + std::lock_guard lock{m_mediaPipelineMutex}; + auto mediaPipelineIter = m_mediaPipelines.find(sessionId); + if (mediaPipelineIter == m_mediaPipelines.end()) + { + RIALTO_SERVER_LOG_ERROR("Session with id: %d does not exists", sessionId); + return false; + } + return mediaPipelineIter->second->setReportDecodeErrors(sourceId, reportDecodeErrors); +} + +bool MediaPipelineService::getQueuedFrames(int sessionId, int32_t sourceId, uint32_t &queuedFrames) +{ + RIALTO_SERVER_LOG_INFO("MediaPipelineService requested to getQueuedFrames, session id: %d", sessionId); + + std::lock_guard lock{m_mediaPipelineMutex}; + auto mediaPipelineIter = m_mediaPipelines.find(sessionId); + if (mediaPipelineIter == m_mediaPipelines.end()) + { + RIALTO_SERVER_LOG_ERROR("Session with id: %d does not exists", sessionId); + return false; + } + return mediaPipelineIter->second->getQueuedFrames(sourceId, queuedFrames); +} + bool MediaPipelineService::getImmediateOutput(int sessionId, int32_t sourceId, bool &immediateOutput) { RIALTO_SERVER_LOG_INFO("MediaPipelineService requested to getImmediateOutput, session id: %d", sessionId); diff --git a/media/server/service/source/MediaPipelineService.h b/media/server/service/source/MediaPipelineService.h index bf9ec234f..5290c2475 100644 --- a/media/server/service/source/MediaPipelineService.h +++ b/media/server/service/source/MediaPipelineService.h @@ -66,6 +66,8 @@ class MediaPipelineService : public IMediaPipelineService bool setPosition(int sessionId, std::int64_t position) override; bool getPosition(int sessionId, std::int64_t &position) override; bool setImmediateOutput(int sessionId, int32_t sourceId, bool immediateOutput) override; + bool setReportDecodeErrors(int sessionId, int32_t sourceId, bool reportDecodeErrors) override; + bool getQueuedFrames(int sessionId, int32_t sourceId, uint32_t &queuedFrames) override; bool getImmediateOutput(int sessionId, int32_t sourceId, bool &immediateOutput) override; bool getStats(int sessionId, int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames) override; bool setVideoWindow(int sessionId, std::uint32_t x, std::uint32_t y, std::uint32_t width, diff --git a/proto/mediapipelinemodule.proto b/proto/mediapipelinemodule.proto index 1807810de..b111b3fb9 100644 --- a/proto/mediapipelinemodule.proto +++ b/proto/mediapipelinemodule.proto @@ -678,6 +678,24 @@ message SetImmediateOutputRequest { message SetImmediateOutputResponse { } +/** + * @fn void setReportDecodeErrors(int session_id, int source_id, bool report_decode_errors) + * @brief Enables or disables decode error reporting for this source. + * + * @param[in] session_id The id of the A/V session. + * @param[in] source_id The id of the media source. + * @param[in] report_decode_errors Enable decode error reporting. + */ +message SetReportDecodeErrorsRequest { + optional int32 session_id = 1 [default = -1]; + optional int32 source_id = 2 [default = -1]; + optional bool report_decode_errors = 3 [default = false]; +} + +message SetReportDecodeErrorsResponse { +} + + /** * @fn void getImmediateOutput(int session_id, int source_id, bool &immediate_output) * @brief Gets the "Immediate Output" property for this source. @@ -695,6 +713,23 @@ message GetImmediateOutputResponse { optional bool immediate_output = 1 [default = false]; } +/** + * @fn void getQueuedFrames(int session_id, int source_id, uint32_t &queued_frames) + * @brief Gets the "Queued Frames" property for this source. + * + * @param[in] session_id The id of the A/V session. + * @param[in] source_id The id of the media source. + * @param[out] queued_frames The number of queued frames on the decoder + * + */ +message GetQueuedFramesRequest { + optional int32 session_id = 1 [default = -1]; + optional int32 source_id = 2 [default = -1]; +} +message GetQueuedFramesResponse { + optional uint32 queued_frames = 1 [default = 0]; +} + /** * @fn void getStats(int session_id, int source_id, uint64 &rendered_frames, uint64 &dropped_frames) * @brief Get various stats from the source. @@ -1078,6 +1113,14 @@ service MediaPipelineModule { rpc setImmediateOutput(SetImmediateOutputRequest) returns (SetImmediateOutputResponse) { } + /** + * @brief Sets the "Report Decode Errors" property for this source. + * @see SetReportDecodeErrorsRequest + */ + rpc setReportDecodeErrors(SetReportDecodeErrorsRequest) + returns (SetReportDecodeErrorsResponse) { + } + /** * @brief Gets the "Immediate Output" property for this source. * @see GetImmediateOutputRequest @@ -1085,6 +1128,13 @@ service MediaPipelineModule { rpc getImmediateOutput(GetImmediateOutputRequest) returns (GetImmediateOutputResponse) { } + /** + * @brief Gets the "Queued Frames" property for this source. + * @see GetQueuedFramesRequest + */ + rpc getQueuedFrames(GetQueuedFramesRequest) returns (GetQueuedFramesResponse) { + } + /** * @brief Gets the current stats property * @see GetStatsRequest diff --git a/tests/common/matchers/MediaPipelineProtoRequestMatchers.h b/tests/common/matchers/MediaPipelineProtoRequestMatchers.h index 04c48ad78..1424cf106 100644 --- a/tests/common/matchers/MediaPipelineProtoRequestMatchers.h +++ b/tests/common/matchers/MediaPipelineProtoRequestMatchers.h @@ -366,6 +366,20 @@ MATCHER_P2(getImmediateOutputRequestMatcher, sessionId, sourceId, "") return (kRequest->session_id() == sessionId) && (kRequest->source_id() == sourceId); } +MATCHER_P2(setReportDecodeErrorsRequestMatcher, sessionId, sourceId, "") +{ + const ::firebolt::rialto::SetReportDecodeErrorsRequest *kRequest = + dynamic_cast(arg); + return (kRequest->session_id() == sessionId) && (kRequest->source_id() == sourceId); +} + +MATCHER_P2(getQueuedFramesRequestMatcher, sessionId, sourceId, "") +{ + const ::firebolt::rialto::GetQueuedFramesRequest *kRequest = + dynamic_cast(arg); + return (kRequest->session_id() == sessionId) && (kRequest->source_id() == sourceId); +} + MATCHER_P2(getStatsRequestMatcher, sessionId, sourceId, "") { const ::firebolt::rialto::GetStatsRequest *kRequest = dynamic_cast(arg); diff --git a/tests/componenttests/client/mocks/MediaPipelineModuleMock.h b/tests/componenttests/client/mocks/MediaPipelineModuleMock.h index a6fc772c0..5cf614593 100644 --- a/tests/componenttests/client/mocks/MediaPipelineModuleMock.h +++ b/tests/componenttests/client/mocks/MediaPipelineModuleMock.h @@ -72,6 +72,13 @@ class MediaPipelineModuleMock : public ::firebolt::rialto::MediaPipelineModule (::google::protobuf::RpcController * controller, const ::firebolt::rialto::GetImmediateOutputRequest *request, ::firebolt::rialto::GetImmediateOutputResponse *response, ::google::protobuf::Closure *done)); + MOCK_METHOD(void, setReportDecodeErrors, + (::google::protobuf::RpcController * controller, + const ::firebolt::rialto::SetReportDecodeErrorsRequest *request, + ::firebolt::rialto::SetReportDecodeErrorsResponse *response, ::google::protobuf::Closure *done)); + MOCK_METHOD(void, getQueuedFrames, + (::google::protobuf::RpcController * controller, const ::firebolt::rialto::GetQueuedFramesRequest *request, + ::firebolt::rialto::GetQueuedFramesResponse *response, ::google::protobuf::Closure *done)); MOCK_METHOD(void, getStats, (::google::protobuf::RpcController * controller, const ::firebolt::rialto::GetStatsRequest *request, ::firebolt::rialto::GetStatsResponse *response, ::google::protobuf::Closure *done)); @@ -231,6 +238,19 @@ class MediaPipelineModuleMock : public ::firebolt::rialto::MediaPipelineModule return response; } + ::firebolt::rialto::SetReportDecodeErrorsResponse SetReportDecodeErrorsResponse() + { + firebolt::rialto::SetReportDecodeErrorsResponse response; + return response; + } + + ::firebolt::rialto::GetQueuedFramesResponse getQueuedFramesResponse(uint32_t queuedFramesResponse) + { + firebolt::rialto::GetQueuedFramesResponse response; + response.set_queued_frames(queuedFramesResponse); + return response; + } + ::firebolt::rialto::GetStatsResponse getStatsResponse(const uint64_t renderedFrames, const uint64_t droppedFrames) { firebolt::rialto::GetStatsResponse response; diff --git a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp index e91d67bbb..a7f2f388a 100644 --- a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp +++ b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp @@ -1450,6 +1450,34 @@ void MediaPipelineTestMethods::getImmediateOutput(bool immediateOutput) EXPECT_TRUE(m_mediaPipeline->getImmediateOutput(kVideoSourceId, immediateOutput)); } +void MediaPipelineTestMethods::shouldSetReportDecodeErrors(bool reportDecodeErrors) +{ + EXPECT_CALL(*m_mediaPipelineModuleMock, + setReportDecodeErrors(_, setReportDecodeErrorsRequestMatcher(kSessionId, kVideoSourceId), _, _)) + .WillOnce(DoAll(SetArgPointee<2>(m_mediaPipelineModuleMock->SetReportDecodeErrorsResponse()), + WithArgs<0, 3>(Invoke(&(*m_mediaPipelineModuleMock), &MediaPipelineModuleMock::defaultReturn)))); +} + +void MediaPipelineTestMethods::setReportDecodeErrors(bool reportDecodeErrors) +{ + EXPECT_TRUE(m_mediaPipeline->setReportDecodeErrors(kVideoSourceId, reportDecodeErrors)); +} + +void MediaPipelineTestMethods::shouldGetQueuedFrames(uint32_t queuedFrames) +{ + EXPECT_CALL(*m_mediaPipelineModuleMock, + getQueuedFrames(_, getQueuedFramesRequestMatcher(kSessionId, kVideoSourceId), _, _)) + .WillOnce(DoAll(SetArgPointee<2>(m_mediaPipelineModuleMock->getQueuedFramesResponse(queuedFrames)), + WithArgs<0, 3>(Invoke(&(*m_mediaPipelineModuleMock), &MediaPipelineModuleMock::defaultReturn)))); +} + +void MediaPipelineTestMethods::getQueuedFrames(uint32_t queuedFrames) +{ + uint32_t returnQueuedFrames; + EXPECT_TRUE(m_mediaPipeline->getQueuedFrames(kVideoSourceId, returnQueuedFrames)); + EXPECT_EQ(returnQueuedFrames, queuedFrames); +} + void MediaPipelineTestMethods::shouldGetStats(uint64_t renderedFrames, uint64_t droppedFrames) { EXPECT_CALL(*m_mediaPipelineModuleMock, getStats(_, getStatsRequestMatcher(kSessionId, kVideoSourceId), _, _)) diff --git a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.h b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.h index c6b9d7f74..13425beaa 100644 --- a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.h +++ b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.h @@ -130,6 +130,8 @@ class MediaPipelineTestMethods void shouldGetPosition(const int64_t position); void shouldSetImmediateOutput(bool immediateOutput); void shouldGetImmediateOutput(bool immediateOutput); + void shouldSetReportDecodeErrors(bool reportDecodeErrors); + void shouldGetQueuedFrames(uint32_t queuedFrames); void shouldGetStats(uint64_t renderedFrames, uint64_t droppedFrames); void shouldFlush(); void shouldFailToFlush(); @@ -248,6 +250,8 @@ class MediaPipelineTestMethods void getPosition(const int64_t expectedPosition); void setImmediateOutput(bool immediateOutput); void getImmediateOutput(bool immediateOutput); + void setReportDecodeErrors(bool reportDecodeErrors); + void getQueuedFrames(uint32_t queuedFrames); void getStats(uint64_t expectedFrames, uint64_t expectedDropped); void createMediaPipelineCapabilitiesObject(); void destroyMediaPipelineCapabilitiesObject(); diff --git a/tests/componenttests/client/tests/mse/PipelinePropertyTest.cpp b/tests/componenttests/client/tests/mse/PipelinePropertyTest.cpp index a64aeadd7..db623007c 100644 --- a/tests/componenttests/client/tests/mse/PipelinePropertyTest.cpp +++ b/tests/componenttests/client/tests/mse/PipelinePropertyTest.cpp @@ -174,5 +174,15 @@ TEST_F(PipelinePropertyTest, setAndGetPipelineProperties) // Step 12: Get UseBuffering MediaPipelineTestMethods::shouldGetUseBuffering(useBuffering); MediaPipelineTestMethods::getUseBuffering(useBuffering); + + // Step 13: Set Report Decode Errors + bool reportDecodeErrors{true}; + MediaPipelineTestMethods::shouldSetReportDecodeErrors(reportDecodeErrors); + MediaPipelineTestMethods::setReportDecodeErrors(reportDecodeErrors); + + // Step 14: Get Queued Frames + uint32_t queuedFrames{123}; + MediaPipelineTestMethods::shouldGetQueuedFrames(queuedFrames); + MediaPipelineTestMethods::getQueuedFrames(queuedFrames); } } // namespace firebolt::rialto::client::ct diff --git a/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp b/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp index 2d11ee4f6..a6410d9a5 100644 --- a/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp @@ -159,6 +159,16 @@ class PipelinePropertyTest : public MediaPipelineTest *returnVal = value ? TRUE : FALSE; })); } + else if constexpr (std::is_same_v) + { + EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq(propertyName.c_str()), _)) + .WillOnce(Invoke( + [&](gpointer, const gchar *, void *val) + { + guint *returnVal = reinterpret_cast(val); + *returnVal = value; + })); + } else if constexpr (std::is_same_v) { EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq(propertyName.c_str()), _)) @@ -587,7 +597,7 @@ TEST_F(PipelinePropertyTest, pipelinePropertyGetAndSetSuccess) willStop(); stop(); - // Step 19: Destroy media session + // Step 18: Destroy media session gstPlayerWillBeDestructed(); destroySession(); } diff --git a/tests/unittests/media/client/ipc/CMakeLists.txt b/tests/unittests/media/client/ipc/CMakeLists.txt index c4018151c..205b9e411 100644 --- a/tests/unittests/media/client/ipc/CMakeLists.txt +++ b/tests/unittests/media/client/ipc/CMakeLists.txt @@ -37,6 +37,8 @@ add_gtests ( mediaPipelineIpc/GetPositionTest.cpp mediaPipelineIpc/SetImmediateOutputTest.cpp mediaPipelineIpc/GetImmediateOutputTest.cpp + mediaPipelineIpc/SetReportDecodeErrorsTest.cpp + mediaPipelineIpc/GetQueuedFramesTest.cpp mediaPipelineIpc/GetStatsTest.cpp mediaPipelineIpc/RenderFrameTest.cpp mediaPipelineIpc/GetVolumeTest.cpp diff --git a/tests/unittests/media/client/ipc/mediaPipelineIpc/GetQueuedFramesTest.cpp b/tests/unittests/media/client/ipc/mediaPipelineIpc/GetQueuedFramesTest.cpp new file mode 100644 index 000000000..17bdd5c84 --- /dev/null +++ b/tests/unittests/media/client/ipc/mediaPipelineIpc/GetQueuedFramesTest.cpp @@ -0,0 +1,100 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MediaPipelineIpcTestBase.h" +#include "MediaPipelineProtoRequestMatchers.h" + +class RialtoClientMediaPipelineIpcGetQueuedFramesTest : public MediaPipelineIpcTestBase +{ +protected: + virtual void SetUp() + { + MediaPipelineIpcTestBase::SetUp(); + + createMediaPipelineIpc(); + } + + virtual void TearDown() + { + destroyMediaPipelineIpc(); + + MediaPipelineIpcTestBase::TearDown(); + } + + const int32_t m_kSourceId{1}; +}; + +/** + * Test that getQueuedFrames can be called successfully. + */ +TEST_F(RialtoClientMediaPipelineIpcGetQueuedFramesTest, Success) +{ + expectIpcApiCallSuccess(); + + EXPECT_CALL(*m_channelMock, + CallMethod(methodMatcher("getQueuedFrames"), m_controllerMock.get(), + getQueuedFramesRequestMatcher(m_sessionId, m_kSourceId), _, m_blockingClosureMock.get())); + + uint32_t queuedFrames; + EXPECT_TRUE(m_mediaPipelineIpc->getQueuedFrames(m_kSourceId, queuedFrames)); +} + +/** + * Test that getQueuedFrames fails if the ipc channel disconnected. + */ +TEST_F(RialtoClientMediaPipelineIpcGetQueuedFramesTest, ChannelDisconnected) +{ + expectIpcApiCallDisconnected(); + expectUnsubscribeEvents(); + + uint32_t queuedFrames; + EXPECT_FALSE(m_mediaPipelineIpc->getQueuedFrames(m_kSourceId, queuedFrames)); + + // Reattach channel on destroySession + EXPECT_CALL(*m_ipcClientMock, getChannel()).WillOnce(Return(m_channelMock)).RetiresOnSaturation(); + expectSubscribeEvents(); +} + +/** + * Test that getQueuedFrames fails if the ipc channel disconnected and succeeds if the channel is reconnected. + */ +TEST_F(RialtoClientMediaPipelineIpcGetQueuedFramesTest, ReconnectChannel) +{ + expectIpcApiCallReconnected(); + expectUnsubscribeEvents(); + expectSubscribeEvents(); + + EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("getQueuedFrames"), _, _, _, _)); + + uint32_t queuedFrames; + EXPECT_TRUE(m_mediaPipelineIpc->getQueuedFrames(m_kSourceId, queuedFrames)); +} + +/** + * Test that getQueuedFrames fails when ipc fails. + */ +TEST_F(RialtoClientMediaPipelineIpcGetQueuedFramesTest, GetQueuedFramesFailure) +{ + expectIpcApiCallFailure(); + + EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("getQueuedFrames"), _, _, _, _)); + + uint32_t queuedFrames; + EXPECT_FALSE(m_mediaPipelineIpc->getQueuedFrames(m_kSourceId, queuedFrames)); +} diff --git a/tests/unittests/media/client/ipc/mediaPipelineIpc/SetReportDecodeErrorsTest.cpp b/tests/unittests/media/client/ipc/mediaPipelineIpc/SetReportDecodeErrorsTest.cpp new file mode 100644 index 000000000..d7e5fb8d2 --- /dev/null +++ b/tests/unittests/media/client/ipc/mediaPipelineIpc/SetReportDecodeErrorsTest.cpp @@ -0,0 +1,96 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2025 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MediaPipelineIpcTestBase.h" +#include "MediaPipelineProtoRequestMatchers.h" + +class RialtoClientMediaPipelineIpcSetReportDecodeErrorsTest : public MediaPipelineIpcTestBase +{ +protected: + virtual void SetUp() + { + MediaPipelineIpcTestBase::SetUp(); + + createMediaPipelineIpc(); + } + + virtual void TearDown() + { + destroyMediaPipelineIpc(); + + MediaPipelineIpcTestBase::TearDown(); + } + + const int32_t m_kSourceId{1}; +}; + +/** + * Test that setReportDecodeErrors can be called successfully. + */ +TEST_F(RialtoClientMediaPipelineIpcSetReportDecodeErrorsTest, Success) +{ + expectIpcApiCallSuccess(); + + EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("setReportDecodeErrors"), m_controllerMock.get(), + setReportDecodeErrorsRequestMatcher(m_sessionId, m_kSourceId), _, + m_blockingClosureMock.get())); + + EXPECT_TRUE(m_mediaPipelineIpc->setReportDecodeErrors(m_kSourceId, true)); +} + +/** + * Test that setReportDecodeErrors fails if the ipc channel disconnected. + */ +TEST_F(RialtoClientMediaPipelineIpcSetReportDecodeErrorsTest, ChannelDisconnected) +{ + expectIpcApiCallDisconnected(); + expectUnsubscribeEvents(); + + EXPECT_FALSE(m_mediaPipelineIpc->setReportDecodeErrors(m_kSourceId, true)); + + // Reattach channel on destroySession + EXPECT_CALL(*m_ipcClientMock, getChannel()).WillOnce(Return(m_channelMock)).RetiresOnSaturation(); + expectSubscribeEvents(); +} + +/** + * Test that setReportDecodeErrors fails if the ipc channel disconnected and succeeds if the channel is reconnected. + */ +TEST_F(RialtoClientMediaPipelineIpcSetReportDecodeErrorsTest, ReconnectChannel) +{ + expectIpcApiCallReconnected(); + expectUnsubscribeEvents(); + expectSubscribeEvents(); + + EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("setReportDecodeErrors"), _, _, _, _)); + + EXPECT_TRUE(m_mediaPipelineIpc->setReportDecodeErrors(m_kSourceId, true)); +} + +/** + * Test that setReportDecodeErrors fails when ipc fails. + */ +TEST_F(RialtoClientMediaPipelineIpcSetReportDecodeErrorsTest, SetReportDecodeErrorsFailure) +{ + expectIpcApiCallFailure(); + + EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("setReportDecodeErrors"), _, _, _, _)); + + EXPECT_FALSE(m_mediaPipelineIpc->setReportDecodeErrors(m_kSourceId, true)); +} diff --git a/tests/unittests/media/client/main/CMakeLists.txt b/tests/unittests/media/client/main/CMakeLists.txt index 17a5b0d95..4387fe598 100644 --- a/tests/unittests/media/client/main/CMakeLists.txt +++ b/tests/unittests/media/client/main/CMakeLists.txt @@ -34,6 +34,8 @@ add_gtests ( mediaPipeline/GetPositionTest.cpp mediaPipeline/SetImmediateOutputTest.cpp mediaPipeline/GetImmediateOutputTest.cpp + mediaPipeline/SetReportDecodeErrorsTest.cpp + mediaPipeline/GetQueuedFramesTest.cpp mediaPipeline/GetStatsTest.cpp mediaPipeline/RenderFrameTest.cpp mediaPipeline/SetVolumeTest.cpp diff --git a/tests/unittests/media/client/main/mediaPipeline/GetQueuedFramesTest.cpp b/tests/unittests/media/client/main/mediaPipeline/GetQueuedFramesTest.cpp new file mode 100644 index 000000000..277b46872 --- /dev/null +++ b/tests/unittests/media/client/main/mediaPipeline/GetQueuedFramesTest.cpp @@ -0,0 +1,63 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2025 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MediaPipelineTestBase.h" + +class RialtoClientMediaPipelineGetQueuedFramesTest : public MediaPipelineTestBase +{ +protected: + const int32_t m_kSourceId{1}; + + virtual void SetUp() + { + MediaPipelineTestBase::SetUp(); + + createMediaPipeline(); + } + + virtual void TearDown() + { + destroyMediaPipeline(); + + MediaPipelineTestBase::TearDown(); + } +}; + +/** + * Test that getQueuedFrames returns success if the IPC API succeeds. + */ +TEST_F(RialtoClientMediaPipelineGetQueuedFramesTest, GetQueuedFramesSuccess) +{ + constexpr uint32_t kExpectedQueuedFrames{123}; + EXPECT_CALL(*m_mediaPipelineIpcMock, getQueuedFrames(m_kSourceId, _)) + .WillOnce(DoAll(SetArgReferee<1>(123), Return(true))); + uint32_t queuedFrames; + EXPECT_TRUE(m_mediaPipeline->getQueuedFrames(m_kSourceId, queuedFrames)); + EXPECT_EQ(kExpectedQueuedFrames, queuedFrames); +} + +/** + * Test that getQueuedFrames returns failure if the IPC API fails. + */ +TEST_F(RialtoClientMediaPipelineGetQueuedFramesTest, GetQueuedFramesFailure) +{ + EXPECT_CALL(*m_mediaPipelineIpcMock, getQueuedFrames(m_kSourceId, _)).WillOnce(Return(false)); + uint32_t queuedFrames; + EXPECT_FALSE(m_mediaPipeline->getQueuedFrames(m_kSourceId, queuedFrames)); +} diff --git a/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp b/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp index 6e22bdf4d..7d9b7a2c7 100644 --- a/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp +++ b/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp @@ -163,6 +163,21 @@ TEST_F(RialtoClientMediaPipelineProxyTest, TestPassthrough) ///////////////////////////////////////////// + EXPECT_CALL(*mediaPipelineMock, setReportDecodeErrors(kSourceId, true)).WillOnce(Return(true)); + EXPECT_TRUE(proxy->setReportDecodeErrors(kSourceId, true)); + + ///////////////////////////////////////////// + + { + const uint32_t kQueuedFrames{123}; + uint32_t returnQueuedFrames; + EXPECT_CALL(*mediaPipelineMock, getQueuedFrames(kSourceId, _)) + .WillOnce(DoAll(SetArgReferee<1>(kQueuedFrames), Return(true))); + EXPECT_TRUE(proxy->getQueuedFrames(kSourceId, returnQueuedFrames)); + EXPECT_EQ(returnQueuedFrames, kQueuedFrames); + } + ///////////////////////////////////////////// + EXPECT_CALL(*mediaPipelineMock, setVideoWindow(1, 2, 3, 4)).WillOnce(Return(true)); EXPECT_TRUE(proxy->setVideoWindow(1, 2, 3, 4)); diff --git a/tests/unittests/media/client/main/mediaPipeline/SetReportDecodeErrorsTest.cpp b/tests/unittests/media/client/main/mediaPipeline/SetReportDecodeErrorsTest.cpp new file mode 100644 index 000000000..29f10533d --- /dev/null +++ b/tests/unittests/media/client/main/mediaPipeline/SetReportDecodeErrorsTest.cpp @@ -0,0 +1,58 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2025 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MediaPipelineTestBase.h" + +class RialtoClientMediaPipelineSetReportDecodeErrorsTest : public MediaPipelineTestBase +{ +protected: + const int32_t m_kSourceId{1}; + + virtual void SetUp() + { + MediaPipelineTestBase::SetUp(); + + createMediaPipeline(); + } + + virtual void TearDown() + { + destroyMediaPipeline(); + + MediaPipelineTestBase::TearDown(); + } +}; + +/** + * Test that setReportDecodeErrors returns success if the IPC API succeeds. + */ +TEST_F(RialtoClientMediaPipelineSetReportDecodeErrorsTest, SetReportDecodeErrorsSuccess) +{ + EXPECT_CALL(*m_mediaPipelineIpcMock, setReportDecodeErrors(m_kSourceId, _)).WillOnce(Return(true)); + EXPECT_TRUE(m_mediaPipeline->setReportDecodeErrors(m_kSourceId, true)); +} + +/** + * Test that setReportDecodeErrors returns failure if the IPC API fails. + */ +TEST_F(RialtoClientMediaPipelineSetReportDecodeErrorsTest, SetReportDecodeErrorsFailure) +{ + EXPECT_CALL(*m_mediaPipelineIpcMock, setReportDecodeErrors(m_kSourceId, _)).WillOnce(Return(false)); + EXPECT_FALSE(m_mediaPipeline->setReportDecodeErrors(m_kSourceId, true)); +} diff --git a/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h b/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h index 32e72ba2c..f08f54fef 100644 --- a/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h +++ b/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h @@ -47,6 +47,8 @@ class MediaPipelineIpcMock : public IMediaPipelineIpc MOCK_METHOD(bool, getPosition, (int64_t & position), (override)); MOCK_METHOD(bool, setImmediateOutput, (int32_t sourceId, bool immediateOutput), (override)); MOCK_METHOD(bool, getImmediateOutput, (int32_t sourceId, bool &immediateOutput), (override)); + MOCK_METHOD(bool, setReportDecodeErrors, (int32_t sourceId, bool reportDecodeErrors), (override)); + MOCK_METHOD(bool, getQueuedFrames, (int32_t sourceId, uint32_t &queuedFrames), (override)); MOCK_METHOD(bool, getStats, (int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames), (override)); MOCK_METHOD(bool, setPlaybackRate, (double rate), (override)); MOCK_METHOD(bool, renderFrame, (), (override)); diff --git a/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h b/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h index c69140210..1be860018 100644 --- a/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h +++ b/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h @@ -50,6 +50,8 @@ class MediaPipelineAndControlClientMock : public IMediaPipelineAndIControlClient MOCK_METHOD(bool, getPosition, (int64_t & position), (override)); MOCK_METHOD(bool, setImmediateOutput, (int32_t sourceId, bool immediateOutput), (override)); MOCK_METHOD(bool, getImmediateOutput, (int32_t sourceId, bool &immediateOutput), (override)); + MOCK_METHOD(bool, setReportDecodeErrors, (int32_t sourceId, bool reportDecodeErrors), (override)); + MOCK_METHOD(bool, getQueuedFrames, (int32_t sourceId, uint32_t &queuedFrames), (override)); MOCK_METHOD(bool, getStats, (int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames), (override)); MOCK_METHOD(bool, setVideoWindow, (uint32_t x, uint32_t y, uint32_t width, uint32_t height), (override)); diff --git a/tests/unittests/media/server/gstplayer/CMakeLists.txt b/tests/unittests/media/server/gstplayer/CMakeLists.txt index ed6635e3b..00a647f56 100644 --- a/tests/unittests/media/server/gstplayer/CMakeLists.txt +++ b/tests/unittests/media/server/gstplayer/CMakeLists.txt @@ -49,6 +49,7 @@ add_gtests(RialtoServerGstPlayerUnitTests genericPlayer/tasksTests/SetBufferingLimitTest.cpp genericPlayer/tasksTests/SetLowLatencyTest.cpp genericPlayer/tasksTests/SetImmediateOutputTest.cpp + genericPlayer/tasksTests/SetReportDecodeErrorsTest.cpp genericPlayer/tasksTests/SetMuteTest.cpp genericPlayer/tasksTests/SetPlaybackRateTest.cpp genericPlayer/tasksTests/SetPositionTest.cpp diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp index 744507881..9db981f26 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp @@ -72,6 +72,7 @@ const std::string kAutoVideoSinkTypeName{"GstAutoVideoSink"}; const std::string kAutoAudioSinkTypeName{"GstAutoAudioSink"}; constexpr bool kResetTime{true}; const std::string kImmediateOutputStr{"immediate-output"}; +const std::string kReportDecodeErrorsStr{"report-decode-errors"}; const std::string kLowLatencyStr{"low-latency"}; const std::string kSyncStr{"sync"}; const std::string kSyncOffStr{"sync-off"}; @@ -355,6 +356,35 @@ TEST_F(GstGenericPlayerPrivateTest, shouldSetImmediateOutput) EXPECT_TRUE(m_sut->setImmediateOutput()); } +TEST_F(GstGenericPlayerPrivateTest, shouldFailToSetReportDecodeErrorsIfPropertyDoesntExist) +{ + modifyContext([&](GenericPlayerContext &context) { context.pendingReportDecodeErrorsForVideo = true; }); + + expectGetVideoDecoder(m_realElement); + + expectPropertyDoesntExist(m_glibWrapperMock, m_gstWrapperMock, m_realElement, kReportDecodeErrorsStr); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_realElement)).Times(1); + EXPECT_FALSE(m_sut->setReportDecodeErrors()); +} + +TEST_F(GstGenericPlayerPrivateTest, shouldSetReportDecodeErrors) +{ + modifyContext([&](GenericPlayerContext &context) { context.pendingReportDecodeErrorsForVideo = true; }); + + expectGetVideoDecoder(m_realElement); + + GParamSpec gParamSpec{}; + + EXPECT_CALL(*m_glibWrapperMock, + gObjectClassFindProperty(G_OBJECT_GET_CLASS(m_realElement), StrEq(kReportDecodeErrorsStr))) + .WillOnce(Return(&gParamSpec)); + + EXPECT_CALL(*m_glibWrapperMock, gObjectSetStub(m_realElement, StrEq(kReportDecodeErrorsStr))).Times(1); + + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_realElement)).Times(1); + EXPECT_TRUE(m_sut->setReportDecodeErrors()); +} + TEST_F(GstGenericPlayerPrivateTest, shouldFailToSetLowLatencyIfSinkIsNull) { modifyContext([&](GenericPlayerContext &context) { context.pendingLowLatency = true; }); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp index 138fbcecf..108b0e423 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp @@ -452,6 +452,43 @@ TEST_F(GstGenericPlayerTest, shouldFailToGetImmediateOutputInPlayingStateIfPrope EXPECT_FALSE(m_sut->getImmediateOutput(MediaSourceType::VIDEO, immediateOutputState)); } +TEST_F(GstGenericPlayerTest, shouldSetReportDecodeErrors) +{ + std::unique_ptr task{std::make_unique>()}; + EXPECT_CALL(dynamic_cast &>(*task), execute()); + EXPECT_CALL(m_taskFactoryMock, createSetReportDecodeErrors(_, _, MediaSourceType::VIDEO, true)) + .WillOnce(Return(ByMove(std::move(task)))); + + EXPECT_TRUE(m_sut->setReportDecodeErrors(MediaSourceType::VIDEO, true)); +} + +TEST_F(GstGenericPlayerTest, shouldGetQueuedFramesInPlayingState) +{ + setPipelineState(GST_STATE_PLAYING); + const uint32_t kTestQueuedFramesValue{123}; + const std::string kPropertyStr{"queued-frames"}; + + expectGetVideoDecoder(m_element); + willGetElementProperty(kPropertyStr, kTestQueuedFramesValue); + + uint32_t queuedFrames; + EXPECT_TRUE(m_sut->getQueuedFrames(queuedFrames)); + EXPECT_EQ(queuedFrames, kTestQueuedFramesValue); +} + +TEST_F(GstGenericPlayerTest, shouldFailToGetQueuedFramesInPlayingStateIfPropertyDoesntExist) +{ + setPipelineState(GST_STATE_PLAYING); + + expectGetVideoDecoder(m_element); + + EXPECT_CALL(*m_glibWrapperMock, gObjectClassFindProperty(_, StrEq("queued-frames"))).WillOnce(Return(nullptr)); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_element)).Times(1); + + uint32_t queuedFrames; + EXPECT_FALSE(m_sut->getQueuedFrames(queuedFrames)); +} + TEST_F(GstGenericPlayerTest, shouldGetStatsInPlayingState) { constexpr guint64 kRenderedFrames{1234}; diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp index 37f814afe..13da7fecc 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp @@ -46,6 +46,7 @@ #include "tasks/generic/SetMute.h" #include "tasks/generic/SetPlaybackRate.h" #include "tasks/generic/SetPosition.h" +#include "tasks/generic/SetReportDecodeErrors.h" #include "tasks/generic/SetSourcePosition.h" #include "tasks/generic/SetStreamSyncMode.h" #include "tasks/generic/SetSubtitleOffset.h" @@ -3342,6 +3343,20 @@ void GenericTasksTestsBase::triggerSetImmediateOutput() EXPECT_EQ(testContext->m_context.pendingImmediateOutputForVideo, true); } +void GenericTasksTestsBase::shouldSetReportDecodeErrors() +{ + EXPECT_CALL(testContext->m_gstPlayer, setReportDecodeErrors()).WillOnce(Return(true)); +} + +void GenericTasksTestsBase::triggerSetReportDecodeErrors() +{ + firebolt::rialto::server::tasks::generic::SetReportDecodeErrors task{testContext->m_context, testContext->m_gstPlayer, + MediaSourceType::VIDEO, true}; + task.execute(); + + EXPECT_EQ(testContext->m_context.pendingReportDecodeErrorsForVideo, true); +} + void GenericTasksTestsBase::shouldSetLowLatency() { EXPECT_CALL(testContext->m_gstPlayer, setLowLatency()).WillOnce(Return(true)); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h index b1ee082db..fc875a400 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h @@ -279,6 +279,10 @@ class GenericTasksTestsBase : public ::testing::Test void shouldSetVideoMute(); void shouldSetSubtitleMute(); + // report-decode-errors decoder property test method + void shouldSetReportDecodeErrors(); + void triggerSetReportDecodeErrors(); + // immediate-output sink property test methods void shouldSetImmediateOutput(); void triggerSetImmediateOutput(); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp index ef78956a1..c4d5d2741 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp @@ -217,6 +217,20 @@ void GstGenericPlayerTestCommon::expectGetDecoder(GstElement *element) EXPECT_CALL(*m_gstWrapperMock, gstIteratorFree(&m_it)); } +void GstGenericPlayerTestCommon::expectGetVideoDecoder(GstElement *element) +{ + EXPECT_CALL(*m_gstWrapperMock, gstBinIterateRecurse(GST_BIN(&m_pipeline))).WillOnce(Return(&m_it)); + EXPECT_CALL(*m_gstWrapperMock, gstIteratorNext(&m_it, _)).WillOnce(Return(GST_ITERATOR_OK)); + EXPECT_CALL(*m_glibWrapperMock, gValueGetObject(_)).WillOnce(Return(element)); + EXPECT_CALL(*m_gstWrapperMock, gstElementGetFactory(element)).WillOnce(Return(m_factory)); + EXPECT_CALL(*m_gstWrapperMock, gstElementFactoryListIsType(m_factory, (GST_ELEMENT_FACTORY_TYPE_DECODER | + GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO))) + .WillOnce(Return(TRUE)); + EXPECT_CALL(*m_gstWrapperMock, gstObjectRef(element)).WillOnce(Return(element)); + EXPECT_CALL(*m_glibWrapperMock, gValueUnset(_)); + EXPECT_CALL(*m_gstWrapperMock, gstIteratorFree(&m_it)); +} + void GstGenericPlayerTestCommon::expectGetVideoParser(GstElement *element) { EXPECT_CALL(*m_gstWrapperMock, gstBinIterateRecurse(GST_BIN(&m_pipeline))).WillOnce(Return(&m_it)); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h index aaae1fc5c..d8e72efaf 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h @@ -119,6 +119,7 @@ class GstGenericPlayerTestCommon : public ::testing::Test void expectCheckPlaySink(); void expectSetMessageCallback(); void expectGetDecoder(GstElement *element); + void expectGetVideoDecoder(GstElement *element); void expectGetVideoParser(GstElement *element); void expectGetAVSink(const std::string &sinkName, GstElement *elementObj); void expectGetSink(const std::string &sinkName, GstElement *elementObj); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp index 70c305e22..d25627cda 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp @@ -53,6 +53,7 @@ #include "tasks/generic/SetMute.h" #include "tasks/generic/SetPlaybackRate.h" #include "tasks/generic/SetPosition.h" +#include "tasks/generic/SetReportDecodeErrors.h" #include "tasks/generic/SetSourcePosition.h" #include "tasks/generic/SetStreamSyncMode.h" #include "tasks/generic/SetSync.h" @@ -344,6 +345,13 @@ TEST_F(GenericPlayerTaskFactoryTest, ShouldCreateSetImmediateOutput) EXPECT_NO_THROW(dynamic_cast(*task)); } +TEST_F(GenericPlayerTaskFactoryTest, ShouldCreateSetReportDecodeErrors) +{ + auto task = m_sut.createSetReportDecodeErrors(m_context, m_gstPlayer, firebolt::rialto::MediaSourceType::VIDEO, true); + EXPECT_NE(task, nullptr); + EXPECT_NO_THROW(dynamic_cast(*task)); +} + TEST_F(GenericPlayerTaskFactoryTest, ShouldCreateSetTextTrackIdentifier) { auto task = m_sut.createSetTextTrackIdentifier(m_context, ""); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/SetReportDecodeErrorsTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/SetReportDecodeErrorsTest.cpp new file mode 100644 index 000000000..3d3f89f96 --- /dev/null +++ b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/SetReportDecodeErrorsTest.cpp @@ -0,0 +1,30 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2025 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GenericTasksTestsBase.h" + +class SetReportDecodeErrorsTest : public GenericTasksTestsBase +{ +}; + +TEST_F(SetReportDecodeErrorsTest, shouldSetReportDecodeErrors) +{ + shouldSetReportDecodeErrors(); + triggerSetReportDecodeErrors(); +} diff --git a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTests.cpp b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTests.cpp index dcad505bb..593a01b1e 100644 --- a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTests.cpp +++ b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTests.cpp @@ -281,6 +281,38 @@ TEST_F(MediaPipelineModuleServiceTests, shouldFailToGetImmediateOutput) sendGetImmediateOutputRequestAndReceiveFail(); } +TEST_F(MediaPipelineModuleServiceTests, shouldSetReportDecodeErrors) +{ + mediaPipelineServiceWillCreateSession(); + sendCreateSessionRequestAndReceiveResponse(); + mediaPipelineServiceWillSetReportDecodeErrors(); + sendSetReportDecodeErrorsRequestAndReceiveResponse(); +} + +TEST_F(MediaPipelineModuleServiceTests, shouldFailToSetReportDecodeErrors) +{ + mediaPipelineServiceWillCreateSession(); + sendCreateSessionRequestAndReceiveResponse(); + mediaPipelineServiceWillFailToSetReportDecodeErrors(); + sendSetReportDecodeErrorsRequestAndReceiveFail(); +} + +TEST_F(MediaPipelineModuleServiceTests, shouldGetQueuedFrames) +{ + mediaPipelineServiceWillCreateSession(); + sendCreateSessionRequestAndReceiveResponse(); + mediaPipelineServiceWillGetQueuedFrames(); + sendGetQueuedFramesRequestAndReceiveResponse(); +} + +TEST_F(MediaPipelineModuleServiceTests, shouldFailToGetQueuedFrames) +{ + mediaPipelineServiceWillCreateSession(); + sendCreateSessionRequestAndReceiveResponse(); + mediaPipelineServiceWillFailToGetQueuedFrames(); + sendGetQueuedFramesRequestAndReceiveFail(); +} + TEST_F(MediaPipelineModuleServiceTests, shouldGetStats) { mediaPipelineServiceWillCreateSession(); diff --git a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp index 31ec98768..5aa608e2e 100644 --- a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp +++ b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp @@ -86,6 +86,8 @@ constexpr uint64_t kDroppedFrames{321}; constexpr uint32_t kDuration{30}; constexpr bool kImmediateOutputVal1{false}; constexpr bool kImmediateOutputVal2{true}; +constexpr bool kReportDecodeErrorsVal{false}; +constexpr uint32_t kQueuedFramesVal{123}; constexpr int64_t kDiscontinuityGap{1}; constexpr bool kIsAudioAac{false}; constexpr uint32_t kBufferingLimit{12341}; @@ -518,6 +520,33 @@ void MediaPipelineModuleServiceTests::mediaPipelineServiceWillFailToGetImmediate EXPECT_CALL(m_mediaPipelineServiceMock, getImmediateOutput(kHardcodedSessionId, _, _)).WillOnce(Return(false)); } +void MediaPipelineModuleServiceTests::mediaPipelineServiceWillSetReportDecodeErrors() +{ + expectRequestSuccess(); + EXPECT_CALL(m_mediaPipelineServiceMock, setReportDecodeErrors(kHardcodedSessionId, _, kReportDecodeErrorsVal)) + .WillOnce(Return(true)); +} + +void MediaPipelineModuleServiceTests::mediaPipelineServiceWillFailToSetReportDecodeErrors() +{ + expectRequestFailure(); + EXPECT_CALL(m_mediaPipelineServiceMock, setReportDecodeErrors(kHardcodedSessionId, _, kReportDecodeErrorsVal)) + .WillOnce(Return(false)); +} + +void MediaPipelineModuleServiceTests::mediaPipelineServiceWillGetQueuedFrames() +{ + expectRequestSuccess(); + EXPECT_CALL(m_mediaPipelineServiceMock, getQueuedFrames(kHardcodedSessionId, _, _)) + .WillOnce(DoAll(SetArgReferee<2>(kQueuedFramesVal), Return(true))); +} + +void MediaPipelineModuleServiceTests::mediaPipelineServiceWillFailToGetQueuedFrames() +{ + expectRequestFailure(); + EXPECT_CALL(m_mediaPipelineServiceMock, getQueuedFrames(kHardcodedSessionId, _, _)).WillOnce(Return(false)); +} + void MediaPipelineModuleServiceTests::mediaPipelineServiceWillGetStats() { expectRequestSuccess(); @@ -1148,6 +1177,50 @@ void MediaPipelineModuleServiceTests::sendGetImmediateOutputRequestAndReceiveFai m_service->getImmediateOutput(m_controllerMock.get(), &request, &response, m_closureMock.get()); } +void MediaPipelineModuleServiceTests::sendSetReportDecodeErrorsRequestAndReceiveResponse() +{ + firebolt::rialto::SetReportDecodeErrorsRequest request; + firebolt::rialto::SetReportDecodeErrorsResponse response; + + request.set_session_id(kHardcodedSessionId); + request.set_report_decode_errors(kReportDecodeErrorsVal); + + m_service->setReportDecodeErrors(m_controllerMock.get(), &request, &response, m_closureMock.get()); +} + +void MediaPipelineModuleServiceTests::sendSetReportDecodeErrorsRequestAndReceiveFail() +{ + firebolt::rialto::SetReportDecodeErrorsRequest request; + firebolt::rialto::SetReportDecodeErrorsResponse response; + + request.set_session_id(kHardcodedSessionId); + request.set_report_decode_errors(kReportDecodeErrorsVal); + + m_service->setReportDecodeErrors(m_controllerMock.get(), &request, &response, m_closureMock.get()); +} + +void MediaPipelineModuleServiceTests::sendGetQueuedFramesRequestAndReceiveResponse() +{ + firebolt::rialto::GetQueuedFramesRequest request; + firebolt::rialto::GetQueuedFramesResponse response; + + request.set_session_id(kHardcodedSessionId); + + m_service->getQueuedFrames(m_controllerMock.get(), &request, &response, m_closureMock.get()); + + EXPECT_EQ(response.queued_frames(), kQueuedFramesVal); +} + +void MediaPipelineModuleServiceTests::sendGetQueuedFramesRequestAndReceiveFail() +{ + firebolt::rialto::GetQueuedFramesRequest request; + firebolt::rialto::GetQueuedFramesResponse response; + + request.set_session_id(kHardcodedSessionId); + + m_service->getQueuedFrames(m_controllerMock.get(), &request, &response, m_closureMock.get()); +} + void MediaPipelineModuleServiceTests::sendGetStatsRequestAndReceiveResponse() { firebolt::rialto::GetStatsRequest request; diff --git a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.h b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.h index 90f02fb4a..b987dd88e 100644 --- a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.h +++ b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.h @@ -78,6 +78,10 @@ class MediaPipelineModuleServiceTests : public testing::Test void mediaPipelineServiceWillFailToSetImmediateOutput(); void mediaPipelineServiceWillGetImmediateOutput(); void mediaPipelineServiceWillFailToGetImmediateOutput(); + void mediaPipelineServiceWillSetReportDecodeErrors(); + void mediaPipelineServiceWillFailToSetReportDecodeErrors(); + void mediaPipelineServiceWillGetQueuedFrames(); + void mediaPipelineServiceWillFailToGetQueuedFrames(); void mediaPipelineServiceWillGetStats(); void mediaPipelineServiceWillFailToGetStats(); void mediaPipelineServiceWillRenderFrame(); @@ -154,6 +158,10 @@ class MediaPipelineModuleServiceTests : public testing::Test void sendSetImmediateOutputRequestAndReceiveFail(); void sendGetImmediateOutputRequestAndReceiveResponse(); void sendGetImmediateOutputRequestAndReceiveFail(); + void sendSetReportDecodeErrorsRequestAndReceiveResponse(); + void sendSetReportDecodeErrorsRequestAndReceiveFail(); + void sendGetQueuedFramesRequestAndReceiveResponse(); + void sendGetQueuedFramesRequestAndReceiveFail(); void sendGetStatsRequestAndReceiveResponse(); void sendGetStatsRequestAndReceiveResponseWithoutStatsMatch(); void sendHaveDataRequestAndReceiveResponse(); diff --git a/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp b/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp index aa4f3e2f3..b1ca63c5b 100644 --- a/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp +++ b/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp @@ -327,6 +327,112 @@ TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetImmediateOutputSu EXPECT_EQ(immediateOutputState, false); } +/** + * Test that SetReportDecodeErrors returns failure if the gstreamer player is not initialized + */ +TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, SetReportDecodeErrorsFailureDueToUninitializedPlayer) +{ + mainThreadWillEnqueueTaskAndWait(); + EXPECT_FALSE(m_mediaPipeline->setReportDecodeErrors(m_kDummySourceId, true)); +} + +/** + * Test that SetReportDecodeErrors returns failure if the gstreamer API fails + */ +TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, SetReportDecodeErrorsFailure) +{ + loadGstPlayer(); + int videoSourceId = attachSource(firebolt::rialto::MediaSourceType::VIDEO, "video/h264"); + mainThreadWillEnqueueTaskAndWait(); + EXPECT_CALL(*m_gstPlayerMock, setReportDecodeErrors(_, _)).WillOnce(Return(false)); + EXPECT_FALSE(m_mediaPipeline->setReportDecodeErrors(videoSourceId, true)); +} + +/** + * Test that SetReportDecodeErrors fails if source is not present. + */ +TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, SetReportDecodeErrorsNoSourcePresent) +{ + loadGstPlayer(); + // No attachment of source + mainThreadWillEnqueueTaskAndWait(); + + EXPECT_FALSE(m_mediaPipeline->setReportDecodeErrors(m_kDummySourceId, true)); +} + +/** + * Test that SetReportDecodeErrors returns success if the gstreamer API succeeds + */ +TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, SetReportDecodeErrorsSuccess) +{ + loadGstPlayer(); + int videoSourceId = attachSource(firebolt::rialto::MediaSourceType::VIDEO, "video/h264"); + + mainThreadWillEnqueueTaskAndWait(); + EXPECT_CALL(*m_gstPlayerMock, setReportDecodeErrors(_, true)).WillOnce(Return(true)); + EXPECT_TRUE(m_mediaPipeline->setReportDecodeErrors(videoSourceId, true)); + + mainThreadWillEnqueueTaskAndWait(); + EXPECT_CALL(*m_gstPlayerMock, setReportDecodeErrors(_, false)).WillOnce(Return(true)); + EXPECT_TRUE(m_mediaPipeline->setReportDecodeErrors(videoSourceId, false)); +} + +/** + * Test that GetQueuedFrames returns failure if the gstreamer player is not initialized + */ +TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetQueuedFramesFailureDueToUninitializedPlayer) +{ + mainThreadWillEnqueueTaskAndWait(); + uint32_t queuedFrames; + EXPECT_FALSE(m_mediaPipeline->getQueuedFrames(m_kDummySourceId, queuedFrames)); +} + +/** + * Test that GetQueuedFrames returns failure if the gstreamer API fails + */ +TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetQueuedFramesFailure) +{ + loadGstPlayer(); + int videoSourceId = attachSource(firebolt::rialto::MediaSourceType::VIDEO, "video/h264"); + mainThreadWillEnqueueTaskAndWait(); + EXPECT_CALL(*m_gstPlayerMock, getQueuedFrames(_)).WillOnce(Return(false)); + uint32_t queuedFrames; + EXPECT_FALSE(m_mediaPipeline->getQueuedFrames(videoSourceId, queuedFrames)); +} + +/** + * Test that GetQueuedFrames fails if source is not present. + */ +TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetQueuedFramesNoSourcePresent) +{ + loadGstPlayer(); + // No attachment of source + mainThreadWillEnqueueTaskAndWait(); + + uint32_t queuedFrames; + EXPECT_FALSE(m_mediaPipeline->getQueuedFrames(m_kDummySourceId, queuedFrames)); +} + +/** + * Test that GetQueuedFrames returns success if the gstreamer API succeeds + */ +TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetQueuedFramesSuccess) +{ + loadGstPlayer(); + int videoSourceId = attachSource(firebolt::rialto::MediaSourceType::VIDEO, "video/h264"); + + uint32_t queuedFrames; + mainThreadWillEnqueueTaskAndWait(); + EXPECT_CALL(*m_gstPlayerMock, getQueuedFrames(_)).WillOnce(DoAll(SetArgReferee<0>(123), Return(true))); + EXPECT_TRUE(m_mediaPipeline->getQueuedFrames(videoSourceId, queuedFrames)); + EXPECT_EQ(queuedFrames, 123); + + mainThreadWillEnqueueTaskAndWait(); + EXPECT_CALL(*m_gstPlayerMock, getQueuedFrames(_)).WillOnce(DoAll(SetArgReferee<0>(456), Return(true))); + EXPECT_TRUE(m_mediaPipeline->getQueuedFrames(videoSourceId, queuedFrames)); + EXPECT_EQ(queuedFrames, 456); +} + /** * Test that GetStats returns failure if the gstreamer player is not initialized */ diff --git a/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h b/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h index 2cb7f0a35..b3fca43e0 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h @@ -133,6 +133,10 @@ class GenericPlayerTaskFactoryMock : public IGenericPlayerTaskFactory (GenericPlayerContext & context, IGstGenericPlayerPrivate &player, const firebolt::rialto::MediaSourceType &type, bool immediateOutput), (const, override)); + MOCK_METHOD(std::unique_ptr, createSetReportDecodeErrors, + (GenericPlayerContext & context, IGstGenericPlayerPrivate &player, + const firebolt::rialto::MediaSourceType &type, bool reportDecodeErrors), + (const, override)); MOCK_METHOD(std::unique_ptr, createSetBufferingLimit, (GenericPlayerContext & context, IGstGenericPlayerPrivate &player, std::uint32_t limit), (const, override)); diff --git a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h index c38c1d9eb..90ab4ebf7 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h @@ -44,6 +44,9 @@ class GstGenericPlayerMock : public IGstGenericPlayer MOCK_METHOD(bool, getPosition, (std::int64_t & position), (override)); MOCK_METHOD(bool, setImmediateOutput, (const MediaSourceType &mediaSourceType, bool immediateOutput), (override)); MOCK_METHOD(bool, getImmediateOutput, (const MediaSourceType &mediaSourceType, bool &immediateOutput), (override)); + MOCK_METHOD(bool, setReportDecodeErrors, (const MediaSourceType &mediaSourceType, bool reportDecodeErrors), + (override)); + MOCK_METHOD(bool, getQueuedFrames, (uint32_t & queuedFrames), (override)); MOCK_METHOD(bool, getStats, (const MediaSourceType &mediaSourceType, uint64_t &renderedFrames, uint64_t &droppedFrames), (override)); MOCK_METHOD(void, setVideoGeometry, (int x, int y, int width, int height), (override)); diff --git a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h index e28c70d8d..d0b018a87 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h @@ -39,6 +39,7 @@ class GstGenericPlayerPrivateMock : public IGstGenericPlayerPrivate MOCK_METHOD(void, scheduleAllSourcesAttached, (), (override)); MOCK_METHOD(bool, setVideoSinkRectangle, (), (override)); MOCK_METHOD(bool, setImmediateOutput, (), (override)); + MOCK_METHOD(bool, setReportDecodeErrors, (), (override)); MOCK_METHOD(bool, setLowLatency, (), (override)); MOCK_METHOD(bool, setSync, (), (override)); MOCK_METHOD(bool, setSyncOff, (), (override)); diff --git a/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h b/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h index 27a12a686..82f56556f 100644 --- a/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h +++ b/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h @@ -44,6 +44,8 @@ class MediaPipelineServerInternalMock : public IMediaPipelineServerInternal MOCK_METHOD(bool, getPosition, (std::int64_t & position), (override)); MOCK_METHOD(bool, setImmediateOutput, (int32_t sourceId, bool immediateOutput), (override)); MOCK_METHOD(bool, getImmediateOutput, (int32_t sourceId, bool &immediateOutput), (override)); + MOCK_METHOD(bool, setReportDecodeErrors, (int32_t sourceId, bool reportDecodeErrors), (override)); + MOCK_METHOD(bool, getQueuedFrames, (int32_t sourceId, uint32_t &queuedFrames), (override)); MOCK_METHOD(bool, getStats, (int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames), (override)); MOCK_METHOD(bool, setVideoWindow, (uint32_t x, uint32_t y, uint32_t width, uint32_t height), (override)); MOCK_METHOD(bool, haveData, (MediaSourceStatus status, uint32_t numFrames, uint32_t needDataRequestId), (override)); diff --git a/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h b/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h index f7b226777..46d46ee4f 100644 --- a/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h +++ b/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h @@ -46,6 +46,8 @@ class MediaPipelineServiceMock : public IMediaPipelineService MOCK_METHOD(bool, getPosition, (int sessionId, int64_t &position), (override)); MOCK_METHOD(bool, setImmediateOutput, (int sessionId, int32_t sourceId, bool immediateOutput), (override)); MOCK_METHOD(bool, getImmediateOutput, (int sessionId, int32_t sourceId, bool &immediateOutput), (override)); + MOCK_METHOD(bool, setReportDecodeErrors, (int sessionId, int32_t sourceId, bool reportDecodeErrors), (override)); + MOCK_METHOD(bool, getQueuedFrames, (int sessionId, int32_t sourceId, uint32_t &queuedFrames), (override)); MOCK_METHOD(bool, getStats, (int sessionId, int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames), (override)); MOCK_METHOD(bool, setVideoWindow, (int, std::uint32_t, std::uint32_t, std::uint32_t, std::uint32_t), (override)); diff --git a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTests.cpp b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTests.cpp index 4cd1f95d1..b922b72fe 100644 --- a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTests.cpp +++ b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTests.cpp @@ -362,6 +362,46 @@ TEST_F(MediaPipelineServiceTests, shouldGetImmediateOutput) getImmediateOutputShouldSucceed(); } +TEST_F(MediaPipelineServiceTests, shouldFailToSetReportDecodeErrorsForNotExistingSession) +{ + createMediaPipelineShouldSuccess(); + setReportDecodeErrorsShouldFail(); +} + +TEST_F(MediaPipelineServiceTests, shouldFailToGetQueuedFramesForNotExistingSession) +{ + createMediaPipelineShouldSuccess(); + getQueuedFramesShouldFail(); +} + +TEST_F(MediaPipelineServiceTests, shouldFailToSetReportDecodeErrors) +{ + initSession(); + mediaPipelineWillFailToSetReportDecodeErrors(); + setReportDecodeErrorsShouldFail(); +} + +TEST_F(MediaPipelineServiceTests, shouldFailToGetQueuedFrames) +{ + initSession(); + mediaPipelineWillFailToGetQueuedFrames(); + getQueuedFramesShouldFail(); +} + +TEST_F(MediaPipelineServiceTests, shouldSetReportDecodeErrors) +{ + initSession(); + mediaPipelineWillSetReportDecodeErrors(); + setReportDecodeErrorsShouldSucceed(); +} + +TEST_F(MediaPipelineServiceTests, shouldGetQueuedFrames) +{ + initSession(); + mediaPipelineWillGetQueuedFrames(); + getQueuedFramesShouldSucceed(); +} + TEST_F(MediaPipelineServiceTests, shouldFailToGetStatsForNotExistingSession) { createMediaPipelineShouldSuccess(); diff --git a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp index 421cb1d8b..6de97e773 100644 --- a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp +++ b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp @@ -69,6 +69,7 @@ const std::string kTextTrackIdentifier{"TextTrackIdentifier"}; constexpr uint32_t kBufferingLimit{4324}; constexpr bool kUseBuffering{true}; constexpr uint64_t kStopPosition{23412}; +constexpr uint32_t kQueuedFrames{123}; } // namespace namespace firebolt::rialto @@ -242,6 +243,26 @@ void MediaPipelineServiceTests::mediaPipelineWillFailToGetImmediateOutput() EXPECT_CALL(m_mediaPipelineMock, getImmediateOutput(_, _)).WillOnce(Return(false)); } +void MediaPipelineServiceTests::mediaPipelineWillSetReportDecodeErrors() +{ + EXPECT_CALL(m_mediaPipelineMock, setReportDecodeErrors(_, _)).WillOnce(Return(true)); +} + +void MediaPipelineServiceTests::mediaPipelineWillFailToSetReportDecodeErrors() +{ + EXPECT_CALL(m_mediaPipelineMock, setReportDecodeErrors(_, _)).WillOnce(Return(false)); +} + +void MediaPipelineServiceTests::mediaPipelineWillGetQueuedFrames() +{ + EXPECT_CALL(m_mediaPipelineMock, getQueuedFrames(_, _)).WillOnce(DoAll(SetArgReferee<1>(123), Return(true))); +} + +void MediaPipelineServiceTests::mediaPipelineWillFailToGetQueuedFrames() +{ + EXPECT_CALL(m_mediaPipelineMock, getQueuedFrames(_, _)).WillOnce(Return(false)); +} + void MediaPipelineServiceTests::mediaPipelineWillGetStats() { EXPECT_CALL(m_mediaPipelineMock, getStats(_, _, _)) @@ -746,6 +767,29 @@ void MediaPipelineServiceTests::getImmediateOutputShouldFail() EXPECT_FALSE(m_sut->getImmediateOutput(kSessionId, kSourceId, immOp)); } +void MediaPipelineServiceTests::setReportDecodeErrorsShouldSucceed() +{ + EXPECT_TRUE(m_sut->setReportDecodeErrors(kSessionId, kSourceId, true)); +} + +void MediaPipelineServiceTests::setReportDecodeErrorsShouldFail() +{ + EXPECT_FALSE(m_sut->setReportDecodeErrors(kSessionId, kSourceId, true)); +} + +void MediaPipelineServiceTests::getQueuedFramesShouldSucceed() +{ + uint32_t queuedFr; + EXPECT_TRUE(m_sut->getQueuedFrames(kSessionId, kSourceId, queuedFr)); + EXPECT_EQ(queuedFr, kQueuedFrames); +} + +void MediaPipelineServiceTests::getQueuedFramesShouldFail() +{ + uint32_t queuedFr; + EXPECT_FALSE(m_sut->getQueuedFrames(kSessionId, kSourceId, queuedFr)); +} + void MediaPipelineServiceTests::getSupportedMimeTypesSucceed() { MediaSourceType type = MediaSourceType::VIDEO; diff --git a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.h b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.h index ead87a642..1a7e28d15 100644 --- a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.h +++ b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.h @@ -66,8 +66,12 @@ class MediaPipelineServiceTests : public testing::Test void mediaPipelineWillFailToGetPosition(); void mediaPipelineWillSetImmediateOutput(); void mediaPipelineWillFailToSetImmediateOutput(); + void mediaPipelineWillSetReportDecodeErrors(); + void mediaPipelineWillFailToSetReportDecodeErrors(); void mediaPipelineWillGetImmediateOutput(); void mediaPipelineWillFailToGetImmediateOutput(); + void mediaPipelineWillGetQueuedFrames(); + void mediaPipelineWillFailToGetQueuedFrames(); void mediaPipelineWillGetStats(); void mediaPipelineWillFailToGetStats(); void mediaPipelineWillRenderFrame(); @@ -156,10 +160,14 @@ class MediaPipelineServiceTests : public testing::Test void haveDataShouldFail(); void getPositionShouldSucceed(); void getPositionShouldFail(); + void setReportDecodeErrorsShouldSucceed(); + void setReportDecodeErrorsShouldFail(); void setImmediateOutputShouldSucceed(); void setImmediateOutputShouldFail(); void getImmediateOutputShouldSucceed(); void getImmediateOutputShouldFail(); + void getQueuedFramesShouldSucceed(); + void getQueuedFramesShouldFail(); void getStatsShouldSucceed(); void getStatsShouldFail(); void getSupportedMimeTypesSucceed(); From 7ddddcbe7c5c2d72adf8f6b709ea38ed306d15ee Mon Sep 17 00:00:00 2001 From: Marcin Wojciechowski <105790697+skywojciechowskim@users.noreply.github.com> Date: Thu, 23 Apr 2026 07:23:34 +0200 Subject: [PATCH 19/29] IMediaPipeline::GetDuration method added (#486) Summary: IMediaPipeline::GetDuration method added Type: Feature Test Plan: UT/CT, Fullstack Jira: RDKEMW-16798 --- media/client/ipc/include/MediaPipelineIpc.h | 2 + .../client/ipc/interface/IMediaPipelineIpc.h | 11 +++ media/client/ipc/source/MediaPipelineIpc.cpp | 31 ++++++ media/client/main/include/MediaPipeline.h | 2 + .../client/main/include/MediaPipelineProxy.h | 2 + media/client/main/source/MediaPipeline.cpp | 7 ++ media/public/include/IMediaPipeline.h | 11 +++ .../gstplayer/include/GstGenericPlayer.h | 1 + .../gstplayer/interface/IGstGenericPlayer.h | 9 ++ .../gstplayer/source/GstGenericPlayer.cpp | 12 +++ .../ipc/include/MediaPipelineModuleService.h | 2 + .../ipc/source/MediaPipelineModuleService.cpp | 19 ++++ .../include/MediaPipelineServerInternal.h | 2 + .../source/MediaPipelineServerInternal.cpp | 14 +++ .../service/include/IMediaPipelineService.h | 1 + .../service/source/MediaPipelineService.cpp | 14 +++ .../service/source/MediaPipelineService.h | 1 + proto/mediapipelinemodule.proto | 24 +++++ .../externalLibraryMocks/GstWrapperMock.h | 1 + .../MediaPipelineProtoRequestMatchers.h | 7 ++ .../client/mocks/MediaPipelineModuleMock.h | 10 ++ .../tests/base/MediaPipelineTestMethods.cpp | 14 +++ .../tests/base/MediaPipelineTestMethods.h | 2 + .../client/tests/mse/PipelinePropertyTest.cpp | 17 ++++ .../server/common/ActionTraits.h | 8 ++ .../server/common/MessageBuilders.cpp | 7 ++ .../server/common/MessageBuilders.h | 1 + .../mediaPipeline/PipelinePropertyTest.cpp | 71 ++++++++++++-- .../unittests/media/client/ipc/CMakeLists.txt | 1 + .../ipc/mediaPipelineIpc/GetDurationTest.cpp | 97 +++++++++++++++++++ .../media/client/main/CMakeLists.txt | 1 + .../main/mediaPipeline/GetDurationTest.cpp | 66 +++++++++++++ .../mediaPipeline/MediaPipelineProxyTest.cpp | 9 ++ .../client/mocks/ipc/MediaPipelineIpcMock.h | 1 + .../main/MediaPipelineAndControlClientMock.h | 1 + .../genericPlayer/GstGenericPlayerTest.cpp | 22 +++++ .../MediaPipelineModuleServiceTests.cpp | 16 +++ ...MediaPipelineModuleServiceTestsFixture.cpp | 40 ++++++++ .../MediaPipelineModuleServiceTestsFixture.h | 4 + .../MiscellaneousFunctionsTest.cpp | 39 ++++++++ .../mocks/gstplayer/GstGenericPlayerMock.h | 1 + .../main/MediaPipelineServerInternalMock.h | 1 + .../mocks/service/MediaPipelineServiceMock.h | 1 + .../MediaPipelineServiceTests.cpp | 20 ++++ .../MediaPipelineServiceTestsFixture.cpp | 29 ++++++ .../MediaPipelineServiceTestsFixture.h | 4 + wrappers/include/GstWrapper.h | 5 + wrappers/interface/IGstWrapper.h | 11 +++ 48 files changed, 662 insertions(+), 10 deletions(-) create mode 100644 tests/unittests/media/client/ipc/mediaPipelineIpc/GetDurationTest.cpp create mode 100644 tests/unittests/media/client/main/mediaPipeline/GetDurationTest.cpp diff --git a/media/client/ipc/include/MediaPipelineIpc.h b/media/client/ipc/include/MediaPipelineIpc.h index e2e907f68..759dcc086 100644 --- a/media/client/ipc/include/MediaPipelineIpc.h +++ b/media/client/ipc/include/MediaPipelineIpc.h @@ -148,6 +148,8 @@ class MediaPipelineIpc : public IMediaPipelineIpc, public IpcModule bool switchSource(const std::unique_ptr &source) override; + bool getDuration(int64_t &duration) override; + private: /** * @brief The media player client ipc. diff --git a/media/client/ipc/interface/IMediaPipelineIpc.h b/media/client/ipc/interface/IMediaPipelineIpc.h index bee5f597d..f5dfc44b2 100644 --- a/media/client/ipc/interface/IMediaPipelineIpc.h +++ b/media/client/ipc/interface/IMediaPipelineIpc.h @@ -477,6 +477,17 @@ class IMediaPipelineIpc * @retval true on success. */ virtual bool switchSource(const std::unique_ptr &source) = 0; + + /** + * @brief Get the playback duration in nanoseconds. + * + * This method is synchronous, it returns current playback duration + * + * @param[out] duration : The playback duration in nanoseconds + * + * @retval true on success. + */ + virtual bool getDuration(int64_t &duration) = 0; }; }; // namespace firebolt::rialto::client diff --git a/media/client/ipc/source/MediaPipelineIpc.cpp b/media/client/ipc/source/MediaPipelineIpc.cpp index 890ab8905..0c8f22a2f 100644 --- a/media/client/ipc/source/MediaPipelineIpc.cpp +++ b/media/client/ipc/source/MediaPipelineIpc.cpp @@ -535,6 +535,37 @@ bool MediaPipelineIpc::getPosition(int64_t &position) return true; } +bool MediaPipelineIpc::getDuration(int64_t &duration) +{ + if (!reattachChannelIfRequired()) + { + RIALTO_CLIENT_LOG_ERROR("Reattachment of the ipc channel failed, ipc disconnected"); + return false; + } + + firebolt::rialto::GetDurationRequest request; + + request.set_session_id(m_sessionId); + + firebolt::rialto::GetDurationResponse response; + auto ipcController = m_ipc.createRpcController(); + auto blockingClosure = m_ipc.createBlockingClosure(); + m_mediaPipelineStub->getDuration(ipcController.get(), &request, &response, blockingClosure.get()); + + // wait for the call to complete + blockingClosure->wait(); + + // check the result + if (ipcController->Failed()) + { + RIALTO_CLIENT_LOG_ERROR("failed to get duration due to '%s'", ipcController->ErrorText().c_str()); + return false; + } + + duration = response.duration(); + return true; +} + bool MediaPipelineIpc::setImmediateOutput(int32_t sourceId, bool immediateOutput) { if (!reattachChannelIfRequired()) diff --git a/media/client/main/include/MediaPipeline.h b/media/client/main/include/MediaPipeline.h index d47f0fe16..34c08b982 100644 --- a/media/client/main/include/MediaPipeline.h +++ b/media/client/main/include/MediaPipeline.h @@ -192,6 +192,8 @@ class MediaPipeline : public IMediaPipelineAndIControlClient, public IMediaPipel bool getStreamSyncMode(int32_t &streamSyncMode) override; + bool getDuration(int64_t &duration) override; + bool flush(int32_t sourceId, bool resetTime, bool &async) override; bool setSourcePosition(int32_t sourceId, int64_t position, bool resetTime, double appliedRate, diff --git a/media/client/main/include/MediaPipelineProxy.h b/media/client/main/include/MediaPipelineProxy.h index af45114aa..992a53e53 100644 --- a/media/client/main/include/MediaPipelineProxy.h +++ b/media/client/main/include/MediaPipelineProxy.h @@ -182,6 +182,8 @@ class MediaPipelineProxy : public IMediaPipelineAndIControlClient return m_mediaPipeline->switchSource(source); } + bool getDuration(int64_t &duration) override { return m_mediaPipeline->getDuration(duration); } + void notifyApplicationState(ApplicationState state) override { m_mediaPipeline->notifyApplicationState(state); } private: diff --git a/media/client/main/source/MediaPipeline.cpp b/media/client/main/source/MediaPipeline.cpp index ff123515e..d7d3c11a3 100644 --- a/media/client/main/source/MediaPipeline.cpp +++ b/media/client/main/source/MediaPipeline.cpp @@ -636,6 +636,13 @@ bool MediaPipeline::switchSource(const std::unique_ptr &source) return m_mediaPipelineIpc->switchSource(source); } +bool MediaPipeline::getDuration(int64_t &duration) +{ + RIALTO_CLIENT_LOG_DEBUG("entry:"); + + return m_mediaPipelineIpc->getDuration(duration); +} + void MediaPipeline::discardNeedDataRequest(uint32_t needDataRequestId) { // Find the needDataRequest for this needDataRequestId diff --git a/media/public/include/IMediaPipeline.h b/media/public/include/IMediaPipeline.h index 10b910f20..ea04e474a 100644 --- a/media/public/include/IMediaPipeline.h +++ b/media/public/include/IMediaPipeline.h @@ -1558,6 +1558,17 @@ class IMediaPipeline * @retval true on success. */ virtual bool switchSource(const std::unique_ptr &source) = 0; + + /** + * @brief Get the playback duration in nanoseconds. + * + * This method is synchronous, it returns current playback duration + * + * @param[out] duration : The playback duration in nanoseconds + * + * @retval true on success. + */ + virtual bool getDuration(int64_t &duration) = 0; }; }; // namespace firebolt::rialto diff --git a/media/server/gstplayer/include/GstGenericPlayer.h b/media/server/gstplayer/include/GstGenericPlayer.h index 8ea963d8d..93e163f5a 100644 --- a/media/server/gstplayer/include/GstGenericPlayer.h +++ b/media/server/gstplayer/include/GstGenericPlayer.h @@ -118,6 +118,7 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva void setEos(const firebolt::rialto::MediaSourceType &type) override; void setPlaybackRate(double rate) override; bool getPosition(std::int64_t &position) override; + bool getDuration(std::int64_t &duration) override; bool setImmediateOutput(const MediaSourceType &mediaSourceType, bool immediateOutput) override; bool setReportDecodeErrors(const MediaSourceType &mediaSourceType, bool reportDecodeErrors) override; bool getImmediateOutput(const MediaSourceType &mediaSourceType, bool &immediateOutput) override; diff --git a/media/server/gstplayer/interface/IGstGenericPlayer.h b/media/server/gstplayer/interface/IGstGenericPlayer.h index bb0931513..3f1c877e7 100644 --- a/media/server/gstplayer/interface/IGstGenericPlayer.h +++ b/media/server/gstplayer/interface/IGstGenericPlayer.h @@ -186,6 +186,15 @@ class IGstGenericPlayer */ virtual bool getPosition(std::int64_t &position) = 0; + /** + * @brief Get the playback duration in nanoseconds. + * + * @param[out] duration : The playback duration in nanoseconds. + * + * @retval True on success + */ + virtual bool getDuration(std::int64_t &duration) = 0; + /** * @brief Sets the "Immediate Output" property for this source. * diff --git a/media/server/gstplayer/source/GstGenericPlayer.cpp b/media/server/gstplayer/source/GstGenericPlayer.cpp index e227e648a..248cedc4a 100644 --- a/media/server/gstplayer/source/GstGenericPlayer.cpp +++ b/media/server/gstplayer/source/GstGenericPlayer.cpp @@ -411,6 +411,18 @@ bool GstGenericPlayer::getPosition(std::int64_t &position) return true; } +bool GstGenericPlayer::getDuration(std::int64_t &duration) +{ + // We are on main thread here, but m_context.pipeline can be used, because it's modified only in GstGenericPlayer + // constructor and destructor. GstGenericPlayer is created/destructed on main thread, so we won't have a crash here. + if (!m_context.pipeline || !m_gstWrapper->gstElementQueryDuration(m_context.pipeline, GST_FORMAT_TIME, &duration)) + { + RIALTO_SERVER_LOG_WARN("Failed to query duration"); + return false; + } + return true; +} + GstElement *GstGenericPlayer::getSink(const MediaSourceType &mediaSourceType) const { const char *kSinkName{nullptr}; diff --git a/media/server/ipc/include/MediaPipelineModuleService.h b/media/server/ipc/include/MediaPipelineModuleService.h index 61d932c08..91787419a 100644 --- a/media/server/ipc/include/MediaPipelineModuleService.h +++ b/media/server/ipc/include/MediaPipelineModuleService.h @@ -84,6 +84,8 @@ class MediaPipelineModuleService : public IMediaPipelineModuleService ::google::protobuf::Closure *done) override; void getPosition(::google::protobuf::RpcController *controller, const ::firebolt::rialto::GetPositionRequest *request, ::firebolt::rialto::GetPositionResponse *response, ::google::protobuf::Closure *done) override; + void getDuration(::google::protobuf::RpcController *controller, const ::firebolt::rialto::GetDurationRequest *request, + ::firebolt::rialto::GetDurationResponse *response, ::google::protobuf::Closure *done) override; void setImmediateOutput(::google::protobuf::RpcController *controller, const ::firebolt::rialto::SetImmediateOutputRequest *request, ::firebolt::rialto::SetImmediateOutputResponse *response, diff --git a/media/server/ipc/source/MediaPipelineModuleService.cpp b/media/server/ipc/source/MediaPipelineModuleService.cpp index 3bd0f2948..a05f52576 100644 --- a/media/server/ipc/source/MediaPipelineModuleService.cpp +++ b/media/server/ipc/source/MediaPipelineModuleService.cpp @@ -665,6 +665,25 @@ void MediaPipelineModuleService::getPosition(::google::protobuf::RpcController * done->Run(); } +void MediaPipelineModuleService::getDuration(::google::protobuf::RpcController *controller, + const ::firebolt::rialto::GetDurationRequest *request, + ::firebolt::rialto::GetDurationResponse *response, + ::google::protobuf::Closure *done) +{ + RIALTO_SERVER_LOG_DEBUG("entry:"); + int64_t duration{}; + if (!m_mediaPipelineService.getDuration(request->session_id(), duration)) + { + RIALTO_SERVER_LOG_ERROR("Get duration failed"); + controller->SetFailed("Operation failed"); + } + else + { + response->set_duration(duration); + } + done->Run(); +} + void MediaPipelineModuleService::setImmediateOutput(::google::protobuf::RpcController *controller, const ::firebolt::rialto::SetImmediateOutputRequest *request, ::firebolt::rialto::SetImmediateOutputResponse *response, diff --git a/media/server/main/include/MediaPipelineServerInternal.h b/media/server/main/include/MediaPipelineServerInternal.h index 8b1eff28c..5df68e454 100644 --- a/media/server/main/include/MediaPipelineServerInternal.h +++ b/media/server/main/include/MediaPipelineServerInternal.h @@ -175,6 +175,8 @@ class MediaPipelineServerInternal : public IMediaPipelineServerInternal, public bool switchSource(const std::unique_ptr &source) override; + bool getDuration(int64_t &duration) override; + AddSegmentStatus addSegment(uint32_t needDataRequestId, const std::unique_ptr &mediaSegment) override; std::weak_ptr getClient() override; diff --git a/media/server/main/source/MediaPipelineServerInternal.cpp b/media/server/main/source/MediaPipelineServerInternal.cpp index e09804541..26bf4ceb3 100644 --- a/media/server/main/source/MediaPipelineServerInternal.cpp +++ b/media/server/main/source/MediaPipelineServerInternal.cpp @@ -481,6 +481,20 @@ bool MediaPipelineServerInternal::getPosition(int64_t &position) return m_gstPlayer->getPosition(position); } +bool MediaPipelineServerInternal::getDuration(int64_t &duration) +{ + RIALTO_SERVER_LOG_DEBUG("entry:"); + + std::shared_lock lock{m_getPropertyMutex}; + + if (!m_gstPlayer) + { + RIALTO_SERVER_LOG_ERROR("Failed to get duration - Gstreamer player has not been loaded"); + return false; + } + return m_gstPlayer->getDuration(duration); +} + bool MediaPipelineServerInternal::getStats(int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames) { RIALTO_SERVER_LOG_DEBUG("entry:"); diff --git a/media/server/service/include/IMediaPipelineService.h b/media/server/service/include/IMediaPipelineService.h index ffe742178..c4658b097 100644 --- a/media/server/service/include/IMediaPipelineService.h +++ b/media/server/service/include/IMediaPipelineService.h @@ -93,6 +93,7 @@ class IMediaPipelineService virtual void ping(const std::shared_ptr &heartbeatProcedure) = 0; virtual bool switchSource(int sessionId, const std::unique_ptr &source) = 0; virtual bool isVideoMaster(bool &isVideoMaster) = 0; + virtual bool getDuration(int sessionId, std::int64_t &duration) = 0; }; } // namespace firebolt::rialto::server::service diff --git a/media/server/service/source/MediaPipelineService.cpp b/media/server/service/source/MediaPipelineService.cpp index 79571867a..37e2ad34c 100644 --- a/media/server/service/source/MediaPipelineService.cpp +++ b/media/server/service/source/MediaPipelineService.cpp @@ -253,6 +253,20 @@ bool MediaPipelineService::getPosition(int sessionId, std::int64_t &position) return mediaPipelineIter->second->getPosition(position); } +bool MediaPipelineService::getDuration(int sessionId, std::int64_t &duration) +{ + RIALTO_SERVER_LOG_INFO("MediaPipelineService requested to get duration, session id: %d", sessionId); + + std::lock_guard lock{m_mediaPipelineMutex}; + auto mediaPipelineIter = m_mediaPipelines.find(sessionId); + if (mediaPipelineIter == m_mediaPipelines.end()) + { + RIALTO_SERVER_LOG_ERROR("Session with id: %d does not exist", sessionId); + return false; + } + return mediaPipelineIter->second->getDuration(duration); +} + bool MediaPipelineService::setImmediateOutput(int sessionId, int32_t sourceId, bool immediateOutput) { RIALTO_SERVER_LOG_INFO("MediaPipelineService requested to setImmediateOutput, session id: %d", sessionId); diff --git a/media/server/service/source/MediaPipelineService.h b/media/server/service/source/MediaPipelineService.h index 5290c2475..1c03670e8 100644 --- a/media/server/service/source/MediaPipelineService.h +++ b/media/server/service/source/MediaPipelineService.h @@ -65,6 +65,7 @@ class MediaPipelineService : public IMediaPipelineService bool setPlaybackRate(int sessionId, double rate) override; bool setPosition(int sessionId, std::int64_t position) override; bool getPosition(int sessionId, std::int64_t &position) override; + bool getDuration(int sessionId, std::int64_t &duration) override; bool setImmediateOutput(int sessionId, int32_t sourceId, bool immediateOutput) override; bool setReportDecodeErrors(int sessionId, int32_t sourceId, bool reportDecodeErrors) override; bool getQueuedFrames(int sessionId, int32_t sourceId, uint32_t &queuedFrames) override; diff --git a/proto/mediapipelinemodule.proto b/proto/mediapipelinemodule.proto index b111b3fb9..534343676 100644 --- a/proto/mediapipelinemodule.proto +++ b/proto/mediapipelinemodule.proto @@ -345,6 +345,22 @@ message GetPositionResponse { optional int64 position = 1 [default = -1]; } +/** + * @fn void getDuration(int session_id) + * @brief Get the playback Duration in nanoseconds. + * + * @param[in] session_id The id of the A/V session. + * + * This method is considered to be synchronous, it returns current playback duration + * + */ +message GetDurationRequest { + optional int32 session_id = 1 [default = -1]; +} +message GetDurationResponse { + optional int64 duration = 1 [default = -1]; +} + /** * @fn void setPlaybackRate(int session_id, double rate) * @brief Set the playback position in nanoseconds. @@ -1303,4 +1319,12 @@ service MediaPipelineModule { */ rpc getUseBuffering(GetUseBufferingRequest) returns (GetUseBufferingResponse) { } + + /** + * @brief Gets the current duration of the playback + * @see GetDurationRequest + */ + rpc getDuration(GetDurationRequest) returns (GetDurationResponse) { + } + } diff --git a/tests/common/externalLibraryMocks/GstWrapperMock.h b/tests/common/externalLibraryMocks/GstWrapperMock.h index c30acf396..35c3a5f83 100644 --- a/tests/common/externalLibraryMocks/GstWrapperMock.h +++ b/tests/common/externalLibraryMocks/GstWrapperMock.h @@ -87,6 +87,7 @@ class GstWrapperMock : public IGstWrapper MOCK_METHOD(void, gstBusSetSyncHandler, (GstBus *, GstBusSyncHandler, gpointer, GDestroyNotify), (override)); MOCK_METHOD(GstFlowReturn, gstAppSrcEndOfStream, (GstAppSrc *), (override)); MOCK_METHOD(gboolean, gstElementQueryPosition, (GstElement *, GstFormat, gint64 *), (override)); + MOCK_METHOD(gboolean, gstElementQueryDuration, (GstElement *, GstFormat, gint64 *), (override)); MOCK_METHOD(GstFlowReturn, gstAppSrcPushBuffer, (GstAppSrc *, GstBuffer *), (override)); MOCK_METHOD(GstBuffer *, gstBufferNew, (), (override)); MOCK_METHOD(GstBuffer *, gstBufferNewAllocate, (GstAllocator *, gsize, GstAllocationParams *), (override)); diff --git a/tests/common/matchers/MediaPipelineProtoRequestMatchers.h b/tests/common/matchers/MediaPipelineProtoRequestMatchers.h index 1424cf106..c98b56c1a 100644 --- a/tests/common/matchers/MediaPipelineProtoRequestMatchers.h +++ b/tests/common/matchers/MediaPipelineProtoRequestMatchers.h @@ -352,6 +352,13 @@ MATCHER_P(getPositionRequestMatcher, sessionId, "") return ((kRequest->session_id() == sessionId)); } +MATCHER_P(getDurationRequestMatcher, sessionId, "") +{ + const ::firebolt::rialto::GetDurationRequest *kRequest = + dynamic_cast(arg); + return ((kRequest->session_id() == sessionId)); +} + MATCHER_P2(setImmediateOutputRequestMatcher, sessionId, sourceId, "") { const ::firebolt::rialto::SetImmediateOutputRequest *kRequest = diff --git a/tests/componenttests/client/mocks/MediaPipelineModuleMock.h b/tests/componenttests/client/mocks/MediaPipelineModuleMock.h index 5cf614593..6988528f4 100644 --- a/tests/componenttests/client/mocks/MediaPipelineModuleMock.h +++ b/tests/componenttests/client/mocks/MediaPipelineModuleMock.h @@ -64,6 +64,9 @@ class MediaPipelineModuleMock : public ::firebolt::rialto::MediaPipelineModule MOCK_METHOD(void, getPosition, (::google::protobuf::RpcController * controller, const ::firebolt::rialto::GetPositionRequest *request, ::firebolt::rialto::GetPositionResponse *response, ::google::protobuf::Closure *done)); + MOCK_METHOD(void, getDuration, + (::google::protobuf::RpcController * controller, const ::firebolt::rialto::GetDurationRequest *request, + ::firebolt::rialto::GetDurationResponse *response, ::google::protobuf::Closure *done)); MOCK_METHOD(void, setImmediateOutput, (::google::protobuf::RpcController * controller, const ::firebolt::rialto::SetImmediateOutputRequest *request, @@ -225,6 +228,13 @@ class MediaPipelineModuleMock : public ::firebolt::rialto::MediaPipelineModule return response; } + ::firebolt::rialto::GetDurationResponse getDurationResponse(const int64_t duration) + { + firebolt::rialto::GetDurationResponse response; + response.set_duration(duration); + return response; + } + ::firebolt::rialto::SetImmediateOutputResponse setImmediateOutputResponse() { firebolt::rialto::SetImmediateOutputResponse response; diff --git a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp index a7f2f388a..ca86e1963 100644 --- a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp +++ b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp @@ -1424,6 +1424,20 @@ void MediaPipelineTestMethods::getPosition(const int64_t expectedPosition) EXPECT_EQ(returnPosition, expectedPosition); } +void MediaPipelineTestMethods::shouldGetDuration(const int64_t duration) +{ + EXPECT_CALL(*m_mediaPipelineModuleMock, getDuration(_, getDurationRequestMatcher(kSessionId), _, _)) + .WillOnce(DoAll(SetArgPointee<2>(m_mediaPipelineModuleMock->getDurationResponse(duration)), + WithArgs<0, 3>(Invoke(&(*m_mediaPipelineModuleMock), &MediaPipelineModuleMock::defaultReturn)))); +} + +void MediaPipelineTestMethods::getDuration(const int64_t expectedDuration) +{ + int64_t returnDuration; + EXPECT_EQ(m_mediaPipeline->getDuration(returnDuration), true); + EXPECT_EQ(returnDuration, expectedDuration); +} + void MediaPipelineTestMethods::shouldSetImmediateOutput(bool immediateOutput) { EXPECT_CALL(*m_mediaPipelineModuleMock, diff --git a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.h b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.h index 13425beaa..a3a245fe6 100644 --- a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.h +++ b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.h @@ -128,6 +128,7 @@ class MediaPipelineTestMethods void shouldRenderFrame(); void shouldRenderFrameFailure(); void shouldGetPosition(const int64_t position); + void shouldGetDuration(const int64_t duration); void shouldSetImmediateOutput(bool immediateOutput); void shouldGetImmediateOutput(bool immediateOutput); void shouldSetReportDecodeErrors(bool reportDecodeErrors); @@ -248,6 +249,7 @@ class MediaPipelineTestMethods void renderFrame(); void renderFrameFailure(); void getPosition(const int64_t expectedPosition); + void getDuration(const int64_t expectedDuration); void setImmediateOutput(bool immediateOutput); void getImmediateOutput(bool immediateOutput); void setReportDecodeErrors(bool reportDecodeErrors); diff --git a/tests/componenttests/client/tests/mse/PipelinePropertyTest.cpp b/tests/componenttests/client/tests/mse/PipelinePropertyTest.cpp index db623007c..dd6164c1f 100644 --- a/tests/componenttests/client/tests/mse/PipelinePropertyTest.cpp +++ b/tests/componenttests/client/tests/mse/PipelinePropertyTest.cpp @@ -108,6 +108,18 @@ class PipelinePropertyTest : public ClientComponentTest * GetUseBuffering * Expect that GetUseBuffering propagated to the server and gets the property * + * Step 13: Set Report Decode Errors + * Set report decode errors + * Expect that Set report decode errors propagated to the server and sets the property + * + * Step 14: Get Queued Frames + * GetQueuedFrames + * Expect that GetQueuedFrames propagated to the server and gets the number of queued frames + * + * Step 15: Get Duration + * GetDuration + * Expect that GetDuration propagated to the server and gets the duration + * * Test Teardown: * Terminate the media session. * Memory region created for the shared buffer is closed. @@ -184,5 +196,10 @@ TEST_F(PipelinePropertyTest, setAndGetPipelineProperties) uint32_t queuedFrames{123}; MediaPipelineTestMethods::shouldGetQueuedFrames(queuedFrames); MediaPipelineTestMethods::getQueuedFrames(queuedFrames); + + // Step 15: Get Duration + constexpr int64_t duration{123456789}; + MediaPipelineTestMethods::shouldGetDuration(duration); + MediaPipelineTestMethods::getDuration(duration); } } // namespace firebolt::rialto::client::ct diff --git a/tests/componenttests/server/common/ActionTraits.h b/tests/componenttests/server/common/ActionTraits.h index d601e8d9f..e0326bb82 100644 --- a/tests/componenttests/server/common/ActionTraits.h +++ b/tests/componenttests/server/common/ActionTraits.h @@ -344,6 +344,14 @@ struct ProcessAudioGap static constexpr auto m_kFunction{&Stub::processAudioGap}; }; +struct GetDuration +{ + using RequestType = ::firebolt::rialto::GetDurationRequest; + using ResponseType = ::firebolt::rialto::GetDurationResponse; + using Stub = ::firebolt::rialto::MediaPipelineModule_Stub; + static constexpr auto m_kFunction{&Stub::getDuration}; +}; + // mediakeys module struct CreateMediaKeys { diff --git a/tests/componenttests/server/common/MessageBuilders.cpp b/tests/componenttests/server/common/MessageBuilders.cpp index a1ae941e1..bdfb44a01 100644 --- a/tests/componenttests/server/common/MessageBuilders.cpp +++ b/tests/componenttests/server/common/MessageBuilders.cpp @@ -407,6 +407,13 @@ ::firebolt::rialto::ProcessAudioGapRequest createProcessAudioGapRequest(int sess return request; } +::firebolt::rialto::GetDurationRequest createGetDurationRequest(int sessionId) +{ + ::firebolt::rialto::GetDurationRequest request; + request.set_session_id(sessionId); + return request; +} + ::firebolt::rialto::CreateMediaKeysRequest createCreateMediaKeysRequestWidevine() { ::firebolt::rialto::CreateMediaKeysRequest request; diff --git a/tests/componenttests/server/common/MessageBuilders.h b/tests/componenttests/server/common/MessageBuilders.h index 815266713..0c255bb5f 100644 --- a/tests/componenttests/server/common/MessageBuilders.h +++ b/tests/componenttests/server/common/MessageBuilders.h @@ -83,6 +83,7 @@ ::firebolt::rialto::SetSourcePositionRequest createSetSourcePositionRequest(int ::firebolt::rialto::ProcessAudioGapRequest createProcessAudioGapRequest(int sessionId, std::int64_t position, unsigned duration, std::int64_t discontinuityGap, bool audioAac); +::firebolt::rialto::GetDurationRequest createGetDurationRequest(int sessionId); // media keys module ::firebolt::rialto::CreateMediaKeysRequest createCreateMediaKeysRequestWidevine(); diff --git a/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp b/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp index a6410d9a5..3b04393d2 100644 --- a/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp @@ -39,6 +39,7 @@ constexpr bool kSyncOff{true}; constexpr int32_t kStreamSyncMode{1}; constexpr int32_t kBufferingLimit{4321}; constexpr bool kUseBuffering{true}; +constexpr int64_t kDuration{434523241}; } // namespace namespace firebolt::rialto::server::ct @@ -225,6 +226,24 @@ class PipelinePropertyTest : public MediaPipelineTest EXPECT_CALL(*m_gstWrapperMock, gstIteratorFree(&m_it)).WillOnce(Invoke(this, &MediaPipelineTest::workerFinished)); } + void willGetDuration() + { + EXPECT_CALL(*m_gstWrapperMock, gstElementQueryDuration(&m_pipeline, GST_FORMAT_TIME, _)) + .WillOnce(Invoke( + [&](GstElement *element, GstFormat format, gint64 *duration) + { + *duration = kDuration; + workerFinished(); + return TRUE; + })); + } + + void willFailToGetDuration() + { + EXPECT_CALL(*m_gstWrapperMock, gstElementQueryDuration(&m_pipeline, GST_FORMAT_TIME, _)) + .WillOnce(DoAll(Invoke(this, &MediaPipelineTest::workerFinished), Return(FALSE))); + } + void setImmediateOutput() { auto req{createSetImmediateOutputRequest(m_sessionId, m_videoSourceId, kImmediateOutput)}; @@ -405,6 +424,23 @@ class PipelinePropertyTest : public MediaPipelineTest ConfigureAction(m_clientStub).send(req).expectFailure(); } + void getDurationSuccess() + { + auto req{createGetDurationRequest(m_sessionId)}; + ConfigureAction(m_clientStub) + .send(req) + .expectSuccess() + .matchResponse([&](const auto &resp) { EXPECT_EQ(resp.duration(), kDuration); }); + waitWorker(); + } + + void getDurationFailure() + { + auto req{createGetDurationRequest(m_sessionId)}; + ConfigureAction(m_clientStub).send(req).expectFailure(); + waitWorker(); + } + private: GstElement *m_element{nullptr}; GstStructure m_testStructure; @@ -498,18 +534,21 @@ class PipelinePropertyTest : public MediaPipelineTest * Step 15: Get Use Buffering * Will get the UseBuffering property of the decodebin on the Rialto Server * - * Step 16: Remove sources + * Step 16: Get Duration + * Will get the duration of the playback on the Rialto Server + * + * Step 17: Remove sources * Remove the audio source. * Expect that audio source is removed. * Remove the video source. * Expect that video source is removed. * - * Step 17: Stop + * Step 18: Stop * Stop the playback. * Expect that stop propagated to the gstreamer pipeline. * Expect that server notifies the client that the Playback state has changed to STOPPED. * - * Step 18: Destroy media session + * Step 19: Destroy media session * Send DestroySessionRequest. * Expect that the session is destroyed on the server. * @@ -589,7 +628,11 @@ TEST_F(PipelinePropertyTest, pipelinePropertyGetAndSetSuccess) // Step 15: Get Use Buffering getUseBuffering(); - // Step 16: Remove sources + // Step 16: Get Duration + willGetDuration(); + getDurationSuccess(); + + // Step 17: Remove sources removeSource(m_audioSourceId); removeSource(m_videoSourceId); @@ -703,18 +746,22 @@ TEST_F(PipelinePropertyTest, pipelinePropertyGetAndSetSuccess) * Rialto client sends UseBufferingRequest and waits for response * UseBufferingResponse is false because the sessionId is wrong * - * Step 16: Remove sources + * Step 16: Fail to Get Duration + * Rialto client sends GetDurationRequest and waits for response + * GetDurationResponse is false because the server couldn't process it + * + * Step 17: Remove sources * Remove the audio source. * Expect that audio source is removed. * Remove the video source. * Expect that video source is removed. * - * Step 17: Stop + * Step 18: Stop * Stop the playback. * Expect that stop propagated to the gstreamer pipeline. * Expect that server notifies the client that the Playback state has changed to STOPPED. * - * Step 18: Destroy media session + * Step 19: Destroy media session * Send DestroySessionRequest. * Expect that the session is destroyed on the server. * @@ -794,15 +841,19 @@ TEST_F(PipelinePropertyTest, pipelinePropertyGetAndSetFailures) // Step 15: Fail to Set Use Buffering setUseBufferingFailure(); - // Step 16: Remove sources + // Step 16: Fail to Get Duration + willFailToGetDuration(); + getDurationFailure(); + + // Step 17: Remove sources removeSource(m_audioSourceId); removeSource(m_videoSourceId); - // Step 17: Stop + // Step 18: Stop willStop(); stop(); - // Step 18: Destroy media session + // Step 19: Destroy media session gstPlayerWillBeDestructed(); destroySession(); } diff --git a/tests/unittests/media/client/ipc/CMakeLists.txt b/tests/unittests/media/client/ipc/CMakeLists.txt index 205b9e411..54347d4df 100644 --- a/tests/unittests/media/client/ipc/CMakeLists.txt +++ b/tests/unittests/media/client/ipc/CMakeLists.txt @@ -35,6 +35,7 @@ add_gtests ( mediaPipelineIpc/SetPositionTest.cpp mediaPipelineIpc/SetPlaybackRateTest.cpp mediaPipelineIpc/GetPositionTest.cpp + mediaPipelineIpc/GetDurationTest.cpp mediaPipelineIpc/SetImmediateOutputTest.cpp mediaPipelineIpc/GetImmediateOutputTest.cpp mediaPipelineIpc/SetReportDecodeErrorsTest.cpp diff --git a/tests/unittests/media/client/ipc/mediaPipelineIpc/GetDurationTest.cpp b/tests/unittests/media/client/ipc/mediaPipelineIpc/GetDurationTest.cpp new file mode 100644 index 000000000..12d83b993 --- /dev/null +++ b/tests/unittests/media/client/ipc/mediaPipelineIpc/GetDurationTest.cpp @@ -0,0 +1,97 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MediaPipelineIpcTestBase.h" +#include "MediaPipelineProtoRequestMatchers.h" + +class RialtoClientMediaPipelineIpcGetDurationTest : public MediaPipelineIpcTestBase +{ +protected: + virtual void SetUp() + { + MediaPipelineIpcTestBase::SetUp(); + + createMediaPipelineIpc(); + } + + virtual void TearDown() + { + destroyMediaPipelineIpc(); + + MediaPipelineIpcTestBase::TearDown(); + } +}; + +/** + * Test that getDuration can be called successfully. + */ +TEST_F(RialtoClientMediaPipelineIpcGetDurationTest, Success) +{ + expectIpcApiCallSuccess(); + + EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("getDuration"), m_controllerMock.get(), + getDurationRequestMatcher(m_sessionId), _, m_blockingClosureMock.get())); + + int64_t duration; + EXPECT_TRUE(m_mediaPipelineIpc->getDuration(duration)); +} + +/** + * Test that getDuration fails if the ipc channel disconnected. + */ +TEST_F(RialtoClientMediaPipelineIpcGetDurationTest, ChannelDisconnected) +{ + expectIpcApiCallDisconnected(); + expectUnsubscribeEvents(); + + int64_t duration; + EXPECT_FALSE(m_mediaPipelineIpc->getDuration(duration)); + + // Reattach channel on destroySession + EXPECT_CALL(*m_ipcClientMock, getChannel()).WillOnce(Return(m_channelMock)).RetiresOnSaturation(); + expectSubscribeEvents(); +} + +/** + * Test that getDuration fails if the ipc channel disconnected and succeeds if the channel is reconnected. + */ +TEST_F(RialtoClientMediaPipelineIpcGetDurationTest, ReconnectChannel) +{ + expectIpcApiCallReconnected(); + expectUnsubscribeEvents(); + expectSubscribeEvents(); + + EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("getDuration"), _, _, _, _)); + + int64_t duration; + EXPECT_TRUE(m_mediaPipelineIpc->getDuration(duration)); +} + +/** + * Test that getDuration fails when ipc fails. + */ +TEST_F(RialtoClientMediaPipelineIpcGetDurationTest, GetDurationFailure) +{ + expectIpcApiCallFailure(); + + EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("getDuration"), _, _, _, _)); + + int64_t duration; + EXPECT_FALSE(m_mediaPipelineIpc->getDuration(duration)); +} diff --git a/tests/unittests/media/client/main/CMakeLists.txt b/tests/unittests/media/client/main/CMakeLists.txt index 4387fe598..36ef5cd7e 100644 --- a/tests/unittests/media/client/main/CMakeLists.txt +++ b/tests/unittests/media/client/main/CMakeLists.txt @@ -32,6 +32,7 @@ add_gtests ( mediaPipeline/SetPositionTest.cpp mediaPipeline/SetPlaybackRateTest.cpp mediaPipeline/GetPositionTest.cpp + mediaPipeline/GetDurationTest.cpp mediaPipeline/SetImmediateOutputTest.cpp mediaPipeline/GetImmediateOutputTest.cpp mediaPipeline/SetReportDecodeErrorsTest.cpp diff --git a/tests/unittests/media/client/main/mediaPipeline/GetDurationTest.cpp b/tests/unittests/media/client/main/mediaPipeline/GetDurationTest.cpp new file mode 100644 index 000000000..64122f833 --- /dev/null +++ b/tests/unittests/media/client/main/mediaPipeline/GetDurationTest.cpp @@ -0,0 +1,66 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MediaPipelineTestBase.h" + +class RialtoClientMediaPipelineGetDurationTest : public MediaPipelineTestBase +{ +protected: + virtual void SetUp() + { + MediaPipelineTestBase::SetUp(); + + createMediaPipeline(); + } + + virtual void TearDown() + { + destroyMediaPipeline(); + + MediaPipelineTestBase::TearDown(); + } +}; + +/** + * Test that GetDuration returns success if the IPC API succeeds. + */ +TEST_F(RialtoClientMediaPipelineGetDurationTest, GetDurationSuccess) +{ + constexpr int64_t kExpectedDuration{123}; + int64_t resultDuration{}; + EXPECT_CALL(*m_mediaPipelineIpcMock, getDuration(resultDuration)) + .WillOnce(Invoke( + [&](int64_t &duration) + { + duration = kExpectedDuration; + return true; + })); + EXPECT_TRUE(m_mediaPipeline->getDuration(resultDuration)); + EXPECT_EQ(resultDuration, kExpectedDuration); +} + +/** + * Test that GetDuration returns failure if the IPC API fails. + */ +TEST_F(RialtoClientMediaPipelineGetDurationTest, GetDurationFailure) +{ + int64_t resultDuration{}; + EXPECT_CALL(*m_mediaPipelineIpcMock, getDuration(resultDuration)).WillOnce(Return(false)); + EXPECT_FALSE(m_mediaPipeline->getDuration(resultDuration)); +} diff --git a/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp b/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp index 7d9b7a2c7..0585e0141 100644 --- a/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp +++ b/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp @@ -135,6 +135,15 @@ TEST_F(RialtoClientMediaPipelineProxyTest, TestPassthrough) ///////////////////////////////////////////// + EXPECT_CALL(*mediaPipelineMock, getDuration(_)).WillOnce(DoAll(SetArgReferee<0>(kDuration), Return(true))); + { + int64_t duration; + EXPECT_TRUE(proxy->getDuration(duration)); + EXPECT_EQ(duration, kDuration); + } + + ///////////////////////////////////////////// + EXPECT_CALL(*mediaPipelineMock, getStats(_, _, _)) .WillOnce(DoAll(SetArgReferee<1>(kRenderedFrames), SetArgReferee<2>(kDroppedFrames), Return(true))); { diff --git a/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h b/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h index f08f54fef..73956accf 100644 --- a/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h +++ b/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h @@ -76,6 +76,7 @@ class MediaPipelineIpcMock : public IMediaPipelineIpc MOCK_METHOD(bool, setUseBuffering, (bool useBuffering), (override)); MOCK_METHOD(bool, getUseBuffering, (bool &useBuffering), (override)); MOCK_METHOD(bool, switchSource, (const std::unique_ptr &source), (override)); + MOCK_METHOD(bool, getDuration, (int64_t & duration), (override)); }; } // namespace firebolt::rialto::client diff --git a/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h b/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h index 1be860018..04b5dfbe5 100644 --- a/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h +++ b/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h @@ -91,6 +91,7 @@ class MediaPipelineAndControlClientMock : public IMediaPipelineAndIControlClient MOCK_METHOD(bool, setUseBuffering, (bool useBuffering), (override)); MOCK_METHOD(bool, getUseBuffering, (bool &useBuffering), (override)); MOCK_METHOD(bool, switchSource, (const std::unique_ptr &source), (override)); + MOCK_METHOD(bool, getDuration, (int64_t & duration), (override)); }; } // namespace firebolt::rialto::client diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp index 108b0e423..c658a080d 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp @@ -1163,3 +1163,25 @@ TEST_F(GstGenericPlayerTest, shouldSwitchSource) m_sut->switchSource(source); } + +TEST_F(GstGenericPlayerTest, shouldReturnInvalidDurationWhenQueryFails) +{ + int64_t targetDuration{}; + EXPECT_CALL(*m_gstWrapperMock, gstElementQueryDuration(_, GST_FORMAT_TIME, _)).WillOnce(Return(FALSE)); + EXPECT_FALSE(m_sut->getDuration(targetDuration)); +} + +TEST_F(GstGenericPlayerTest, shouldReturnDuration) +{ + constexpr gint64 kExpectedDuration{123}; + int64_t targetDuration{}; + EXPECT_CALL(*m_gstWrapperMock, gstElementQueryDuration(_, GST_FORMAT_TIME, _)) + .WillOnce(Invoke( + [&](GstElement *element, GstFormat format, gint64 *cur) + { + *cur = kExpectedDuration; + return TRUE; + })); + EXPECT_TRUE(m_sut->getDuration(targetDuration)); + EXPECT_EQ(kExpectedDuration, targetDuration); +} diff --git a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTests.cpp b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTests.cpp index 593a01b1e..e4776c7c3 100644 --- a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTests.cpp +++ b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTests.cpp @@ -249,6 +249,22 @@ TEST_F(MediaPipelineModuleServiceTests, shouldFailToGetPosition) sendGetPositionRequestAndReceiveResponseWithoutPositionMatch(); } +TEST_F(MediaPipelineModuleServiceTests, shouldGetDuration) +{ + mediaPipelineServiceWillCreateSession(); + sendCreateSessionRequestAndReceiveResponse(); + mediaPipelineServiceWillGetDuration(); + sendGetDurationRequestAndReceiveResponse(); +} + +TEST_F(MediaPipelineModuleServiceTests, shouldFailToGetDuration) +{ + mediaPipelineServiceWillCreateSession(); + sendCreateSessionRequestAndReceiveResponse(); + mediaPipelineServiceWillFailToGetDuration(); + sendGetDurationRequestAndReceiveResponseWithoutDurationMatch(); +} + TEST_F(MediaPipelineModuleServiceTests, shouldSetImmediateOutput) { mediaPipelineServiceWillCreateSession(); diff --git a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp index 5aa608e2e..b51b9ac40 100644 --- a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp +++ b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp @@ -493,6 +493,24 @@ void MediaPipelineModuleServiceTests::mediaPipelineServiceWillFailToGetPosition( EXPECT_CALL(m_mediaPipelineServiceMock, getPosition(kHardcodedSessionId, _)).WillOnce(Return(false)); } +void MediaPipelineModuleServiceTests::mediaPipelineServiceWillGetDuration() +{ + expectRequestSuccess(); + EXPECT_CALL(m_mediaPipelineServiceMock, getDuration(kHardcodedSessionId, _)) + .WillOnce(Invoke( + [&](int, std::int64_t &pos) + { + pos = kDuration; + return true; + })); +} + +void MediaPipelineModuleServiceTests::mediaPipelineServiceWillFailToGetDuration() +{ + expectRequestFailure(); + EXPECT_CALL(m_mediaPipelineServiceMock, getDuration(kHardcodedSessionId, _)).WillOnce(Return(false)); +} + void MediaPipelineModuleServiceTests::mediaPipelineServiceWillSetImmediateOutput() { expectRequestSuccess(); @@ -1133,6 +1151,28 @@ void MediaPipelineModuleServiceTests::sendGetPositionRequestAndReceiveResponseWi m_service->getPosition(m_controllerMock.get(), &request, &response, m_closureMock.get()); } +void MediaPipelineModuleServiceTests::sendGetDurationRequestAndReceiveResponse() +{ + firebolt::rialto::GetDurationRequest request; + firebolt::rialto::GetDurationResponse response; + + request.set_session_id(kHardcodedSessionId); + + m_service->getDuration(m_controllerMock.get(), &request, &response, m_closureMock.get()); + + EXPECT_EQ(response.duration(), kDuration); +} + +void MediaPipelineModuleServiceTests::sendGetDurationRequestAndReceiveResponseWithoutDurationMatch() +{ + firebolt::rialto::GetDurationRequest request; + firebolt::rialto::GetDurationResponse response; + + request.set_session_id(kHardcodedSessionId); + + m_service->getDuration(m_controllerMock.get(), &request, &response, m_closureMock.get()); +} + void MediaPipelineModuleServiceTests::sendSetImmediateOutputRequestAndReceiveResponse() { firebolt::rialto::SetImmediateOutputRequest request; diff --git a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.h b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.h index b987dd88e..f71a7f8c5 100644 --- a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.h +++ b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.h @@ -74,6 +74,8 @@ class MediaPipelineModuleServiceTests : public testing::Test void mediaPipelineServiceWillFailToSetPlaybackRate(); void mediaPipelineServiceWillGetPosition(); void mediaPipelineServiceWillFailToGetPosition(); + void mediaPipelineServiceWillGetDuration(); + void mediaPipelineServiceWillFailToGetDuration(); void mediaPipelineServiceWillSetImmediateOutput(); void mediaPipelineServiceWillFailToSetImmediateOutput(); void mediaPipelineServiceWillGetImmediateOutput(); @@ -154,6 +156,8 @@ class MediaPipelineModuleServiceTests : public testing::Test void sendSetPositionRequestAndReceiveResponse(); void sendGetPositionRequestAndReceiveResponse(); void sendGetPositionRequestAndReceiveResponseWithoutPositionMatch(); + void sendGetDurationRequestAndReceiveResponse(); + void sendGetDurationRequestAndReceiveResponseWithoutDurationMatch(); void sendSetImmediateOutputRequestAndReceiveResponse(); void sendSetImmediateOutputRequestAndReceiveFail(); void sendGetImmediateOutputRequestAndReceiveResponse(); diff --git a/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp b/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp index b1ca63c5b..c503ddb0f 100644 --- a/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp +++ b/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp @@ -28,6 +28,7 @@ class RialtoServerMediaPipelineMiscellaneousFunctionsTest : public MediaPipeline { protected: const int64_t m_kPosition{4028596027}; + const int64_t m_kDuration{8057192054}; const double m_kPlaybackRate{1.5}; uint64_t m_kRenderedFrames{3141}; uint64_t m_kDroppedFrames{95}; @@ -221,6 +222,44 @@ TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetPositionSuccess) EXPECT_EQ(targetPosition, m_kPosition); } +/** + * Test that GetDuration returns failure if the gstreamer player is not initialized + */ +TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetDurationFailureDueToUninitializedPlayer) +{ + int64_t targetDuration{}; + EXPECT_FALSE(m_mediaPipeline->getDuration(targetDuration)); +} + +/** + * Test that GetDuration returns failure if the gstreamer API fails + */ +TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetDurationFailure) +{ + loadGstPlayer(); + int64_t targetDuration{}; + EXPECT_CALL(*m_gstPlayerMock, getDuration(_)).WillOnce(Return(false)); + EXPECT_FALSE(m_mediaPipeline->getDuration(targetDuration)); +} + +/** + * Test that GetDuration returns success if the gstreamer API succeeds and gets the duration + */ +TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetDurationSuccess) +{ + loadGstPlayer(); + int64_t targetDuration{}; + EXPECT_CALL(*m_gstPlayerMock, getDuration(_)) + .WillOnce(Invoke( + [&](int64_t &pos) + { + pos = m_kDuration; + return true; + })); + EXPECT_TRUE(m_mediaPipeline->getDuration(targetDuration)); + EXPECT_EQ(targetDuration, m_kDuration); +} + /** * Test that SetImmediateOutput returns failure if the gstreamer player is not initialized */ diff --git a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h index 90ab4ebf7..087cd452e 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h @@ -42,6 +42,7 @@ class GstGenericPlayerMock : public IGstGenericPlayer MOCK_METHOD(void, attachSamples, (const std::shared_ptr &dataReader), (override)); MOCK_METHOD(void, setPosition, (std::int64_t position), (override)); MOCK_METHOD(bool, getPosition, (std::int64_t & position), (override)); + MOCK_METHOD(bool, getDuration, (std::int64_t & duration), (override)); MOCK_METHOD(bool, setImmediateOutput, (const MediaSourceType &mediaSourceType, bool immediateOutput), (override)); MOCK_METHOD(bool, getImmediateOutput, (const MediaSourceType &mediaSourceType, bool &immediateOutput), (override)); MOCK_METHOD(bool, setReportDecodeErrors, (const MediaSourceType &mediaSourceType, bool reportDecodeErrors), diff --git a/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h b/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h index 82f56556f..397672c4c 100644 --- a/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h +++ b/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h @@ -77,6 +77,7 @@ class MediaPipelineServerInternalMock : public IMediaPipelineServerInternal MOCK_METHOD(bool, setUseBuffering, (bool useBuffering), (override)); MOCK_METHOD(bool, getUseBuffering, (bool &useBuffering), (override)); MOCK_METHOD(bool, switchSource, (const std::unique_ptr &source), (override)); + MOCK_METHOD(bool, getDuration, (int64_t & duration), (override)); MOCK_METHOD(bool, setSubtitleOffset, (int32_t sourceId, int64_t position), (override)); }; } // namespace firebolt::rialto::server diff --git a/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h b/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h index 46d46ee4f..950b7d0bd 100644 --- a/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h +++ b/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h @@ -44,6 +44,7 @@ class MediaPipelineServiceMock : public IMediaPipelineService MOCK_METHOD(bool, setPlaybackRate, (int, double), (override)); MOCK_METHOD(bool, setPosition, (int, int64_t), (override)); MOCK_METHOD(bool, getPosition, (int sessionId, int64_t &position), (override)); + MOCK_METHOD(bool, getDuration, (int sessionId, int64_t &duration), (override)); MOCK_METHOD(bool, setImmediateOutput, (int sessionId, int32_t sourceId, bool immediateOutput), (override)); MOCK_METHOD(bool, getImmediateOutput, (int sessionId, int32_t sourceId, bool &immediateOutput), (override)); MOCK_METHOD(bool, setReportDecodeErrors, (int sessionId, int32_t sourceId, bool reportDecodeErrors), (override)); diff --git a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTests.cpp b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTests.cpp index b922b72fe..838928736 100644 --- a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTests.cpp +++ b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTests.cpp @@ -322,6 +322,26 @@ TEST_F(MediaPipelineServiceTests, shouldGetPosition) getPositionShouldSucceed(); } +TEST_F(MediaPipelineServiceTests, shouldFailToGetDurationForNotExistingSession) +{ + createMediaPipelineShouldSuccess(); + getDurationShouldFail(); +} + +TEST_F(MediaPipelineServiceTests, shouldFailToGetDuration) +{ + initSession(); + mediaPipelineWillFailToGetDuration(); + getDurationShouldFail(); +} + +TEST_F(MediaPipelineServiceTests, shouldGetDuration) +{ + initSession(); + mediaPipelineWillGetDuration(); + getDurationShouldSucceed(); +} + TEST_F(MediaPipelineServiceTests, shouldFailToSetImmediateOutputForNotExistingSession) { createMediaPipelineShouldSuccess(); diff --git a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp index 6de97e773..73f5cc3d5 100644 --- a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp +++ b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp @@ -223,6 +223,22 @@ void MediaPipelineServiceTests::mediaPipelineWillFailToGetPosition() EXPECT_CALL(m_mediaPipelineMock, getPosition(_)).WillOnce(Return(false)); } +void MediaPipelineServiceTests::mediaPipelineWillGetDuration() +{ + EXPECT_CALL(m_mediaPipelineMock, getDuration(_)) + .WillOnce(Invoke( + [&](int64_t &pos) + { + pos = kDuration; + return true; + })); +} + +void MediaPipelineServiceTests::mediaPipelineWillFailToGetDuration() +{ + EXPECT_CALL(m_mediaPipelineMock, getDuration(_)).WillOnce(Return(false)); +} + void MediaPipelineServiceTests::mediaPipelineWillSetImmediateOutput() { EXPECT_CALL(m_mediaPipelineMock, setImmediateOutput(_, _)).WillOnce(Return(true)); @@ -728,6 +744,19 @@ void MediaPipelineServiceTests::getPositionShouldFail() EXPECT_FALSE(m_sut->getPosition(kSessionId, targetPosition)); } +void MediaPipelineServiceTests::getDurationShouldSucceed() +{ + std::int64_t targetDuration{}; + EXPECT_TRUE(m_sut->getDuration(kSessionId, targetDuration)); + EXPECT_EQ(targetDuration, kDuration); +} + +void MediaPipelineServiceTests::getDurationShouldFail() +{ + std::int64_t targetDuration{}; + EXPECT_FALSE(m_sut->getDuration(kSessionId, targetDuration)); +} + void MediaPipelineServiceTests::getStatsShouldSucceed() { std::uint64_t renderedFrames; diff --git a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.h b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.h index 1a7e28d15..d76ca55a3 100644 --- a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.h +++ b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.h @@ -64,6 +64,8 @@ class MediaPipelineServiceTests : public testing::Test void mediaPipelineWillFailToHaveData(); void mediaPipelineWillGetPosition(); void mediaPipelineWillFailToGetPosition(); + void mediaPipelineWillGetDuration(); + void mediaPipelineWillFailToGetDuration(); void mediaPipelineWillSetImmediateOutput(); void mediaPipelineWillFailToSetImmediateOutput(); void mediaPipelineWillSetReportDecodeErrors(); @@ -160,6 +162,8 @@ class MediaPipelineServiceTests : public testing::Test void haveDataShouldFail(); void getPositionShouldSucceed(); void getPositionShouldFail(); + void getDurationShouldSucceed(); + void getDurationShouldFail(); void setReportDecodeErrorsShouldSucceed(); void setReportDecodeErrorsShouldFail(); void setImmediateOutputShouldSucceed(); diff --git a/wrappers/include/GstWrapper.h b/wrappers/include/GstWrapper.h index 1c109aa74..bde083520 100644 --- a/wrappers/include/GstWrapper.h +++ b/wrappers/include/GstWrapper.h @@ -188,6 +188,11 @@ class GstWrapper : public IGstWrapper return gst_element_query_position(element, format, cur); } + gboolean gstElementQueryDuration(GstElement *element, GstFormat format, gint64 *duration) override + { + return gst_element_query_duration(element, format, duration); + } + GstPad *gstGhostPadNew(const gchar *name, GstPad *target) override { return gst_ghost_pad_new(name, target); } void gstPadSetQueryFunction(GstPad *pad, GstPadQueryFunction query) override diff --git a/wrappers/interface/IGstWrapper.h b/wrappers/interface/IGstWrapper.h index a7a184624..d8859c028 100644 --- a/wrappers/interface/IGstWrapper.h +++ b/wrappers/interface/IGstWrapper.h @@ -412,6 +412,17 @@ class IGstWrapper */ virtual gboolean gstElementQueryPosition(GstElement *element, GstFormat format, gint64 *cur) = 0; + /** + * @brief Queries an element (usually top-level pipeline or playbin element) for the total stream duration in nanoseconds. + * + * @param[in] element : a GstElement to invoke the duration query on. + * @param[in] format : the GstFormat requested + * @param[out] duration : A location in which to store the total duration, or NULL. + * + * @retval TRUE on success, FALSE otherwise. + */ + virtual gboolean gstElementQueryDuration(GstElement *element, GstFormat format, gint64 *duration) = 0; + /** * @brief Create a new ghost pad. * From ca3a62ea436d85710650364056e5e6b938a3c313 Mon Sep 17 00:00:00 2001 From: Marcin Wojciechowski <105790697+skywojciechowskim@users.noreply.github.com> Date: Thu, 30 Apr 2026 07:12:45 +0200 Subject: [PATCH 20/29] Implementation of the isLive parameter (#489) Summary: Implementation of the isLive parameter Type: Fix Test Plan: UT/CT, Fullstack Jira: RDKEMW-15424 --- media/client/ipc/include/MediaPipelineIpc.h | 2 +- .../client/ipc/interface/IMediaPipelineIpc.h | 3 +- media/client/ipc/source/MediaPipelineIpc.cpp | 3 +- media/client/main/include/MediaPipeline.h | 2 +- .../client/main/include/MediaPipelineProxy.h | 4 +- media/client/main/source/MediaPipeline.cpp | 4 +- media/public/include/IMediaPipeline.h | 3 +- .../gstplayer/include/GenericPlayerContext.h | 6 ++ .../gstplayer/include/GstGenericPlayer.h | 4 +- .../gstplayer/interface/IGstGenericPlayer.h | 3 +- .../gstplayer/source/GstGenericPlayer.cpp | 7 +- .../source/tasks/generic/SetupElement.cpp | 6 ++ .../ipc/source/MediaPipelineModuleService.cpp | 2 +- .../include/MediaPipelineServerInternal.h | 5 +- .../source/MediaPipelineServerInternal.cpp | 9 +-- .../service/include/IMediaPipelineService.h | 2 +- .../service/source/MediaPipelineService.cpp | 5 +- .../service/source/MediaPipelineService.h | 2 +- proto/mediapipelinemodule.proto | 4 +- .../MediaPipelineProtoRequestMatchers.h | 4 +- .../tests/base/MediaPipelineTestMethods.cpp | 5 +- .../unittests/common/unittests/TimerTests.cpp | 27 ++++++++ .../client/ipc/mediaPipelineIpc/LoadTest.cpp | 11 +-- .../client/main/mediaPipeline/LoadTest.cpp | 9 +-- .../mediaPipeline/MediaPipelineProxyTest.cpp | 5 +- .../client/mocks/ipc/MediaPipelineIpcMock.h | 3 +- .../main/MediaPipelineAndControlClientMock.h | 3 +- .../gstplayer/genericPlayer/CreateTest.cpp | 69 ++++++++++--------- .../GstDispatcherThreadClientTest.cpp | 3 +- .../GstGenericPlayerPrivateTest.cpp | 3 +- .../genericPlayer/GstGenericPlayerTest.cpp | 3 +- .../common/GenericTasksTestsBase.cpp | 15 ++++ .../common/GenericTasksTestsBase.h | 1 + .../tasksTests/SetupElementTest.cpp | 6 ++ ...MediaPipelineModuleServiceTestsFixture.cpp | 8 ++- .../server/main/mediaPipeline/LoadTest.cpp | 11 +-- .../base/MediaPipelineTestBase.cpp | 4 +- .../gstplayer/GstGenericPlayerFactoryMock.h | 2 +- .../main/MediaPipelineServerInternalMock.h | 3 +- .../mocks/service/MediaPipelineServiceMock.h | 2 +- .../MediaPipelineServiceTestsFixture.cpp | 9 +-- 41 files changed, 187 insertions(+), 95 deletions(-) diff --git a/media/client/ipc/include/MediaPipelineIpc.h b/media/client/ipc/include/MediaPipelineIpc.h index 759dcc086..b69dca326 100644 --- a/media/client/ipc/include/MediaPipelineIpc.h +++ b/media/client/ipc/include/MediaPipelineIpc.h @@ -75,7 +75,7 @@ class MediaPipelineIpc : public IMediaPipelineIpc, public IpcModule bool allSourcesAttached() override; - bool load(MediaType type, const std::string &mimeType, const std::string &url) override; + bool load(MediaType type, const std::string &mimeType, const std::string &url, bool isLive) override; bool setVideoWindow(uint32_t x, uint32_t y, uint32_t width, uint32_t height) override; diff --git a/media/client/ipc/interface/IMediaPipelineIpc.h b/media/client/ipc/interface/IMediaPipelineIpc.h index f5dfc44b2..54e6e5c1b 100644 --- a/media/client/ipc/interface/IMediaPipelineIpc.h +++ b/media/client/ipc/interface/IMediaPipelineIpc.h @@ -106,10 +106,11 @@ class IMediaPipelineIpc * @param[in] type : The media type. * @param[in] mimeType : The MIME type. * @param[in] url : The URL. + * @param[in] isLive : Indicates if the media is live. * * @retval true on success. */ - virtual bool load(MediaType type, const std::string &mimeType, const std::string &url) = 0; + virtual bool load(MediaType type, const std::string &mimeType, const std::string &url, bool isLive) = 0; /** * @brief Request to set the coordinates of the video window. diff --git a/media/client/ipc/source/MediaPipelineIpc.cpp b/media/client/ipc/source/MediaPipelineIpc.cpp index 0c8f22a2f..10fb8cfd9 100644 --- a/media/client/ipc/source/MediaPipelineIpc.cpp +++ b/media/client/ipc/source/MediaPipelineIpc.cpp @@ -182,7 +182,7 @@ bool MediaPipelineIpc::subscribeToEvents(const std::shared_ptr &i return true; } -bool MediaPipelineIpc::load(MediaType type, const std::string &mimeType, const std::string &url) +bool MediaPipelineIpc::load(MediaType type, const std::string &mimeType, const std::string &url, bool isLive) { if (!reattachChannelIfRequired()) { @@ -196,6 +196,7 @@ bool MediaPipelineIpc::load(MediaType type, const std::string &mimeType, const s request.set_type(convertLoadRequestMediaType(type)); request.set_mime_type(mimeType); request.set_url(url); + request.set_is_live(isLive); firebolt::rialto::LoadResponse response; auto ipcController = m_ipc.createRpcController(); diff --git a/media/client/main/include/MediaPipeline.h b/media/client/main/include/MediaPipeline.h index 34c08b982..94e5eddd5 100644 --- a/media/client/main/include/MediaPipeline.h +++ b/media/client/main/include/MediaPipeline.h @@ -110,7 +110,7 @@ class MediaPipeline : public IMediaPipelineAndIControlClient, public IMediaPipel */ virtual ~MediaPipeline(); - bool load(MediaType type, const std::string &mimeType, const std::string &url) override; + bool load(MediaType type, const std::string &mimeType, const std::string &url, bool isLive) override; bool attachSource(const std::unique_ptr &source) override; diff --git a/media/client/main/include/MediaPipelineProxy.h b/media/client/main/include/MediaPipelineProxy.h index 992a53e53..0fbfa3376 100644 --- a/media/client/main/include/MediaPipelineProxy.h +++ b/media/client/main/include/MediaPipelineProxy.h @@ -38,9 +38,9 @@ class MediaPipelineProxy : public IMediaPipelineAndIControlClient std::weak_ptr getClient() override { return m_mediaPipeline->getClient(); } - bool load(MediaType type, const std::string &mimeType, const std::string &url) override + bool load(MediaType type, const std::string &mimeType, const std::string &url, bool isLive) override { - return m_mediaPipeline->load(type, mimeType, url); + return m_mediaPipeline->load(type, mimeType, url, isLive); } bool attachSource(const std::unique_ptr &source) override diff --git a/media/client/main/source/MediaPipeline.cpp b/media/client/main/source/MediaPipeline.cpp index d7d3c11a3..f889b5b06 100644 --- a/media/client/main/source/MediaPipeline.cpp +++ b/media/client/main/source/MediaPipeline.cpp @@ -203,11 +203,11 @@ MediaPipeline::~MediaPipeline() m_mediaPipelineIpc.reset(); } -bool MediaPipeline::load(MediaType type, const std::string &mimeType, const std::string &url) +bool MediaPipeline::load(MediaType type, const std::string &mimeType, const std::string &url, bool isLive) { RIALTO_CLIENT_LOG_DEBUG("entry:"); - return m_mediaPipelineIpc->load(type, mimeType, url); + return m_mediaPipelineIpc->load(type, mimeType, url, isLive); } bool MediaPipeline::attachSource(const std::unique_ptr &source) diff --git a/media/public/include/IMediaPipeline.h b/media/public/include/IMediaPipeline.h index ea04e474a..0f1c23ad2 100644 --- a/media/public/include/IMediaPipeline.h +++ b/media/public/include/IMediaPipeline.h @@ -1072,8 +1072,9 @@ class IMediaPipeline * @param[in] type : The media type. * @param[in] mimeType : The MIME type. * @param[in] url : The URL. + * @param[in] isLive : Indicates if the media is live. */ - virtual bool load(MediaType type, const std::string &mimeType, const std::string &url) = 0; + virtual bool load(MediaType type, const std::string &mimeType, const std::string &url, bool isLive) = 0; /** * @brief Attaches a source stream to the backend. diff --git a/media/server/gstplayer/include/GenericPlayerContext.h b/media/server/gstplayer/include/GenericPlayerContext.h index 237646ca3..946c80d4a 100644 --- a/media/server/gstplayer/include/GenericPlayerContext.h +++ b/media/server/gstplayer/include/GenericPlayerContext.h @@ -269,6 +269,12 @@ struct GenericPlayerContext * @brief Workaround for the gstreamer flush issue */ std::shared_ptr flushOnPrerollController{std::make_shared()}; + + /** + * @brief Flag used to check if the stream is live + * This is a workaround for Broadcom decoder issue with audio cuts during playback rate change. + */ + bool isLive{false}; }; } // namespace firebolt::rialto::server diff --git a/media/server/gstplayer/include/GstGenericPlayer.h b/media/server/gstplayer/include/GstGenericPlayer.h index 93e163f5a..a23056b3d 100644 --- a/media/server/gstplayer/include/GstGenericPlayer.h +++ b/media/server/gstplayer/include/GstGenericPlayer.h @@ -60,7 +60,7 @@ class GstGenericPlayerFactory : public IGstGenericPlayerFactory std::unique_ptr createGstGenericPlayer(IGstGenericPlayerClient *client, IDecryptionService &decryptionService, MediaType type, - const VideoRequirements &videoRequirements, + const VideoRequirements &videoRequirements, bool isLive, const std::shared_ptr &rdkGstreamerUtilsWrapperFactory) override; }; @@ -89,7 +89,7 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva * @param[in] gstDispatcherThreadFactory : The gst dispatcher thread factory */ GstGenericPlayer(IGstGenericPlayerClient *client, IDecryptionService &decryptionService, MediaType type, - const VideoRequirements &videoRequirements, + const VideoRequirements &videoRequirements, bool isLive, const std::shared_ptr &gstWrapper, const std::shared_ptr &glibWrapper, const std::shared_ptr &rdkGstreamerUtilsWrapper, diff --git a/media/server/gstplayer/interface/IGstGenericPlayer.h b/media/server/gstplayer/interface/IGstGenericPlayer.h index 3f1c877e7..a4036227a 100644 --- a/media/server/gstplayer/interface/IGstGenericPlayer.h +++ b/media/server/gstplayer/interface/IGstGenericPlayer.h @@ -59,12 +59,13 @@ class IGstGenericPlayerFactory * @param[in] decryptionService : The decryption service. * @param[in] type : The media type the gstreamer player shall support. * @param[in] videoRequirements : The video requirements for the playback. + * @param[in] isLive : Indicates if the media is live. * * @retval the new player instance or null on error. */ virtual std::unique_ptr createGstGenericPlayer(IGstGenericPlayerClient *client, IDecryptionService &decryptionService, MediaType type, - const VideoRequirements &videoRequirements, + const VideoRequirements &videoRequirements, bool isLive, const std::shared_ptr &rdkGstreamerUtilsWrapperFactory) = 0; }; diff --git a/media/server/gstplayer/source/GstGenericPlayer.cpp b/media/server/gstplayer/source/GstGenericPlayer.cpp index 248cedc4a..5f18f485f 100644 --- a/media/server/gstplayer/source/GstGenericPlayer.cpp +++ b/media/server/gstplayer/source/GstGenericPlayer.cpp @@ -80,7 +80,7 @@ std::shared_ptr IGstGenericPlayerFactory::getFactory() std::unique_ptr GstGenericPlayerFactory::createGstGenericPlayer( IGstGenericPlayerClient *client, IDecryptionService &decryptionService, MediaType type, - const VideoRequirements &videoRequirements, + const VideoRequirements &videoRequirements, bool isLive, const std::shared_ptr &rdkGstreamerUtilsWrapperFactory) { std::unique_ptr gstPlayer; @@ -106,7 +106,7 @@ std::unique_ptr GstGenericPlayerFactory::createGstGenericPlay throw std::runtime_error("Cannot create RdkGstreamerUtilsWrapper"); } gstPlayer = std::make_unique< - GstGenericPlayer>(client, decryptionService, type, videoRequirements, gstWrapper, glibWrapper, + GstGenericPlayer>(client, decryptionService, type, videoRequirements, isLive, gstWrapper, glibWrapper, rdkGstreamerUtilsWrapper, IGstInitialiser::instance(), std::make_unique(), IGstSrcFactory::getFactory(), common::ITimerFactory::getFactory(), std::make_unique(client, gstWrapper, glibWrapper, @@ -125,7 +125,7 @@ std::unique_ptr GstGenericPlayerFactory::createGstGenericPlay GstGenericPlayer::GstGenericPlayer( IGstGenericPlayerClient *client, IDecryptionService &decryptionService, MediaType type, - const VideoRequirements &videoRequirements, + const VideoRequirements &videoRequirements, bool isLive, const std::shared_ptr &gstWrapper, const std::shared_ptr &glibWrapper, const std::shared_ptr &rdkGstreamerUtilsWrapper, @@ -142,6 +142,7 @@ GstGenericPlayer::GstGenericPlayer( gstInitialiser.waitForInitialisation(); + m_context.isLive = isLive; m_context.decryptionService = &decryptionService; if ((!gstSrcFactory) || (!(m_context.gstSrc = gstSrcFactory->getGstSrc()))) diff --git a/media/server/gstplayer/source/tasks/generic/SetupElement.cpp b/media/server/gstplayer/source/tasks/generic/SetupElement.cpp index 00541a00e..9ff98430c 100644 --- a/media/server/gstplayer/source/tasks/generic/SetupElement.cpp +++ b/media/server/gstplayer/source/tasks/generic/SetupElement.cpp @@ -313,6 +313,12 @@ void SetupElement::execute() const { m_player.setBufferingLimit(); } + if (m_context.isLive && + m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(m_element), "enable-rate-correction")) + { + RIALTO_SERVER_LOG_INFO("Enabling rate correction for broadcom decoder."); + m_glibWrapper->gObjectSet(m_element, "enable-rate-correction", TRUE, nullptr); + } } else if (isAudioSink(*m_gstWrapper, m_element)) { diff --git a/media/server/ipc/source/MediaPipelineModuleService.cpp b/media/server/ipc/source/MediaPipelineModuleService.cpp index a05f52576..c04860ad4 100644 --- a/media/server/ipc/source/MediaPipelineModuleService.cpp +++ b/media/server/ipc/source/MediaPipelineModuleService.cpp @@ -392,7 +392,7 @@ void MediaPipelineModuleService::load(::google::protobuf::RpcController *control { RIALTO_SERVER_LOG_DEBUG("entry:"); if (!m_mediaPipelineService.load(request->session_id(), convertMediaType(request->type()), request->mime_type(), - request->url())) + request->url(), request->is_live())) { RIALTO_SERVER_LOG_ERROR("Load failed"); controller->SetFailed("Operation failed"); diff --git a/media/server/main/include/MediaPipelineServerInternal.h b/media/server/main/include/MediaPipelineServerInternal.h index 5df68e454..7b0be9834 100644 --- a/media/server/main/include/MediaPipelineServerInternal.h +++ b/media/server/main/include/MediaPipelineServerInternal.h @@ -92,7 +92,7 @@ class MediaPipelineServerInternal : public IMediaPipelineServerInternal, public */ virtual ~MediaPipelineServerInternal(); - bool load(MediaType type, const std::string &mimeType, const std::string &url) override; + bool load(MediaType type, const std::string &mimeType, const std::string &url, bool isLive) override; bool attachSource(const std::unique_ptr &source) override; @@ -320,10 +320,11 @@ class MediaPipelineServerInternal : public IMediaPipelineServerInternal, public * @param[in] type : The media type. * @param[in] mimeType : The MIME type. * @param[in] url : The URL. + * @param[in] isLive : Indicates if the media is live. * * @retval true on success. */ - bool loadInternal(MediaType type, const std::string &mimeType, const std::string &url); + bool loadInternal(MediaType type, const std::string &mimeType, const std::string &url, bool isLive); /** * @brief Attach source internally, only to be called on the main thread. diff --git a/media/server/main/source/MediaPipelineServerInternal.cpp b/media/server/main/source/MediaPipelineServerInternal.cpp index 26bf4ceb3..a3533abaf 100644 --- a/media/server/main/source/MediaPipelineServerInternal.cpp +++ b/media/server/main/source/MediaPipelineServerInternal.cpp @@ -192,18 +192,19 @@ MediaPipelineServerInternal::~MediaPipelineServerInternal() m_mainThread->enqueueTaskAndWait(m_mainThreadClientId, task); } -bool MediaPipelineServerInternal::load(MediaType type, const std::string &mimeType, const std::string &url) +bool MediaPipelineServerInternal::load(MediaType type, const std::string &mimeType, const std::string &url, bool isLive) { RIALTO_SERVER_LOG_DEBUG("entry:"); bool result; - auto task = [&]() { result = loadInternal(type, mimeType, url); }; + auto task = [&]() { result = loadInternal(type, mimeType, url, isLive); }; m_mainThread->enqueueTaskAndWait(m_mainThreadClientId, task); return result; } -bool MediaPipelineServerInternal::loadInternal(MediaType type, const std::string &mimeType, const std::string &url) +bool MediaPipelineServerInternal::loadInternal(MediaType type, const std::string &mimeType, const std::string &url, + bool isLive) { std::unique_lock lock{m_getPropertyMutex}; /* If gstreamer player already created, destroy the old one first */ @@ -214,7 +215,7 @@ bool MediaPipelineServerInternal::loadInternal(MediaType type, const std::string m_gstPlayer = m_kGstPlayerFactory - ->createGstGenericPlayer(this, m_decryptionService, type, m_kVideoRequirements, + ->createGstGenericPlayer(this, m_decryptionService, type, m_kVideoRequirements, isLive, firebolt::rialto::wrappers::IRdkGstreamerUtilsWrapperFactory::getFactory()); if (!m_gstPlayer) { diff --git a/media/server/service/include/IMediaPipelineService.h b/media/server/service/include/IMediaPipelineService.h index c4658b097..a70476b19 100644 --- a/media/server/service/include/IMediaPipelineService.h +++ b/media/server/service/include/IMediaPipelineService.h @@ -44,7 +44,7 @@ class IMediaPipelineService virtual bool createSession(int sessionId, const std::shared_ptr &mediaPipelineClient, std::uint32_t maxWidth, std::uint32_t maxHeight) = 0; virtual bool destroySession(int sessionId) = 0; - virtual bool load(int sessionId, MediaType type, const std::string &mimeType, const std::string &url) = 0; + virtual bool load(int sessionId, MediaType type, const std::string &mimeType, const std::string &url, bool isLive) = 0; virtual bool attachSource(int sessionId, const std::unique_ptr &source) = 0; virtual bool removeSource(int sessionId, std::int32_t sourceId) = 0; virtual bool allSourcesAttached(int sessionId) = 0; diff --git a/media/server/service/source/MediaPipelineService.cpp b/media/server/service/source/MediaPipelineService.cpp index 37e2ad34c..717063564 100644 --- a/media/server/service/source/MediaPipelineService.cpp +++ b/media/server/service/source/MediaPipelineService.cpp @@ -113,7 +113,8 @@ bool MediaPipelineService::destroySession(int sessionId) return true; } -bool MediaPipelineService::load(int sessionId, MediaType type, const std::string &mimeType, const std::string &url) +bool MediaPipelineService::load(int sessionId, MediaType type, const std::string &mimeType, const std::string &url, + bool isLive) { RIALTO_SERVER_LOG_INFO("MediaPipelineService requested to load session with id: %d", sessionId); @@ -124,7 +125,7 @@ bool MediaPipelineService::load(int sessionId, MediaType type, const std::string RIALTO_SERVER_LOG_ERROR("Session with id: %d does not exists", sessionId); return false; } - return mediaPipelineIter->second->load(type, mimeType, url); + return mediaPipelineIter->second->load(type, mimeType, url, isLive); } bool MediaPipelineService::attachSource(int sessionId, const std::unique_ptr &source) diff --git a/media/server/service/source/MediaPipelineService.h b/media/server/service/source/MediaPipelineService.h index 1c03670e8..7e777c777 100644 --- a/media/server/service/source/MediaPipelineService.h +++ b/media/server/service/source/MediaPipelineService.h @@ -55,7 +55,7 @@ class MediaPipelineService : public IMediaPipelineService bool createSession(int sessionId, const std::shared_ptr &mediaPipelineClient, std::uint32_t maxWidth, std::uint32_t maxHeight) override; bool destroySession(int sessionId) override; - bool load(int sessionId, MediaType type, const std::string &mimeType, const std::string &url) override; + bool load(int sessionId, MediaType type, const std::string &mimeType, const std::string &url, bool isLive) override; bool attachSource(int sessionId, const std::unique_ptr &source) override; bool removeSource(int sessionId, std::int32_t sourceId) override; bool allSourcesAttached(int sessionId) override; diff --git a/proto/mediapipelinemodule.proto b/proto/mediapipelinemodule.proto index 534343676..d98fcd41a 100644 --- a/proto/mediapipelinemodule.proto +++ b/proto/mediapipelinemodule.proto @@ -67,13 +67,14 @@ message DestroySessionResponse { } /** - * @fn void load(MediaType type, string mime_type, string url) + * @fn void load(MediaType type, string mime_type, string url, bool isLive) * @brief Loads the media pipeline. * * @param[in] session_id The id of the A/V session. * @param[in] type The type of media. * @param[in] mime_type The mime type. * @param[in] url The url. + * @param[in] isLive Indicates if the media is live. * */ message LoadRequest { @@ -86,6 +87,7 @@ message LoadRequest { optional MediaType type = 2; optional string mime_type = 3; optional string url = 4; + optional bool is_live = 5; } message LoadResponse { } diff --git a/tests/common/matchers/MediaPipelineProtoRequestMatchers.h b/tests/common/matchers/MediaPipelineProtoRequestMatchers.h index c98b56c1a..f8186d4c0 100644 --- a/tests/common/matchers/MediaPipelineProtoRequestMatchers.h +++ b/tests/common/matchers/MediaPipelineProtoRequestMatchers.h @@ -37,11 +37,11 @@ MATCHER_P2(createSessionRequestMatcher, maxWidth, maxHeight, "") return ((kRequest->max_width() == maxWidth) && (kRequest->max_height() == maxHeight)); } -MATCHER_P4(loadRequestMatcher, sessionId, type, mimeType, url, "") +MATCHER_P5(loadRequestMatcher, sessionId, type, mimeType, url, isLive, "") { const ::firebolt::rialto::LoadRequest *kRequest = dynamic_cast(arg); return ((kRequest->session_id() == sessionId) && (kRequest->type() == type) && - (kRequest->mime_type() == mimeType) && (kRequest->url() == url)); + (kRequest->mime_type() == mimeType) && (kRequest->url() == url) && (kRequest->is_live() == isLive)); } MATCHER_P(playRequestMatcher, sessionId, "") diff --git a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp index ca86e1963..3ff237a05 100644 --- a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp +++ b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp @@ -91,6 +91,7 @@ constexpr int64_t kDiscontinuityGap{1}; constexpr bool kIsAudioAac{false}; const std::vector kSupportedProperties{"immediate-output", "testProp2"}; constexpr uint64_t kStopPosition{452345}; +constexpr bool kIsLive{false}; } // namespace namespace firebolt::rialto::client::ct @@ -1873,7 +1874,7 @@ void MediaPipelineTestMethods::shouldLoadInternal(const int32_t sessionId, const const std::string &mimeType, const std::string &url) { EXPECT_CALL(*m_mediaPipelineModuleMock, - load(_, loadRequestMatcher(sessionId, convertMediaType(mediaType), mimeType, url), _, _)) + load(_, loadRequestMatcher(sessionId, convertMediaType(mediaType), mimeType, url, kIsLive), _, _)) .WillOnce(WithArgs<0, 3>(Invoke(&(*m_mediaPipelineModuleMock), &MediaPipelineModuleMock::defaultReturn))); } @@ -1992,7 +1993,7 @@ void MediaPipelineTestMethods::loadInternal(const std::unique_ptrload(mediaType, mimeType, url), status); + EXPECT_EQ(mediaPipeline->load(mediaType, mimeType, url, kIsLive), status); } void MediaPipelineTestMethods::removeSourceInternal(const std::unique_ptr &mediaPipeline, diff --git a/tests/unittests/common/unittests/TimerTests.cpp b/tests/unittests/common/unittests/TimerTests.cpp index dfc86c8f2..0825e16ee 100644 --- a/tests/unittests/common/unittests/TimerTests.cpp +++ b/tests/unittests/common/unittests/TimerTests.cpp @@ -116,3 +116,30 @@ TEST(TimerTests, ShouldTimeoutPeriodicTimer) EXPECT_GE(callCounter, 3); } } + +TEST(TimerTests, ShouldCancelPeriodicTimerInCallback) +{ + std::mutex mtx; + { // This scope is required to suppress a false warning from cppcheck (about the mutex above) + std::condition_variable cv; + std::unique_lock lock{mtx}; + unsigned callCounter{0}; + std::unique_ptr timer{ITimerFactory::getFactory()->createTimer( + std::chrono::milliseconds{30}, + [&]() + { + std::unique_lock lock{mtx}; + ++callCounter; + if (callCounter >= 3) + { + timer->cancel(); + cv.notify_one(); + } + }, + TimerType::PERIODIC)}; + EXPECT_TRUE(timer->isActive()); + cv.wait_for(lock, kEnoughTimeForTestToComplete); + std::this_thread::sleep_for(std::chrono::milliseconds{30}); + EXPECT_EQ(callCounter, 3); + } +} diff --git a/tests/unittests/media/client/ipc/mediaPipelineIpc/LoadTest.cpp b/tests/unittests/media/client/ipc/mediaPipelineIpc/LoadTest.cpp index cd2a96e34..e8cbccbcd 100644 --- a/tests/unittests/media/client/ipc/mediaPipelineIpc/LoadTest.cpp +++ b/tests/unittests/media/client/ipc/mediaPipelineIpc/LoadTest.cpp @@ -27,6 +27,7 @@ class RialtoClientMediaPipelineIpcLoadTest : public MediaPipelineIpcTestBase firebolt::rialto::LoadRequest_MediaType m_protoType = firebolt::rialto::LoadRequest_MediaType_MSE; const std::string m_mimeType = "mime"; const std::string m_url = "mse://1"; + const bool m_isLive = true; virtual void SetUp() { @@ -51,10 +52,10 @@ TEST_F(RialtoClientMediaPipelineIpcLoadTest, Success) expectIpcApiCallSuccess(); EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("load"), m_controllerMock.get(), - loadRequestMatcher(m_sessionId, m_protoType, m_mimeType, m_url), _, + loadRequestMatcher(m_sessionId, m_protoType, m_mimeType, m_url, m_isLive), _, m_blockingClosureMock.get())); - EXPECT_EQ(m_mediaPipelineIpc->load(m_type, m_mimeType, m_url), true); + EXPECT_EQ(m_mediaPipelineIpc->load(m_type, m_mimeType, m_url, m_isLive), true); } /** @@ -65,7 +66,7 @@ TEST_F(RialtoClientMediaPipelineIpcLoadTest, ChannelDisconnected) expectIpcApiCallDisconnected(); expectUnsubscribeEvents(); - EXPECT_EQ(m_mediaPipelineIpc->load(m_type, m_mimeType, m_url), false); + EXPECT_EQ(m_mediaPipelineIpc->load(m_type, m_mimeType, m_url, m_isLive), false); // Reattach channel on destroySession EXPECT_CALL(*m_ipcClientMock, getChannel()).WillOnce(Return(m_channelMock)).RetiresOnSaturation(); @@ -83,7 +84,7 @@ TEST_F(RialtoClientMediaPipelineIpcLoadTest, ReconnectChannel) EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("load"), _, _, _, _)); - EXPECT_EQ(m_mediaPipelineIpc->load(m_type, m_mimeType, m_url), true); + EXPECT_EQ(m_mediaPipelineIpc->load(m_type, m_mimeType, m_url, m_isLive), true); } /** @@ -95,5 +96,5 @@ TEST_F(RialtoClientMediaPipelineIpcLoadTest, LoadFailure) EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("load"), _, _, _, _)); - EXPECT_EQ(m_mediaPipelineIpc->load(m_type, m_mimeType, m_url), false); + EXPECT_EQ(m_mediaPipelineIpc->load(m_type, m_mimeType, m_url, m_isLive), false); } diff --git a/tests/unittests/media/client/main/mediaPipeline/LoadTest.cpp b/tests/unittests/media/client/main/mediaPipeline/LoadTest.cpp index 977168d82..7d2486d65 100644 --- a/tests/unittests/media/client/main/mediaPipeline/LoadTest.cpp +++ b/tests/unittests/media/client/main/mediaPipeline/LoadTest.cpp @@ -25,6 +25,7 @@ class RialtoClientMediaPipelineLoadTest : public MediaPipelineTestBase MediaType m_type = MediaType::MSE; const std::string m_kMimeType = "mime"; const std::string m_kUrl = "mse://1"; + const bool m_kIsLive = false; virtual void SetUp() { @@ -46,9 +47,9 @@ class RialtoClientMediaPipelineLoadTest : public MediaPipelineTestBase */ TEST_F(RialtoClientMediaPipelineLoadTest, Success) { - EXPECT_CALL(*m_mediaPipelineIpcMock, load(m_type, m_kMimeType, m_kUrl)).WillOnce(Return(true)); + EXPECT_CALL(*m_mediaPipelineIpcMock, load(m_type, m_kMimeType, m_kUrl, m_kIsLive)).WillOnce(Return(true)); - EXPECT_EQ(m_mediaPipeline->load(m_type, m_kMimeType, m_kUrl), true); + EXPECT_EQ(m_mediaPipeline->load(m_type, m_kMimeType, m_kUrl, m_kIsLive), true); } /** @@ -56,7 +57,7 @@ TEST_F(RialtoClientMediaPipelineLoadTest, Success) */ TEST_F(RialtoClientMediaPipelineLoadTest, Failure) { - EXPECT_CALL(*m_mediaPipelineIpcMock, load(m_type, m_kMimeType, m_kUrl)).WillOnce(Return(false)); + EXPECT_CALL(*m_mediaPipelineIpcMock, load(m_type, m_kMimeType, m_kUrl, m_kIsLive)).WillOnce(Return(false)); - EXPECT_EQ(m_mediaPipeline->load(m_type, m_kMimeType, m_kUrl), false); + EXPECT_EQ(m_mediaPipeline->load(m_type, m_kMimeType, m_kUrl, m_kIsLive), false); } diff --git a/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp b/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp index 0585e0141..563291b6b 100644 --- a/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp +++ b/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp @@ -77,11 +77,12 @@ TEST_F(RialtoClientMediaPipelineProxyTest, TestPassthrough) constexpr bool kEnabled{true}; constexpr uint32_t kBufferingLimit{5326}; constexpr uint64_t kStopPosition{4234}; + constexpr bool kIsLive{false}; ///////////////////////////////////////////// - EXPECT_CALL(*mediaPipelineMock, load(MediaType::MSE, StrEq(kMimeType), StrEq(kUrl))).WillOnce(Return(true)); - EXPECT_TRUE(proxy->load(MediaType::MSE, kMimeType, kUrl)); + EXPECT_CALL(*mediaPipelineMock, load(MediaType::MSE, StrEq(kMimeType), StrEq(kUrl), kIsLive)).WillOnce(Return(true)); + EXPECT_TRUE(proxy->load(MediaType::MSE, kMimeType, kUrl, kIsLive)); ///////////////////////////////////////////// diff --git a/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h b/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h index 73956accf..2d90eb922 100644 --- a/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h +++ b/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h @@ -37,7 +37,8 @@ class MediaPipelineIpcMock : public IMediaPipelineIpc (override)); MOCK_METHOD(bool, removeSource, (int32_t sourceId), (override)); MOCK_METHOD(bool, allSourcesAttached, (), (override)); - MOCK_METHOD(bool, load, (MediaType type, const std::string &mimeType, const std::string &url), (override)); + MOCK_METHOD(bool, load, (MediaType type, const std::string &mimeType, const std::string &url, bool isLive), + (override)); MOCK_METHOD(bool, setVideoWindow, (uint32_t x, uint32_t y, uint32_t width, uint32_t height), (override)); MOCK_METHOD(bool, play, (bool &async), (override)); MOCK_METHOD(bool, pause, (), (override)); diff --git a/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h b/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h index 04b5dfbe5..c6fd26247 100644 --- a/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h +++ b/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h @@ -31,7 +31,8 @@ namespace firebolt::rialto::client class MediaPipelineAndControlClientMock : public IMediaPipelineAndIControlClient { public: - MOCK_METHOD(bool, load, (MediaType type, const std::string &mimeType, const std::string &url), (override)); + MOCK_METHOD(bool, load, (MediaType type, const std::string &mimeType, const std::string &url, bool isLive), + (override)); MOCK_METHOD(bool, attachSource, (const std::unique_ptr &source), (override)); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/CreateTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/CreateTest.cpp index 901d222ad..c817e4962 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/CreateTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/CreateTest.cpp @@ -49,6 +49,7 @@ class RialtoServerCreateGstGenericPlayerTest : public GstGenericPlayerTestCommon GstStructure m_contextStructure{}; GstElement m_westerosSink{}; GParamSpec m_rectangleSpec{}; + const bool m_kIsLive{false}; void expectCreatePipeline() { @@ -71,13 +72,14 @@ class RialtoServerCreateGstGenericPlayerTest : public GstGenericPlayerTestCommon gstPlayerWillBeCreated(); EXPECT_NO_THROW( - m_gstPlayer = - std::make_unique(&m_gstPlayerClient, m_decryptionServiceMock, m_type, m_videoReq, - m_gstWrapperMock, m_glibWrapperMock, m_rdkGstreamerUtilsWrapperMock, - m_gstInitialiserMock, std::move(m_flushWatcher), m_gstSrcFactoryMock, - m_timerFactoryMock, std::move(m_taskFactory), - std::move(workerThreadFactory), std::move(gstDispatcherThreadFactory), - m_gstProtectionMetadataFactoryMock)); + m_gstPlayer = std::make_unique(&m_gstPlayerClient, m_decryptionServiceMock, m_type, + m_videoReq, m_kIsLive, m_gstWrapperMock, m_glibWrapperMock, + m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, + std::move(m_flushWatcher), m_gstSrcFactoryMock, + m_timerFactoryMock, std::move(m_taskFactory), + std::move(workerThreadFactory), + std::move(gstDispatcherThreadFactory), + m_gstProtectionMetadataFactoryMock)); EXPECT_NE(m_gstPlayer, nullptr); } @@ -150,7 +152,7 @@ TEST_F(RialtoServerCreateGstGenericPlayerTest, FactoryCreatesObject) firebolt::rialto::server::IGstGenericPlayerFactory::getFactory(); ASSERT_NE(factory, nullptr); auto player{factory->createGstGenericPlayer(&m_gstPlayerClient, m_decryptionServiceMock, m_type, m_videoReq, - m_rdkGstreamerUtilsWrapperFactoryMock)}; + m_kIsLive, m_rdkGstreamerUtilsWrapperFactoryMock)}; EXPECT_NE(player, nullptr); // Destroy expectations @@ -336,11 +338,11 @@ TEST_F(RialtoServerCreateGstGenericPlayerTest, CreateWesterossinkFailsCreateCont EXPECT_CALL(*m_gstWrapperMock, gstContextNew(StrEq("erm"), false)).WillOnce(Return(nullptr)); EXPECT_THROW(m_gstPlayer = std::make_unique(&m_gstPlayerClient, m_decryptionServiceMock, m_type, - m_videoReq, m_gstWrapperMock, m_glibWrapperMock, - m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, - std::move(m_flushWatcher), m_gstSrcFactoryMock, - m_timerFactoryMock, std::move(m_taskFactory), - std::move(workerThreadFactory), + m_videoReq, m_kIsLive, m_gstWrapperMock, + m_glibWrapperMock, m_rdkGstreamerUtilsWrapperMock, + m_gstInitialiserMock, std::move(m_flushWatcher), + m_gstSrcFactoryMock, m_timerFactoryMock, + std::move(m_taskFactory), std::move(workerThreadFactory), std::move(gstDispatcherThreadFactory), m_gstProtectionMetadataFactoryMock), std::runtime_error); @@ -354,10 +356,10 @@ TEST_F(RialtoServerCreateGstGenericPlayerTest, GstSrcFactoryNull) { EXPECT_CALL(m_gstInitialiserMock, waitForInitialisation()); EXPECT_THROW(m_gstPlayer = std::make_unique(&m_gstPlayerClient, m_decryptionServiceMock, m_type, - m_videoReq, m_gstWrapperMock, m_glibWrapperMock, - m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, - std::move(m_flushWatcher), nullptr, - m_timerFactoryMock, std::move(m_taskFactory), + m_videoReq, m_kIsLive, m_gstWrapperMock, + m_glibWrapperMock, m_rdkGstreamerUtilsWrapperMock, + m_gstInitialiserMock, std::move(m_flushWatcher), + nullptr, m_timerFactoryMock, std::move(m_taskFactory), std::move(workerThreadFactory), std::move(gstDispatcherThreadFactory), m_gstProtectionMetadataFactoryMock), @@ -374,10 +376,10 @@ TEST_F(RialtoServerCreateGstGenericPlayerTest, TimerFactoryFails) initFactories(); EXPECT_THROW(m_gstPlayer = std::make_unique(&m_gstPlayerClient, m_decryptionServiceMock, m_type, - m_videoReq, m_gstWrapperMock, m_glibWrapperMock, - m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, - std::move(m_flushWatcher), m_gstSrcFactoryMock, - nullptr, std::move(m_taskFactory), + m_videoReq, m_kIsLive, m_gstWrapperMock, + m_glibWrapperMock, m_rdkGstreamerUtilsWrapperMock, + m_gstInitialiserMock, std::move(m_flushWatcher), + m_gstSrcFactoryMock, nullptr, std::move(m_taskFactory), std::move(workerThreadFactory), std::move(gstDispatcherThreadFactory), m_gstProtectionMetadataFactoryMock), @@ -394,11 +396,11 @@ TEST_F(RialtoServerCreateGstGenericPlayerTest, GstSrcFactoryFails) EXPECT_CALL(*m_gstSrcFactoryMock, getGstSrc()).WillOnce(Return(nullptr)); EXPECT_THROW(m_gstPlayer = std::make_unique(&m_gstPlayerClient, m_decryptionServiceMock, m_type, - m_videoReq, m_gstWrapperMock, m_glibWrapperMock, - m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, - std::move(m_flushWatcher), m_gstSrcFactoryMock, - m_timerFactoryMock, std::move(m_taskFactory), - std::move(workerThreadFactory), + m_videoReq, m_kIsLive, m_gstWrapperMock, + m_glibWrapperMock, m_rdkGstreamerUtilsWrapperMock, + m_gstInitialiserMock, std::move(m_flushWatcher), + m_gstSrcFactoryMock, m_timerFactoryMock, + std::move(m_taskFactory), std::move(workerThreadFactory), std::move(gstDispatcherThreadFactory), m_gstProtectionMetadataFactoryMock), std::runtime_error); @@ -420,11 +422,12 @@ TEST_F(RialtoServerCreateGstGenericPlayerTest, UnknownMediaType) .WillOnce(Return(ByMove(std::move(m_gstProtectionMetadataWrapper)))); EXPECT_THROW(m_gstPlayer = std::make_unique(&m_gstPlayerClient, m_decryptionServiceMock, - MediaType::UNKNOWN, m_videoReq, m_gstWrapperMock, - m_glibWrapperMock, m_rdkGstreamerUtilsWrapperMock, - m_gstInitialiserMock, std::move(m_flushWatcher), - m_gstSrcFactoryMock, m_timerFactoryMock, - std::move(m_taskFactory), std::move(workerThreadFactory), + MediaType::UNKNOWN, m_videoReq, m_kIsLive, + m_gstWrapperMock, m_glibWrapperMock, + m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, + std::move(m_flushWatcher), m_gstSrcFactoryMock, + m_timerFactoryMock, std::move(m_taskFactory), + std::move(workerThreadFactory), std::move(gstDispatcherThreadFactory), m_gstProtectionMetadataFactoryMock), std::runtime_error); @@ -457,7 +460,7 @@ TEST_F(RialtoServerCreateGstGenericPlayerTest, PlaysinkNotFound) EXPECT_NO_THROW( m_gstPlayer = - std::make_unique(&m_gstPlayerClient, m_decryptionServiceMock, m_type, m_videoReq, + std::make_unique(&m_gstPlayerClient, m_decryptionServiceMock, m_type, m_videoReq, m_kIsLive, m_gstWrapperMock, m_glibWrapperMock, m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, std::move(m_flushWatcher), m_gstSrcFactoryMock, m_timerFactoryMock, std::move(m_taskFactory), @@ -493,7 +496,7 @@ TEST_F(RialtoServerCreateGstGenericPlayerTest, SetNativeAudioForBrcmAudioSink) EXPECT_NO_THROW( m_gstPlayer = - std::make_unique(&m_gstPlayerClient, m_decryptionServiceMock, m_type, m_videoReq, + std::make_unique(&m_gstPlayerClient, m_decryptionServiceMock, m_type, m_videoReq, m_kIsLive, m_gstWrapperMock, m_glibWrapperMock, m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, std::move(m_flushWatcher), m_gstSrcFactoryMock, m_timerFactoryMock, std::move(m_taskFactory), diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstDispatcherThreadClientTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstDispatcherThreadClientTest.cpp index ed883ce93..e5c8610b2 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstDispatcherThreadClientTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstDispatcherThreadClientTest.cpp @@ -33,12 +33,13 @@ class GstDispatcherThreadClientTest : public GstGenericPlayerTestCommon protected: std::unique_ptr m_sut; VideoRequirements m_videoReq = {kMinPrimaryVideoWidth, kMinPrimaryVideoHeight}; + const bool m_kIsLive{false}; GstDispatcherThreadClientTest() { gstPlayerWillBeCreated(); m_sut = std::make_unique(&m_gstPlayerClient, m_decryptionServiceMock, MediaType::MSE, - m_videoReq, m_gstWrapperMock, m_glibWrapperMock, + m_videoReq, m_kIsLive, m_gstWrapperMock, m_glibWrapperMock, m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, std::move(m_flushWatcher), m_gstSrcFactoryMock, m_timerFactoryMock, std::move(m_taskFactory), std::move(workerThreadFactory), diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp index 9db981f26..ba97f60b1 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp @@ -85,6 +85,7 @@ constexpr bool kShowVideoWindow{true}; constexpr gint64 kPosition{123}; constexpr double kVolume{0.5}; constexpr firebolt::rialto::PlaybackInfo kPlaybackInfo{kPosition, kVolume}; +constexpr bool kIsLive{false}; } // namespace bool operator==(const GstRialtoProtectionData &lhs, const GstRialtoProtectionData &rhs) @@ -118,7 +119,7 @@ class GstGenericPlayerPrivateTest : public GstGenericPlayerTestCommon { gstPlayerWillBeCreated(); m_sut = std::make_unique(&m_gstPlayerClient, m_decryptionServiceMock, MediaType::MSE, - m_videoReq, m_gstWrapperMock, m_glibWrapperMock, + m_videoReq, kIsLive, m_gstWrapperMock, m_glibWrapperMock, m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, std::move(m_flushWatcher), m_gstSrcFactoryMock, m_timerFactoryMock, std::move(m_taskFactory), std::move(workerThreadFactory), diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp index c658a080d..fdb8defe2 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp @@ -41,6 +41,7 @@ class GstGenericPlayerTest : public GstGenericPlayerTestCommon protected: std::unique_ptr m_sut; VideoRequirements m_videoReq = {kMinPrimaryVideoWidth, kMinPrimaryVideoHeight}; + bool m_isLive{false}; GstElement *m_pipeline; GstIterator m_it{}; char m_dummy{0}; @@ -52,7 +53,7 @@ class GstGenericPlayerTest : public GstGenericPlayerTestCommon { gstPlayerWillBeCreated(); m_sut = std::make_unique(&m_gstPlayerClient, m_decryptionServiceMock, MediaType::MSE, - m_videoReq, m_gstWrapperMock, m_glibWrapperMock, + m_videoReq, m_isLive, m_gstWrapperMock, m_glibWrapperMock, m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, std::move(m_flushWatcher), m_gstSrcFactoryMock, m_timerFactoryMock, std::move(m_taskFactory), std::move(workerThreadFactory), diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp index 13da7fecc..d82e8be35 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp @@ -775,6 +775,21 @@ void GenericTasksTestsBase::shouldSetupAudioDecoderElementWithPendingBufferingLi expectSetupAudioDecoderElement(); } +void GenericTasksTestsBase::shouldSetupAudioDecoderElementWithIsLiveParameter() +{ + testContext->m_context.isLive = true; + EXPECT_CALL(*testContext->m_glibWrapper, gTypeName(G_OBJECT_TYPE(testContext->m_element))) + .WillOnce(Return(kElementTypeName.c_str())); + + EXPECT_CALL(*testContext->m_glibWrapper, + gObjectClassFindProperty(G_OBJECT_GET_CLASS(testContext->m_element), StrEq("enable-rate-correction"))) + .WillOnce(Return(&testContext->m_paramSpec)); + EXPECT_CALL(*testContext->m_glibWrapper, + gObjectSetStub(G_OBJECT(testContext->m_element), StrEq("enable-rate-correction"))); + + expectSetupAudioDecoderElement(); +} + void GenericTasksTestsBase::shouldSetupVideoSinkElementWithPendingRenderFrame() { testContext->m_context.pendingRenderFrame = true; diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h index fc875a400..c62fbdd23 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h @@ -86,6 +86,7 @@ class GenericTasksTestsBase : public ::testing::Test void shouldSetupAudioDecoderElementWithPendingStreamSyncMode(); void shouldSetupVideoParserElementWithPendingStreamSyncMode(); void shouldSetupAudioDecoderElementWithPendingBufferingLimit(); + void shouldSetupAudioDecoderElementWithIsLiveParameter(); void shouldSetupVideoSinkElementWithPendingRenderFrame(); void shouldSetupVideoSinkElementWithPendingShowVideoWindow(); void shouldSetupAudioElementAmlhalasinkWhenNoVideo(); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/SetupElementTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/SetupElementTest.cpp index 89b5bc979..6f2fa12df 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/SetupElementTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/SetupElementTest.cpp @@ -77,6 +77,12 @@ TEST_F(SetupElementTest, shouldSetupAudioElementWithPendingBufferingLimit) triggerSetupElement(); } +TEST_F(SetupElementTest, shouldSetupAudioElementWithIsLiveParameter) +{ + shouldSetupAudioDecoderElementWithIsLiveParameter(); + triggerSetupElement(); +} + TEST_F(SetupElementTest, shouldSetupVideoElementWithPendingRenderFrame) { shouldSetupVideoSinkElementWithPendingRenderFrame(); diff --git a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp index b51b9ac40..656f84864 100644 --- a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp +++ b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp @@ -95,6 +95,7 @@ constexpr bool kUseBuffering{true}; constexpr uint64_t kStopPosition{2423}; constexpr bool kFramed{true}; constexpr bool kIsVideoMaster{true}; +constexpr bool kIsLive{true}; } // namespace MATCHER_P(AttachedSourceMatcher, source, "") @@ -286,13 +287,15 @@ void MediaPipelineModuleServiceTests::mediaPipelineServiceWillFailToDestroySessi void MediaPipelineModuleServiceTests::mediaPipelineServiceWillLoadSession() { expectRequestSuccess(); - EXPECT_CALL(m_mediaPipelineServiceMock, load(kHardcodedSessionId, kMediaType, kMimeType, kUrl)).WillOnce(Return(true)); + EXPECT_CALL(m_mediaPipelineServiceMock, load(kHardcodedSessionId, kMediaType, kMimeType, kUrl, kIsLive)) + .WillOnce(Return(true)); } void MediaPipelineModuleServiceTests::mediaPipelineServiceWillFailToLoadSession() { expectRequestFailure(); - EXPECT_CALL(m_mediaPipelineServiceMock, load(kHardcodedSessionId, kMediaType, kMimeType, kUrl)).WillOnce(Return(false)); + EXPECT_CALL(m_mediaPipelineServiceMock, load(kHardcodedSessionId, kMediaType, kMimeType, kUrl, kIsLive)) + .WillOnce(Return(false)); } void MediaPipelineModuleServiceTests::mediaPipelineServiceWillAttachSource() @@ -966,6 +969,7 @@ void MediaPipelineModuleServiceTests::sendLoadRequestAndReceiveResponse() request.set_type(convertMediaType(kMediaType)); request.set_mime_type(kMimeType); request.set_url(kUrl); + request.set_is_live(kIsLive); m_service->load(m_controllerMock.get(), &request, &response, m_closureMock.get()); } diff --git a/tests/unittests/media/server/main/mediaPipeline/LoadTest.cpp b/tests/unittests/media/server/main/mediaPipeline/LoadTest.cpp index 131a647f1..0e961781b 100644 --- a/tests/unittests/media/server/main/mediaPipeline/LoadTest.cpp +++ b/tests/unittests/media/server/main/mediaPipeline/LoadTest.cpp @@ -32,6 +32,7 @@ class RialtoServerMediaPipelineLoadTest : public MediaPipelineTestBase MediaType m_type = MediaType::MSE; const std::string m_kMimeType = "mime"; const std::string m_kUrl = "mse://1"; + const bool m_kIsLive = true; RialtoServerMediaPipelineLoadTest() { createMediaPipeline(); } @@ -45,11 +46,12 @@ TEST_F(RialtoServerMediaPipelineLoadTest, Success) { mainThreadWillEnqueueTaskAndWait(); mainThreadWillEnqueueTask(); - EXPECT_CALL(*m_gstPlayerFactoryMock, createGstGenericPlayer(_, _, m_type, VideoRequirementsMatcher(m_videoReq), _)) + EXPECT_CALL(*m_gstPlayerFactoryMock, + createGstGenericPlayer(_, _, m_type, VideoRequirementsMatcher(m_videoReq), m_kIsLive, _)) .WillOnce(Return(ByMove(std::move(m_gstPlayer)))); EXPECT_CALL(*m_mediaPipelineClientMock, notifyNetworkState(NetworkState::BUFFERING)); - EXPECT_EQ(m_mediaPipeline->load(m_type, m_kMimeType, m_kUrl), true); + EXPECT_EQ(m_mediaPipeline->load(m_type, m_kMimeType, m_kUrl, m_kIsLive), true); } /** @@ -59,9 +61,10 @@ TEST_F(RialtoServerMediaPipelineLoadTest, Success) TEST_F(RialtoServerMediaPipelineLoadTest, CreateGstPlayerFailure) { mainThreadWillEnqueueTaskAndWait(); - EXPECT_CALL(*m_gstPlayerFactoryMock, createGstGenericPlayer(_, _, m_type, VideoRequirementsMatcher(m_videoReq), _)) + EXPECT_CALL(*m_gstPlayerFactoryMock, + createGstGenericPlayer(_, _, m_type, VideoRequirementsMatcher(m_videoReq), m_kIsLive, _)) .WillOnce(Return(ByMove(nullptr))); EXPECT_CALL(*m_mediaPipelineClientMock, notifyNetworkState(_)).Times(0); - EXPECT_EQ(m_mediaPipeline->load(m_type, m_kMimeType, m_kUrl), false); + EXPECT_EQ(m_mediaPipeline->load(m_type, m_kMimeType, m_kUrl, m_kIsLive), false); } diff --git a/tests/unittests/media/server/main/mediaPipeline/base/MediaPipelineTestBase.cpp b/tests/unittests/media/server/main/mediaPipeline/base/MediaPipelineTestBase.cpp index 1543184af..cdcad0ab5 100644 --- a/tests/unittests/media/server/main/mediaPipeline/base/MediaPipelineTestBase.cpp +++ b/tests/unittests/media/server/main/mediaPipeline/base/MediaPipelineTestBase.cpp @@ -93,11 +93,11 @@ void MediaPipelineTestBase::loadGstPlayer() { mainThreadWillEnqueueTaskAndWait(); mainThreadWillEnqueueTask(); - EXPECT_CALL(*m_gstPlayerFactoryMock, createGstGenericPlayer(_, _, _, _, _)) + EXPECT_CALL(*m_gstPlayerFactoryMock, createGstGenericPlayer(_, _, _, _, _, _)) .WillOnce(DoAll(SaveArg<0>(&m_gstPlayerCallback), Return(ByMove(std::move(m_gstPlayer))))); EXPECT_CALL(*m_mediaPipelineClientMock, notifyNetworkState(NetworkState::BUFFERING)); - EXPECT_EQ(m_mediaPipeline->load(MediaType::MSE, "mime", "mse://1"), true); + EXPECT_EQ(m_mediaPipeline->load(MediaType::MSE, "mime", "mse://1", false), true); ASSERT_NE(m_gstPlayerCallback, nullptr); } diff --git a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerFactoryMock.h b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerFactoryMock.h index 08a82a373..0139f54da 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerFactoryMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerFactoryMock.h @@ -34,7 +34,7 @@ class GstGenericPlayerFactoryMock : public IGstGenericPlayerFactory MOCK_METHOD(std::unique_ptr, createGstGenericPlayer, (IGstGenericPlayerClient * client, IDecryptionService &decryptionService, MediaType type, - const VideoRequirements &videoRequirements, + const VideoRequirements &videoRequirements, bool isLive, const std::shared_ptr &rdkGstreamerUtilsWrapperFactory), (override)); diff --git a/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h b/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h index 397672c4c..8e7db9fe2 100644 --- a/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h +++ b/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h @@ -32,7 +32,8 @@ class MediaPipelineServerInternalMock : public IMediaPipelineServerInternal { public: MOCK_METHOD(std::weak_ptr, getClient, (), (override)); - MOCK_METHOD(bool, load, (MediaType type, const std::string &mimeType, const std::string &url), (override)); + MOCK_METHOD(bool, load, (MediaType type, const std::string &mimeType, const std::string &url, bool isLive), + (override)); MOCK_METHOD(bool, attachSource, (const std::unique_ptr &source), (override)); MOCK_METHOD(bool, removeSource, (int32_t id), (override)); MOCK_METHOD(bool, allSourcesAttached, (), (override)); diff --git a/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h b/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h index 950b7d0bd..7c9179031 100644 --- a/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h +++ b/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h @@ -34,7 +34,7 @@ class MediaPipelineServiceMock : public IMediaPipelineService MOCK_METHOD(bool, createSession, (int, const std::shared_ptr &, std::uint32_t, std::uint32_t), (override)); MOCK_METHOD(bool, destroySession, (int), (override)); - MOCK_METHOD(bool, load, (int, MediaType, const std::string &, const std::string &), (override)); + MOCK_METHOD(bool, load, (int, MediaType, const std::string &, const std::string &, bool), (override)); MOCK_METHOD(bool, attachSource, (int, const std::unique_ptr &), (override)); MOCK_METHOD(bool, removeSource, (int, std::int32_t), (override)); MOCK_METHOD(bool, allSourcesAttached, (int), (override)); diff --git a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp index 73f5cc3d5..0c43860d9 100644 --- a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp +++ b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp @@ -69,6 +69,7 @@ const std::string kTextTrackIdentifier{"TextTrackIdentifier"}; constexpr uint32_t kBufferingLimit{4324}; constexpr bool kUseBuffering{true}; constexpr uint64_t kStopPosition{23412}; +constexpr bool kIsLive{false}; constexpr uint32_t kQueuedFrames{123}; } // namespace @@ -99,12 +100,12 @@ MediaPipelineServiceTests::MediaPipelineServiceTests() void MediaPipelineServiceTests::mediaPipelineWillLoad() { - EXPECT_CALL(m_mediaPipelineMock, load(kType, kMimeType, kUrl)).WillOnce(Return(true)); + EXPECT_CALL(m_mediaPipelineMock, load(kType, kMimeType, kUrl, kIsLive)).WillOnce(Return(true)); } void MediaPipelineServiceTests::mediaPipelineWillFailToLoad() { - EXPECT_CALL(m_mediaPipelineMock, load(kType, kMimeType, kUrl)).WillOnce(Return(false)); + EXPECT_CALL(m_mediaPipelineMock, load(kType, kMimeType, kUrl, kIsLive)).WillOnce(Return(false)); } void MediaPipelineServiceTests::mediaPipelineWillAttachSource() @@ -617,12 +618,12 @@ void MediaPipelineServiceTests::destroySessionShouldFail() void MediaPipelineServiceTests::loadShouldSucceed() { - EXPECT_TRUE(m_sut->load(kSessionId, kType, kMimeType, kUrl)); + EXPECT_TRUE(m_sut->load(kSessionId, kType, kMimeType, kUrl, kIsLive)); } void MediaPipelineServiceTests::loadShouldFail() { - EXPECT_FALSE(m_sut->load(kSessionId, kType, kMimeType, kUrl)); + EXPECT_FALSE(m_sut->load(kSessionId, kType, kMimeType, kUrl, kIsLive)); } void MediaPipelineServiceTests::attachSourceShouldSucceed() From 1dbfdedb23ae0f9b5c5f2e6713e14e68975c37c6 Mon Sep 17 00:00:00 2001 From: smudri85 Date: Mon, 4 May 2026 08:05:16 +0200 Subject: [PATCH 21/29] Added profiler to Rialto Server (#441) Summary: Added profiling to Rialto Server. IProfiler for use throughout server and IGstProfiler for use on server GST media pipeline. Added IGstProfiler method calls throughout media/server pipeline for recording points of interest. Added metrics calculation on top, with following one line log. Type: Feature Test Plan: UT/CT, Fullstack Jira: RDKEMW-11764 --------- Co-authored-by: Sasa Mudri --- common/CMakeLists.txt | 1 + common/include/Profiler.h | 69 +++ common/interface/IProfiler.h | 136 +++++ common/source/Profiler.cpp | 208 +++++++ media/server/gstplayer/CMakeLists.txt | 1 + .../gstplayer/include/GenericPlayerContext.h | 6 + .../gstplayer/include/GstGenericPlayer.h | 8 + media/server/gstplayer/include/GstProfiler.h | 137 +++++ media/server/gstplayer/include/IGstProfiler.h | 75 +++ .../gstplayer/include/IGstProfilerPrivate.h | 50 ++ .../gstplayer/source/GstGenericPlayer.cpp | 33 +- media/server/gstplayer/source/GstProfiler.cpp | 410 +++++++++++++ .../source/tasks/generic/AttachSource.cpp | 11 + .../source/tasks/generic/DeepElementAdded.cpp | 2 + .../tasks/generic/FinishSetupSource.cpp | 3 + .../source/tasks/generic/HandleBusMessage.cpp | 17 +- scripts/gtest/build_and_run_tests.py | 5 +- .../externalLibraryMocks/GstWrapperMock.h | 5 + .../server/fixtures/MediaPipelineTest.cpp | 4 +- .../mediaPipeline/DualVideoPlaybackTest.cpp | 4 +- .../common/mocks/ProfilerFactoryMock.h | 40 ++ tests/unittests/common/mocks/ProfilerMock.h | 51 ++ .../unittests/common/unittests/CMakeLists.txt | 1 + .../common/unittests/ProfilerTests.cpp | 261 ++++++++ .../media/server/gstplayer/CMakeLists.txt | 3 + .../gstplayer/genericPlayer/CreateTest.cpp | 55 +- .../GstDispatcherThreadClientTest.cpp | 6 +- .../GstGenericPlayerPrivateTest.cpp | 6 +- .../genericPlayer/GstGenericPlayerTest.cpp | 6 +- .../common/GenericTasksTestsBase.cpp | 1 + .../common/GenericTasksTestsContext.h | 3 + .../common/GstGenericPlayerTestCommon.cpp | 2 + .../common/GstGenericPlayerTestCommon.h | 7 + .../tasksTests/HandleBusMessageTest.cpp | 48 +- .../gstplayer/profiler/GstProfilerTests.cpp | 563 ++++++++++++++++++ .../mocks/gstplayer/GstProfilerFactoryMock.h | 46 ++ .../server/mocks/gstplayer/GstProfilerMock.h | 50 ++ wrappers/include/GstWrapper.h | 8 + wrappers/interface/IGstWrapper.h | 22 + 39 files changed, 2299 insertions(+), 65 deletions(-) create mode 100644 common/include/Profiler.h create mode 100644 common/interface/IProfiler.h create mode 100644 common/source/Profiler.cpp create mode 100644 media/server/gstplayer/include/GstProfiler.h create mode 100644 media/server/gstplayer/include/IGstProfiler.h create mode 100644 media/server/gstplayer/include/IGstProfilerPrivate.h create mode 100644 media/server/gstplayer/source/GstProfiler.cpp create mode 100644 tests/unittests/common/mocks/ProfilerFactoryMock.h create mode 100644 tests/unittests/common/mocks/ProfilerMock.h create mode 100644 tests/unittests/common/unittests/ProfilerTests.cpp create mode 100644 tests/unittests/media/server/gstplayer/profiler/GstProfilerTests.cpp create mode 100644 tests/unittests/media/server/mocks/gstplayer/GstProfilerFactoryMock.h create mode 100644 tests/unittests/media/server/mocks/gstplayer/GstProfilerMock.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 42d7a115f..58fd49fa0 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -31,6 +31,7 @@ add_library( source/EventThread.cpp source/LinuxUtils.cpp source/Timer.cpp + source/Profiler.cpp ) set_property ( diff --git a/common/include/Profiler.h b/common/include/Profiler.h new file mode 100644 index 000000000..c45a1653c --- /dev/null +++ b/common/include/Profiler.h @@ -0,0 +1,69 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIREBOLT_RIALTO_COMMON_PROFILER_H_ +#define FIREBOLT_RIALTO_COMMON_PROFILER_H_ + +#include "IProfiler.h" + +#include +#include +#include +#include +#include +#include + +namespace firebolt::rialto::common +{ +class ProfilerFactory : public IProfilerFactory +{ +public: + std::unique_ptr createProfiler(std::string moduleName) const override; +}; + +class Profiler final : public IProfiler +{ +public: + explicit Profiler(std::string module); + + bool isEnabled() const noexcept override; + + std::optional record(const std::string &stage) override; + std::optional record(const std::string &stage, const std::string &info) override; + + void log(const RecordId id) override; + + bool dumpToFile() const override; + std::vector getRecords() const override; + +private: + std::optional findById(RecordId id) const; + + std::string m_module; + bool m_enabled; + std::optional m_dumpFileName; + + mutable std::mutex m_mutex; + RecordId m_id{1}; + std::vector m_records; +}; + +}; // namespace firebolt::rialto::common + +#endif // FIREBOLT_RIALTO_COMMON_PROFILER_H_ diff --git a/common/interface/IProfiler.h b/common/interface/IProfiler.h new file mode 100644 index 000000000..bf03acff5 --- /dev/null +++ b/common/interface/IProfiler.h @@ -0,0 +1,136 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIREBOLT_RIALTO_COMMON_I_PROFILER_H_ +#define FIREBOLT_RIALTO_COMMON_I_PROFILER_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace firebolt::rialto::common +{ +class IProfiler; + +/** + * @brief IProfiler factory class, returns a concrete implementation of IProfiler + */ +class IProfilerFactory +{ +public: + IProfilerFactory() = default; + virtual ~IProfilerFactory() = default; + + /** + * @brief Creates a IProfilerFactory instance. + * + * @retval the factory instance or null on error. + */ + static std::shared_ptr createFactory(); + + /** + * @brief Creates an IProfiler object. + * + * @param[in] moduleName : The name of the module + * + * @retval the new profiler instance or null on error. + */ + virtual std::unique_ptr createProfiler(std::string moduleName) const = 0; +}; + +class IProfiler +{ +public: + using RecordId = std::uint64_t; + using Clock = std::chrono::system_clock; + + struct Record + { + std::string moduleName; + uint64_t id{0}; + std::string stage; + std::string info; + Clock::time_point time; + }; + + virtual ~IProfiler() = default; + + IProfiler(const IProfiler &) = delete; + IProfiler &operator=(const IProfiler &) = delete; + IProfiler(IProfiler &&) = delete; + IProfiler &operator=(IProfiler &&) = delete; + + /** + * @brief Checks if profiler is enabled. + * + * @retval true if profiler is enabled, false otherwise. + */ + virtual bool isEnabled() const noexcept = 0; + + /** + * @brief Creates a record for given stage. + * + * @param[in] stage : Stage name used for record creation + * + * @retval Record identifier for created record or std::nullopt. + */ + virtual std::optional record(const std::string &stage) = 0; + + /** + * @brief Creates a record for given stage and info. + * + * @param[in] stage : Stage name used for record creation + * @param[in] info : Additional information used for record creation + * + * @retval Record identifier for created record or std::nullopt. + */ + virtual std::optional record(const std::string &stage, const std::string &info) = 0; + + /** + * @brief Logs a record for given identifier. + * + * @param[in] id : Record identifier + */ + virtual void log(RecordId id) = 0; + + /** + * @brief Dumps all records into pre-configured file. + * + * @retval true if file is created and records are dumped, false otherwise. + */ + virtual bool dumpToFile() const = 0; + + /** + * @brief Retrieves existing records. + * + * @retval Snapshot copy of existing records. + */ + virtual std::vector getRecords() const = 0; + +protected: + IProfiler() = default; +}; + +} // namespace firebolt::rialto::common + +#endif // FIREBOLT_RIALTO_COMMON_I_PROFILER_H_ diff --git a/common/source/Profiler.cpp b/common/source/Profiler.cpp new file mode 100644 index 000000000..172e204fc --- /dev/null +++ b/common/source/Profiler.cpp @@ -0,0 +1,208 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2025 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Profiler.h" +#include "RialtoCommonLogging.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ +inline constexpr const char *kProfilerEnv{"PROFILER_ENABLED"}; +inline constexpr const char *kProfilerDumpFileEnv{"PROFILER_DUMP_FILE_NAME"}; +inline constexpr std::size_t kMaxRecords{100}; + +bool getProfilerEnabled() +{ + const char *value = std::getenv(kProfilerEnv); + if (!value || (value[0] == '\0')) + return false; + + std::string stringValue(value); + std::transform(stringValue.begin(), stringValue.end(), stringValue.begin(), + [](unsigned char c) { return static_cast(std::tolower(c)); }); + + if (stringValue == "1" || stringValue == "true" || stringValue == "yes" || stringValue == "on") + return true; + if (stringValue == "0" || stringValue == "false" || stringValue == "no" || stringValue == "off") + return false; + + return false; +} + +std::optional getDumpFileName() +{ + const char *value = std::getenv(kProfilerDumpFileEnv); + if (!value || value[0] == '\0') + return std::nullopt; + + return std::string{value}; +} +} // namespace + +namespace firebolt::rialto::common +{ +std::shared_ptr IProfilerFactory::createFactory() +{ + std::shared_ptr factory; + + try + { + factory = std::make_shared(); + } + catch (const std::exception &e) + { + RIALTO_COMMON_LOG_ERROR("Failed to create the profiler factory, reason: %s", e.what()); + } + + return factory; +} + +std::unique_ptr ProfilerFactory::createProfiler(std::string moduleName) const +{ + return std::make_unique(moduleName); +} + +Profiler::Profiler(std::string module) + : m_module(std::move(module)), m_enabled(getProfilerEnabled()), m_dumpFileName(getDumpFileName()) +{ +} + +bool Profiler::isEnabled() const noexcept +{ + return m_enabled; +} + +std::optional Profiler::record(const std::string &stage) +{ + if (!m_enabled) + return std::nullopt; + + auto now = Clock::now(); + std::lock_guard lock(m_mutex); + + if (m_records.size() >= kMaxRecords) + { + return std::nullopt; + } + + const Profiler::RecordId id = m_id++; + + m_records.push_back(Record{m_module, id, stage, std::string{}, now}); + + return id; +} + +std::optional Profiler::record(const std::string &stage, const std::string &info) +{ + if (!m_enabled) + return std::nullopt; + + auto now = Clock::now(); + std::lock_guard lock(m_mutex); + + if (m_records.size() >= kMaxRecords) + { + return std::nullopt; + } + + const Profiler::RecordId id = m_id++; + + m_records.push_back(Record{m_module, id, stage, info, now}); + + return id; +} + +void Profiler::log(const RecordId id) +{ + if (!m_enabled) + return; + + const auto record = findById(id); + if (record) + { + const auto ms = std::chrono::duration_cast(record->time.time_since_epoch()).count(); + + const auto idStr = std::to_string(static_cast(record->id)); + const auto tsStr = std::to_string(static_cast(ms)); + + RIALTO_COMMON_LOG_MIL("PROFILER | RECORD | MODULE[%s] ID[%s] STAGE[%s] INFO[%s] TIMESTAMP[%s]", + record->moduleName.c_str(), idStr.c_str(), record->stage.c_str(), record->info.c_str(), + tsStr.c_str()); + } +} + +bool Profiler::dumpToFile() const +{ + if (!m_dumpFileName) + return false; + + if (!m_enabled) + return false; + + std::vector copy; + { + std::lock_guard lock(m_mutex); + copy = m_records; + } + + std::ofstream out(*m_dumpFileName, std::ios::out | std::ios::app); + if (!out.is_open()) + return false; + + for (const auto &record : copy) + { + const auto us = std::chrono::duration_cast(record.time.time_since_epoch()).count(); + + out << "MODULE[" << record.moduleName << "] " << "ID[" << record.id << "] " << "STAGE[" << record.stage << "] " + << "INFO[" << record.info << "] " << "TIMESTAMP[" << us << "]" << '\n'; + } + + return static_cast(out); +} + +std::vector Profiler::getRecords() const +{ + if (!m_enabled) + return {}; + + std::lock_guard lock(m_mutex); + return m_records; +} + +std::optional Profiler::findById(Profiler::RecordId id) const +{ + std::lock_guard lock(m_mutex); + + const auto it = std::find_if(m_records.begin(), m_records.end(), [&](const auto &record) { return record.id == id; }); + + if (it != m_records.end()) + return *it; + + return std::nullopt; +} + +}; // namespace firebolt::rialto::common diff --git a/media/server/gstplayer/CMakeLists.txt b/media/server/gstplayer/CMakeLists.txt index 58278418a..1adfc4e66 100644 --- a/media/server/gstplayer/CMakeLists.txt +++ b/media/server/gstplayer/CMakeLists.txt @@ -97,6 +97,7 @@ add_library( source/GstGenericPlayer.cpp source/GstInitialiser.cpp source/GstLogForwarding.cpp + source/GstProfiler.cpp source/GstProtectionMetadata.cpp source/GstProtectionMetadataHelper.cpp source/GstSrc.cpp diff --git a/media/server/gstplayer/include/GenericPlayerContext.h b/media/server/gstplayer/include/GenericPlayerContext.h index 946c80d4a..7aaceb099 100644 --- a/media/server/gstplayer/include/GenericPlayerContext.h +++ b/media/server/gstplayer/include/GenericPlayerContext.h @@ -21,6 +21,7 @@ #define FIREBOLT_RIALTO_SERVER_GENERIC_PLAYER_CONTEXT_H_ #include "FlushOnPrerollController.h" +#include "IGstProfiler.h" #include "IGstSrc.h" #include "IRdkGstreamerUtilsWrapper.h" #include "ITimer.h" @@ -275,6 +276,11 @@ struct GenericPlayerContext * This is a workaround for Broadcom decoder issue with audio cuts during playback rate change. */ bool isLive{false}; + + /** + * @brief Profiler for player pipeline + */ + std::unique_ptr gstProfiler; }; } // namespace firebolt::rialto::server diff --git a/media/server/gstplayer/include/GstGenericPlayer.h b/media/server/gstplayer/include/GstGenericPlayer.h index a23056b3d..408545d45 100644 --- a/media/server/gstplayer/include/GstGenericPlayer.h +++ b/media/server/gstplayer/include/GstGenericPlayer.h @@ -28,6 +28,7 @@ #include "IGstGenericPlayer.h" #include "IGstGenericPlayerPrivate.h" #include "IGstInitialiser.h" +#include "IGstProfiler.h" #include "IGstProtectionMetadataHelperFactory.h" #include "IGstSrc.h" #include "IGstWrapper.h" @@ -83,6 +84,7 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva * @param[in] gstInitialiser : The gst initialiser * @param[in] flushWatcher : The flush watcher * @param[in] gstSrcFactory : The gstreamer rialto src factory. + * @param[in] gstProfilerFactory : The gstreamer rialto profiler factory. * @param[in] timerFactory : The Timer factory * @param[in] taskFactory : The task factory * @param[in] workerThreadFactory : The worker thread factory @@ -95,6 +97,7 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva const std::shared_ptr &rdkGstreamerUtilsWrapper, const IGstInitialiser &gstInitialiser, std::unique_ptr &&flushWatcher, const std::shared_ptr &gstSrcFactory, + const std::shared_ptr &gstProfilerFactory, std::shared_ptr timerFactory, std::unique_ptr taskFactory, std::unique_ptr workerThreadFactory, @@ -431,6 +434,11 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva */ std::shared_ptr m_rdkGstreamerUtilsWrapper; + /** + * @brief Factory creating gst profilers + */ + std::shared_ptr m_gstProfilerFactory; + /** * @brief Thread for handling player tasks. */ diff --git a/media/server/gstplayer/include/GstProfiler.h b/media/server/gstplayer/include/GstProfiler.h new file mode 100644 index 000000000..f767918aa --- /dev/null +++ b/media/server/gstplayer/include/GstProfiler.h @@ -0,0 +1,137 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIREBOLT_RIALTO_SERVER_GST_PROFILER_H_ +#define FIREBOLT_RIALTO_SERVER_GST_PROFILER_H_ + +#include "IGlibWrapper.h" +#include "IGstProfiler.h" +#include "IGstProfilerPrivate.h" +#include "IGstWrapper.h" +#include "IProfiler.h" + +#include + +#include +#include +#include +#include +#include +#include + +namespace firebolt::rialto::server +{ +class GstProfilerFactory : public IGstProfilerFactory +{ +public: + std::unique_ptr createGstProfiler(GstElement *pipeline, const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper) const override; + + static std::weak_ptr m_factory; +}; + +class GstProfiler : public IGstProfiler, public IGstProfilerPrivate +{ +public: + using RecordId = IGstProfiler::RecordId; + + GstProfiler(GstElement *pipeline, const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper); + ~GstProfiler() override; + + std::optional createRecord(const std::string &stage) override; + std::optional createRecord(const std::string &stage, const std::string &info) override; + + void scheduleGstElementRecord(GstElement *element) override; + std::vector getRecords() const override; + + void logRecord(const RecordId id) override; + void dumpToFile() const override; + void logPipelineSummary() const override; + +private: + using Clock = std::chrono::system_clock; + using IGstWrapper = firebolt::rialto::wrappers::IGstWrapper; + using IGlibWrapper = firebolt::rialto::wrappers::IGlibWrapper; + using IProfiler = firebolt::rialto::common::IProfiler; + + struct PipelineStageTimestamps + { + std::optional pipelineCreated; + std::optional allSourcesAttached; + + std::optional firstSegmentReceivedVideo; + std::optional firstSegmentReceivedAudio; + + std::optional sourceFbExitVideo; + std::optional sourceFbExitAudio; + + std::optional decryptorFbExitVideo; + std::optional decryptorFbExitAudio; + + std::optional decoderFbExitVideo; + std::optional decoderFbExitAudio; + + std::optional pipelinePaused; + std::optional pipelinePlaying; + }; + + struct PipelineMetrics + { + std::optional preparation; + std::optional videoDownload; + std::optional audioDownload; + std::optional videoSource; + std::optional audioSource; + std::optional videoDecryption; + std::optional audioDecryption; + std::optional videoDecode; + std::optional audioDecode; + std::optional preRoll; + std::optional play; + std::optional total; + std::optional totalWithoutApp; + }; + + std::optional getFirstBufferExitStage(GstElement *element); + const gchar *getElementClassMetadata(GstElement *element); + std::string deriveElementInfoFromName(const std::string &name) const; + GstPadProbeReturn handleProbeCb(GstPad *pad, GstPadProbeInfo *info) override; + void removeProbeCtx(GstPad *pad); + + std::optional calculateMetrics() const; + + struct ProbeCtx + { + std::shared_ptr profiler; + std::string stage; + std::string info; + GstPad *pad; + gulong id; + }; + + GstElement *m_pipeline = nullptr; + std::shared_ptr m_gstWrapper; + std::shared_ptr m_glibWrapper; + std::shared_ptr m_profiler; + std::vector m_probeCtxs; + bool m_enabled = false; +}; +} // namespace firebolt::rialto::server +#endif // FIREBOLT_RIALTO_SERVER_GST_PROFILER_H_ diff --git a/media/server/gstplayer/include/IGstProfiler.h b/media/server/gstplayer/include/IGstProfiler.h new file mode 100644 index 000000000..8ea182005 --- /dev/null +++ b/media/server/gstplayer/include/IGstProfiler.h @@ -0,0 +1,75 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIREBOLT_RIALTO_SERVER_I_GST_PROFILER_H_ +#define FIREBOLT_RIALTO_SERVER_I_GST_PROFILER_H_ + +#include "IGlibWrapper.h" +#include "IGstWrapper.h" +#include "IProfiler.h" + +#include + +#include +#include +#include +#include +#include + +namespace firebolt::rialto::server +{ +class IGstProfiler; + +class IGstProfilerFactory +{ +public: + using IGstWrapper = firebolt::rialto::wrappers::IGstWrapper; + using IGlibWrapper = firebolt::rialto::wrappers::IGlibWrapper; + + IGstProfilerFactory() = default; + virtual ~IGstProfilerFactory() = default; + + static std::shared_ptr getFactory(); + + virtual std::unique_ptr createGstProfiler(GstElement *pipeline, + const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper) const = 0; +}; + +class IGstProfiler +{ +public: + using RecordId = std::uint64_t; + using Record = firebolt::rialto::common::IProfiler::Record; + + virtual ~IGstProfiler() = default; + + virtual std::optional createRecord(const std::string &stage) = 0; + virtual std::optional createRecord(const std::string &stage, const std::string &info) = 0; + + virtual void scheduleGstElementRecord(GstElement *element) = 0; + virtual std::vector getRecords() const = 0; + + virtual void logRecord(RecordId id) = 0; + virtual void dumpToFile() const = 0; + virtual void logPipelineSummary() const = 0; +}; +} // namespace firebolt::rialto::server + +#endif // FIREBOLT_RIALTO_SERVER_I_GST_PROFILER_H_ diff --git a/media/server/gstplayer/include/IGstProfilerPrivate.h b/media/server/gstplayer/include/IGstProfilerPrivate.h new file mode 100644 index 000000000..c5e5ccfc1 --- /dev/null +++ b/media/server/gstplayer/include/IGstProfilerPrivate.h @@ -0,0 +1,50 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIREBOLT_RIALTO_SERVER_I_GST_PROFILER_PRIVATE_H_ +#define FIREBOLT_RIALTO_SERVER_I_GST_PROFILER_PRIVATE_H_ + +#include + +namespace firebolt::rialto::server +{ +class IGstProfilerPrivate +{ +public: + IGstProfilerPrivate() = default; + virtual ~IGstProfilerPrivate() = default; + + IGstProfilerPrivate(const IGstProfilerPrivate &) = delete; + IGstProfilerPrivate &operator=(const IGstProfilerPrivate &) = delete; + IGstProfilerPrivate(IGstProfilerPrivate &&) = delete; + IGstProfilerPrivate &operator=(IGstProfilerPrivate &&) = delete; + + /** + * @brief Handles pad probe callback. + * + * @param[in] pad : Pad where the probe is attached. + * @param[in] info : Probe info. + * + * @retval The probe return code. + */ + virtual GstPadProbeReturn handleProbeCb(GstPad *pad, GstPadProbeInfo *info) = 0; +}; +} // namespace firebolt::rialto::server + +#endif // FIREBOLT_RIALTO_SERVER_I_GST_PROFILER_PRIVATE_H_ diff --git a/media/server/gstplayer/source/GstGenericPlayer.cpp b/media/server/gstplayer/source/GstGenericPlayer.cpp index 5f18f485f..8b2bccd4f 100644 --- a/media/server/gstplayer/source/GstGenericPlayer.cpp +++ b/media/server/gstplayer/source/GstGenericPlayer.cpp @@ -26,6 +26,7 @@ #include "FlushWatcher.h" #include "GstDispatcherThread.h" #include "GstGenericPlayer.h" +#include "GstProfiler.h" #include "GstProtectionMetadata.h" #include "IGstTextTrackSinkFactory.h" #include "IMediaPipeline.h" @@ -105,10 +106,12 @@ std::unique_ptr GstGenericPlayerFactory::createGstGenericPlay { throw std::runtime_error("Cannot create RdkGstreamerUtilsWrapper"); } + gstPlayer = std::make_unique< GstGenericPlayer>(client, decryptionService, type, videoRequirements, isLive, gstWrapper, glibWrapper, rdkGstreamerUtilsWrapper, IGstInitialiser::instance(), std::make_unique(), - IGstSrcFactory::getFactory(), common::ITimerFactory::getFactory(), + IGstSrcFactory::getFactory(), IGstProfilerFactory::getFactory(), + common::ITimerFactory::getFactory(), std::make_unique(client, gstWrapper, glibWrapper, rdkGstreamerUtilsWrapper, IGstTextTrackSinkFactory::createFactory()), @@ -130,13 +133,14 @@ GstGenericPlayer::GstGenericPlayer( const std::shared_ptr &glibWrapper, const std::shared_ptr &rdkGstreamerUtilsWrapper, const IGstInitialiser &gstInitialiser, std::unique_ptr &&flushWatcher, - const std::shared_ptr &gstSrcFactory, std::shared_ptr timerFactory, + const std::shared_ptr &gstSrcFactory, + const std::shared_ptr &gstProfilerFactory, std::shared_ptr timerFactory, std::unique_ptr taskFactory, std::unique_ptr workerThreadFactory, std::unique_ptr gstDispatcherThreadFactory, std::shared_ptr gstProtectionMetadataFactory) : m_gstPlayerClient(client), m_gstWrapper{gstWrapper}, m_glibWrapper{glibWrapper}, - m_rdkGstreamerUtilsWrapper{rdkGstreamerUtilsWrapper}, m_timerFactory{timerFactory}, - m_taskFactory{std::move(taskFactory)}, m_flushWatcher{std::move(flushWatcher)} + m_rdkGstreamerUtilsWrapper{rdkGstreamerUtilsWrapper}, m_gstProfilerFactory{gstProfilerFactory}, + m_timerFactory{timerFactory}, m_taskFactory{std::move(taskFactory)}, m_flushWatcher{std::move(flushWatcher)} { RIALTO_SERVER_LOG_DEBUG("GstGenericPlayer is constructed."); @@ -149,6 +153,10 @@ GstGenericPlayer::GstGenericPlayer( { throw std::runtime_error("Cannot create GstSrc"); } + if (!m_gstProfilerFactory) + { + throw std::runtime_error("No gst profiler factory provided"); + } if (!timerFactory) { @@ -227,6 +235,12 @@ void GstGenericPlayer::initMsePipeline() // Set pipeline flags setPlaybinFlags(true); + m_context.gstProfiler = m_gstProfilerFactory->createGstProfiler(m_context.pipeline, m_gstWrapper, m_glibWrapper); + if (!m_context.gstProfiler) + { + throw std::runtime_error("Cannot create GstProfiler"); + } + // Set callbacks m_glibWrapper->gSignalConnect(m_context.pipeline, "source-setup", G_CALLBACK(&GstGenericPlayer::setupSource), this); m_glibWrapper->gSignalConnect(m_context.pipeline, "element-setup", G_CALLBACK(&GstGenericPlayer::setupElement), this); @@ -252,6 +266,9 @@ void GstGenericPlayer::initMsePipeline() GST_WARNING("Failed to set pipeline to READY state"); } RIALTO_SERVER_LOG_MIL("New RialtoServer's pipeline created"); + auto recordId = m_context.gstProfiler->createRecord("Pipeline Created"); + if (recordId) + m_context.gstProfiler->logRecord(recordId.value()); } void GstGenericPlayer::resetWorkerThread() @@ -308,6 +325,11 @@ void GstGenericPlayer::termPipeline() m_context.playbackGroup.m_curAudioPlaysinkBin = nullptr; } + auto recordId = m_context.gstProfiler->createRecord("Pipeline Terminated"); + if (recordId) + m_context.gstProfiler->logRecord(recordId.value()); + m_context.gstProfiler->dumpToFile(); + // Delete the pipeline m_gstWrapper->gstObjectUnref(m_context.pipeline); @@ -1493,6 +1515,9 @@ void GstGenericPlayer::pushSampleIfRequired(GstElement *source, const std::strin "], rate: %f, appliedRate %f, reset_time: %d\n", typeStr.c_str(), GST_TIME_ARGS(segment->start), GST_TIME_ARGS(segment->stop), segment->rate, segment->applied_rate, resetTime); + auto recordId = m_context.gstProfiler->createRecord("First Segment Received", typeStr); + if (recordId) + m_context.gstProfiler->logRecord(recordId.value()); GstCaps *currentCaps = m_gstWrapper->gstAppSrcGetCaps(GST_APP_SRC(source)); // We can't pass buffer in GstSample, because implementation of gst_app_src_push_sample diff --git a/media/server/gstplayer/source/GstProfiler.cpp b/media/server/gstplayer/source/GstProfiler.cpp new file mode 100644 index 000000000..3a87fe248 --- /dev/null +++ b/media/server/gstplayer/source/GstProfiler.cpp @@ -0,0 +1,410 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GstProfiler.h" +#include "RialtoServerLogging.h" +#include "Utils.h" + +#include +#include +#include +#include +#include + +#include +#include + +namespace firebolt::rialto::server +{ +std::weak_ptr GstProfilerFactory::m_factory; + +std::shared_ptr IGstProfilerFactory::getFactory() +{ + std::shared_ptr factory = GstProfilerFactory::m_factory.lock(); + + if (!factory) + { + try + { + factory = std::make_shared(); + } + catch (const std::exception &e) + { + RIALTO_SERVER_LOG_ERROR("Failed to create the gst profiler factory, reason: %s", e.what()); + } + + GstProfilerFactory::m_factory = factory; + } + + return factory; +} + +std::unique_ptr GstProfilerFactory::createGstProfiler(GstElement *pipeline, + const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper) const +{ + return std::make_unique(pipeline, gstWrapper, glibWrapper); +} + +namespace +{ +inline constexpr std::array kKlassTokens{ + std::string_view{"Source"}, + std::string_view{"Decryptor"}, + std::string_view{"Decoder"}, +}; +inline constexpr std::string_view kModuleName{"GstProfiler"}; + +using Clock = std::chrono::system_clock; +using IProfiler = firebolt::rialto::common::IProfiler; + +GstPadProbeReturn probeCb(GstPad *pad, GstPadProbeInfo *info, gpointer userData) +{ + firebolt::rialto::server::IGstProfilerPrivate *self = + static_cast(userData); + return self->handleProbeCb(pad, info); +} + +std::optional diffMs(const std::optional &end, const std::optional &start) +{ + if (!end || !start) + return std::nullopt; + + return std::chrono::duration_cast(*end - *start).count(); +} + +std::optional maxTime(const std::optional &a, + const std::optional &b) +{ + if (a && b) + return std::max(*a, *b); + if (a) + return a; + if (b) + return b; + return std::nullopt; +} +} // namespace + +GstProfiler::GstProfiler(GstElement *pipeline, const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper) + : m_pipeline{pipeline}, m_gstWrapper{gstWrapper}, m_glibWrapper{glibWrapper} +{ + auto profilerFactory = firebolt::rialto::common::IProfilerFactory::createFactory(); + m_profiler = profilerFactory ? std::shared_ptr{profilerFactory->createProfiler(std::string{kModuleName})} + : nullptr; + m_enabled = (m_profiler != nullptr) && m_profiler->isEnabled(); + + if (m_enabled && m_pipeline) + m_gstWrapper->gstObjectRef(m_pipeline); +} + +GstProfiler::~GstProfiler() +{ + while (!m_probeCtxs.empty()) + { + auto &probeCtx = m_probeCtxs.back(); + if (probeCtx.id != 0) + { + m_gstWrapper->gstPadRemoveProbe(probeCtx.pad, probeCtx.id); + } + m_gstWrapper->gstObjectUnref(probeCtx.pad); + m_probeCtxs.pop_back(); + } + + if (m_enabled && m_pipeline) + m_gstWrapper->gstObjectUnref(m_pipeline); +} + +std::optional GstProfiler::createRecord(const std::string &stage) +{ + if (!m_enabled || !m_profiler) + return std::nullopt; + + auto id = m_profiler->record(stage); + if (!id) + return std::nullopt; + + return static_cast(*id); +} + +std::optional GstProfiler::createRecord(const std::string &stage, const std::string &info) +{ + if (!m_enabled || !m_profiler) + return std::nullopt; + + auto id = m_profiler->record(stage, info); + if (!id) + return std::nullopt; + + return static_cast(*id); +} + +void GstProfiler::scheduleGstElementRecord(GstElement *element) +{ + if (!m_enabled || !m_profiler) + return; + + if (!element) + return; + + auto stage = getFirstBufferExitStage(element); + if (!stage) + return; + + GstPad *pad = m_gstWrapper->gstElementGetStaticPad(element, "src"); + if (!pad) + return; + + std::string elementInfo; + if (isVideo(*m_gstWrapper, element)) + { + elementInfo = "Video"; + } + else if (isAudio(*m_gstWrapper, element)) + { + elementInfo = "Audio"; + } + else + { + gchar *rawName = m_gstWrapper->gstElementGetName(element); + elementInfo = deriveElementInfoFromName(rawName ? rawName : ""); + if (rawName) + m_glibWrapper->gFree(rawName); + } + + const auto probeId = m_gstWrapper->gstPadAddProbe(pad, GST_PAD_PROBE_TYPE_BUFFER, &probeCb, + static_cast(this), nullptr); + if (probeId == 0) + { + m_gstWrapper->gstObjectUnref(pad); + return; + } + + m_probeCtxs.emplace_back(ProbeCtx{m_profiler, stage.value(), std::move(elementInfo), pad, probeId}); +} + +std::vector GstProfiler::getRecords() const +{ + if (!m_profiler) + return {}; + + return m_profiler->getRecords(); +} + +void GstProfiler::logRecord(GstProfiler::RecordId id) +{ + if (!m_enabled || !m_profiler) + return; + + m_profiler->log(static_cast(id)); +} + +void GstProfiler::dumpToFile() const +{ + if (!m_enabled || !m_profiler) + return; + + if (!m_profiler->dumpToFile()) + { + RIALTO_SERVER_LOG_WARN("Failed to dump profiler records to file"); + } +} + +void GstProfiler::logPipelineSummary() const +{ + if (!m_enabled || !m_profiler) + return; + + const auto metrics = calculateMetrics(); + if (metrics) + { + RIALTO_SERVER_LOG_MIL("PROFILER | TUNETIME: %lld, %lld, %lld, %lld, %lld, %lld, %lld, %lld, %lld, " + "%lld, %lld, %lld, %lld", + metrics->preparation ? static_cast(*metrics->preparation) : -1, // NOLINT + metrics->videoDownload ? static_cast(*metrics->videoDownload) : -1, // NOLINT + metrics->audioDownload ? static_cast(*metrics->audioDownload) : -1, // NOLINT + metrics->videoSource ? static_cast(*metrics->videoSource) : -1, // NOLINT + metrics->audioSource ? static_cast(*metrics->audioSource) : -1, // NOLINT + metrics->videoDecryption ? static_cast(*metrics->videoDecryption) : -1, // NOLINT + metrics->audioDecryption ? static_cast(*metrics->audioDecryption) : -1, // NOLINT + metrics->videoDecode ? static_cast(*metrics->videoDecode) : -1, // NOLINT + metrics->audioDecode ? static_cast(*metrics->audioDecode) : -1, // NOLINT + metrics->preRoll ? static_cast(*metrics->preRoll) : -1, // NOLINT + metrics->play ? static_cast(*metrics->play) : -1, // NOLINT + metrics->total ? static_cast(*metrics->total) : -1, // NOLINT + metrics->totalWithoutApp ? static_cast(*metrics->totalWithoutApp) : -1); // NOLINT + } +} + +GstPadProbeReturn GstProfiler::handleProbeCb(GstPad *pad, GstPadProbeInfo *info) +{ + if (!(info->type & GST_PAD_PROBE_TYPE_BUFFER)) + return GST_PAD_PROBE_OK; + + if (!GST_PAD_PROBE_INFO_BUFFER(info)) + return GST_PAD_PROBE_OK; + + const auto probeCtx = + std::find_if(m_probeCtxs.begin(), m_probeCtxs.end(), [pad](const auto &ctx) { return ctx.pad == pad; }); + if (probeCtx == m_probeCtxs.end()) + return GST_PAD_PROBE_REMOVE; + + if (probeCtx->profiler) + { + const auto id = probeCtx->profiler->record(probeCtx->stage, probeCtx->info); + if (id) + { + probeCtx->profiler->log(id.value()); + } + } + + removeProbeCtx(pad); + return GST_PAD_PROBE_REMOVE; +} + +std::optional GstProfiler::getFirstBufferExitStage(GstElement *element) +{ + const gchar *klass = getElementClassMetadata(element); + if (!klass) + return std::nullopt; + + for (auto token : kKlassTokens) + { + if (m_glibWrapper->gStrrstr(klass, token.data()) != nullptr) + { + return std::string(token.data()) + " FB Exit"; + } + } + + return std::nullopt; +} + +const gchar *GstProfiler::getElementClassMetadata(GstElement *element) +{ + return m_gstWrapper->gstElementClassGetMetadata(GST_ELEMENT_CLASS(G_OBJECT_GET_CLASS(element)), + GST_ELEMENT_METADATA_KLASS); +} + +std::string GstProfiler::deriveElementInfoFromName(const std::string &name) const +{ + std::string lower = name; + std::transform(lower.begin(), lower.end(), lower.begin(), [](unsigned char c) { return std::tolower(c); }); + + if (lower.find("vid") != std::string::npos || lower.find("video") != std::string::npos) + { + return "Video"; + } + + if (lower.find("aud") != std::string::npos || lower.find("audio") != std::string::npos) + { + return "Audio"; + } + + return name; +} + +void GstProfiler::removeProbeCtx(GstPad *pad) +{ + const auto probeCtx = + std::find_if(m_probeCtxs.begin(), m_probeCtxs.end(), [pad](const auto &ctx) { return ctx.pad == pad; }); + if (probeCtx == m_probeCtxs.end()) + return; + + m_gstWrapper->gstObjectUnref(probeCtx->pad); + m_probeCtxs.erase(probeCtx); +} + +std::optional GstProfiler::calculateMetrics() const +{ + const auto records = m_profiler->getRecords(); + + PipelineStageTimestamps timestamps; + + for (const auto &record : records) + { + const auto &stage = record.stage; + const auto &info = record.info; + + if (!timestamps.pipelineCreated && stage == "Pipeline Created") + timestamps.pipelineCreated = record.time; + else if (!timestamps.allSourcesAttached && stage == "All Sources Attached") + timestamps.allSourcesAttached = record.time; + else if (!timestamps.firstSegmentReceivedVideo && stage == "First Segment Received" && info == "Video") + timestamps.firstSegmentReceivedVideo = record.time; + else if (!timestamps.firstSegmentReceivedAudio && stage == "First Segment Received" && info == "Audio") + timestamps.firstSegmentReceivedAudio = record.time; + else if (!timestamps.sourceFbExitVideo && stage == "Source FB Exit" && info == "Video") + timestamps.sourceFbExitVideo = record.time; + else if (!timestamps.sourceFbExitAudio && stage == "Source FB Exit" && info == "Audio") + timestamps.sourceFbExitAudio = record.time; + else if (!timestamps.decryptorFbExitVideo && stage == "Decryptor FB Exit" && info == "Video") + timestamps.decryptorFbExitVideo = record.time; + else if (!timestamps.decryptorFbExitAudio && stage == "Decryptor FB Exit" && info == "Audio") + timestamps.decryptorFbExitAudio = record.time; + else if (!timestamps.decoderFbExitVideo && stage == "Decoder FB Exit" && info == "Video") + timestamps.decoderFbExitVideo = record.time; + else if (!timestamps.decoderFbExitAudio && stage == "Decoder FB Exit" && info == "Audio") + timestamps.decoderFbExitAudio = record.time; + else if (!timestamps.pipelinePaused && stage == "Pipeline State Changed" && info == "PAUSED") + timestamps.pipelinePaused = record.time; + else if (!timestamps.pipelinePlaying && stage == "Pipeline State Changed" && info == "PLAYING") + timestamps.pipelinePlaying = record.time; + } + + if (!timestamps.pipelineCreated || !timestamps.allSourcesAttached || !timestamps.firstSegmentReceivedVideo || + !timestamps.firstSegmentReceivedAudio || !timestamps.sourceFbExitVideo || !timestamps.sourceFbExitAudio || + !timestamps.decoderFbExitVideo || !timestamps.decoderFbExitAudio || !timestamps.pipelinePaused || + !timestamps.pipelinePlaying) + { + return std::nullopt; + } + + PipelineMetrics metrics; + + metrics.preparation = diffMs(timestamps.allSourcesAttached, timestamps.pipelineCreated); + metrics.videoDownload = diffMs(timestamps.firstSegmentReceivedVideo, timestamps.allSourcesAttached); + metrics.audioDownload = diffMs(timestamps.firstSegmentReceivedAudio, timestamps.allSourcesAttached); + metrics.videoSource = diffMs(timestamps.sourceFbExitVideo, timestamps.firstSegmentReceivedVideo); + metrics.audioSource = diffMs(timestamps.sourceFbExitAudio, timestamps.firstSegmentReceivedAudio); + + if (timestamps.decryptorFbExitVideo && timestamps.decryptorFbExitAudio) + { + metrics.videoDecryption = diffMs(timestamps.decryptorFbExitVideo, timestamps.sourceFbExitVideo); + metrics.audioDecryption = diffMs(timestamps.decryptorFbExitAudio, timestamps.sourceFbExitAudio); + metrics.videoDecode = diffMs(timestamps.decoderFbExitVideo, timestamps.decryptorFbExitVideo); + metrics.audioDecode = diffMs(timestamps.decoderFbExitAudio, timestamps.decryptorFbExitAudio); + } + else + { + metrics.videoDecode = diffMs(timestamps.decoderFbExitVideo, timestamps.sourceFbExitVideo); + metrics.audioDecode = diffMs(timestamps.decoderFbExitAudio, timestamps.sourceFbExitAudio); + } + + const auto firstMediaReady = maxTime(timestamps.firstSegmentReceivedVideo, timestamps.firstSegmentReceivedAudio); + + metrics.preRoll = diffMs(timestamps.pipelinePaused, firstMediaReady); + metrics.play = diffMs(timestamps.pipelinePlaying, timestamps.pipelinePaused); + metrics.total = diffMs(timestamps.pipelinePlaying, timestamps.pipelineCreated); + metrics.totalWithoutApp = diffMs(timestamps.pipelinePlaying, firstMediaReady); + + return metrics; +} + +} // namespace firebolt::rialto::server diff --git a/media/server/gstplayer/source/tasks/generic/AttachSource.cpp b/media/server/gstplayer/source/tasks/generic/AttachSource.cpp index 8396a4d1b..8ca2b2405 100644 --- a/media/server/gstplayer/source/tasks/generic/AttachSource.cpp +++ b/media/server/gstplayer/source/tasks/generic/AttachSource.cpp @@ -77,22 +77,26 @@ void AttachSource::addSource() const RIALTO_SERVER_LOG_ERROR("Failed to create caps from media source"); return; } + std::string profilerInfo; gchar *capsStr = m_gstWrapper->gstCapsToString(caps); GstElement *appSrc = nullptr; if (m_attachedSource->getType() == MediaSourceType::AUDIO) { RIALTO_SERVER_LOG_MIL("Adding Audio appsrc with caps %s", capsStr); appSrc = m_gstWrapper->gstElementFactoryMake("appsrc", "audsrc"); + profilerInfo = "audsrc"; } else if (m_attachedSource->getType() == MediaSourceType::VIDEO) { RIALTO_SERVER_LOG_MIL("Adding Video appsrc with caps %s", capsStr); appSrc = m_gstWrapper->gstElementFactoryMake("appsrc", "vidsrc"); + profilerInfo = "vidsrc"; } else if (m_attachedSource->getType() == MediaSourceType::SUBTITLE) { RIALTO_SERVER_LOG_MIL("Adding Subtitle appsrc with caps %s", capsStr); appSrc = m_gstWrapper->gstElementFactoryMake("appsrc", "subsrc"); + profilerInfo = "subsrc"; if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(m_context.pipeline), "text-sink")) { @@ -102,6 +106,13 @@ void AttachSource::addSource() const m_glibWrapper->gObjectSet(m_context.pipeline, "text-sink", elem, nullptr); } } + if (appSrc) + { + auto recordId = m_context.gstProfiler->createRecord("Created AppSrc Element", profilerInfo); + if (recordId) + m_context.gstProfiler->logRecord(recordId.value()); + } + m_glibWrapper->gFree(capsStr); m_gstWrapper->gstAppSrcSetCaps(GST_APP_SRC(appSrc), caps); diff --git a/media/server/gstplayer/source/tasks/generic/DeepElementAdded.cpp b/media/server/gstplayer/source/tasks/generic/DeepElementAdded.cpp index 4eb337ead..59d9cf21b 100644 --- a/media/server/gstplayer/source/tasks/generic/DeepElementAdded.cpp +++ b/media/server/gstplayer/source/tasks/generic/DeepElementAdded.cpp @@ -54,6 +54,8 @@ DeepElementAdded::DeepElementAdded(GenericPlayerContext &context, IGstGenericPla m_glibWrapper->gSignalConnect(G_OBJECT(m_element), "have-type", G_CALLBACK(onHaveType), &m_player); m_callbackRegistered = true; } + + m_context.gstProfiler->scheduleGstElementRecord(m_element); } } } diff --git a/media/server/gstplayer/source/tasks/generic/FinishSetupSource.cpp b/media/server/gstplayer/source/tasks/generic/FinishSetupSource.cpp index f2a5cf323..a4d49fdf4 100644 --- a/media/server/gstplayer/source/tasks/generic/FinishSetupSource.cpp +++ b/media/server/gstplayer/source/tasks/generic/FinishSetupSource.cpp @@ -124,5 +124,8 @@ void FinishSetupSource::execute() const m_context.setupSourceFinished = true; RIALTO_SERVER_LOG_MIL("All sources attached."); + auto recordId = m_context.gstProfiler->createRecord("All Sources Attached"); + if (recordId) + m_context.gstProfiler->logRecord(recordId.value()); } } // namespace firebolt::rialto::server::tasks::generic diff --git a/media/server/gstplayer/source/tasks/generic/HandleBusMessage.cpp b/media/server/gstplayer/source/tasks/generic/HandleBusMessage.cpp index 9bb55e116..c8af5f1d2 100644 --- a/media/server/gstplayer/source/tasks/generic/HandleBusMessage.cpp +++ b/media/server/gstplayer/source/tasks/generic/HandleBusMessage.cpp @@ -54,13 +54,18 @@ void HandleBusMessage::execute() const { GstState oldState, newState, pending; m_gstWrapper->gstMessageParseStateChanged(m_message, &oldState, &newState, &pending); - RIALTO_SERVER_LOG_MIL("State changed (old: %s, new: %s, pending: %s)", - m_gstWrapper->gstElementStateGetName(oldState), - m_gstWrapper->gstElementStateGetName(newState), - m_gstWrapper->gstElementStateGetName(pending)); + const char *oldStateName = m_gstWrapper->gstElementStateGetName(oldState); + const char *newStateName = m_gstWrapper->gstElementStateGetName(newState); + const char *pendingStateName = m_gstWrapper->gstElementStateGetName(pending); + RIALTO_SERVER_LOG_MIL("State changed (old: %s, new: %s, pending: %s)", oldStateName, newStateName, + pendingStateName); + auto recordId = m_context.gstProfiler->createRecord("Pipeline State Changed", newStateName); + if (recordId) + m_context.gstProfiler->logRecord(recordId.value()); + if (newState == GST_STATE_PLAYING) + m_context.gstProfiler->logPipelineSummary(); - std::string filename = std::string(m_gstWrapper->gstElementStateGetName(oldState)) + "-" + - std::string(m_gstWrapper->gstElementStateGetName(newState)); + std::string filename = std::string(oldStateName) + "-" + std::string(newStateName); m_gstWrapper->gstDebugBinToDotFileWithTs(GST_BIN(m_context.pipeline), GST_DEBUG_GRAPH_SHOW_ALL, filename.c_str()); if (!m_gstPlayerClient) diff --git a/scripts/gtest/build_and_run_tests.py b/scripts/gtest/build_and_run_tests.py index 231f538a9..3ad119f44 100644 --- a/scripts/gtest/build_and_run_tests.py +++ b/scripts/gtest/build_and_run_tests.py @@ -73,6 +73,8 @@ def buildAndRunGTests(args, f, buildDefines, suitesToRun): os.environ["RIALTO_CONSOLE_LOG"] = "1" # Set env variable to enable debug prints os.environ["RIALTO_DEBUG"] = "5" + # Enable profiler for all test suites. + os.environ["PROFILER_ENABLED"] = "true" # Clean if required if args['clean'] == True: @@ -147,7 +149,8 @@ def runTests (suites, doListTests, gtestFilter, outputDir, resultsFile, xmlFile, # Run the command if resultsFile != None: - status = runcmd(executeCmd, cwd=os.getcwd() + '/' + outputDir, stdout=resultsFile, stderr=subprocess.STDOUT) + status = runcmd(executeCmd, cwd=os.getcwd() + '/' + outputDir, stdout=resultsFile, + stderr=subprocess.STDOUT) else: status = runcmd(executeCmd, cwd=os.getcwd() + '/' + outputDir, stderr=subprocess.STDOUT) diff --git a/tests/common/externalLibraryMocks/GstWrapperMock.h b/tests/common/externalLibraryMocks/GstWrapperMock.h index 35c3a5f83..185084ff5 100644 --- a/tests/common/externalLibraryMocks/GstWrapperMock.h +++ b/tests/common/externalLibraryMocks/GstWrapperMock.h @@ -228,6 +228,11 @@ class GstWrapperMock : public IGstWrapper MOCK_METHOD(GstPadLinkReturn, gstPadLink, (GstPad * srcpad, GstPad *sinkpad), (override)); MOCK_METHOD(gboolean, gstBinRemove, (GstBin * bin, GstElement *element), (override)); MOCK_METHOD(GstObject *, gstPadGetParent, (GstPad * pad), (override)); + MOCK_METHOD(gulong, gstPadAddProbe, + (GstPad * pad, GstPadProbeType mask, GstPadProbeCallback callback, gpointer userData, + GDestroyNotify destroyData), + (override)); + MOCK_METHOD(void, gstPadRemoveProbe, (GstPad * pad, gulong id), (override)); GstCaps *gstCapsNewSimple(const char *media_type, const char *fieldname, ...) const override { diff --git a/tests/componenttests/server/fixtures/MediaPipelineTest.cpp b/tests/componenttests/server/fixtures/MediaPipelineTest.cpp index 34fd4738a..85d52a419 100644 --- a/tests/componenttests/server/fixtures/MediaPipelineTest.cpp +++ b/tests/componenttests/server/fixtures/MediaPipelineTest.cpp @@ -35,6 +35,7 @@ using testing::_; using testing::AtLeast; +using testing::AtMost; using testing::DoAll; using testing::Invoke; using testing::Return; @@ -92,6 +93,7 @@ void MediaPipelineTest::gstPlayerWillBeCreated() EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(&m_playsink)); EXPECT_CALL(*m_gstWrapperMock, gstElementSetState(&m_pipeline, GST_STATE_READY)) .WillOnce(Return(GST_STATE_CHANGE_SUCCESS)); + EXPECT_CALL(*m_gstWrapperMock, gstObjectRef(&m_pipeline)).Times(AtMost(1)); // In case of longer testruns, GstPlayer may request to query position and volume EXPECT_CALL(*m_gstWrapperMock, gstStateLock(_)).Times(AtLeast(0)); @@ -129,7 +131,7 @@ void MediaPipelineTest::gstPlayerWillBeDestructed() EXPECT_CALL(*m_gstWrapperMock, gstPipelineGetBus(GST_PIPELINE(&m_pipeline))).WillOnce(Return(&m_bus)); EXPECT_CALL(*m_gstWrapperMock, gstBusSetSyncHandler(&m_bus, nullptr, nullptr, nullptr)); EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(&m_bus)); - EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(&m_pipeline)); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(&m_pipeline)).Times(testing::Between(1, 2)); } void MediaPipelineTest::audioSourceWillBeAttached() diff --git a/tests/componenttests/server/tests/mediaPipeline/DualVideoPlaybackTest.cpp b/tests/componenttests/server/tests/mediaPipeline/DualVideoPlaybackTest.cpp index ec861b42b..f5f478ac7 100644 --- a/tests/componenttests/server/tests/mediaPipeline/DualVideoPlaybackTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/DualVideoPlaybackTest.cpp @@ -42,6 +42,7 @@ const std::string kDummyStateName{"dummy"}; using testing::_; using testing::AtLeast; +using testing::AtMost; using testing::DoAll; using testing::Invoke; using testing::Return; @@ -98,6 +99,7 @@ class DualVideoPlaybackTest : public MediaPipelineTest EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(&m_secondaryPlaysink)); EXPECT_CALL(*m_gstWrapperMock, gstElementSetState(&m_secondaryPipeline, GST_STATE_READY)) .WillOnce(Return(GST_STATE_CHANGE_SUCCESS)); + EXPECT_CALL(*m_gstWrapperMock, gstObjectRef(&m_secondaryPipeline)).Times(AtMost(1)); // In case of longer testruns, GstPlayer may request to query position EXPECT_CALL(*m_gstWrapperMock, gstElementQueryPosition(&m_secondaryPipeline, GST_FORMAT_TIME, _)) @@ -279,7 +281,7 @@ class DualVideoPlaybackTest : public MediaPipelineTest .WillOnce(Return(&m_secondaryBus)); EXPECT_CALL(*m_gstWrapperMock, gstBusSetSyncHandler(&m_secondaryBus, nullptr, nullptr, nullptr)); EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(&m_secondaryBus)); - EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(&m_secondaryPipeline)); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(&m_secondaryPipeline)).Times(testing::Between(1, 2)); } void createSecondaryFullSession() diff --git a/tests/unittests/common/mocks/ProfilerFactoryMock.h b/tests/unittests/common/mocks/ProfilerFactoryMock.h new file mode 100644 index 000000000..9f46b47d9 --- /dev/null +++ b/tests/unittests/common/mocks/ProfilerFactoryMock.h @@ -0,0 +1,40 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIREBOLT_RIALTO_COMMON_PROFILER_FACTORY_MOCK_H_ +#define FIREBOLT_RIALTO_COMMON_PROFILER_FACTORY_MOCK_H_ + +#include "IProfiler.h" +#include +#include +#include + +namespace firebolt::rialto::common +{ +class ProfilerFactoryMock : public IProfilerFactory +{ +public: + ProfilerFactoryMock() = default; + virtual ~ProfilerFactoryMock() = default; + + MOCK_METHOD(std::unique_ptr, createProfiler, (std::string moduleName), (const, override)); +}; +} // namespace firebolt::rialto::common + +#endif // FIREBOLT_RIALTO_COMMON_PROFILER_FACTORY_MOCK_H_ diff --git a/tests/unittests/common/mocks/ProfilerMock.h b/tests/unittests/common/mocks/ProfilerMock.h new file mode 100644 index 000000000..a8872a150 --- /dev/null +++ b/tests/unittests/common/mocks/ProfilerMock.h @@ -0,0 +1,51 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIREBOLT_RIALTO_COMMON_PROFILER_MOCK_H_ +#define FIREBOLT_RIALTO_COMMON_PROFILER_MOCK_H_ + +#include "IProfiler.h" + +#include +#include +#include +#include + +namespace firebolt::rialto::common +{ +class ProfilerMock : public IProfiler +{ +public: + ProfilerMock() = default; + ~ProfilerMock() override = default; + + MOCK_METHOD(bool, isEnabled, (), (const, noexcept, override)); + + MOCK_METHOD(std::optional, record, (const std::string &stage), (override)); + MOCK_METHOD(std::optional, record, (const std::string &stage, const std::string &info), (override)); + + MOCK_METHOD(void, log, (RecordId id), (override)); + + MOCK_METHOD(bool, dumpToFile, (), (const, override)); + + MOCK_METHOD(std::vector, getRecords, (), (const, override)); +}; +} // namespace firebolt::rialto::common + +#endif // FIREBOLT_RIALTO_COMMON_PROFILER_MOCK_H_ diff --git a/tests/unittests/common/unittests/CMakeLists.txt b/tests/unittests/common/unittests/CMakeLists.txt index b9d1ab2a8..b7977dfe4 100644 --- a/tests/unittests/common/unittests/CMakeLists.txt +++ b/tests/unittests/common/unittests/CMakeLists.txt @@ -28,6 +28,7 @@ add_gtests ( # gtest code TimerTests.cpp EventThreadTests.cpp + ProfilerTests.cpp ) target_include_directories( diff --git a/tests/unittests/common/unittests/ProfilerTests.cpp b/tests/unittests/common/unittests/ProfilerTests.cpp new file mode 100644 index 000000000..10ee1cbd5 --- /dev/null +++ b/tests/unittests/common/unittests/ProfilerTests.cpp @@ -0,0 +1,261 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "IProfiler.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace firebolt::rialto::common; + +namespace +{ +std::optional findRecord(const std::vector &records, const std::string &stage, + const std::optional &info = std::nullopt) +{ + const auto it = std::find_if(records.begin(), records.end(), [&](const auto &record) + { return record.stage == stage && (!info.has_value() || record.info == info.value()); }); + + if (it != records.end()) + { + return *it; + } + return std::nullopt; +} +} // namespace + +class ProfilerTests : public ::testing::Test +{ +protected: + void SetUp() override + { + saveEnv("PROFILER_ENABLED", m_originalProfilerEnabled); + saveEnv("PROFILER_DUMP_FILE_NAME", m_originalProfilerDumpFileName); + setenv("PROFILER_ENABLED", "true", 1); + + factory = IProfilerFactory::createFactory(); + ASSERT_TRUE(factory); + + profiler = factory->createProfiler("UnitTestModule"); + ASSERT_TRUE(profiler); + ASSERT_TRUE(profiler->isEnabled()); + } + + void TearDown() override + { + restoreEnv("PROFILER_ENABLED", m_originalProfilerEnabled); + restoreEnv("PROFILER_DUMP_FILE_NAME", m_originalProfilerDumpFileName); + } + + static void saveEnv(const char *name, std::optional &value) + { + const char *envValue = std::getenv(name); + if (envValue) + value = envValue; + else + value = std::nullopt; + } + + static void restoreEnv(const char *name, const std::optional &value) + { + if (value) + setenv(name, value->c_str(), 1); + else + unsetenv(name); + } + + std::shared_ptr factory; + std::unique_ptr profiler; + std::optional m_originalProfilerEnabled; + std::optional m_originalProfilerDumpFileName; +}; + +TEST_F(ProfilerTests, RecordAndFindByStage) +{ + const auto id = profiler->record("Stage1"); + ASSERT_TRUE(id.has_value()); + + const auto found = findRecord(profiler->getRecords(), "Stage1"); + ASSERT_TRUE(found.has_value()); + + EXPECT_EQ(found->id, id.value()); +} + +TEST_F(ProfilerTests, RecordAndFindByStageAndInfo) +{ + const auto id = profiler->record("Stage1", "InfoA"); + ASSERT_TRUE(id.has_value()); + + const auto found = findRecord(profiler->getRecords(), "Stage1", "InfoA"); + ASSERT_TRUE(found.has_value()); + EXPECT_EQ(found->id, id.value()); + + EXPECT_FALSE(findRecord(profiler->getRecords(), "Stage1", "InfoB").has_value()); +} + +TEST_F(ProfilerTests, GetRecordsReturnsRecordedEntries) +{ + const auto id1 = profiler->record("Stage1"); + ASSERT_TRUE(id1.has_value()); + + const auto id2 = profiler->record("Stage2", "Info2"); + ASSERT_TRUE(id2.has_value()); + + const auto records = profiler->getRecords(); + ASSERT_GE(records.size(), 2U); + + const auto &record1 = records[records.size() - 2]; + EXPECT_EQ(record1.moduleName, "UnitTestModule"); + EXPECT_EQ(record1.id, id1.value()); + EXPECT_EQ(record1.stage, "Stage1"); + EXPECT_TRUE(record1.info.empty()); + + const auto &record2 = records[records.size() - 1]; + EXPECT_EQ(record2.moduleName, "UnitTestModule"); + EXPECT_EQ(record2.id, id2.value()); + EXPECT_EQ(record2.stage, "Stage2"); + EXPECT_EQ(record2.info, "Info2"); +} + +TEST_F(ProfilerTests, DumpCreatesFile) +{ + (void)profiler->record("StageDump", "InfoDump"); + const auto suffix = std::to_string(std::random_device{}()); + const std::string path = std::string{"/tmp/rialto_profiler_ut_dump_"} + suffix + ".txt"; + setenv("PROFILER_DUMP_FILE_NAME", path.c_str(), 1); + + auto dumpProfiler = factory->createProfiler("UnitTestModule"); + ASSERT_TRUE(dumpProfiler); + ASSERT_TRUE(dumpProfiler->record("StageDump", "InfoDump").has_value()); + ASSERT_TRUE(dumpProfiler->dumpToFile()); + + std::ifstream in(path); + ASSERT_TRUE(in.good()); + + const std::string content((std::istreambuf_iterator(in)), std::istreambuf_iterator()); + EXPECT_FALSE(content.empty()); + EXPECT_NE(content.find("StageDump"), std::string::npos); + + std::remove(path.c_str()); +} + +TEST_F(ProfilerTests, DumpAppendsToExistingFile) +{ + const auto suffix = std::to_string(std::random_device{}()); + const std::string path = std::string{"/tmp/rialto_profiler_ut_append_"} + suffix + ".txt"; + setenv("PROFILER_DUMP_FILE_NAME", path.c_str(), 1); + + auto dumpProfiler = factory->createProfiler("UnitTestModule"); + ASSERT_TRUE(dumpProfiler); + ASSERT_TRUE(dumpProfiler->record("Stage1").has_value()); + ASSERT_TRUE(dumpProfiler->dumpToFile()); + + ASSERT_TRUE(dumpProfiler->record("Stage2").has_value()); + ASSERT_TRUE(dumpProfiler->dumpToFile()); + + std::ifstream in(path); + ASSERT_TRUE(in.good()); + + const std::string content((std::istreambuf_iterator(in)), std::istreambuf_iterator()); + EXPECT_NE(content.find("Stage1"), std::string::npos); + EXPECT_NE(content.find("Stage2"), std::string::npos); + EXPECT_LT(content.find("Stage1"), content.rfind("Stage2")); + + std::remove(path.c_str()); +} + +TEST_F(ProfilerTests, DumpToFileUsesCachedEnvValue) +{ + const auto suffix = std::to_string(std::random_device{}()); + const std::string path = std::string{"/tmp/rialto_profiler_ut_configured_"} + suffix + ".txt"; + setenv("PROFILER_DUMP_FILE_NAME", path.c_str(), 1); + + auto configuredProfiler = factory->createProfiler("UnitTestModule"); + ASSERT_TRUE(configuredProfiler); + ASSERT_TRUE(configuredProfiler->record("StageConfigured").has_value()); + ASSERT_TRUE(configuredProfiler->dumpToFile()); + + unsetenv("PROFILER_DUMP_FILE_NAME"); + ASSERT_TRUE(configuredProfiler->record("StageConfiguredCached").has_value()); + ASSERT_TRUE(configuredProfiler->dumpToFile()); + + std::ifstream in(path); + ASSERT_TRUE(in.good()); + + const std::string content((std::istreambuf_iterator(in)), std::istreambuf_iterator()); + EXPECT_NE(content.find("StageConfigured"), std::string::npos); + EXPECT_NE(content.find("StageConfiguredCached"), std::string::npos); + + std::remove(path.c_str()); +} + +TEST_F(ProfilerTests, StartsEnabledWhenEnvTrue) +{ + setenv("PROFILER_ENABLED", "true", 1); + + auto envProfiler = factory->createProfiler("UnitTestModule"); + + ASSERT_TRUE(envProfiler); + EXPECT_TRUE(envProfiler->isEnabled()); +} + +TEST_F(ProfilerTests, StartsDisabledWhenEnvValueInvalid) +{ + setenv("PROFILER_ENABLED", "definitely-not-a-bool", 1); + + auto envProfiler = factory->createProfiler("UnitTestModule"); + + ASSERT_TRUE(envProfiler); + EXPECT_FALSE(envProfiler->isEnabled()); +} + +TEST_F(ProfilerTests, StartsDisabledWhenEnvFalse) +{ + setenv("PROFILER_ENABLED", "false", 1); + + auto envProfiler = factory->createProfiler("UnitTestModule"); + + ASSERT_TRUE(envProfiler); + EXPECT_FALSE(envProfiler->isEnabled()); +} + +TEST_F(ProfilerTests, GetRecordsDoesNotContainMissingStage) +{ + ASSERT_TRUE(profiler->record("Stage1").has_value()); + + EXPECT_FALSE(findRecord(profiler->getRecords(), "MissingStage").has_value()); +} + +TEST_F(ProfilerTests, DumpToFileReturnsFalseForInvalidPath) +{ + setenv("PROFILER_DUMP_FILE_NAME", "/proc/rialto_profiler_ut_dump.txt", 1); + auto dumpProfiler = factory->createProfiler("UnitTestModule"); + ASSERT_TRUE(dumpProfiler); + EXPECT_FALSE(dumpProfiler->dumpToFile()); +} + +TEST_F(ProfilerTests, DumpToFileReturnsFalseWhenEnvMissing) +{ + EXPECT_FALSE(profiler->dumpToFile()); +} diff --git a/tests/unittests/media/server/gstplayer/CMakeLists.txt b/tests/unittests/media/server/gstplayer/CMakeLists.txt index 00a647f56..5bfdf7f05 100644 --- a/tests/unittests/media/server/gstplayer/CMakeLists.txt +++ b/tests/unittests/media/server/gstplayer/CMakeLists.txt @@ -121,6 +121,9 @@ add_gtests(RialtoServerGstPlayerUnitTests #FlushWatcher unittests flushWatcher/FlushWatcherTests.cpp + #GstProfiler unittests + profiler/GstProfilerTests.cpp + ) target_include_directories(RialtoServerGstPlayerUnitTests diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/CreateTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/CreateTest.cpp index c817e4962..a48656c62 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/CreateTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/CreateTest.cpp @@ -51,6 +51,12 @@ class RialtoServerCreateGstGenericPlayerTest : public GstGenericPlayerTestCommon GParamSpec m_rectangleSpec{}; const bool m_kIsLive{false}; + void expectCreateProfiler() + { + EXPECT_CALL(*m_gstProfilerFactoryMock, createGstProfiler(&m_pipeline, _, _)) + .WillOnce(Return(ByMove(std::move(m_gstProfiler)))); + } + void expectCreatePipeline() { EXPECT_CALL(m_gstInitialiserMock, waitForInitialisation()); @@ -60,6 +66,7 @@ class RialtoServerCreateGstGenericPlayerTest : public GstGenericPlayerTestCommon expectSetSignalCallbacks(); expectSetUri(); expectCheckPlaySink(); + expectCreateProfiler(); EXPECT_CALL(*m_gstSrcMock, initSrc()); EXPECT_CALL(m_workerThreadFactoryMock, createWorkerThread()).WillOnce(Return(ByMove(std::move(workerThread)))); @@ -76,8 +83,8 @@ class RialtoServerCreateGstGenericPlayerTest : public GstGenericPlayerTestCommon m_videoReq, m_kIsLive, m_gstWrapperMock, m_glibWrapperMock, m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, std::move(m_flushWatcher), m_gstSrcFactoryMock, - m_timerFactoryMock, std::move(m_taskFactory), - std::move(workerThreadFactory), + m_gstProfilerFactoryMock, m_timerFactoryMock, + std::move(m_taskFactory), std::move(workerThreadFactory), std::move(gstDispatcherThreadFactory), m_gstProtectionMetadataFactoryMock)); EXPECT_NE(m_gstPlayer, nullptr); @@ -147,6 +154,7 @@ TEST_F(RialtoServerCreateGstGenericPlayerTest, FactoryCreatesObject) expectCheckPlaySink(); EXPECT_CALL(*m_gstWrapperMock, gstElementSetState(&m_pipeline, GST_STATE_READY)) .WillOnce(Return(GST_STATE_CHANGE_SUCCESS)); + EXPECT_CALL(*m_gstWrapperMock, gstObjectRef(&m_pipeline)).WillOnce(Return(&m_pipeline)); std::shared_ptr factory = firebolt::rialto::server::IGstGenericPlayerFactory::getFactory(); @@ -158,7 +166,7 @@ TEST_F(RialtoServerCreateGstGenericPlayerTest, FactoryCreatesObject) // Destroy expectations EXPECT_CALL(*m_gstWrapperMock, gstBusSetSyncHandler(nullptr, nullptr, nullptr, nullptr)); EXPECT_CALL(*m_gstWrapperMock, gstElementSetState(_, GST_STATE_NULL)).WillOnce(Return(GST_STATE_CHANGE_SUCCESS)); - EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(_)).Times(2); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(_)).Times(3); player.reset(); // Cleanup @@ -341,8 +349,9 @@ TEST_F(RialtoServerCreateGstGenericPlayerTest, CreateWesterossinkFailsCreateCont m_videoReq, m_kIsLive, m_gstWrapperMock, m_glibWrapperMock, m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, std::move(m_flushWatcher), - m_gstSrcFactoryMock, m_timerFactoryMock, - std::move(m_taskFactory), std::move(workerThreadFactory), + m_gstSrcFactoryMock, m_gstProfilerFactoryMock, + m_timerFactoryMock, std::move(m_taskFactory), + std::move(workerThreadFactory), std::move(gstDispatcherThreadFactory), m_gstProtectionMetadataFactoryMock), std::runtime_error); @@ -359,8 +368,8 @@ TEST_F(RialtoServerCreateGstGenericPlayerTest, GstSrcFactoryNull) m_videoReq, m_kIsLive, m_gstWrapperMock, m_glibWrapperMock, m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, std::move(m_flushWatcher), - nullptr, m_timerFactoryMock, std::move(m_taskFactory), - std::move(workerThreadFactory), + nullptr, m_gstProfilerFactoryMock, m_timerFactoryMock, + std::move(m_taskFactory), std::move(workerThreadFactory), std::move(gstDispatcherThreadFactory), m_gstProtectionMetadataFactoryMock), std::runtime_error); @@ -379,7 +388,8 @@ TEST_F(RialtoServerCreateGstGenericPlayerTest, TimerFactoryFails) m_videoReq, m_kIsLive, m_gstWrapperMock, m_glibWrapperMock, m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, std::move(m_flushWatcher), - m_gstSrcFactoryMock, nullptr, std::move(m_taskFactory), + m_gstSrcFactoryMock, m_gstProfilerFactoryMock, + nullptr, std::move(m_taskFactory), std::move(workerThreadFactory), std::move(gstDispatcherThreadFactory), m_gstProtectionMetadataFactoryMock), @@ -399,8 +409,9 @@ TEST_F(RialtoServerCreateGstGenericPlayerTest, GstSrcFactoryFails) m_videoReq, m_kIsLive, m_gstWrapperMock, m_glibWrapperMock, m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, std::move(m_flushWatcher), - m_gstSrcFactoryMock, m_timerFactoryMock, - std::move(m_taskFactory), std::move(workerThreadFactory), + m_gstSrcFactoryMock, m_gstProfilerFactoryMock, + m_timerFactoryMock, std::move(m_taskFactory), + std::move(workerThreadFactory), std::move(gstDispatcherThreadFactory), m_gstProtectionMetadataFactoryMock), std::runtime_error); @@ -421,15 +432,15 @@ TEST_F(RialtoServerCreateGstGenericPlayerTest, UnknownMediaType) EXPECT_CALL(*m_gstProtectionMetadataFactoryMock, createProtectionMetadataWrapper(_)) .WillOnce(Return(ByMove(std::move(m_gstProtectionMetadataWrapper)))); - EXPECT_THROW(m_gstPlayer = std::make_unique(&m_gstPlayerClient, m_decryptionServiceMock, - MediaType::UNKNOWN, m_videoReq, m_kIsLive, - m_gstWrapperMock, m_glibWrapperMock, - m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, - std::move(m_flushWatcher), m_gstSrcFactoryMock, - m_timerFactoryMock, std::move(m_taskFactory), - std::move(workerThreadFactory), - std::move(gstDispatcherThreadFactory), - m_gstProtectionMetadataFactoryMock), + EXPECT_THROW(m_gstPlayer = + std::make_unique(&m_gstPlayerClient, m_decryptionServiceMock, MediaType::UNKNOWN, + m_videoReq, m_kIsLive, m_gstWrapperMock, m_glibWrapperMock, + m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, + std::move(m_flushWatcher), m_gstSrcFactoryMock, + m_gstProfilerFactoryMock, m_timerFactoryMock, + std::move(m_taskFactory), std::move(workerThreadFactory), + std::move(gstDispatcherThreadFactory), + m_gstProtectionMetadataFactoryMock), std::runtime_error); EXPECT_EQ(m_gstPlayer, nullptr); } @@ -448,6 +459,7 @@ TEST_F(RialtoServerCreateGstGenericPlayerTest, PlaysinkNotFound) expectSetUri(); expectSetMessageCallback(); + expectCreateProfiler(); EXPECT_CALL(*m_gstSrcMock, initSrc()); EXPECT_CALL(m_workerThreadFactoryMock, createWorkerThread()).WillOnce(Return(ByMove(std::move(workerThread)))); @@ -463,7 +475,7 @@ TEST_F(RialtoServerCreateGstGenericPlayerTest, PlaysinkNotFound) std::make_unique(&m_gstPlayerClient, m_decryptionServiceMock, m_type, m_videoReq, m_kIsLive, m_gstWrapperMock, m_glibWrapperMock, m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, std::move(m_flushWatcher), m_gstSrcFactoryMock, - m_timerFactoryMock, std::move(m_taskFactory), + m_gstProfilerFactoryMock, m_timerFactoryMock, std::move(m_taskFactory), std::move(workerThreadFactory), std::move(gstDispatcherThreadFactory), m_gstProtectionMetadataFactoryMock)); EXPECT_NE(m_gstPlayer, nullptr); @@ -486,6 +498,7 @@ TEST_F(RialtoServerCreateGstGenericPlayerTest, SetNativeAudioForBrcmAudioSink) expectSetUri(); expectCheckPlaySink(); expectSetMessageCallback(); + expectCreateProfiler(); EXPECT_CALL(*m_gstWrapperMock, gstElementSetState(&m_pipeline, GST_STATE_READY)) .WillOnce(Return(GST_STATE_CHANGE_SUCCESS)); @@ -499,7 +512,7 @@ TEST_F(RialtoServerCreateGstGenericPlayerTest, SetNativeAudioForBrcmAudioSink) std::make_unique(&m_gstPlayerClient, m_decryptionServiceMock, m_type, m_videoReq, m_kIsLive, m_gstWrapperMock, m_glibWrapperMock, m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, std::move(m_flushWatcher), m_gstSrcFactoryMock, - m_timerFactoryMock, std::move(m_taskFactory), + m_gstProfilerFactoryMock, m_timerFactoryMock, std::move(m_taskFactory), std::move(workerThreadFactory), std::move(gstDispatcherThreadFactory), m_gstProtectionMetadataFactoryMock)); EXPECT_NE(m_gstPlayer, nullptr); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstDispatcherThreadClientTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstDispatcherThreadClientTest.cpp index e5c8610b2..f0f455fad 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstDispatcherThreadClientTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstDispatcherThreadClientTest.cpp @@ -41,9 +41,9 @@ class GstDispatcherThreadClientTest : public GstGenericPlayerTestCommon m_sut = std::make_unique(&m_gstPlayerClient, m_decryptionServiceMock, MediaType::MSE, m_videoReq, m_kIsLive, m_gstWrapperMock, m_glibWrapperMock, m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, - std::move(m_flushWatcher), m_gstSrcFactoryMock, m_timerFactoryMock, - std::move(m_taskFactory), std::move(workerThreadFactory), - std::move(gstDispatcherThreadFactory), + std::move(m_flushWatcher), m_gstSrcFactoryMock, + m_gstProfilerFactoryMock, m_timerFactoryMock, std::move(m_taskFactory), + std::move(workerThreadFactory), std::move(gstDispatcherThreadFactory), m_gstProtectionMetadataFactoryMock); } diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp index ba97f60b1..5e34f24b6 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp @@ -121,9 +121,9 @@ class GstGenericPlayerPrivateTest : public GstGenericPlayerTestCommon m_sut = std::make_unique(&m_gstPlayerClient, m_decryptionServiceMock, MediaType::MSE, m_videoReq, kIsLive, m_gstWrapperMock, m_glibWrapperMock, m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, - std::move(m_flushWatcher), m_gstSrcFactoryMock, m_timerFactoryMock, - std::move(m_taskFactory), std::move(workerThreadFactory), - std::move(gstDispatcherThreadFactory), + std::move(m_flushWatcher), m_gstSrcFactoryMock, + m_gstProfilerFactoryMock, m_timerFactoryMock, std::move(m_taskFactory), + std::move(workerThreadFactory), std::move(gstDispatcherThreadFactory), m_gstProtectionMetadataFactoryMock); m_realElement = initRealElement(); } diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp index fdb8defe2..2352dacf9 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp @@ -55,9 +55,9 @@ class GstGenericPlayerTest : public GstGenericPlayerTestCommon m_sut = std::make_unique(&m_gstPlayerClient, m_decryptionServiceMock, MediaType::MSE, m_videoReq, m_isLive, m_gstWrapperMock, m_glibWrapperMock, m_rdkGstreamerUtilsWrapperMock, m_gstInitialiserMock, - std::move(m_flushWatcher), m_gstSrcFactoryMock, m_timerFactoryMock, - std::move(m_taskFactory), std::move(workerThreadFactory), - std::move(gstDispatcherThreadFactory), + std::move(m_flushWatcher), m_gstSrcFactoryMock, + m_gstProfilerFactoryMock, m_timerFactoryMock, std::move(m_taskFactory), + std::move(workerThreadFactory), std::move(gstDispatcherThreadFactory), m_gstProtectionMetadataFactoryMock); m_element = fakeElement(); } diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp index d82e8be35..c7a6f39e6 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp @@ -270,6 +270,7 @@ GenericTasksTestsBase::GenericTasksTestsBase() testContext->m_context.gstSrc = testContext->m_gstSrc; testContext->m_context.source = testContext->m_element; testContext->m_context.decryptionService = testContext->m_decryptionServiceMock.get(); + testContext->m_context.gstProfiler = std::move(testContext->m_gstProfilerMock); } GenericTasksTestsBase::~GenericTasksTestsBase() diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsContext.h b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsContext.h index e51d1095e..4918f723c 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsContext.h +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsContext.h @@ -26,6 +26,7 @@ #include "GlibWrapperMock.h" #include "GstGenericPlayerClientMock.h" #include "GstGenericPlayerPrivateMock.h" +#include "GstProfilerMock.h" #include "GstSrcMock.h" #include "GstTextTrackSinkFactoryMock.h" #include "GstWrapperMock.h" @@ -61,6 +62,8 @@ class GenericTasksTestsContext std::make_shared>()}; std::shared_ptr m_gstTextTrackSinkFactoryMock{ std::make_shared>()}; + std::unique_ptr<::testing::NiceMock> m_gstProfilerMock{ + std::make_unique<::testing::NiceMock>()}; // Gstreamer members GstElement *m_element{}; diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp index c4d5d2741..3384ef99e 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp @@ -44,6 +44,8 @@ void GstGenericPlayerTestCommon::gstPlayerWillBeCreated() EXPECT_CALL(*m_gstWrapperMock, gstElementSetState(&m_pipeline, GST_STATE_READY)) .WillOnce(Return(GST_STATE_CHANGE_SUCCESS)); EXPECT_CALL(*m_gstSrcMock, initSrc()); + EXPECT_CALL(*m_gstProfilerFactoryMock, createGstProfiler(&m_pipeline, _, _)) + .WillOnce(Return(ByMove(std::move(m_gstProfiler)))); EXPECT_CALL(m_workerThreadFactoryMock, createWorkerThread()).WillOnce(Return(ByMove(std::move(workerThread)))); EXPECT_CALL(*m_gstProtectionMetadataFactoryMock, createProtectionMetadataWrapper(_)) .WillOnce(Return(ByMove(std::move(m_gstProtectionMetadataWrapper)))); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h index d8e72efaf..6b3c97c2d 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h @@ -29,6 +29,8 @@ #include "GstGenericPlayer.h" #include "GstGenericPlayerClientMock.h" #include "GstInitialiserMock.h" +#include "GstProfilerFactoryMock.h" +#include "GstProfilerMock.h" #include "GstProtectionMetadataHelperFactoryMock.h" #include "GstProtectionMetadataHelperMock.h" #include "GstSrcFactoryMock.h" @@ -47,6 +49,7 @@ using namespace firebolt::rialto; using namespace firebolt::rialto::server; using namespace firebolt::rialto::wrappers; +using ::testing::NiceMock; using ::testing::StrictMock; namespace @@ -75,6 +78,10 @@ class GstGenericPlayerTestCommon : public ::testing::Test std::make_shared>()}; std::shared_ptr> m_gstSrcFactoryMock{std::make_shared>()}; std::shared_ptr> m_gstSrcMock{std::make_shared>()}; + std::shared_ptr> m_gstProfilerFactoryMock{ + std::make_shared>()}; + std::unique_ptr> m_gstProfiler{std::make_unique>()}; + NiceMock *m_gstProfilerMock{m_gstProfiler.get()}; std::shared_ptr> m_timerFactoryMock{std::make_shared>()}; std::unique_ptr m_taskFactory{std::make_unique>()}; StrictMock &m_taskFactoryMock{ diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/HandleBusMessageTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/HandleBusMessageTest.cpp index 783c88cd1..cf449908b 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/HandleBusMessageTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/HandleBusMessageTest.cpp @@ -23,6 +23,7 @@ #include "GlibWrapperMock.h" #include "GstGenericPlayerClientMock.h" #include "GstGenericPlayerPrivateMock.h" +#include "GstProfilerMock.h" #include "GstWrapperMock.h" #include "Matchers.h" #include @@ -56,6 +57,8 @@ class HandleBusMessageTest : public testing::Test std::shared_ptr m_glibWrapper{ std::make_shared>()}; StrictMock m_flushWatcherMock; + std::unique_ptr<::testing::NiceMock> m_gstProfilerMock{ + std::make_unique<::testing::NiceMock>()}; GstElement m_pipeline{}; GstAppSrc m_audioSrc{}; GstAppSrc m_videoSrc{}; @@ -72,6 +75,7 @@ class HandleBusMessageTest : public testing::Test m_context.pipeline = &m_pipeline; m_context.streamInfo.emplace(firebolt::rialto::MediaSourceType::AUDIO, GST_ELEMENT(&m_audioSrc)); m_context.streamInfo.emplace(firebolt::rialto::MediaSourceType::VIDEO, GST_ELEMENT(&m_videoSrc)); + m_context.gstProfiler = std::move(m_gstProfilerMock); } void expectFreeErrorMessage() @@ -227,8 +231,8 @@ TEST_F(HandleBusMessageTest, shouldNotHandleStateChangedMessageWhenGstPlayerClie EXPECT_CALL(*m_gstWrapper, gstMessageParseStateChanged(&m_message, _, _, _)) .WillRepeatedly(DoAll(SetArgPointee<1>(oldState), SetArgPointee<2>(newState), SetArgPointee<3>(pending))); - EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(oldState)).Times(2).WillRepeatedly(Return("Ready")); - EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(2).WillRepeatedly(Return("Null")); + EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(oldState)).Times(1).WillRepeatedly(Return("Ready")); + EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(1).WillRepeatedly(Return("Null")); EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(pending)).WillOnce(Return("Void")); EXPECT_CALL(*m_gstWrapper, gstDebugBinToDotFileWithTs(GST_BIN(&m_pipeline), _, _)); EXPECT_CALL(*m_gstWrapper, gstMessageUnref(&m_message)); @@ -250,8 +254,8 @@ TEST_F(HandleBusMessageTest, shouldHandleStateChangedToNullMessage) EXPECT_CALL(*m_gstWrapper, gstMessageParseStateChanged(&m_message, _, _, _)) .WillRepeatedly(DoAll(SetArgPointee<1>(oldState), SetArgPointee<2>(newState), SetArgPointee<3>(pending))); - EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(oldState)).Times(2).WillRepeatedly(Return("Ready")); - EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(2).WillRepeatedly(Return("Null")); + EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(oldState)).Times(1).WillRepeatedly(Return("Ready")); + EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(1).WillRepeatedly(Return("Null")); EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(pending)).WillOnce(Return("Void")); EXPECT_CALL(*m_gstWrapper, gstDebugBinToDotFileWithTs(GST_BIN(&m_pipeline), _, _)); EXPECT_CALL(m_gstPlayerClient, notifyPlaybackState(firebolt::rialto::PlaybackState::STOPPED)); @@ -276,8 +280,8 @@ TEST_F(HandleBusMessageTest, shouldHandleStateChangedToPausedMessage) EXPECT_CALL(*m_gstWrapper, gstMessageParseStateChanged(&m_message, _, _, _)) .WillRepeatedly(DoAll(SetArgPointee<1>(oldState), SetArgPointee<2>(newState), SetArgPointee<3>(pending))); EXPECT_CALL(m_gstPlayer, hasSourceType(firebolt::rialto::MediaSourceType::SUBTITLE)).WillOnce(Return(false)); - EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(oldState)).Times(2).WillRepeatedly(Return("Ready")); - EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(2).WillRepeatedly(Return("Paused")); + EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(oldState)).Times(1).WillRepeatedly(Return("Ready")); + EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(1).WillRepeatedly(Return("Paused")); EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(pending)).WillOnce(Return("Void")); EXPECT_CALL(*m_gstWrapper, gstDebugBinToDotFileWithTs(GST_BIN(&m_pipeline), _, _)); EXPECT_CALL(m_gstPlayerClient, notifyPlaybackState(firebolt::rialto::PlaybackState::PAUSED)); @@ -306,8 +310,8 @@ TEST_F(HandleBusMessageTest, shouldHandleStateChangedToPausedMessageWhenSyncFlus EXPECT_CALL(*m_gstWrapper, gstMessageParseStateChanged(&m_message, _, _, _)) .WillRepeatedly(DoAll(SetArgPointee<1>(oldState), SetArgPointee<2>(newState), SetArgPointee<3>(pending))); EXPECT_CALL(m_gstPlayer, hasSourceType(firebolt::rialto::MediaSourceType::SUBTITLE)).WillOnce(Return(false)); - EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(oldState)).Times(2).WillRepeatedly(Return("Ready")); - EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(2).WillRepeatedly(Return("Paused")); + EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(oldState)).Times(1).WillRepeatedly(Return("Ready")); + EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(1).WillRepeatedly(Return("Paused")); EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(pending)).WillOnce(Return("Void")); EXPECT_CALL(*m_gstWrapper, gstDebugBinToDotFileWithTs(GST_BIN(&m_pipeline), _, _)); EXPECT_CALL(m_gstPlayerClient, notifyPlaybackState(firebolt::rialto::PlaybackState::PAUSED)); @@ -335,8 +339,8 @@ TEST_F(HandleBusMessageTest, shouldSkipHandlingStateChangedToPausedMessageWhenAs EXPECT_CALL(*m_gstWrapper, gstMessageParseStateChanged(&m_message, _, _, _)) .WillRepeatedly(DoAll(SetArgPointee<1>(oldState), SetArgPointee<2>(newState), SetArgPointee<3>(pending))); - EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(oldState)).Times(2).WillRepeatedly(Return("Ready")); - EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(2).WillRepeatedly(Return("Paused")); + EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(oldState)).Times(1).WillRepeatedly(Return("Ready")); + EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(1).WillRepeatedly(Return("Paused")); EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(pending)).WillOnce(Return("Void")); EXPECT_CALL(*m_gstWrapper, gstDebugBinToDotFileWithTs(GST_BIN(&m_pipeline), _, _)); EXPECT_CALL(m_gstPlayer, stopPositionReportingAndCheckAudioUnderflowTimer()); @@ -363,8 +367,8 @@ TEST_F(HandleBusMessageTest, shouldSkipHandlingStateChangedToPausedMessageWhenAs EXPECT_CALL(*m_gstWrapper, gstMessageParseStateChanged(&m_message, _, _, _)) .WillRepeatedly(DoAll(SetArgPointee<1>(oldState), SetArgPointee<2>(newState), SetArgPointee<3>(pending))); - EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(oldState)).Times(2).WillRepeatedly(Return("Ready")); - EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(2).WillRepeatedly(Return("Paused")); + EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(oldState)).Times(1).WillRepeatedly(Return("Ready")); + EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(1).WillRepeatedly(Return("Paused")); EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(pending)).WillOnce(Return("Void")); EXPECT_CALL(*m_gstWrapper, gstDebugBinToDotFileWithTs(GST_BIN(&m_pipeline), _, _)); EXPECT_CALL(m_gstPlayer, stopPositionReportingAndCheckAudioUnderflowTimer()); @@ -416,8 +420,8 @@ TEST_F(HandleBusMessageTest, shouldHandleStateChangedToPlayingMessage) EXPECT_CALL(*m_gstWrapper, gstMessageParseStateChanged(&m_message, _, _, _)) .WillRepeatedly(DoAll(SetArgPointee<1>(oldState), SetArgPointee<2>(newState), SetArgPointee<3>(pending))); EXPECT_CALL(m_gstPlayer, hasSourceType(firebolt::rialto::MediaSourceType::SUBTITLE)).WillOnce(Return(false)); - EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(oldState)).Times(2).WillRepeatedly(Return("Ready")); - EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(2).WillRepeatedly(Return("Playing")); + EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(oldState)).Times(1).WillRepeatedly(Return("Ready")); + EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(1).WillRepeatedly(Return("Playing")); EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(pending)).WillOnce(Return("Void")); EXPECT_CALL(*m_gstWrapper, gstDebugBinToDotFileWithTs(GST_BIN(&m_pipeline), _, _)); EXPECT_CALL(m_gstPlayer, startPositionReportingAndCheckAudioUnderflowTimer()); @@ -448,8 +452,8 @@ TEST_F(HandleBusMessageTest, shouldHandleStateChangedToPlayingMessageWhenSyncFlu EXPECT_CALL(*m_gstWrapper, gstMessageParseStateChanged(&m_message, _, _, _)) .WillRepeatedly(DoAll(SetArgPointee<1>(oldState), SetArgPointee<2>(newState), SetArgPointee<3>(pending))); EXPECT_CALL(m_gstPlayer, hasSourceType(firebolt::rialto::MediaSourceType::SUBTITLE)).WillOnce(Return(false)); - EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(oldState)).Times(2).WillRepeatedly(Return("Ready")); - EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(2).WillRepeatedly(Return("Playing")); + EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(oldState)).Times(1).WillRepeatedly(Return("Ready")); + EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(1).WillRepeatedly(Return("Playing")); EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(pending)).WillOnce(Return("Void")); EXPECT_CALL(*m_gstWrapper, gstDebugBinToDotFileWithTs(GST_BIN(&m_pipeline), _, _)); EXPECT_CALL(m_gstPlayer, startPositionReportingAndCheckAudioUnderflowTimer()); @@ -479,8 +483,8 @@ TEST_F(HandleBusMessageTest, shouldSkipHandlingStateChangedToPlayingMessageWhenA EXPECT_CALL(*m_gstWrapper, gstMessageParseStateChanged(&m_message, _, _, _)) .WillRepeatedly(DoAll(SetArgPointee<1>(oldState), SetArgPointee<2>(newState), SetArgPointee<3>(pending))); - EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(oldState)).Times(2).WillRepeatedly(Return("Ready")); - EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(2).WillRepeatedly(Return("Playing")); + EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(oldState)).Times(1).WillRepeatedly(Return("Ready")); + EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(1).WillRepeatedly(Return("Playing")); EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(pending)).WillOnce(Return("Void")); EXPECT_CALL(*m_gstWrapper, gstDebugBinToDotFileWithTs(GST_BIN(&m_pipeline), _, _)); EXPECT_CALL(*m_gstWrapper, gstMessageUnref(&m_message)); @@ -506,8 +510,8 @@ TEST_F(HandleBusMessageTest, shouldSkipHandlingStateChangedToPlayingMessageWhenA EXPECT_CALL(*m_gstWrapper, gstMessageParseStateChanged(&m_message, _, _, _)) .WillRepeatedly(DoAll(SetArgPointee<1>(oldState), SetArgPointee<2>(newState), SetArgPointee<3>(pending))); - EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(oldState)).Times(2).WillRepeatedly(Return("Ready")); - EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(2).WillRepeatedly(Return("Playing")); + EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(oldState)).Times(1).WillRepeatedly(Return("Ready")); + EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(1).WillRepeatedly(Return("Playing")); EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(pending)).WillOnce(Return("Void")); EXPECT_CALL(*m_gstWrapper, gstDebugBinToDotFileWithTs(GST_BIN(&m_pipeline), _, _)); EXPECT_CALL(*m_gstWrapper, gstMessageUnref(&m_message)); @@ -532,8 +536,8 @@ TEST_F(HandleBusMessageTest, shouldHandleStateChangedToPlayingMessageAndSetPendi EXPECT_CALL(*m_gstWrapper, gstMessageParseStateChanged(&m_message, _, _, _)) .WillRepeatedly(DoAll(SetArgPointee<1>(oldState), SetArgPointee<2>(newState), SetArgPointee<3>(pending))); EXPECT_CALL(m_gstPlayer, hasSourceType(firebolt::rialto::MediaSourceType::SUBTITLE)).WillOnce(Return(false)); - EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(oldState)).Times(2).WillRepeatedly(Return("Ready")); - EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(2).WillRepeatedly(Return("Playing")); + EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(oldState)).Times(1).WillRepeatedly(Return("Ready")); + EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(newState)).Times(1).WillRepeatedly(Return("Playing")); EXPECT_CALL(*m_gstWrapper, gstElementStateGetName(pending)).WillOnce(Return("Void")); EXPECT_CALL(*m_gstWrapper, gstDebugBinToDotFileWithTs(GST_BIN(&m_pipeline), _, _)); EXPECT_CALL(m_gstPlayer, startPositionReportingAndCheckAudioUnderflowTimer()); diff --git a/tests/unittests/media/server/gstplayer/profiler/GstProfilerTests.cpp b/tests/unittests/media/server/gstplayer/profiler/GstProfilerTests.cpp new file mode 100644 index 000000000..0a31abe16 --- /dev/null +++ b/tests/unittests/media/server/gstplayer/profiler/GstProfilerTests.cpp @@ -0,0 +1,563 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GlibWrapperMock.h" +#include "GstWrapperMock.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GstProfiler.h" + +using namespace firebolt::rialto::server; +using namespace firebolt::rialto::wrappers; + +using ::testing::_; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::ReturnNull; +using ::testing::StrEq; +using ::testing::StrictMock; + +class GstProfilerTests : public ::testing::Test +{ +protected: + std::shared_ptr> m_gstWrapperMock; + std::shared_ptr> m_glibWrapperMock; + std::unique_ptr m_gstProfiler; + std::optional m_profilerEnabledEnv; + std::optional m_profilerDumpFileNameEnv; + GstElement *m_realElement{nullptr}; + + GstElement m_pipeline = {}; + GstElement m_element = {}; + GstPad m_pad = {}; + + void SetUp() override + { + saveEnv("PROFILER_ENABLED", m_profilerEnabledEnv); + saveEnv("PROFILER_DUMP_FILE_NAME", m_profilerDumpFileNameEnv); + + m_gstWrapperMock = std::make_shared>(); + m_glibWrapperMock = std::make_shared>(); + + gst_init(nullptr, nullptr); + m_realElement = gst_element_factory_make("fakesrc", "profiler-test-source"); + ASSERT_NE(m_realElement, nullptr); + } + + void TearDown() override + { + if (m_realElement) + { + gst_object_unref(m_realElement); + m_realElement = nullptr; + } + + if (m_profilerEnabledEnv) + setenv("PROFILER_ENABLED", m_profilerEnabledEnv->c_str(), 1); + else + unsetenv("PROFILER_ENABLED"); + + if (m_profilerDumpFileNameEnv) + setenv("PROFILER_DUMP_FILE_NAME", m_profilerDumpFileNameEnv->c_str(), 1); + else + unsetenv("PROFILER_DUMP_FILE_NAME"); + } + + void setProfilerEnabledEnv(bool enabled) { setenv("PROFILER_ENABLED", enabled ? "true" : "false", 1); } + void setProfilerDumpFileEnv(const std::string &path) { setenv("PROFILER_DUMP_FILE_NAME", path.c_str(), 1); } + + static void saveEnv(const char *name, std::optional &value) + { + if (const char *env = std::getenv(name)) + value = env; + else + value = std::nullopt; + } + + void createGstProfiler(GstElement *pipeline = nullptr) + { + const char *profilerEnabledEnv = std::getenv("PROFILER_ENABLED"); + const bool expectPipelineRef = pipeline && profilerEnabledEnv && std::string_view{profilerEnabledEnv} == "true"; + if (expectPipelineRef) + { + EXPECT_CALL(*m_gstWrapperMock, gstObjectRef(pipeline)); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(pipeline)); + } + + EXPECT_NO_THROW(m_gstProfiler = std::make_unique(pipeline, m_gstWrapperMock, m_glibWrapperMock)); + ASSERT_NE(m_gstProfiler, nullptr); + } + + void expectElementRecognizedAsSource(GstElement *element) + { + EXPECT_CALL(*m_gstWrapperMock, gstElementClassGetMetadata(_, _)).WillOnce(Return("Source")); + + EXPECT_CALL(*m_glibWrapperMock, gStrrstr(_, _)).WillOnce(Return(const_cast("Source"))); + } +}; + +/** + * Test that GstProfiler can be created with null pipeline. + */ +TEST_F(GstProfilerTests, CreateWithNullPipeline) +{ + setProfilerEnabledEnv(true); + createGstProfiler(); + EXPECT_TRUE(m_gstProfiler->createRecord("CreateWithNullPipeline").has_value()); +} + +/** + * Test that GstProfiler can be created with valid pipeline. + */ +TEST_F(GstProfilerTests, CreateWithPipeline) +{ + setProfilerEnabledEnv(true); + createGstProfiler(&m_pipeline); + EXPECT_TRUE(m_gstProfiler->createRecord("CreateWithPipeline").has_value()); +} + +/** + * Test that GstProfiler can create a record with stage only. + */ +TEST_F(GstProfilerTests, CreateRecordStageOnly) +{ + setProfilerEnabledEnv(true); + createGstProfiler(); + + EXPECT_NO_THROW({ [[maybe_unused]] const auto id = m_gstProfiler->createRecord("PipelineCreated"); }); +} + +/** + * Test that GstProfiler can create a record with stage and info. + */ +TEST_F(GstProfilerTests, CreateRecordStageAndInfo) +{ + setProfilerEnabledEnv(true); + createGstProfiler(); + + EXPECT_NO_THROW({ [[maybe_unused]] const auto id = m_gstProfiler->createRecord("SourceAttached", "video"); }); +} + +/** + * Test that GstProfiler can log a record. + */ +TEST_F(GstProfilerTests, LogRecord) +{ + setProfilerEnabledEnv(true); + createGstProfiler(); + + EXPECT_NO_THROW(m_gstProfiler->logRecord(1)); +} + +TEST_F(GstProfilerTests, DumpToFileCreatesAndAppendsDump) +{ + setProfilerEnabledEnv(true); + const auto suffix = std::to_string(std::random_device{}()); + const std::string path = std::string{"/tmp/rialto_gst_profiler_ut_dump_"} + suffix + ".txt"; + setProfilerDumpFileEnv(path); + createGstProfiler(); + + ASSERT_TRUE(m_gstProfiler->createRecord("Pipeline Created").has_value()); + ASSERT_TRUE(m_gstProfiler->createRecord("Pipeline Terminated").has_value()); + + EXPECT_NO_THROW(m_gstProfiler->dumpToFile()); + EXPECT_NO_THROW(m_gstProfiler->dumpToFile()); + + std::ifstream in(path); + ASSERT_TRUE(in.good()); + + const std::string content((std::istreambuf_iterator(in)), std::istreambuf_iterator()); + EXPECT_NE(content.find("Pipeline Created"), std::string::npos); + EXPECT_NE(content.find("Pipeline Terminated"), std::string::npos); + EXPECT_NE(content.find("Pipeline Created"), content.rfind("Pipeline Created")); + + std::remove(path.c_str()); +} + +TEST_F(GstProfilerTests, DumpToFileUsesCachedEnvValue) +{ + setProfilerEnabledEnv(true); + const auto suffix = std::to_string(std::random_device{}()); + const std::string path = std::string{"/tmp/rialto_gst_profiler_cached_dump_"} + suffix + ".txt"; + setProfilerDumpFileEnv(path); + createGstProfiler(); + + unsetenv("PROFILER_DUMP_FILE_NAME"); + ASSERT_TRUE(m_gstProfiler->createRecord("CachedDumpStage").has_value()); + + EXPECT_NO_THROW(m_gstProfiler->dumpToFile()); + + std::ifstream in(path); + ASSERT_TRUE(in.good()); + + const std::string content((std::istreambuf_iterator(in)), std::istreambuf_iterator()); + EXPECT_NE(content.find("CachedDumpStage"), std::string::npos); + + std::remove(path.c_str()); +} + +/** + * Test that GstProfiler can log pipeline metrics. + */ +TEST_F(GstProfilerTests, LogPipelineSummary) +{ + setProfilerEnabledEnv(true); + createGstProfiler(); + + EXPECT_NO_THROW(m_gstProfiler->logPipelineSummary()); +} + +/** + * Test that disabled profiler prevents record creation and scheduling. + */ +TEST_F(GstProfilerTests, DisabledProfilerPreventsRecordCreationAndScheduling) +{ + setProfilerEnabledEnv(false); + createGstProfiler(); + + EXPECT_FALSE(m_gstProfiler->createRecord("Pipeline Created").has_value()); + + EXPECT_NO_THROW(m_gstProfiler->scheduleGstElementRecord(m_realElement)); +} + +/** + * Test that enabled profiler allows scheduling path to proceed up to pad lookup. + */ +TEST_F(GstProfilerTests, EnabledProfilerAllowsScheduling) +{ + setProfilerEnabledEnv(true); + EXPECT_NO_THROW(m_gstProfiler = std::make_unique(nullptr, m_gstWrapperMock, m_glibWrapperMock)); + ASSERT_NE(m_gstProfiler, nullptr); + ASSERT_TRUE(m_gstProfiler->createRecord("EnabledProfilerAllowsScheduling").has_value()); + + expectElementRecognizedAsSource(m_realElement); + EXPECT_CALL(*m_gstWrapperMock, gstElementGetStaticPad(m_realElement, StrEq("src"))).WillOnce(Return(nullptr)); + + EXPECT_NO_THROW(m_gstProfiler->scheduleGstElementRecord(m_realElement)); +} + +/** + * Test that scheduling record for null element is safe. + */ +TEST_F(GstProfilerTests, ScheduleNullElementRecord) +{ + setProfilerEnabledEnv(true); + createGstProfiler(); + + EXPECT_NO_THROW(m_gstProfiler->scheduleGstElementRecord(nullptr)); +} + +/** + * Test that scheduling record for valid element is safe. + */ +TEST_F(GstProfilerTests, ScheduleElementRecord) +{ + setProfilerEnabledEnv(true); + createGstProfiler(); + + auto *factory = reinterpret_cast(0x1); + + EXPECT_CALL(*m_gstWrapperMock, gstElementGetFactory(m_realElement)).WillOnce(Return(factory)).WillOnce(Return(factory)); + EXPECT_CALL(*m_gstWrapperMock, gstElementClassGetMetadata(_, _)).WillOnce(Return("Source")); + EXPECT_CALL(*m_glibWrapperMock, gStrrstr(_, _)).WillOnce(Return(const_cast("Source"))); + + EXPECT_CALL(*m_gstWrapperMock, gstElementGetStaticPad(m_realElement, StrEq("src"))).WillOnce(Return(&m_pad)); + EXPECT_CALL(*m_gstWrapperMock, gstElementFactoryListIsType(_, GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO)) + .WillOnce(Return(false)); + EXPECT_CALL(*m_gstWrapperMock, gstElementFactoryListIsType(_, GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) + .WillOnce(Return(false)); + + gchar *rawName = g_strdup("videoDecoder"); + EXPECT_CALL(*m_gstWrapperMock, gstElementGetName(m_realElement)).WillOnce(Return(rawName)); + EXPECT_CALL(*m_glibWrapperMock, gFree(rawName)).WillOnce(Invoke([](gpointer ptr) { g_free(ptr); })); + + EXPECT_CALL(*m_gstWrapperMock, gstPadAddProbe(&m_pad, _, _, _, _)) + .WillOnce(Invoke( + [](GstPad *pad, GstPadProbeType mask, GstPadProbeCallback callback, gpointer userData, + GDestroyNotify destroyData) -> gulong + { + EXPECT_EQ(destroyData, nullptr); + return 1; + })); + + EXPECT_CALL(*m_gstWrapperMock, gstPadRemoveProbe(&m_pad, 1)); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(&m_pad)); + + EXPECT_NO_THROW(m_gstProfiler->scheduleGstElementRecord(m_realElement)); +} + +/** + * Test that scheduled probe creates record with normalized video info. + */ +TEST_F(GstProfilerTests, ScheduleElementRecordCreatesProbeRecordWithVideoInfo) +{ + setProfilerEnabledEnv(true); + createGstProfiler(); + + auto *factory = reinterpret_cast(0x1); + + EXPECT_CALL(*m_gstWrapperMock, gstElementGetFactory(m_realElement)).WillOnce(Return(factory)); + EXPECT_CALL(*m_gstWrapperMock, gstElementClassGetMetadata(_, _)).WillOnce(Return("Source")); + EXPECT_CALL(*m_glibWrapperMock, gStrrstr(_, _)).WillOnce(Return(const_cast("Source"))); + + EXPECT_CALL(*m_gstWrapperMock, gstElementGetStaticPad(m_realElement, StrEq("src"))).WillOnce(Return(&m_pad)); + EXPECT_CALL(*m_gstWrapperMock, gstElementFactoryListIsType(_, GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO)) + .WillOnce(Return(true)); + + GstPadProbeCallback callback = nullptr; + gpointer userData = nullptr; + EXPECT_CALL(*m_gstWrapperMock, gstPadAddProbe(&m_pad, _, _, _, _)) + .WillOnce(Invoke( + [&callback, &userData](GstPad *pad, GstPadProbeType mask, GstPadProbeCallback probeCallback, + gpointer probeUserData, GDestroyNotify destroyData) -> gulong + { + callback = probeCallback; + userData = probeUserData; + EXPECT_EQ(destroyData, nullptr); + + return 1; + })); + + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(&m_pad)); + + m_gstProfiler->scheduleGstElementRecord(m_realElement); + + ASSERT_NE(callback, nullptr); + ASSERT_NE(userData, nullptr); + GstBuffer *buffer = gst_buffer_new(); + GstPadProbeInfo info{}; + info.type = GST_PAD_PROBE_TYPE_BUFFER; + info.data = buffer; + + EXPECT_EQ(callback(&m_pad, &info, userData), GST_PAD_PROBE_REMOVE); + + gst_buffer_unref(buffer); + + const auto records = m_gstProfiler->getRecords(); + ASSERT_FALSE(records.empty()); + EXPECT_EQ(records.back().stage, "Source FB Exit"); + EXPECT_EQ(records.back().info, "Video"); +} + +/** + * Test that scheduled probe falls back to element name classification when media type helpers fail. + */ +TEST_F(GstProfilerTests, ScheduleElementRecordCreatesProbeRecordWithVideoInfoFromNameFallback) +{ + setProfilerEnabledEnv(true); + createGstProfiler(); + + auto *factory = reinterpret_cast(0x1); + + EXPECT_CALL(*m_gstWrapperMock, gstElementGetFactory(m_realElement)).WillOnce(Return(factory)).WillOnce(Return(factory)); + EXPECT_CALL(*m_gstWrapperMock, gstElementClassGetMetadata(_, _)).WillOnce(Return("Source")); + EXPECT_CALL(*m_glibWrapperMock, gStrrstr(_, _)).WillOnce(Return(const_cast("Source"))); + + EXPECT_CALL(*m_gstWrapperMock, gstElementGetStaticPad(m_realElement, StrEq("src"))).WillOnce(Return(&m_pad)); + EXPECT_CALL(*m_gstWrapperMock, gstElementFactoryListIsType(_, GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO)) + .WillOnce(Return(false)); + EXPECT_CALL(*m_gstWrapperMock, gstElementFactoryListIsType(_, GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) + .WillOnce(Return(false)); + + gchar *rawName = g_strdup("videoDecoder"); + EXPECT_CALL(*m_gstWrapperMock, gstElementGetName(m_realElement)).WillOnce(Return(rawName)); + EXPECT_CALL(*m_glibWrapperMock, gFree(rawName)).WillOnce(Invoke([](gpointer ptr) { g_free(ptr); })); + + GstPadProbeCallback callback = nullptr; + gpointer userData = nullptr; + EXPECT_CALL(*m_gstWrapperMock, gstPadAddProbe(&m_pad, _, _, _, _)) + .WillOnce(Invoke( + [&callback, &userData](GstPad *pad, GstPadProbeType mask, GstPadProbeCallback probeCallback, + gpointer probeUserData, GDestroyNotify destroyData) -> gulong + { + callback = probeCallback; + userData = probeUserData; + EXPECT_EQ(destroyData, nullptr); + + return 1; + })); + + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(&m_pad)); + + m_gstProfiler->scheduleGstElementRecord(m_realElement); + + ASSERT_NE(callback, nullptr); + ASSERT_NE(userData, nullptr); + GstBuffer *buffer = gst_buffer_new(); + GstPadProbeInfo info{}; + info.type = GST_PAD_PROBE_TYPE_BUFFER; + info.data = buffer; + + EXPECT_EQ(callback(&m_pad, &info, userData), GST_PAD_PROBE_REMOVE); + + gst_buffer_unref(buffer); + + const auto records = m_gstProfiler->getRecords(); + ASSERT_FALSE(records.empty()); + EXPECT_EQ(records.back().stage, "Source FB Exit"); + EXPECT_EQ(records.back().info, "Video"); +} + +/** + * Test that scheduled probe falls back to raw element name when media type cannot be classified. + */ +TEST_F(GstProfilerTests, ScheduleElementRecordFallsBackToRawElementName) +{ + setProfilerEnabledEnv(true); + createGstProfiler(); + + auto *factory = reinterpret_cast(0x1); + + EXPECT_CALL(*m_gstWrapperMock, gstElementGetFactory(m_realElement)).WillOnce(Return(factory)).WillOnce(Return(factory)); + EXPECT_CALL(*m_gstWrapperMock, gstElementClassGetMetadata(_, _)).WillOnce(Return("Source")); + EXPECT_CALL(*m_glibWrapperMock, gStrrstr(_, _)).WillOnce(Return(const_cast("Source"))); + + EXPECT_CALL(*m_gstWrapperMock, gstElementGetStaticPad(m_realElement, StrEq("src"))).WillOnce(Return(&m_pad)); + EXPECT_CALL(*m_gstWrapperMock, gstElementFactoryListIsType(_, GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO)) + .WillOnce(Return(false)); + EXPECT_CALL(*m_gstWrapperMock, gstElementFactoryListIsType(_, GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) + .WillOnce(Return(false)); + + gchar *rawName = g_strdup("custom-element"); + EXPECT_CALL(*m_gstWrapperMock, gstElementGetName(m_realElement)).WillOnce(Return(rawName)); + EXPECT_CALL(*m_glibWrapperMock, gFree(rawName)).WillOnce(Invoke([](gpointer ptr) { g_free(ptr); })); + + GstPadProbeCallback callback = nullptr; + gpointer userData = nullptr; + EXPECT_CALL(*m_gstWrapperMock, gstPadAddProbe(&m_pad, _, _, _, _)) + .WillOnce(Invoke( + [&callback, &userData](GstPad *pad, GstPadProbeType mask, GstPadProbeCallback probeCallback, + gpointer probeUserData, GDestroyNotify destroyData) -> gulong + { + callback = probeCallback; + userData = probeUserData; + EXPECT_EQ(destroyData, nullptr); + + return 1; + })); + + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(&m_pad)); + + m_gstProfiler->scheduleGstElementRecord(m_realElement); + + ASSERT_NE(callback, nullptr); + ASSERT_NE(userData, nullptr); + GstBuffer *buffer = gst_buffer_new(); + GstPadProbeInfo info{}; + info.type = GST_PAD_PROBE_TYPE_BUFFER; + info.data = buffer; + + EXPECT_EQ(callback(&m_pad, &info, userData), GST_PAD_PROBE_REMOVE); + + gst_buffer_unref(buffer); + + const auto records = m_gstProfiler->getRecords(); + ASSERT_FALSE(records.empty()); + EXPECT_EQ(records.back().stage, "Source FB Exit"); + EXPECT_EQ(records.back().info, "custom-element"); +} + +/** + * Test that scheduling record handles missing src pad. + */ +TEST_F(GstProfilerTests, ScheduleElementRecordNoPad) +{ + setProfilerEnabledEnv(true); + createGstProfiler(); + + expectElementRecognizedAsSource(m_realElement); + + EXPECT_CALL(*m_gstWrapperMock, gstElementGetStaticPad(m_realElement, StrEq("src"))).WillOnce(Return(nullptr)); + + EXPECT_NO_THROW(m_gstProfiler->scheduleGstElementRecord(m_realElement)); +} + +/** + * Test that scheduling record handles probe installation failure without leaking context ownership. + */ +TEST_F(GstProfilerTests, ScheduleElementRecordProbeAddFails) +{ + setProfilerEnabledEnv(true); + createGstProfiler(); + + auto *factory = reinterpret_cast(0x1); + + EXPECT_CALL(*m_gstWrapperMock, gstElementGetFactory(m_realElement)).WillOnce(Return(factory)).WillOnce(Return(factory)); + EXPECT_CALL(*m_gstWrapperMock, gstElementClassGetMetadata(_, _)).WillOnce(Return("Source")); + EXPECT_CALL(*m_glibWrapperMock, gStrrstr(_, _)).WillOnce(Return(const_cast("Source"))); + + EXPECT_CALL(*m_gstWrapperMock, gstElementGetStaticPad(m_realElement, StrEq("src"))).WillOnce(Return(&m_pad)); + EXPECT_CALL(*m_gstWrapperMock, gstElementFactoryListIsType(_, GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO)) + .WillOnce(Return(false)); + EXPECT_CALL(*m_gstWrapperMock, gstElementFactoryListIsType(_, GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) + .WillOnce(Return(false)); + + gchar *rawName = g_strdup("videoDecoder"); + EXPECT_CALL(*m_gstWrapperMock, gstElementGetName(m_realElement)).WillOnce(Return(rawName)); + EXPECT_CALL(*m_glibWrapperMock, gFree(rawName)).WillOnce(Invoke([](gpointer ptr) { g_free(ptr); })); + + EXPECT_CALL(*m_gstWrapperMock, gstPadAddProbe(&m_pad, _, _, _, _)).WillOnce(Return(0)); + EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(&m_pad)); + + EXPECT_NO_THROW(m_gstProfiler->scheduleGstElementRecord(m_realElement)); +} + +/** + * Test that GstProfiler can be destroyed after creation with pipeline. + */ +TEST_F(GstProfilerTests, DestroyAfterCreateWithPipeline) +{ + setProfilerEnabledEnv(true); + createGstProfiler(&m_pipeline); + + EXPECT_NO_THROW(m_gstProfiler.reset()); +} + +/** + * Test that public API can be called multiple times. + */ +TEST_F(GstProfilerTests, MultipleCalls) +{ + setProfilerEnabledEnv(true); + createGstProfiler(); + + expectElementRecognizedAsSource(m_realElement); + + EXPECT_CALL(*m_gstWrapperMock, gstElementGetStaticPad(m_realElement, StrEq("src"))).WillOnce(Return(nullptr)); + + EXPECT_NO_THROW({ + [[maybe_unused]] const auto id1 = m_gstProfiler->createRecord("PipelineCreated"); + [[maybe_unused]] const auto id2 = m_gstProfiler->createRecord("AllSourcesAttached", "audio+video"); + m_gstProfiler->logRecord(1); + m_gstProfiler->scheduleGstElementRecord(m_realElement); + m_gstProfiler->logPipelineSummary(); + }); +} diff --git a/tests/unittests/media/server/mocks/gstplayer/GstProfilerFactoryMock.h b/tests/unittests/media/server/mocks/gstplayer/GstProfilerFactoryMock.h new file mode 100644 index 000000000..f8357fd55 --- /dev/null +++ b/tests/unittests/media/server/mocks/gstplayer/GstProfilerFactoryMock.h @@ -0,0 +1,46 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIREBOLT_RIALTO_SERVER_GST_PROFILER_FACTORY_MOCK_H_ +#define FIREBOLT_RIALTO_SERVER_GST_PROFILER_FACTORY_MOCK_H_ + +#include "IGstProfiler.h" + +#include +#include + +namespace firebolt::rialto::server +{ +class GstProfilerFactoryMock : public IGstProfilerFactory +{ +public: + using IGstWrapper = firebolt::rialto::wrappers::IGstWrapper; + using IGlibWrapper = firebolt::rialto::wrappers::IGlibWrapper; + + GstProfilerFactoryMock() = default; + ~GstProfilerFactoryMock() override = default; + + MOCK_METHOD(std::unique_ptr, createGstProfiler, + (GstElement * pipeline, const std::shared_ptr &gstWrapper, + const std::shared_ptr &glibWrapper), + (const, override)); +}; +} // namespace firebolt::rialto::server + +#endif // FIREBOLT_RIALTO_SERVER_GST_PROFILER_FACTORY_MOCK_H_ diff --git a/tests/unittests/media/server/mocks/gstplayer/GstProfilerMock.h b/tests/unittests/media/server/mocks/gstplayer/GstProfilerMock.h new file mode 100644 index 000000000..ef549dbc2 --- /dev/null +++ b/tests/unittests/media/server/mocks/gstplayer/GstProfilerMock.h @@ -0,0 +1,50 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 Sky UK + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIREBOLT_RIALTO_SERVER_GST_PROFILER_MOCK_H_ +#define FIREBOLT_RIALTO_SERVER_GST_PROFILER_MOCK_H_ + +#include "IGstProfiler.h" + +#include +#include +#include +#include + +namespace firebolt::rialto::server +{ +class GstProfilerMock : public IGstProfiler +{ +public: + GstProfilerMock() = default; + ~GstProfilerMock() override = default; + + MOCK_METHOD(std::optional, createRecord, (const std::string &stage), (override)); + MOCK_METHOD(std::optional, createRecord, (const std::string &stage, const std::string &info), (override)); + + MOCK_METHOD(void, scheduleGstElementRecord, (GstElement * element), (override)); + MOCK_METHOD(std::vector, getRecords, (), (const, override)); + + MOCK_METHOD(void, logRecord, (RecordId id), (override)); + MOCK_METHOD(void, dumpToFile, (), (const, override)); + MOCK_METHOD(void, logPipelineSummary, (), (const, override)); +}; +} // namespace firebolt::rialto::server + +#endif // FIREBOLT_RIALTO_SERVER_GST_PROFILER_MOCK_H_ diff --git a/wrappers/include/GstWrapper.h b/wrappers/include/GstWrapper.h index bde083520..f09c67e65 100644 --- a/wrappers/include/GstWrapper.h +++ b/wrappers/include/GstWrapper.h @@ -616,6 +616,14 @@ class GstWrapper : public IGstWrapper gboolean gstBinRemove(GstBin *bin, GstElement *element) override { return gst_bin_remove(bin, element); } GstObject *gstPadGetParent(GstPad *pad) override { return gst_pad_get_parent(pad); } + + gulong gstPadAddProbe(GstPad *pad, GstPadProbeType mask, GstPadProbeCallback callback, gpointer userData, + GDestroyNotify destroyData) override + { + return gst_pad_add_probe(pad, mask, callback, userData, destroyData); + } + + void gstPadRemoveProbe(GstPad *pad, gulong id) override { gst_pad_remove_probe(pad, id); } }; }; // namespace firebolt::rialto::wrappers diff --git a/wrappers/interface/IGstWrapper.h b/wrappers/interface/IGstWrapper.h index d8859c028..05c9aaaf3 100644 --- a/wrappers/interface/IGstWrapper.h +++ b/wrappers/interface/IGstWrapper.h @@ -1476,6 +1476,28 @@ class IGstWrapper * @retval The parent GstObject. Unref after usage. NULL if pad has no parent. */ virtual GstObject *gstPadGetParent(GstPad *pad) = 0; + + /** + * @brief Adds probe to the pad. + * + * @param[in] pad : The GstPad to add the probe to. + * @param[in] mask : The probe mask. + * @param[in] callback : GstPadProbeCallback that will be called with notifications of the pad state. + * @param[in] userData : User data passed to the callback. + * @param[in] destroyData : GDestroyNotify for user_data. + * + * @retval An id or 0 if no probe is pending. The id can be used to remove the probe with gst_pad_remove_probe. + */ + virtual gulong gstPadAddProbe(GstPad *pad, GstPadProbeType mask, GstPadProbeCallback callback, gpointer userData, + GDestroyNotify destroyData) = 0; + + /** + * @brief Removes a probe from the pad. + * + * @param[in] pad : The GstPad to remove the probe from. + * @param[in] id : The probe id returned by gstPadAddProbe. + */ + virtual void gstPadRemoveProbe(GstPad *pad, gulong id) = 0; }; }; // namespace firebolt::rialto::wrappers From 8b10d0a15af37c57a98e7eafd0e68c41c7cbc0ea Mon Sep 17 00:00:00 2001 From: rekhap2kandhavelan Date: Tue, 5 May 2026 21:36:12 +0530 Subject: [PATCH 22/29] retain preroll frames count (#475) Summary: retain preroll frames count Type: Fix Test Plan: UT/CT, Fullstack Jira: RDKEMW-13498 --------- Co-authored-by: rkandh015 Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Marcin Wojciechowski --- media/server/main/include/ShmUtils.h | 1 + media/server/main/source/NeedMediaData.cpp | 6 +++++ .../componenttests/server/common/Constants.h | 2 ++ .../server/fixtures/MediaPipelineTest.cpp | 27 +++++++++---------- .../server/fixtures/MediaPipelineTest.h | 9 ++++--- .../mediaPipeline/AudioOnlyPlaybackTest.cpp | 4 +-- .../mediaPipeline/DualVideoPlaybackTest.cpp | 10 +++---- .../mediaPipeline/EncryptedPlaybackTest.cpp | 9 +++---- .../mediaPipeline/HaveDataFailureTest.cpp | 4 +-- .../PlayPauseStopFailuresTest.cpp | 2 +- .../tests/mediaPipeline/PlaybackTest.cpp | 8 +++--- .../mediaPipeline/PositionUpdatesTest.cpp | 10 +++---- .../tests/mediaPipeline/SetPositionTest.cpp | 6 ++--- .../tests/mediaPipeline/WriteSegmentsTest.cpp | 4 +-- .../main/mediaPipeline/CallbackTest.cpp | 2 +- .../server/main/mediaPipeline/FlushTest.cpp | 2 +- .../main/mediaPipeline/HaveDataTest.cpp | 2 +- .../main/needMediaData/NeedMediaDataTests.cpp | 6 +++++ .../NeedMediaDataTestsFixture.cpp | 14 ++++++++++ .../needMediaData/NeedMediaDataTestsFixture.h | 1 + 20 files changed, 79 insertions(+), 50 deletions(-) diff --git a/media/server/main/include/ShmUtils.h b/media/server/main/include/ShmUtils.h index 6ebf425a6..ede9aa5a4 100644 --- a/media/server/main/include/ShmUtils.h +++ b/media/server/main/include/ShmUtils.h @@ -25,6 +25,7 @@ namespace firebolt::rialto::server { +constexpr std::uint32_t kPrerollNumFrames{3}; constexpr std::uint32_t kMaxFrames{24}; constexpr std::uint32_t getMaxMetadataBytes() { diff --git a/media/server/main/source/NeedMediaData.cpp b/media/server/main/source/NeedMediaData.cpp index 3d24cdc4e..9437b9267 100644 --- a/media/server/main/source/NeedMediaData.cpp +++ b/media/server/main/source/NeedMediaData.cpp @@ -33,6 +33,12 @@ NeedMediaData::NeedMediaData(std::weak_ptr client, IActive : m_client{client}, m_activeRequests{activeRequests}, m_mediaSourceType{mediaSourceType}, m_frameCount{kMaxFrames}, m_sourceId{sourceId} { + if (PlaybackState::PLAYING != currentPlaybackState) + { + RIALTO_SERVER_LOG_DEBUG("Pipeline in prerolling state. Sending smaller frame count for %s", + common::convertMediaSourceType(m_mediaSourceType)); + m_frameCount = kPrerollNumFrames; + } if (MediaSourceType::AUDIO != mediaSourceType && MediaSourceType::VIDEO != mediaSourceType && MediaSourceType::SUBTITLE != mediaSourceType) { diff --git a/tests/componenttests/server/common/Constants.h b/tests/componenttests/server/common/Constants.h index 0072a59a9..422e9c33c 100644 --- a/tests/componenttests/server/common/Constants.h +++ b/tests/componenttests/server/common/Constants.h @@ -54,6 +54,8 @@ constexpr double kRate{1.0}; constexpr uint64_t kRenderedFrames{54321}; constexpr uint64_t kDroppedFrames{76}; constexpr uint64_t kStopPosition{234234}; +constexpr int kPrerollNumFrames{3}; +constexpr int kFrameCountInPlayingState{24}; } // namespace firebolt::rialto::server::ct #endif // FIREBOLT_RIALTO_SERVER_CT_CONSTANTS_H_ diff --git a/tests/componenttests/server/fixtures/MediaPipelineTest.cpp b/tests/componenttests/server/fixtures/MediaPipelineTest.cpp index 85d52a419..94f69405e 100644 --- a/tests/componenttests/server/fixtures/MediaPipelineTest.cpp +++ b/tests/componenttests/server/fixtures/MediaPipelineTest.cpp @@ -53,7 +53,6 @@ constexpr GType kGstPlayFlagsType{static_cast(123)}; constexpr unsigned kNeededDataLength{1}; constexpr std::chrono::milliseconds kWorkerTimeout{200}; GstAudioClippingMeta kClippingMeta{}; -constexpr int kNeedDataFrameCount{24}; } // namespace namespace firebolt::rialto::server::ct @@ -479,28 +478,28 @@ void MediaPipelineTest::setupSource() void MediaPipelineTest::indicateAllSourcesAttached(const std::vector &appsrcs) { ExpectMessage expectedPlaybackStateChange(m_clientStub); - std::map>> expectedNeedDataMap; + std::vector>>> expectedNeedData; for (const GstAppSrc *appSrc : appsrcs) { const int kSourceId = ((appSrc == &m_audioAppSrc) ? m_audioSourceId : m_videoSourceId); auto expectation{std::make_unique>(m_clientStub)}; expectation->setFilter([kSourceId](const firebolt::rialto::NeedMediaDataEvent &msg) { return msg.source_id() == kSourceId; }); - expectedNeedDataMap.emplace(kSourceId, std::move(expectation)); + expectedNeedData.emplace_back(kSourceId, std::move(expectation)); } auto allSourcesAttachedReq{createAllSourcesAttachedRequest(m_sessionId)}; ConfigureAction(m_clientStub).send(allSourcesAttachedReq).expectSuccess(); - for (const auto &[sourceId, expectedNeedData] : expectedNeedDataMap) + for (const auto &[sourceId, expectedNeedDataEntry] : expectedNeedData) { auto &needDataPtr = ((sourceId == m_audioSourceId) ? m_lastAudioNeedData : m_lastVideoNeedData); - auto receivedNeedData{expectedNeedData->getMessage()}; + auto receivedNeedData{expectedNeedDataEntry->getMessage()}; ASSERT_TRUE(receivedNeedData); EXPECT_EQ(receivedNeedData->session_id(), m_sessionId); EXPECT_EQ(receivedNeedData->source_id(), sourceId); - EXPECT_EQ(receivedNeedData->frame_count(), kNeedDataFrameCount); + EXPECT_EQ(receivedNeedData->frame_count(), kPrerollNumFrames); needDataPtr = receivedNeedData; } @@ -535,7 +534,7 @@ void MediaPipelineTest::notifyPaused() ASSERT_TRUE(receivedPlaybackInfo); } -void MediaPipelineTest::pushAudioData(unsigned dataCountToPush) +void MediaPipelineTest::pushAudioData(unsigned dataCountToPush, int needDataFrameCount) { // First, generate new data std::vector> segments(dataCountToPush); @@ -568,11 +567,11 @@ void MediaPipelineTest::pushAudioData(unsigned dataCountToPush) ASSERT_TRUE(receivedNeedData); EXPECT_EQ(receivedNeedData->session_id(), m_sessionId); EXPECT_EQ(receivedNeedData->source_id(), m_audioSourceId); - EXPECT_EQ(receivedNeedData->frame_count(), kNeedDataFrameCount); + EXPECT_EQ(receivedNeedData->frame_count(), needDataFrameCount); m_lastAudioNeedData = receivedNeedData; } -void MediaPipelineTest::pushVideoData(unsigned dataCountToPush) +void MediaPipelineTest::pushVideoData(unsigned dataCountToPush, int needDataFrameCount) { // First, generate new data std::vector> segments(dataCountToPush); @@ -605,11 +604,11 @@ void MediaPipelineTest::pushVideoData(unsigned dataCountToPush) ASSERT_TRUE(receivedNeedData); EXPECT_EQ(receivedNeedData->session_id(), m_sessionId); EXPECT_EQ(receivedNeedData->source_id(), m_videoSourceId); - EXPECT_EQ(receivedNeedData->frame_count(), kNeedDataFrameCount); + EXPECT_EQ(receivedNeedData->frame_count(), needDataFrameCount); m_lastVideoNeedData = receivedNeedData; } -void MediaPipelineTest::pushAudioSample() +void MediaPipelineTest::pushAudioSample(int needDataFrameCount) { // First, generate new data std::unique_ptr segment{SegmentBuilder().basicAudioSegment(m_audioSourceId)()}; @@ -637,11 +636,11 @@ void MediaPipelineTest::pushAudioSample() ASSERT_TRUE(receivedNeedData); EXPECT_EQ(receivedNeedData->session_id(), m_sessionId); EXPECT_EQ(receivedNeedData->source_id(), m_audioSourceId); - EXPECT_EQ(receivedNeedData->frame_count(), kNeedDataFrameCount); + EXPECT_EQ(receivedNeedData->frame_count(), needDataFrameCount); m_lastAudioNeedData = receivedNeedData; } -void MediaPipelineTest::pushVideoSample() +void MediaPipelineTest::pushVideoSample(int needDataFrameCount) { // First, generate new data std::unique_ptr segment{SegmentBuilder().basicVideoSegment(m_videoSourceId)()}; @@ -669,7 +668,7 @@ void MediaPipelineTest::pushVideoSample() ASSERT_TRUE(receivedNeedData); EXPECT_EQ(receivedNeedData->session_id(), m_sessionId); EXPECT_EQ(receivedNeedData->source_id(), m_videoSourceId); - EXPECT_EQ(receivedNeedData->frame_count(), kNeedDataFrameCount); + EXPECT_EQ(receivedNeedData->frame_count(), needDataFrameCount); m_lastVideoNeedData = receivedNeedData; } diff --git a/tests/componenttests/server/fixtures/MediaPipelineTest.h b/tests/componenttests/server/fixtures/MediaPipelineTest.h index b87393ee3..b44e5d298 100644 --- a/tests/componenttests/server/fixtures/MediaPipelineTest.h +++ b/tests/componenttests/server/fixtures/MediaPipelineTest.h @@ -20,6 +20,7 @@ #ifndef FIREBOLT_RIALTO_SERVER_CT_MEDIA_PIPELINE_TEST_H_ #define FIREBOLT_RIALTO_SERVER_CT_MEDIA_PIPELINE_TEST_H_ +#include "Constants.h" #include "GstSrc.h" #include "GstreamerStub.h" #include "IMediaPipeline.h" @@ -68,10 +69,10 @@ class MediaPipelineTest : public RialtoServerComponentTest void indicateAllSourcesAttached(const std::vector &appsrcs); void pause(); void notifyPaused(); - void pushAudioData(unsigned dataCountToPush); - void pushVideoData(unsigned dataCountToPush); - void pushAudioSample(); - void pushVideoSample(); + void pushAudioData(unsigned dataCountToPush, int needDataFrameCount = kPrerollNumFrames); + void pushVideoData(unsigned dataCountToPush, int needDataFrameCount = kPrerollNumFrames); + void pushAudioSample(int needDataFrameCount = kPrerollNumFrames); + void pushVideoSample(int needDataFrameCount = kPrerollNumFrames); void play(); void eosAudio(unsigned dataCountToPush); void eosVideo(unsigned dataCountToPush); diff --git a/tests/componenttests/server/tests/mediaPipeline/AudioOnlyPlaybackTest.cpp b/tests/componenttests/server/tests/mediaPipeline/AudioOnlyPlaybackTest.cpp index 67bcf5283..abbe7431e 100644 --- a/tests/componenttests/server/tests/mediaPipeline/AudioOnlyPlaybackTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/AudioOnlyPlaybackTest.cpp @@ -23,7 +23,7 @@ namespace { constexpr unsigned kFramesToPush{1}; -constexpr int kFrameCountInPausedState{3}; +constexpr int kPrerollNumFrames{3}; constexpr int kFrameCountInPlayingState{24}; } // namespace @@ -175,7 +175,7 @@ TEST_F(MediaPipelineTest, AudioOnlyPlayback) play(); // Step 9: Write 1 audio frame - pushAudioData(kFramesToPush); + pushAudioData(kFramesToPush, kFrameCountInPlayingState); // Step 10: End of audio stream willEos(&m_audioAppSrc); diff --git a/tests/componenttests/server/tests/mediaPipeline/DualVideoPlaybackTest.cpp b/tests/componenttests/server/tests/mediaPipeline/DualVideoPlaybackTest.cpp index f5f478ac7..b92fdc4d0 100644 --- a/tests/componenttests/server/tests/mediaPipeline/DualVideoPlaybackTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/DualVideoPlaybackTest.cpp @@ -344,7 +344,7 @@ class DualVideoPlaybackTest : public MediaPipelineTest ASSERT_TRUE(receivedNeedData); EXPECT_EQ(receivedNeedData->session_id(), m_secondarySessionId); EXPECT_EQ(receivedNeedData->source_id(), m_secondaryVideoSourceId); - EXPECT_EQ(receivedNeedData->frame_count(), kFrameCountInPlayingState); + EXPECT_EQ(receivedNeedData->frame_count(), kPrerollNumFrames); m_lastSecondaryNeedData = receivedNeedData; auto receivedPlaybackStateChange{expectedPlaybackStateChange.getMessage()}; @@ -689,8 +689,8 @@ TEST_F(DualVideoPlaybackTest, playbackFullDualVideo) { ExpectMessage expectedNetworkStateChange{m_clientStub}; - pushAudioData(kFramesToPush); - pushVideoData(kFramesToPush); + pushAudioData(kFramesToPush, kFrameCountInPlayingState); + pushVideoData(kFramesToPush, kFrameCountInPlayingState); auto receivedNetworkStateChange{expectedNetworkStateChange.getMessage()}; ASSERT_TRUE(receivedNetworkStateChange); @@ -941,8 +941,8 @@ TEST_F(DualVideoPlaybackTest, playbackNoResouceManagerSecondaryVideo) { ExpectMessage expectedNetworkStateChange{m_clientStub}; - pushAudioData(kFramesToPush); - pushVideoData(kFramesToPush); + pushAudioData(kFramesToPush, kFrameCountInPlayingState); + pushVideoData(kFramesToPush, kFrameCountInPlayingState); auto receivedNetworkStateChange{expectedNetworkStateChange.getMessage()}; ASSERT_TRUE(receivedNetworkStateChange); diff --git a/tests/componenttests/server/tests/mediaPipeline/EncryptedPlaybackTest.cpp b/tests/componenttests/server/tests/mediaPipeline/EncryptedPlaybackTest.cpp index c4b7399ea..e6334c454 100644 --- a/tests/componenttests/server/tests/mediaPipeline/EncryptedPlaybackTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/EncryptedPlaybackTest.cpp @@ -29,7 +29,6 @@ namespace { constexpr unsigned kFramesToPush{1}; -constexpr int kFrameCount{24}; } // namespace using testing::_; @@ -300,8 +299,8 @@ TEST_F(EncryptedPlaybackTest, EncryptedPlayback) { ExpectMessage expectedNetworkStateChange{m_clientStub}; - pushEncryptedAudioData(kFrameCount); - pushEncryptedVideoData(kFrameCount); + pushEncryptedAudioData(kPrerollNumFrames); + pushEncryptedVideoData(kPrerollNumFrames); auto receivedNetworkStateChange{expectedNetworkStateChange.getMessage()}; ASSERT_TRUE(receivedNetworkStateChange); @@ -319,8 +318,8 @@ TEST_F(EncryptedPlaybackTest, EncryptedPlayback) // Step 10: Write 1 encrypted audio frame // Step 11: Write 1 encrypted video frame - pushEncryptedAudioData(kFrameCount); - pushEncryptedVideoData(kFrameCount); + pushEncryptedAudioData(kFrameCountInPlayingState); + pushEncryptedVideoData(kFrameCountInPlayingState); // Step 12: End of audio stream // Step 13: End of video stream diff --git a/tests/componenttests/server/tests/mediaPipeline/HaveDataFailureTest.cpp b/tests/componenttests/server/tests/mediaPipeline/HaveDataFailureTest.cpp index 601c13bdd..e96458c3f 100644 --- a/tests/componenttests/server/tests/mediaPipeline/HaveDataFailureTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/HaveDataFailureTest.cpp @@ -26,7 +26,7 @@ namespace { constexpr unsigned kFramesToPush{1}; -constexpr int kFrameCountInPausedState{24}; +constexpr int kTestFrameCount{3}; } // namespace namespace firebolt::rialto::server::ct @@ -50,7 +50,7 @@ class HaveDataFailureTest : public MediaPipelineTest ASSERT_TRUE(receivedNeedData); EXPECT_EQ(receivedNeedData->session_id(), m_sessionId); EXPECT_EQ(receivedNeedData->source_id(), needData->source_id()); - EXPECT_EQ(receivedNeedData->frame_count(), kFrameCountInPausedState); + EXPECT_EQ(receivedNeedData->frame_count(), kTestFrameCount); needData = receivedNeedData; } }; diff --git a/tests/componenttests/server/tests/mediaPipeline/PlayPauseStopFailuresTest.cpp b/tests/componenttests/server/tests/mediaPipeline/PlayPauseStopFailuresTest.cpp index d487d4f1c..8c40ab32c 100644 --- a/tests/componenttests/server/tests/mediaPipeline/PlayPauseStopFailuresTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/PlayPauseStopFailuresTest.cpp @@ -28,7 +28,7 @@ using testing::Return; namespace { constexpr unsigned kFramesToPush{1}; -constexpr int kFrameCountInPausedState{3}; +constexpr int kPrerollNumFrames{3}; } // namespace namespace firebolt::rialto::server::ct diff --git a/tests/componenttests/server/tests/mediaPipeline/PlaybackTest.cpp b/tests/componenttests/server/tests/mediaPipeline/PlaybackTest.cpp index e4f9bceb6..1f713c22b 100644 --- a/tests/componenttests/server/tests/mediaPipeline/PlaybackTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/PlaybackTest.cpp @@ -180,8 +180,8 @@ TEST_F(MediaPipelineTest, playback) { ExpectMessage expectedNetworkStateChange{m_clientStub}; - pushAudioData(kFramesToPush); - pushVideoData(kFramesToPush); + pushAudioData(kFramesToPush, kPrerollNumFrames); + pushVideoData(kFramesToPush, kPrerollNumFrames); auto receivedNetworkStateChange{expectedNetworkStateChange.getMessage()}; ASSERT_TRUE(receivedNetworkStateChange); @@ -199,8 +199,8 @@ TEST_F(MediaPipelineTest, playback) // Step 10: Write 1 audio frame // Step 11: Write 1 video frame - pushAudioData(kFramesToPush); - pushVideoData(kFramesToPush); + pushAudioData(kFramesToPush, kFrameCountInPlayingState); + pushVideoData(kFramesToPush, kFrameCountInPlayingState); // Step 12: End of audio stream // Step 13: End of video stream diff --git a/tests/componenttests/server/tests/mediaPipeline/PositionUpdatesTest.cpp b/tests/componenttests/server/tests/mediaPipeline/PositionUpdatesTest.cpp index 906278f5a..f4509e43f 100644 --- a/tests/componenttests/server/tests/mediaPipeline/PositionUpdatesTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/PositionUpdatesTest.cpp @@ -227,8 +227,8 @@ TEST_F(PositionUpdatesTest, PositionUpdate) // Step 9: Write 1 audio frame // Step 10: Write 1 video frame - pushAudioData(kFramesToPush); - pushVideoData(kFramesToPush); + pushAudioData(kFramesToPush, kFrameCountInPlayingState); + pushVideoData(kFramesToPush, kFrameCountInPlayingState); // Step 11: Expect position update waitForPositionUpdate(); @@ -423,8 +423,8 @@ TEST_F(PositionUpdatesTest, GetPositionSuccess) // Step 9: Write 1 audio frame // Step 10: Write 1 video frame - pushAudioData(kFramesToPush); - pushVideoData(kFramesToPush); + pushAudioData(kFramesToPush, kFrameCountInPlayingState); + pushVideoData(kFramesToPush, kFrameCountInPlayingState); // Step 11: Get Position getPosition(); @@ -546,7 +546,7 @@ TEST_F(PositionUpdatesTest, getPositionFailure) willSetStateInvalidForQueryPosition(); getPositionFailure(); - // Step 7: Remove sources + // Step 7: Remove sources, kFrameCountInPlayingState removeSource(m_audioSourceId); removeSource(m_videoSourceId); diff --git a/tests/componenttests/server/tests/mediaPipeline/SetPositionTest.cpp b/tests/componenttests/server/tests/mediaPipeline/SetPositionTest.cpp index 6256fb8e0..26eae1041 100644 --- a/tests/componenttests/server/tests/mediaPipeline/SetPositionTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/SetPositionTest.cpp @@ -29,7 +29,7 @@ namespace constexpr unsigned kFramesToPush{1}; constexpr int kPositionInPaused{10}; constexpr int kPositionInPlaying{0}; -constexpr double kPlaybackRate{1.0}; +constexpr double kSeekPlaybackRate{1.0}; } // namespace using testing::Return; @@ -44,7 +44,7 @@ class SetPositionTest : public MediaPipelineTest void willSetPosition(std::int64_t position) { - EXPECT_CALL(*m_gstWrapperMock, gstElementSeek(&m_pipeline, kPlaybackRate, GST_FORMAT_TIME, + EXPECT_CALL(*m_gstWrapperMock, gstElementSeek(&m_pipeline, kSeekPlaybackRate, GST_FORMAT_TIME, static_cast(GST_SEEK_FLAG_FLUSH), GST_SEEK_TYPE_SET, position, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) .WillOnce(Return(TRUE)); @@ -99,7 +99,7 @@ class SetPositionTest : public MediaPipelineTest void willFailToSetPosition() { - EXPECT_CALL(*m_gstWrapperMock, gstElementSeek(&m_pipeline, kPlaybackRate, GST_FORMAT_TIME, + EXPECT_CALL(*m_gstWrapperMock, gstElementSeek(&m_pipeline, kSeekPlaybackRate, GST_FORMAT_TIME, static_cast(GST_SEEK_FLAG_FLUSH), GST_SEEK_TYPE_SET, kPositionInPaused, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) .WillOnce(Return(FALSE)); diff --git a/tests/componenttests/server/tests/mediaPipeline/WriteSegmentsTest.cpp b/tests/componenttests/server/tests/mediaPipeline/WriteSegmentsTest.cpp index a6cd32e64..2c8658313 100644 --- a/tests/componenttests/server/tests/mediaPipeline/WriteSegmentsTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/WriteSegmentsTest.cpp @@ -194,8 +194,8 @@ TEST_F(MediaPipelineTest, WriteSegments) // Step 9: Write 3 audio frames // Step 10: Write 3 video frames - pushAudioData(kFramesToPushBeforePreroll); - pushVideoData(kFramesToPushBeforePreroll); + pushAudioData(kFramesToPushBeforePreroll, kFrameCountInPlayingState); + pushVideoData(kFramesToPushBeforePreroll, kFrameCountInPlayingState); // Step 11: Send 4 frames and end of audio stream // Step 12: Send 4 frames and end of video stream diff --git a/tests/unittests/media/server/main/mediaPipeline/CallbackTest.cpp b/tests/unittests/media/server/main/mediaPipeline/CallbackTest.cpp index 3abe1efe1..627f111d5 100644 --- a/tests/unittests/media/server/main/mediaPipeline/CallbackTest.cpp +++ b/tests/unittests/media/server/main/mediaPipeline/CallbackTest.cpp @@ -88,7 +88,7 @@ TEST_F(RialtoServerMediaPipelineCallbackTest, notifyNeedMediaDataInPrerollingSta { auto mediaSourceType = firebolt::rialto::MediaSourceType::VIDEO; int sourceId = attachSource(mediaSourceType, "video/h264"); - constexpr int kNumFrames{24}; + constexpr int kNumFrames{3}; expectNotifyNeedData(mediaSourceType, sourceId, kNumFrames); diff --git a/tests/unittests/media/server/main/mediaPipeline/FlushTest.cpp b/tests/unittests/media/server/main/mediaPipeline/FlushTest.cpp index c4ef4c947..a9ca62986 100644 --- a/tests/unittests/media/server/main/mediaPipeline/FlushTest.cpp +++ b/tests/unittests/media/server/main/mediaPipeline/FlushTest.cpp @@ -100,6 +100,6 @@ TEST_F(RialtoServerMediaPipelineFlushTest, FlushResetEos) EXPECT_TRUE(m_mediaPipeline->flush(sourceId, m_kResetTime, async)); // Expect need data notified to client - expectNotifyNeedData(firebolt::rialto::MediaSourceType::VIDEO, sourceId, 24); + expectNotifyNeedData(firebolt::rialto::MediaSourceType::VIDEO, sourceId, 3); m_gstPlayerCallback->notifyNeedMediaData(firebolt::rialto::MediaSourceType::VIDEO); } diff --git a/tests/unittests/media/server/main/mediaPipeline/HaveDataTest.cpp b/tests/unittests/media/server/main/mediaPipeline/HaveDataTest.cpp index b1980c220..8f0cc0094 100644 --- a/tests/unittests/media/server/main/mediaPipeline/HaveDataTest.cpp +++ b/tests/unittests/media/server/main/mediaPipeline/HaveDataTest.cpp @@ -28,7 +28,7 @@ using ::testing::Throw; class RialtoServerMediaPipelineHaveDataTest : public MediaPipelineTestBase { protected: - const uint32_t m_kNumFrames{24}; + const uint32_t m_kNumFrames{3}; const uint32_t m_kNeedDataRequestId{0}; const std::chrono::milliseconds m_kDefaultNeedMediaDataResendTimeout{15}; const std::chrono::milliseconds m_kLowLatencyNeedMediaDataResendTimeout{5}; diff --git a/tests/unittests/media/server/main/needMediaData/NeedMediaDataTests.cpp b/tests/unittests/media/server/main/needMediaData/NeedMediaDataTests.cpp index 746d78012..462f45d73 100644 --- a/tests/unittests/media/server/main/needMediaData/NeedMediaDataTests.cpp +++ b/tests/unittests/media/server/main/needMediaData/NeedMediaDataTests.cpp @@ -30,3 +30,9 @@ TEST_F(NeedMediaDataTests, shouldSendMessageInPlayingState) initialize(firebolt::rialto::PlaybackState::PLAYING); needMediaDataWillBeSentInPlayingState(); } + +TEST_F(NeedMediaDataTests, shouldSendMessageInPrerollingState) +{ + initialize(firebolt::rialto::PlaybackState::PAUSED); + needMediaDataWillBeSentBelowPlayingState(); +} diff --git a/tests/unittests/media/server/main/needMediaData/NeedMediaDataTestsFixture.cpp b/tests/unittests/media/server/main/needMediaData/NeedMediaDataTestsFixture.cpp index ac5fc88f6..2237002df 100644 --- a/tests/unittests/media/server/main/needMediaData/NeedMediaDataTestsFixture.cpp +++ b/tests/unittests/media/server/main/needMediaData/NeedMediaDataTestsFixture.cpp @@ -30,6 +30,7 @@ constexpr int kSourceId{1}; constexpr std::uint32_t kBufferLen{7 * 1024 * 1024}; constexpr std::uint32_t kMetadataOffset{1024}; constexpr int kRequestId{0}; +constexpr int kPrerollingNumFrames{3}; constexpr int kMaxFrames{24}; constexpr int kMaxMetadataBytes{2500}; } // namespace @@ -88,6 +89,19 @@ void NeedMediaDataTests::needMediaDataWillBeSentInPlayingState() EXPECT_TRUE(m_sut->send()); } +void NeedMediaDataTests::needMediaDataWillBeSentBelowPlayingState() +{ + std::shared_ptr expectedShmInfo{ + std::make_shared()}; + expectedShmInfo->maxMetadataBytes = kMaxMetadataBytes; + expectedShmInfo->metadataOffset = kMetadataOffset; + expectedShmInfo->mediaDataOffset = kMetadataOffset + kMaxMetadataBytes; + ASSERT_TRUE(m_sut); + EXPECT_CALL(activeRequestsMock, insert(kValidMediaSourceType, _)).WillOnce(Return(kRequestId)); + EXPECT_CALL(*m_clientMock, notifyNeedMediaData(kSourceId, kPrerollingNumFrames, kRequestId, expectedShmInfo)); + EXPECT_TRUE(m_sut->send()); +} + void NeedMediaDataTests::needMediaDataWillNotBeSent() { ASSERT_TRUE(m_sut); diff --git a/tests/unittests/media/server/main/needMediaData/NeedMediaDataTestsFixture.h b/tests/unittests/media/server/main/needMediaData/NeedMediaDataTestsFixture.h index 00dd9a1c1..6f725cf8c 100644 --- a/tests/unittests/media/server/main/needMediaData/NeedMediaDataTestsFixture.h +++ b/tests/unittests/media/server/main/needMediaData/NeedMediaDataTestsFixture.h @@ -41,6 +41,7 @@ class NeedMediaDataTests : public testing::Test void needMediaDataWillBeSentInPlayingState(); void needMediaDataWillNotBeSent(); + void needMediaDataWillBeSentBelowPlayingState(); private: std::unique_ptr m_sut; From 671aa0d9f653c40b1f3c597ecaabba41b9b257ab Mon Sep 17 00:00:00 2001 From: Koky2701 <90915184+Koky2701@users.noreply.github.com> Date: Wed, 6 May 2026 09:51:18 +0200 Subject: [PATCH 23/29] =?UTF-8?q?Revert=20"Bringing=20back=20support=20for?= =?UTF-8?q?=20"queued-frames"=20and=20"report-decode-=E2=80=A6=20(#492)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert "Bringing back support for "queued-frames" and "report-decode-errors" properties (#485)" Summary: Temporary revert of 'report-decode-errors' and 'queued-frames' properties missing from RialtoMSEVideoSink. It revealed issues in some apps. Type: Feature Test Plan: UT/CT, Fullstack Jira: RDKEMW-12692 --- media/client/ipc/include/MediaPipelineIpc.h | 4 - .../client/ipc/interface/IMediaPipelineIpc.h | 24 ---- media/client/ipc/source/MediaPipelineIpc.cpp | 67 ----------- media/client/main/include/MediaPipeline.h | 4 - .../client/main/include/MediaPipelineProxy.h | 8 -- media/client/main/source/MediaPipeline.cpp | 10 -- media/public/include/IMediaPipeline.h | 24 ---- media/server/gstplayer/CMakeLists.txt | 1 - .../gstplayer/include/GenericPlayerContext.h | 5 - .../gstplayer/include/GstGenericPlayer.h | 3 - .../include/IGstGenericPlayerPrivate.h | 7 -- .../include/tasks/IGenericPlayerTaskFactory.h | 15 --- .../tasks/generic/GenericPlayerTaskFactory.h | 4 - .../tasks/generic/SetReportDecodeErrors.h | 49 -------- .../gstplayer/interface/IGstGenericPlayer.h | 19 ---- .../gstplayer/source/GstGenericPlayer.cpp | 80 ------------- .../generic/GenericPlayerTaskFactory.cpp | 9 -- .../tasks/generic/SetReportDecodeErrors.cpp | 52 --------- .../source/tasks/generic/SetupElement.cpp | 5 +- .../ipc/include/MediaPipelineModuleService.h | 8 -- .../ipc/source/MediaPipelineModuleService.cpp | 34 ------ .../include/MediaPipelineServerInternal.h | 28 ----- .../source/MediaPipelineServerInternal.cpp | 57 ---------- .../service/include/IMediaPipelineService.h | 2 - .../service/source/MediaPipelineService.cpp | 28 ----- .../service/source/MediaPipelineService.h | 2 - proto/mediapipelinemodule.proto | 50 --------- .../MediaPipelineProtoRequestMatchers.h | 14 --- .../client/mocks/MediaPipelineModuleMock.h | 20 ---- .../tests/base/MediaPipelineTestMethods.cpp | 28 ----- .../tests/base/MediaPipelineTestMethods.h | 4 - .../client/tests/mse/PipelinePropertyTest.cpp | 12 +- .../mediaPipeline/PipelinePropertyTest.cpp | 12 +- .../unittests/media/client/ipc/CMakeLists.txt | 2 - .../mediaPipelineIpc/GetQueuedFramesTest.cpp | 100 ----------------- .../SetReportDecodeErrorsTest.cpp | 96 ---------------- .../media/client/main/CMakeLists.txt | 2 - .../mediaPipeline/GetQueuedFramesTest.cpp | 63 ----------- .../mediaPipeline/MediaPipelineProxyTest.cpp | 15 --- .../SetReportDecodeErrorsTest.cpp | 58 ---------- .../client/mocks/ipc/MediaPipelineIpcMock.h | 2 - .../main/MediaPipelineAndControlClientMock.h | 2 - .../media/server/gstplayer/CMakeLists.txt | 1 - .../GstGenericPlayerPrivateTest.cpp | 30 ----- .../genericPlayer/GstGenericPlayerTest.cpp | 37 ------ .../common/GenericTasksTestsBase.cpp | 15 --- .../common/GenericTasksTestsBase.h | 4 - .../common/GstGenericPlayerTestCommon.cpp | 14 --- .../common/GstGenericPlayerTestCommon.h | 1 - .../GenericPlayerTaskFactoryTest.cpp | 8 -- .../tasksTests/SetReportDecodeErrorsTest.cpp | 30 ----- .../MediaPipelineModuleServiceTests.cpp | 32 ------ ...MediaPipelineModuleServiceTestsFixture.cpp | 73 ------------ .../MediaPipelineModuleServiceTestsFixture.h | 8 -- .../MiscellaneousFunctionsTest.cpp | 106 ------------------ .../gstplayer/GenericPlayerTaskFactoryMock.h | 4 - .../mocks/gstplayer/GstGenericPlayerMock.h | 3 - .../gstplayer/GstGenericPlayerPrivateMock.h | 1 - .../main/MediaPipelineServerInternalMock.h | 2 - .../mocks/service/MediaPipelineServiceMock.h | 2 - .../MediaPipelineServiceTests.cpp | 40 ------- .../MediaPipelineServiceTestsFixture.cpp | 44 -------- .../MediaPipelineServiceTestsFixture.h | 8 -- 63 files changed, 3 insertions(+), 1489 deletions(-) delete mode 100644 media/server/gstplayer/include/tasks/generic/SetReportDecodeErrors.h delete mode 100644 media/server/gstplayer/source/tasks/generic/SetReportDecodeErrors.cpp delete mode 100644 tests/unittests/media/client/ipc/mediaPipelineIpc/GetQueuedFramesTest.cpp delete mode 100644 tests/unittests/media/client/ipc/mediaPipelineIpc/SetReportDecodeErrorsTest.cpp delete mode 100644 tests/unittests/media/client/main/mediaPipeline/GetQueuedFramesTest.cpp delete mode 100644 tests/unittests/media/client/main/mediaPipeline/SetReportDecodeErrorsTest.cpp delete mode 100644 tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/SetReportDecodeErrorsTest.cpp diff --git a/media/client/ipc/include/MediaPipelineIpc.h b/media/client/ipc/include/MediaPipelineIpc.h index b69dca326..b93cd98fc 100644 --- a/media/client/ipc/include/MediaPipelineIpc.h +++ b/media/client/ipc/include/MediaPipelineIpc.h @@ -93,10 +93,6 @@ class MediaPipelineIpc : public IMediaPipelineIpc, public IpcModule bool setImmediateOutput(int32_t sourceId, bool immediateOutput) override; - bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) override; - - bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) override; - bool getImmediateOutput(int32_t sourceId, bool &immediateOutput) override; bool getStats(int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames) override; diff --git a/media/client/ipc/interface/IMediaPipelineIpc.h b/media/client/ipc/interface/IMediaPipelineIpc.h index 54e6e5c1b..59aea535b 100644 --- a/media/client/ipc/interface/IMediaPipelineIpc.h +++ b/media/client/ipc/interface/IMediaPipelineIpc.h @@ -189,30 +189,6 @@ class IMediaPipelineIpc */ virtual bool setImmediateOutput(int32_t sourceId, bool immediateOutput) = 0; - /** - * @brief Sets the "Report Decode Errors" property for this source. - * - * This method is asynchronous, it will set the "Report Decode Errors" property - * - * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() - * @param[in] reportDecodeErrors : Set Report Decode Errors mode on the sink - * - * @retval true on success. - */ - virtual bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) = 0; - - /** - * @brief Gets the queued frames for this source. - * - * This method is synchronous, it gets the queued frames property - * - * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() - * @param[out] queuedFrames : Get queued frames on the decoder - * - * @retval true on success. - */ - virtual bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) = 0; - /** * @brief Gets the "Immediate Output" property for this source. * diff --git a/media/client/ipc/source/MediaPipelineIpc.cpp b/media/client/ipc/source/MediaPipelineIpc.cpp index 10fb8cfd9..3949629ed 100644 --- a/media/client/ipc/source/MediaPipelineIpc.cpp +++ b/media/client/ipc/source/MediaPipelineIpc.cpp @@ -599,73 +599,6 @@ bool MediaPipelineIpc::setImmediateOutput(int32_t sourceId, bool immediateOutput return true; } -bool MediaPipelineIpc::setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) -{ - if (!reattachChannelIfRequired()) - { - RIALTO_CLIENT_LOG_ERROR("Reattachment of the ipc channel failed, ipc disconnected"); - return false; - } - - firebolt::rialto::SetReportDecodeErrorsRequest request; - - request.set_session_id(m_sessionId); - request.set_source_id(sourceId); - request.set_report_decode_errors(reportDecodeErrors); - - firebolt::rialto::SetReportDecodeErrorsResponse response; - auto ipcController = m_ipc.createRpcController(); - auto blockingClosure = m_ipc.createBlockingClosure(); - m_mediaPipelineStub->setReportDecodeErrors(ipcController.get(), &request, &response, blockingClosure.get()); - - // wait for the call to complete - blockingClosure->wait(); - - // check the result - if (ipcController->Failed()) - { - RIALTO_CLIENT_LOG_ERROR("failed to set report decode error due to '%s'", ipcController->ErrorText().c_str()); - return false; - } - - return true; -} - -bool MediaPipelineIpc::getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) -{ - if (!reattachChannelIfRequired()) - { - RIALTO_CLIENT_LOG_ERROR("Reattachment of the ipc channel failed, ipc disconnected"); - return false; - } - - firebolt::rialto::GetQueuedFramesRequest request; - - request.set_session_id(m_sessionId); - request.set_source_id(sourceId); - - firebolt::rialto::GetQueuedFramesResponse response; - auto ipcController = m_ipc.createRpcController(); - auto blockingClosure = m_ipc.createBlockingClosure(); - m_mediaPipelineStub->getQueuedFrames(ipcController.get(), &request, &response, blockingClosure.get()); - - // wait for the call to complete - blockingClosure->wait(); - - // check the result - if (ipcController->Failed()) - { - RIALTO_CLIENT_LOG_ERROR("failed to get queued frames due to '%s'", ipcController->ErrorText().c_str()); - return false; - } - else - { - queuedFrames = response.queued_frames(); - } - - return true; -} - bool MediaPipelineIpc::getImmediateOutput(int32_t sourceId, bool &immediateOutput) { if (!reattachChannelIfRequired()) diff --git a/media/client/main/include/MediaPipeline.h b/media/client/main/include/MediaPipeline.h index 94e5eddd5..14dad7f81 100644 --- a/media/client/main/include/MediaPipeline.h +++ b/media/client/main/include/MediaPipeline.h @@ -132,10 +132,6 @@ class MediaPipeline : public IMediaPipelineAndIControlClient, public IMediaPipel bool setImmediateOutput(int32_t sourceId, bool immediateOutput) override; - bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) override; - - bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) override; - bool getImmediateOutput(int32_t sourceId, bool &immediateOutput) override; bool getStats(int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames) override; diff --git a/media/client/main/include/MediaPipelineProxy.h b/media/client/main/include/MediaPipelineProxy.h index 0fbfa3376..80f019d95 100644 --- a/media/client/main/include/MediaPipelineProxy.h +++ b/media/client/main/include/MediaPipelineProxy.h @@ -68,14 +68,6 @@ class MediaPipelineProxy : public IMediaPipelineAndIControlClient { return m_mediaPipeline->setImmediateOutput(sourceId, immediateOutput); } - bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) - { - return m_mediaPipeline->setReportDecodeErrors(sourceId, reportDecodeErrors); - } - bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) - { - return m_mediaPipeline->getQueuedFrames(sourceId, queuedFrames); - } bool getImmediateOutput(int32_t sourceId, bool &immediateOutput) { return m_mediaPipeline->getImmediateOutput(sourceId, immediateOutput); diff --git a/media/client/main/source/MediaPipeline.cpp b/media/client/main/source/MediaPipeline.cpp index f889b5b06..ed0506432 100644 --- a/media/client/main/source/MediaPipeline.cpp +++ b/media/client/main/source/MediaPipeline.cpp @@ -313,16 +313,6 @@ bool MediaPipeline::setImmediateOutput(int32_t sourceId, bool immediateOutput) return m_mediaPipelineIpc->setImmediateOutput(sourceId, immediateOutput); } -bool MediaPipeline::setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) -{ - return m_mediaPipelineIpc->setReportDecodeErrors(sourceId, reportDecodeErrors); -} - -bool MediaPipeline::getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) -{ - return m_mediaPipelineIpc->getQueuedFrames(sourceId, queuedFrames); -} - bool MediaPipeline::getImmediateOutput(int32_t sourceId, bool &immediateOutput) { return m_mediaPipelineIpc->getImmediateOutput(sourceId, immediateOutput); diff --git a/media/public/include/IMediaPipeline.h b/media/public/include/IMediaPipeline.h index 0f1c23ad2..c1820f302 100644 --- a/media/public/include/IMediaPipeline.h +++ b/media/public/include/IMediaPipeline.h @@ -1228,30 +1228,6 @@ class IMediaPipeline */ virtual bool setImmediateOutput(int32_t sourceId, bool immediateOutput) = 0; - /** - * @brief Sets the "Report Decode Errors" property for this source. - * - * This method is asynchronous, it will set the "Report Decode Errors" property - * - * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() - * @param[in] reportDecodeErrors : Set Report Decode Errors mode on the sink - * - * @retval true on success. - */ - virtual bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) = 0; - - /** - * @brief Gets the queued frames for this source. - * - * This method is synchronous, it gets the queued frames property - * - * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() - * @param[out] queuedFrames : Get queued frames on the decoder - * - * @retval true on success. - */ - virtual bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) = 0; - /** * @brief Gets the "Immediate Output" property for this source. * diff --git a/media/server/gstplayer/CMakeLists.txt b/media/server/gstplayer/CMakeLists.txt index 1adfc4e66..11d7fc63b 100644 --- a/media/server/gstplayer/CMakeLists.txt +++ b/media/server/gstplayer/CMakeLists.txt @@ -57,7 +57,6 @@ add_library( source/tasks/generic/SetMute.cpp source/tasks/generic/SetPlaybackRate.cpp source/tasks/generic/SetPosition.cpp - source/tasks/generic/SetReportDecodeErrors.cpp source/tasks/generic/SetSourcePosition.cpp source/tasks/generic/SetSubtitleOffset.cpp source/tasks/generic/SetStreamSyncMode.cpp diff --git a/media/server/gstplayer/include/GenericPlayerContext.h b/media/server/gstplayer/include/GenericPlayerContext.h index 7aaceb099..af72f88a6 100644 --- a/media/server/gstplayer/include/GenericPlayerContext.h +++ b/media/server/gstplayer/include/GenericPlayerContext.h @@ -156,11 +156,6 @@ struct GenericPlayerContext */ std::optional pendingImmediateOutputForVideo{}; - /** - * @brief Pending report decode errors for MediaSourceType::VIDEO - */ - std::optional pendingReportDecodeErrorsForVideo{}; - /** * @brief Pending low latency */ diff --git a/media/server/gstplayer/include/GstGenericPlayer.h b/media/server/gstplayer/include/GstGenericPlayer.h index 408545d45..bab6fbc87 100644 --- a/media/server/gstplayer/include/GstGenericPlayer.h +++ b/media/server/gstplayer/include/GstGenericPlayer.h @@ -123,9 +123,7 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva bool getPosition(std::int64_t &position) override; bool getDuration(std::int64_t &duration) override; bool setImmediateOutput(const MediaSourceType &mediaSourceType, bool immediateOutput) override; - bool setReportDecodeErrors(const MediaSourceType &mediaSourceType, bool reportDecodeErrors) override; bool getImmediateOutput(const MediaSourceType &mediaSourceType, bool &immediateOutput) override; - bool getQueuedFrames(uint32_t &queuedFrames) override; bool getStats(const MediaSourceType &mediaSourceType, uint64_t &renderedFrames, uint64_t &droppedFrames) override; void setVolume(double targetVolume, uint32_t volumeDuration, firebolt::rialto::EaseType easeType) override; bool getVolume(double &volume) override; @@ -159,7 +157,6 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva void scheduleAllSourcesAttached() override; bool setVideoSinkRectangle() override; bool setImmediateOutput() override; - bool setReportDecodeErrors() override; bool setShowVideoWindow() override; bool setLowLatency() override; bool setSync() override; diff --git a/media/server/gstplayer/include/IGstGenericPlayerPrivate.h b/media/server/gstplayer/include/IGstGenericPlayerPrivate.h index 9b6d54af0..fb48a2816 100644 --- a/media/server/gstplayer/include/IGstGenericPlayerPrivate.h +++ b/media/server/gstplayer/include/IGstGenericPlayerPrivate.h @@ -80,13 +80,6 @@ class IGstGenericPlayerPrivate */ virtual bool setImmediateOutput() = 0; - /** - * @brief Sets report decode error. Called by the worker thread. - * - * @retval true on success. - */ - virtual bool setReportDecodeErrors() = 0; - /** * @brief Sets the low latency property. Called by the worker thread. * diff --git a/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h b/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h index 54b8fc621..f8de67cb4 100644 --- a/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h +++ b/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h @@ -482,21 +482,6 @@ class IGenericPlayerTaskFactory const firebolt::rialto::MediaSourceType &type, bool immediateOutput) const = 0; - /** - * @brief Creates a SetReportDecodeErrors task. - * - * @param[in] context : The GstPlayer context - * @param[in] player : The GstPlayer instance - * @param[in] type : The media source type - * @param[in] reportDecodeErrors : the value to set for report decode error - * - * @retval the new SetReportDecodeErrors task instance. - */ - virtual std::unique_ptr createSetReportDecodeErrors(GenericPlayerContext &context, - IGstGenericPlayerPrivate &player, - const firebolt::rialto::MediaSourceType &type, - bool reportDecodeErrors) const = 0; - /** * @brief Creates a SetBufferingLimit task. * diff --git a/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h b/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h index 79e31838b..aa9a06e55 100644 --- a/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h +++ b/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h @@ -120,10 +120,6 @@ class GenericPlayerTaskFactory : public IGenericPlayerTaskFactory std::unique_ptr createSetImmediateOutput(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, const firebolt::rialto::MediaSourceType &type, bool immediateOutput) const override; - std::unique_ptr createSetReportDecodeErrors(GenericPlayerContext &context, - IGstGenericPlayerPrivate &player, - const firebolt::rialto::MediaSourceType &type, - bool reportDecodeErrors) const override; std::unique_ptr createSetBufferingLimit(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, std::uint32_t limit) const override; std::unique_ptr createSetUseBuffering(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, diff --git a/media/server/gstplayer/include/tasks/generic/SetReportDecodeErrors.h b/media/server/gstplayer/include/tasks/generic/SetReportDecodeErrors.h deleted file mode 100644 index 9cf30b310..000000000 --- a/media/server/gstplayer/include/tasks/generic/SetReportDecodeErrors.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2026 Sky UK - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef FIREBOLT_RIALTO_SERVER_TASKS_GENERIC_SET_REPORT_DECODE_ERRORS_H_ -#define FIREBOLT_RIALTO_SERVER_TASKS_GENERIC_SET_REPORT_DECODE_ERRORS_H_ - -#include "GenericPlayerContext.h" -#include "IGlibWrapper.h" -#include "IGstGenericPlayerPrivate.h" -#include "IGstWrapper.h" -#include "IPlayerTask.h" - -#include - -namespace firebolt::rialto::server::tasks::generic -{ -class SetReportDecodeErrors : public IPlayerTask -{ -public: - explicit SetReportDecodeErrors(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, - const MediaSourceType &type, bool reportDecodeErrors); - ~SetReportDecodeErrors() override; - void execute() const override; - -private: - GenericPlayerContext &m_context; - IGstGenericPlayerPrivate &m_player; - const MediaSourceType m_type; - bool m_reportDecodeErrors; -}; -} // namespace firebolt::rialto::server::tasks::generic - -#endif // FIREBOLT_RIALTO_SERVER_TASKS_GENERIC_SET_REPORT_DECODE_ERRORS_H_ diff --git a/media/server/gstplayer/interface/IGstGenericPlayer.h b/media/server/gstplayer/interface/IGstGenericPlayer.h index a4036227a..f0d5111a1 100644 --- a/media/server/gstplayer/interface/IGstGenericPlayer.h +++ b/media/server/gstplayer/interface/IGstGenericPlayer.h @@ -206,25 +206,6 @@ class IGstGenericPlayer */ virtual bool setImmediateOutput(const MediaSourceType &mediaSourceType, bool immediateOutput) = 0; - /** - * @brief Sets the "Report Decode Error" property for this source. - * - * @param[in] mediaSourceType : The media source type - * @param[in] reportDecodeErrors : Set report decode error - * - * @retval true on success. - */ - virtual bool setReportDecodeErrors(const MediaSourceType &mediaSourceType, bool reportDecodeErrors) = 0; - - /** - * @brief Gets the queued frames for this source. - * - * @param[out] queuedFrames : Get queued frames mode on the decoder - * - * @retval true on success. - */ - virtual bool getQueuedFrames(uint32_t &queuedFrames) = 0; - /** * @brief Gets the "Immediate Output" property for this source. * diff --git a/media/server/gstplayer/source/GstGenericPlayer.cpp b/media/server/gstplayer/source/GstGenericPlayer.cpp index 8b2bccd4f..099b1e62b 100644 --- a/media/server/gstplayer/source/GstGenericPlayer.cpp +++ b/media/server/gstplayer/source/GstGenericPlayer.cpp @@ -1134,41 +1134,6 @@ bool GstGenericPlayer::setImmediateOutput(const MediaSourceType &mediaSourceType return true; } -bool GstGenericPlayer::setReportDecodeErrors(const MediaSourceType &mediaSourceType, bool reportDecodeErrors) -{ - if (!m_workerThread) - return false; - - m_workerThread->enqueueTask( - m_taskFactory->createSetReportDecodeErrors(m_context, *this, mediaSourceType, reportDecodeErrors)); - return true; -} - -bool GstGenericPlayer::getQueuedFrames(uint32_t &queuedFrames) -{ - bool returnValue{false}; - GstElement *decoder{getDecoder(MediaSourceType::VIDEO)}; - if (decoder) - { - if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(decoder), "queued-frames")) - { - m_glibWrapper->gObjectGet(decoder, "queued-frames", &queuedFrames, nullptr); - returnValue = true; - } - else - { - RIALTO_SERVER_LOG_ERROR("queued-frames not supported in element %s", GST_ELEMENT_NAME(decoder)); - } - m_gstWrapper->gstObjectUnref(decoder); - } - else - { - RIALTO_SERVER_LOG_ERROR("Failed to get queued-frames property, decoder is NULL"); - } - - return returnValue; -} - bool GstGenericPlayer::getImmediateOutput(const MediaSourceType &mediaSourceType, bool &immediateOutputRef) { bool returnValue{false}; @@ -1906,51 +1871,6 @@ bool GstGenericPlayer::setImmediateOutput() return result; } -bool GstGenericPlayer::setReportDecodeErrors() -{ - bool result{false}; - bool reportDecodeErrors{false}; - - { - std::unique_lock lock{m_context.propertyMutex}; - if (!m_context.pendingReportDecodeErrorsForVideo.has_value()) - { - return false; - } - reportDecodeErrors = m_context.pendingReportDecodeErrorsForVideo.value(); - } - - GstElement *decoder = getDecoder(MediaSourceType::VIDEO); - if (decoder) - { - RIALTO_SERVER_LOG_DEBUG("Set report decode errors to %s", reportDecodeErrors ? "TRUE" : "FALSE"); - - if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(decoder), "report-decode-errors")) - { - gboolean reportDecodeErrorsGboolean{reportDecodeErrors ? TRUE : FALSE}; - m_glibWrapper->gObjectSet(decoder, "report-decode-errors", reportDecodeErrorsGboolean, nullptr); - result = true; - } - else - { - RIALTO_SERVER_LOG_ERROR("Failed to set report-decode-errors property on decoder '%s'", - GST_ELEMENT_NAME(decoder)); - } - - m_gstWrapper->gstObjectUnref(decoder); - - { - std::unique_lock lock{m_context.propertyMutex}; - m_context.pendingReportDecodeErrorsForVideo.reset(); - } - } - else - { - RIALTO_SERVER_LOG_DEBUG("Pending report-decode-errors, decoder is NULL"); - } - return result; -} - bool GstGenericPlayer::setShowVideoWindow() { if (!m_context.pendingShowVideoWindow.has_value()) diff --git a/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp b/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp index a7886a335..318f04379 100644 --- a/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp +++ b/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp @@ -41,7 +41,6 @@ #include "tasks/generic/SetMute.h" #include "tasks/generic/SetPlaybackRate.h" #include "tasks/generic/SetPosition.h" -#include "tasks/generic/SetReportDecodeErrors.h" #include "tasks/generic/SetSourcePosition.h" #include "tasks/generic/SetStreamSyncMode.h" #include "tasks/generic/SetSubtitleOffset.h" @@ -327,14 +326,6 @@ GenericPlayerTaskFactory::createSetImmediateOutput(GenericPlayerContext &context return std::make_unique(context, player, type, immediateOutput); } -std::unique_ptr -GenericPlayerTaskFactory::createSetReportDecodeErrors(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, - const firebolt::rialto::MediaSourceType &type, - bool reportDecodeErrors) const -{ - return std::make_unique(context, player, type, reportDecodeErrors); -} - std::unique_ptr GenericPlayerTaskFactory::createSetBufferingLimit(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, std::uint32_t limit) const diff --git a/media/server/gstplayer/source/tasks/generic/SetReportDecodeErrors.cpp b/media/server/gstplayer/source/tasks/generic/SetReportDecodeErrors.cpp deleted file mode 100644 index 6815df0ce..000000000 --- a/media/server/gstplayer/source/tasks/generic/SetReportDecodeErrors.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2024 Sky UK - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SetReportDecodeErrors.h" -#include "RialtoServerLogging.h" -#include "TypeConverters.h" - -namespace firebolt::rialto::server::tasks::generic -{ -SetReportDecodeErrors::SetReportDecodeErrors(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, - const MediaSourceType &type, bool reportDecodeErrors) - : m_context{context}, m_player(player), m_type{type}, m_reportDecodeErrors{reportDecodeErrors} -{ - RIALTO_SERVER_LOG_DEBUG("Constructing SetReportDecodeErrors"); -} - -SetReportDecodeErrors::~SetReportDecodeErrors() -{ - RIALTO_SERVER_LOG_DEBUG("SetReportDecodeErrors finished"); -} - -void SetReportDecodeErrors::execute() const -{ - RIALTO_SERVER_LOG_DEBUG("Executing SetReportDecodeErrors for %s source", common::convertMediaSourceType(m_type)); - - m_context.pendingReportDecodeErrorsForVideo = m_reportDecodeErrors; - - if (!m_context.pipeline) - { - RIALTO_SERVER_LOG_WARN("Pipeline not available yet - cannot apply report-decode-errors setting"); - return; - } - - m_player.setReportDecodeErrors(); -} -} // namespace firebolt::rialto::server::tasks::generic diff --git a/media/server/gstplayer/source/tasks/generic/SetupElement.cpp b/media/server/gstplayer/source/tasks/generic/SetupElement.cpp index 9ff98430c..d3fd0f12a 100644 --- a/media/server/gstplayer/source/tasks/generic/SetupElement.cpp +++ b/media/server/gstplayer/source/tasks/generic/SetupElement.cpp @@ -246,10 +246,6 @@ void SetupElement::execute() const RIALTO_SERVER_LOG_INFO("Setting video decoder handle for subtitle sink: %p", m_element); m_context.isVideoHandleSet = true; } - if (m_context.pendingReportDecodeErrorsForVideo.has_value()) - { - m_player.setReportDecodeErrors(); - } } } @@ -343,6 +339,7 @@ void SetupElement::execute() const { m_gstWrapper->gstBaseParseSetPtsInterpolation(GST_BASE_PARSE(m_element), FALSE); } + m_gstWrapper->gstObjectUnref(m_element); } } // namespace firebolt::rialto::server::tasks::generic diff --git a/media/server/ipc/include/MediaPipelineModuleService.h b/media/server/ipc/include/MediaPipelineModuleService.h index 91787419a..92e54e463 100644 --- a/media/server/ipc/include/MediaPipelineModuleService.h +++ b/media/server/ipc/include/MediaPipelineModuleService.h @@ -90,14 +90,6 @@ class MediaPipelineModuleService : public IMediaPipelineModuleService const ::firebolt::rialto::SetImmediateOutputRequest *request, ::firebolt::rialto::SetImmediateOutputResponse *response, ::google::protobuf::Closure *done) override; - void setReportDecodeErrors(::google::protobuf::RpcController *controller, - const ::firebolt::rialto::SetReportDecodeErrorsRequest *request, - ::firebolt::rialto::SetReportDecodeErrorsResponse *response, - ::google::protobuf::Closure *done) override; - void getQueuedFrames(::google::protobuf::RpcController *controller, - const ::firebolt::rialto::GetQueuedFramesRequest *request, - ::firebolt::rialto::GetQueuedFramesResponse *response, - ::google::protobuf::Closure *done) override; void getImmediateOutput(::google::protobuf::RpcController *controller, const ::firebolt::rialto::GetImmediateOutputRequest *request, ::firebolt::rialto::GetImmediateOutputResponse *response, diff --git a/media/server/ipc/source/MediaPipelineModuleService.cpp b/media/server/ipc/source/MediaPipelineModuleService.cpp index c04860ad4..b25712cf5 100644 --- a/media/server/ipc/source/MediaPipelineModuleService.cpp +++ b/media/server/ipc/source/MediaPipelineModuleService.cpp @@ -699,40 +699,6 @@ void MediaPipelineModuleService::setImmediateOutput(::google::protobuf::RpcContr done->Run(); } -void MediaPipelineModuleService::setReportDecodeErrors(::google::protobuf::RpcController *controller, - const ::firebolt::rialto::SetReportDecodeErrorsRequest *request, - ::firebolt::rialto::SetReportDecodeErrorsResponse *response, - ::google::protobuf::Closure *done) -{ - RIALTO_SERVER_LOG_DEBUG("entry:"); - if (!m_mediaPipelineService.setReportDecodeErrors(request->session_id(), request->source_id(), - request->report_decode_errors())) - { - RIALTO_SERVER_LOG_ERROR("Set Report Decode Error failed"); - controller->SetFailed("Operation failed"); - } - done->Run(); -} - -void MediaPipelineModuleService::getQueuedFrames(::google::protobuf::RpcController *controller, - const ::firebolt::rialto::GetQueuedFramesRequest *request, - ::firebolt::rialto::GetQueuedFramesResponse *response, - ::google::protobuf::Closure *done) -{ - RIALTO_SERVER_LOG_DEBUG("entry:"); - uint32_t queuedFramesNumber; - if (!m_mediaPipelineService.getQueuedFrames(request->session_id(), request->source_id(), queuedFramesNumber)) - { - RIALTO_SERVER_LOG_ERROR("Get queued frames failed"); - controller->SetFailed("Operation failed"); - } - else - { - response->set_queued_frames(queuedFramesNumber); - } - done->Run(); -} - void MediaPipelineModuleService::getImmediateOutput(::google::protobuf::RpcController *controller, const ::firebolt::rialto::GetImmediateOutputRequest *request, ::firebolt::rialto::GetImmediateOutputResponse *response, diff --git a/media/server/main/include/MediaPipelineServerInternal.h b/media/server/main/include/MediaPipelineServerInternal.h index 7b0be9834..b165419ee 100644 --- a/media/server/main/include/MediaPipelineServerInternal.h +++ b/media/server/main/include/MediaPipelineServerInternal.h @@ -114,10 +114,6 @@ class MediaPipelineServerInternal : public IMediaPipelineServerInternal, public bool setImmediateOutput(int32_t sourceId, bool immediateOutput) override; - bool setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) override; - - bool getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) override; - bool getImmediateOutput(int32_t sourceId, bool &immediateOutput) override; bool getStats(int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames) override; @@ -404,30 +400,6 @@ class MediaPipelineServerInternal : public IMediaPipelineServerInternal, public */ bool setImmediateOutputInternal(int32_t sourceId, bool immediateOutput); - /** - * @brief Sets the "Report Decode Errors" property for this source. - * - * This method is asynchronous - * - * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() - * @param[in] reportDecodeErrors : The desired Set Report Decode Errors mode on the sink - * - * @retval true on success. - */ - bool setReportDecodeErrorsInternal(int32_t sourceId, bool reportDecodeErrors); - - /** - * @brief Gets the queued frames for this source. - * - * This method is asynchronous - * - * @param[in] sourceId : The source id. Value should be set to the MediaSource.id returned after attachSource() - * @param[in] queuedFrames : Number of queued frames - * - * @retval true on success. - */ - bool getQueuedFramesInternal(int32_t sourceId, uint32_t &queuedFrames); - /** * @brief Gets the "Immediate Output" property for this source. * diff --git a/media/server/main/source/MediaPipelineServerInternal.cpp b/media/server/main/source/MediaPipelineServerInternal.cpp index a3533abaf..50660642a 100644 --- a/media/server/main/source/MediaPipelineServerInternal.cpp +++ b/media/server/main/source/MediaPipelineServerInternal.cpp @@ -554,63 +554,6 @@ bool MediaPipelineServerInternal::setImmediateOutputInternal(int32_t sourceId, b return m_gstPlayer->setImmediateOutput(sourceIter->first, immediateOutput); } -bool MediaPipelineServerInternal::setReportDecodeErrors(int32_t sourceId, bool reportDecodeErrors) -{ - RIALTO_SERVER_LOG_DEBUG("entry:"); - - bool result; - auto task = [&]() { result = setReportDecodeErrorsInternal(sourceId, reportDecodeErrors); }; - - m_mainThread->enqueueTaskAndWait(m_mainThreadClientId, task); - return result; -} - -bool MediaPipelineServerInternal::setReportDecodeErrorsInternal(int32_t sourceId, bool reportDecodeErrors) -{ - if (!m_gstPlayer) - { - RIALTO_SERVER_LOG_ERROR("Failed - Gstreamer player has not been loaded"); - return false; - } - auto sourceIter = std::find_if(m_attachedSources.begin(), m_attachedSources.end(), - [sourceId](const auto &src) { return src.second == sourceId; }); - if (sourceIter == m_attachedSources.end()) - { - RIALTO_SERVER_LOG_ERROR("Failed - Source not found"); - return false; - } - - return m_gstPlayer->setReportDecodeErrors(sourceIter->first, reportDecodeErrors); -} - -bool MediaPipelineServerInternal::getQueuedFrames(int32_t sourceId, uint32_t &queuedFrames) -{ - RIALTO_SERVER_LOG_DEBUG("entry:"); - - bool result; - auto task = [&]() { result = getQueuedFramesInternal(sourceId, queuedFrames); }; - - m_mainThread->enqueueTaskAndWait(m_mainThreadClientId, task); - return result; -} - -bool MediaPipelineServerInternal::getQueuedFramesInternal(int32_t sourceId, uint32_t &queuedFrames) -{ - if (!m_gstPlayer) - { - RIALTO_SERVER_LOG_ERROR("Failed - Gstreamer player has not been loaded"); - return false; - } - auto sourceIter = std::find_if(m_attachedSources.begin(), m_attachedSources.end(), - [sourceId](const auto &src) { return src.second == sourceId; }); - if (sourceIter == m_attachedSources.end()) - { - RIALTO_SERVER_LOG_ERROR("Failed - Source not found"); - return false; - } - return m_gstPlayer->getQueuedFrames(queuedFrames); -} - bool MediaPipelineServerInternal::getImmediateOutput(int32_t sourceId, bool &immediateOutput) { RIALTO_SERVER_LOG_DEBUG("entry:"); diff --git a/media/server/service/include/IMediaPipelineService.h b/media/server/service/include/IMediaPipelineService.h index a70476b19..77a27d34a 100644 --- a/media/server/service/include/IMediaPipelineService.h +++ b/media/server/service/include/IMediaPipelineService.h @@ -55,8 +55,6 @@ class IMediaPipelineService virtual bool setPosition(int sessionId, std::int64_t position) = 0; virtual bool getPosition(int sessionId, std::int64_t &position) = 0; virtual bool setImmediateOutput(int sessionId, int32_t sourceId, bool immediateOutput) = 0; - virtual bool setReportDecodeErrors(int sessionId, int32_t sourceId, bool reportDecodeErrors) = 0; - virtual bool getQueuedFrames(int sessionId, int32_t sourceId, uint32_t &queuedFrames) = 0; virtual bool getImmediateOutput(int sessionId, int32_t sourceId, bool &immediateOutput) = 0; virtual bool getStats(int sessionId, int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames) = 0; virtual bool setVideoWindow(int sessionId, std::uint32_t x, std::uint32_t y, std::uint32_t width, diff --git a/media/server/service/source/MediaPipelineService.cpp b/media/server/service/source/MediaPipelineService.cpp index 717063564..f3d8136ab 100644 --- a/media/server/service/source/MediaPipelineService.cpp +++ b/media/server/service/source/MediaPipelineService.cpp @@ -282,34 +282,6 @@ bool MediaPipelineService::setImmediateOutput(int sessionId, int32_t sourceId, b return mediaPipelineIter->second->setImmediateOutput(sourceId, immediateOutput); } -bool MediaPipelineService::setReportDecodeErrors(int sessionId, int32_t sourceId, bool reportDecodeErrors) -{ - RIALTO_SERVER_LOG_INFO("MediaPipelineService requested to setReportDecodeErrors, session id: %d", sessionId); - - std::lock_guard lock{m_mediaPipelineMutex}; - auto mediaPipelineIter = m_mediaPipelines.find(sessionId); - if (mediaPipelineIter == m_mediaPipelines.end()) - { - RIALTO_SERVER_LOG_ERROR("Session with id: %d does not exists", sessionId); - return false; - } - return mediaPipelineIter->second->setReportDecodeErrors(sourceId, reportDecodeErrors); -} - -bool MediaPipelineService::getQueuedFrames(int sessionId, int32_t sourceId, uint32_t &queuedFrames) -{ - RIALTO_SERVER_LOG_INFO("MediaPipelineService requested to getQueuedFrames, session id: %d", sessionId); - - std::lock_guard lock{m_mediaPipelineMutex}; - auto mediaPipelineIter = m_mediaPipelines.find(sessionId); - if (mediaPipelineIter == m_mediaPipelines.end()) - { - RIALTO_SERVER_LOG_ERROR("Session with id: %d does not exists", sessionId); - return false; - } - return mediaPipelineIter->second->getQueuedFrames(sourceId, queuedFrames); -} - bool MediaPipelineService::getImmediateOutput(int sessionId, int32_t sourceId, bool &immediateOutput) { RIALTO_SERVER_LOG_INFO("MediaPipelineService requested to getImmediateOutput, session id: %d", sessionId); diff --git a/media/server/service/source/MediaPipelineService.h b/media/server/service/source/MediaPipelineService.h index 7e777c777..06eaf320f 100644 --- a/media/server/service/source/MediaPipelineService.h +++ b/media/server/service/source/MediaPipelineService.h @@ -67,8 +67,6 @@ class MediaPipelineService : public IMediaPipelineService bool getPosition(int sessionId, std::int64_t &position) override; bool getDuration(int sessionId, std::int64_t &duration) override; bool setImmediateOutput(int sessionId, int32_t sourceId, bool immediateOutput) override; - bool setReportDecodeErrors(int sessionId, int32_t sourceId, bool reportDecodeErrors) override; - bool getQueuedFrames(int sessionId, int32_t sourceId, uint32_t &queuedFrames) override; bool getImmediateOutput(int sessionId, int32_t sourceId, bool &immediateOutput) override; bool getStats(int sessionId, int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames) override; bool setVideoWindow(int sessionId, std::uint32_t x, std::uint32_t y, std::uint32_t width, diff --git a/proto/mediapipelinemodule.proto b/proto/mediapipelinemodule.proto index d98fcd41a..ed8bbab63 100644 --- a/proto/mediapipelinemodule.proto +++ b/proto/mediapipelinemodule.proto @@ -696,24 +696,6 @@ message SetImmediateOutputRequest { message SetImmediateOutputResponse { } -/** - * @fn void setReportDecodeErrors(int session_id, int source_id, bool report_decode_errors) - * @brief Enables or disables decode error reporting for this source. - * - * @param[in] session_id The id of the A/V session. - * @param[in] source_id The id of the media source. - * @param[in] report_decode_errors Enable decode error reporting. - */ -message SetReportDecodeErrorsRequest { - optional int32 session_id = 1 [default = -1]; - optional int32 source_id = 2 [default = -1]; - optional bool report_decode_errors = 3 [default = false]; -} - -message SetReportDecodeErrorsResponse { -} - - /** * @fn void getImmediateOutput(int session_id, int source_id, bool &immediate_output) * @brief Gets the "Immediate Output" property for this source. @@ -731,23 +713,6 @@ message GetImmediateOutputResponse { optional bool immediate_output = 1 [default = false]; } -/** - * @fn void getQueuedFrames(int session_id, int source_id, uint32_t &queued_frames) - * @brief Gets the "Queued Frames" property for this source. - * - * @param[in] session_id The id of the A/V session. - * @param[in] source_id The id of the media source. - * @param[out] queued_frames The number of queued frames on the decoder - * - */ -message GetQueuedFramesRequest { - optional int32 session_id = 1 [default = -1]; - optional int32 source_id = 2 [default = -1]; -} -message GetQueuedFramesResponse { - optional uint32 queued_frames = 1 [default = 0]; -} - /** * @fn void getStats(int session_id, int source_id, uint64 &rendered_frames, uint64 &dropped_frames) * @brief Get various stats from the source. @@ -1131,14 +1096,6 @@ service MediaPipelineModule { rpc setImmediateOutput(SetImmediateOutputRequest) returns (SetImmediateOutputResponse) { } - /** - * @brief Sets the "Report Decode Errors" property for this source. - * @see SetReportDecodeErrorsRequest - */ - rpc setReportDecodeErrors(SetReportDecodeErrorsRequest) - returns (SetReportDecodeErrorsResponse) { - } - /** * @brief Gets the "Immediate Output" property for this source. * @see GetImmediateOutputRequest @@ -1146,13 +1103,6 @@ service MediaPipelineModule { rpc getImmediateOutput(GetImmediateOutputRequest) returns (GetImmediateOutputResponse) { } - /** - * @brief Gets the "Queued Frames" property for this source. - * @see GetQueuedFramesRequest - */ - rpc getQueuedFrames(GetQueuedFramesRequest) returns (GetQueuedFramesResponse) { - } - /** * @brief Gets the current stats property * @see GetStatsRequest diff --git a/tests/common/matchers/MediaPipelineProtoRequestMatchers.h b/tests/common/matchers/MediaPipelineProtoRequestMatchers.h index f8186d4c0..ac34ace4b 100644 --- a/tests/common/matchers/MediaPipelineProtoRequestMatchers.h +++ b/tests/common/matchers/MediaPipelineProtoRequestMatchers.h @@ -373,20 +373,6 @@ MATCHER_P2(getImmediateOutputRequestMatcher, sessionId, sourceId, "") return (kRequest->session_id() == sessionId) && (kRequest->source_id() == sourceId); } -MATCHER_P2(setReportDecodeErrorsRequestMatcher, sessionId, sourceId, "") -{ - const ::firebolt::rialto::SetReportDecodeErrorsRequest *kRequest = - dynamic_cast(arg); - return (kRequest->session_id() == sessionId) && (kRequest->source_id() == sourceId); -} - -MATCHER_P2(getQueuedFramesRequestMatcher, sessionId, sourceId, "") -{ - const ::firebolt::rialto::GetQueuedFramesRequest *kRequest = - dynamic_cast(arg); - return (kRequest->session_id() == sessionId) && (kRequest->source_id() == sourceId); -} - MATCHER_P2(getStatsRequestMatcher, sessionId, sourceId, "") { const ::firebolt::rialto::GetStatsRequest *kRequest = dynamic_cast(arg); diff --git a/tests/componenttests/client/mocks/MediaPipelineModuleMock.h b/tests/componenttests/client/mocks/MediaPipelineModuleMock.h index 6988528f4..4960afae5 100644 --- a/tests/componenttests/client/mocks/MediaPipelineModuleMock.h +++ b/tests/componenttests/client/mocks/MediaPipelineModuleMock.h @@ -75,13 +75,6 @@ class MediaPipelineModuleMock : public ::firebolt::rialto::MediaPipelineModule (::google::protobuf::RpcController * controller, const ::firebolt::rialto::GetImmediateOutputRequest *request, ::firebolt::rialto::GetImmediateOutputResponse *response, ::google::protobuf::Closure *done)); - MOCK_METHOD(void, setReportDecodeErrors, - (::google::protobuf::RpcController * controller, - const ::firebolt::rialto::SetReportDecodeErrorsRequest *request, - ::firebolt::rialto::SetReportDecodeErrorsResponse *response, ::google::protobuf::Closure *done)); - MOCK_METHOD(void, getQueuedFrames, - (::google::protobuf::RpcController * controller, const ::firebolt::rialto::GetQueuedFramesRequest *request, - ::firebolt::rialto::GetQueuedFramesResponse *response, ::google::protobuf::Closure *done)); MOCK_METHOD(void, getStats, (::google::protobuf::RpcController * controller, const ::firebolt::rialto::GetStatsRequest *request, ::firebolt::rialto::GetStatsResponse *response, ::google::protobuf::Closure *done)); @@ -248,19 +241,6 @@ class MediaPipelineModuleMock : public ::firebolt::rialto::MediaPipelineModule return response; } - ::firebolt::rialto::SetReportDecodeErrorsResponse SetReportDecodeErrorsResponse() - { - firebolt::rialto::SetReportDecodeErrorsResponse response; - return response; - } - - ::firebolt::rialto::GetQueuedFramesResponse getQueuedFramesResponse(uint32_t queuedFramesResponse) - { - firebolt::rialto::GetQueuedFramesResponse response; - response.set_queued_frames(queuedFramesResponse); - return response; - } - ::firebolt::rialto::GetStatsResponse getStatsResponse(const uint64_t renderedFrames, const uint64_t droppedFrames) { firebolt::rialto::GetStatsResponse response; diff --git a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp index 3ff237a05..00693bbdf 100644 --- a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp +++ b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.cpp @@ -1465,34 +1465,6 @@ void MediaPipelineTestMethods::getImmediateOutput(bool immediateOutput) EXPECT_TRUE(m_mediaPipeline->getImmediateOutput(kVideoSourceId, immediateOutput)); } -void MediaPipelineTestMethods::shouldSetReportDecodeErrors(bool reportDecodeErrors) -{ - EXPECT_CALL(*m_mediaPipelineModuleMock, - setReportDecodeErrors(_, setReportDecodeErrorsRequestMatcher(kSessionId, kVideoSourceId), _, _)) - .WillOnce(DoAll(SetArgPointee<2>(m_mediaPipelineModuleMock->SetReportDecodeErrorsResponse()), - WithArgs<0, 3>(Invoke(&(*m_mediaPipelineModuleMock), &MediaPipelineModuleMock::defaultReturn)))); -} - -void MediaPipelineTestMethods::setReportDecodeErrors(bool reportDecodeErrors) -{ - EXPECT_TRUE(m_mediaPipeline->setReportDecodeErrors(kVideoSourceId, reportDecodeErrors)); -} - -void MediaPipelineTestMethods::shouldGetQueuedFrames(uint32_t queuedFrames) -{ - EXPECT_CALL(*m_mediaPipelineModuleMock, - getQueuedFrames(_, getQueuedFramesRequestMatcher(kSessionId, kVideoSourceId), _, _)) - .WillOnce(DoAll(SetArgPointee<2>(m_mediaPipelineModuleMock->getQueuedFramesResponse(queuedFrames)), - WithArgs<0, 3>(Invoke(&(*m_mediaPipelineModuleMock), &MediaPipelineModuleMock::defaultReturn)))); -} - -void MediaPipelineTestMethods::getQueuedFrames(uint32_t queuedFrames) -{ - uint32_t returnQueuedFrames; - EXPECT_TRUE(m_mediaPipeline->getQueuedFrames(kVideoSourceId, returnQueuedFrames)); - EXPECT_EQ(returnQueuedFrames, queuedFrames); -} - void MediaPipelineTestMethods::shouldGetStats(uint64_t renderedFrames, uint64_t droppedFrames) { EXPECT_CALL(*m_mediaPipelineModuleMock, getStats(_, getStatsRequestMatcher(kSessionId, kVideoSourceId), _, _)) diff --git a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.h b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.h index a3a245fe6..ebe103cc3 100644 --- a/tests/componenttests/client/tests/base/MediaPipelineTestMethods.h +++ b/tests/componenttests/client/tests/base/MediaPipelineTestMethods.h @@ -131,8 +131,6 @@ class MediaPipelineTestMethods void shouldGetDuration(const int64_t duration); void shouldSetImmediateOutput(bool immediateOutput); void shouldGetImmediateOutput(bool immediateOutput); - void shouldSetReportDecodeErrors(bool reportDecodeErrors); - void shouldGetQueuedFrames(uint32_t queuedFrames); void shouldGetStats(uint64_t renderedFrames, uint64_t droppedFrames); void shouldFlush(); void shouldFailToFlush(); @@ -252,8 +250,6 @@ class MediaPipelineTestMethods void getDuration(const int64_t expectedDuration); void setImmediateOutput(bool immediateOutput); void getImmediateOutput(bool immediateOutput); - void setReportDecodeErrors(bool reportDecodeErrors); - void getQueuedFrames(uint32_t queuedFrames); void getStats(uint64_t expectedFrames, uint64_t expectedDropped); void createMediaPipelineCapabilitiesObject(); void destroyMediaPipelineCapabilitiesObject(); diff --git a/tests/componenttests/client/tests/mse/PipelinePropertyTest.cpp b/tests/componenttests/client/tests/mse/PipelinePropertyTest.cpp index dd6164c1f..374253730 100644 --- a/tests/componenttests/client/tests/mse/PipelinePropertyTest.cpp +++ b/tests/componenttests/client/tests/mse/PipelinePropertyTest.cpp @@ -187,17 +187,7 @@ TEST_F(PipelinePropertyTest, setAndGetPipelineProperties) MediaPipelineTestMethods::shouldGetUseBuffering(useBuffering); MediaPipelineTestMethods::getUseBuffering(useBuffering); - // Step 13: Set Report Decode Errors - bool reportDecodeErrors{true}; - MediaPipelineTestMethods::shouldSetReportDecodeErrors(reportDecodeErrors); - MediaPipelineTestMethods::setReportDecodeErrors(reportDecodeErrors); - - // Step 14: Get Queued Frames - uint32_t queuedFrames{123}; - MediaPipelineTestMethods::shouldGetQueuedFrames(queuedFrames); - MediaPipelineTestMethods::getQueuedFrames(queuedFrames); - - // Step 15: Get Duration + // Step 13: Get Duration constexpr int64_t duration{123456789}; MediaPipelineTestMethods::shouldGetDuration(duration); MediaPipelineTestMethods::getDuration(duration); diff --git a/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp b/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp index 3b04393d2..e75140fb1 100644 --- a/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp +++ b/tests/componenttests/server/tests/mediaPipeline/PipelinePropertyTest.cpp @@ -160,16 +160,6 @@ class PipelinePropertyTest : public MediaPipelineTest *returnVal = value ? TRUE : FALSE; })); } - else if constexpr (std::is_same_v) - { - EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq(propertyName.c_str()), _)) - .WillOnce(Invoke( - [&](gpointer, const gchar *, void *val) - { - guint *returnVal = reinterpret_cast(val); - *returnVal = value; - })); - } else if constexpr (std::is_same_v) { EXPECT_CALL(*m_glibWrapperMock, gObjectGetStub(_, StrEq(propertyName.c_str()), _)) @@ -640,7 +630,7 @@ TEST_F(PipelinePropertyTest, pipelinePropertyGetAndSetSuccess) willStop(); stop(); - // Step 18: Destroy media session + // Step 19: Destroy media session gstPlayerWillBeDestructed(); destroySession(); } diff --git a/tests/unittests/media/client/ipc/CMakeLists.txt b/tests/unittests/media/client/ipc/CMakeLists.txt index 54347d4df..a86efde21 100644 --- a/tests/unittests/media/client/ipc/CMakeLists.txt +++ b/tests/unittests/media/client/ipc/CMakeLists.txt @@ -38,8 +38,6 @@ add_gtests ( mediaPipelineIpc/GetDurationTest.cpp mediaPipelineIpc/SetImmediateOutputTest.cpp mediaPipelineIpc/GetImmediateOutputTest.cpp - mediaPipelineIpc/SetReportDecodeErrorsTest.cpp - mediaPipelineIpc/GetQueuedFramesTest.cpp mediaPipelineIpc/GetStatsTest.cpp mediaPipelineIpc/RenderFrameTest.cpp mediaPipelineIpc/GetVolumeTest.cpp diff --git a/tests/unittests/media/client/ipc/mediaPipelineIpc/GetQueuedFramesTest.cpp b/tests/unittests/media/client/ipc/mediaPipelineIpc/GetQueuedFramesTest.cpp deleted file mode 100644 index 17bdd5c84..000000000 --- a/tests/unittests/media/client/ipc/mediaPipelineIpc/GetQueuedFramesTest.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2026 Sky UK - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "MediaPipelineIpcTestBase.h" -#include "MediaPipelineProtoRequestMatchers.h" - -class RialtoClientMediaPipelineIpcGetQueuedFramesTest : public MediaPipelineIpcTestBase -{ -protected: - virtual void SetUp() - { - MediaPipelineIpcTestBase::SetUp(); - - createMediaPipelineIpc(); - } - - virtual void TearDown() - { - destroyMediaPipelineIpc(); - - MediaPipelineIpcTestBase::TearDown(); - } - - const int32_t m_kSourceId{1}; -}; - -/** - * Test that getQueuedFrames can be called successfully. - */ -TEST_F(RialtoClientMediaPipelineIpcGetQueuedFramesTest, Success) -{ - expectIpcApiCallSuccess(); - - EXPECT_CALL(*m_channelMock, - CallMethod(methodMatcher("getQueuedFrames"), m_controllerMock.get(), - getQueuedFramesRequestMatcher(m_sessionId, m_kSourceId), _, m_blockingClosureMock.get())); - - uint32_t queuedFrames; - EXPECT_TRUE(m_mediaPipelineIpc->getQueuedFrames(m_kSourceId, queuedFrames)); -} - -/** - * Test that getQueuedFrames fails if the ipc channel disconnected. - */ -TEST_F(RialtoClientMediaPipelineIpcGetQueuedFramesTest, ChannelDisconnected) -{ - expectIpcApiCallDisconnected(); - expectUnsubscribeEvents(); - - uint32_t queuedFrames; - EXPECT_FALSE(m_mediaPipelineIpc->getQueuedFrames(m_kSourceId, queuedFrames)); - - // Reattach channel on destroySession - EXPECT_CALL(*m_ipcClientMock, getChannel()).WillOnce(Return(m_channelMock)).RetiresOnSaturation(); - expectSubscribeEvents(); -} - -/** - * Test that getQueuedFrames fails if the ipc channel disconnected and succeeds if the channel is reconnected. - */ -TEST_F(RialtoClientMediaPipelineIpcGetQueuedFramesTest, ReconnectChannel) -{ - expectIpcApiCallReconnected(); - expectUnsubscribeEvents(); - expectSubscribeEvents(); - - EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("getQueuedFrames"), _, _, _, _)); - - uint32_t queuedFrames; - EXPECT_TRUE(m_mediaPipelineIpc->getQueuedFrames(m_kSourceId, queuedFrames)); -} - -/** - * Test that getQueuedFrames fails when ipc fails. - */ -TEST_F(RialtoClientMediaPipelineIpcGetQueuedFramesTest, GetQueuedFramesFailure) -{ - expectIpcApiCallFailure(); - - EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("getQueuedFrames"), _, _, _, _)); - - uint32_t queuedFrames; - EXPECT_FALSE(m_mediaPipelineIpc->getQueuedFrames(m_kSourceId, queuedFrames)); -} diff --git a/tests/unittests/media/client/ipc/mediaPipelineIpc/SetReportDecodeErrorsTest.cpp b/tests/unittests/media/client/ipc/mediaPipelineIpc/SetReportDecodeErrorsTest.cpp deleted file mode 100644 index d7e5fb8d2..000000000 --- a/tests/unittests/media/client/ipc/mediaPipelineIpc/SetReportDecodeErrorsTest.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2025 Sky UK - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "MediaPipelineIpcTestBase.h" -#include "MediaPipelineProtoRequestMatchers.h" - -class RialtoClientMediaPipelineIpcSetReportDecodeErrorsTest : public MediaPipelineIpcTestBase -{ -protected: - virtual void SetUp() - { - MediaPipelineIpcTestBase::SetUp(); - - createMediaPipelineIpc(); - } - - virtual void TearDown() - { - destroyMediaPipelineIpc(); - - MediaPipelineIpcTestBase::TearDown(); - } - - const int32_t m_kSourceId{1}; -}; - -/** - * Test that setReportDecodeErrors can be called successfully. - */ -TEST_F(RialtoClientMediaPipelineIpcSetReportDecodeErrorsTest, Success) -{ - expectIpcApiCallSuccess(); - - EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("setReportDecodeErrors"), m_controllerMock.get(), - setReportDecodeErrorsRequestMatcher(m_sessionId, m_kSourceId), _, - m_blockingClosureMock.get())); - - EXPECT_TRUE(m_mediaPipelineIpc->setReportDecodeErrors(m_kSourceId, true)); -} - -/** - * Test that setReportDecodeErrors fails if the ipc channel disconnected. - */ -TEST_F(RialtoClientMediaPipelineIpcSetReportDecodeErrorsTest, ChannelDisconnected) -{ - expectIpcApiCallDisconnected(); - expectUnsubscribeEvents(); - - EXPECT_FALSE(m_mediaPipelineIpc->setReportDecodeErrors(m_kSourceId, true)); - - // Reattach channel on destroySession - EXPECT_CALL(*m_ipcClientMock, getChannel()).WillOnce(Return(m_channelMock)).RetiresOnSaturation(); - expectSubscribeEvents(); -} - -/** - * Test that setReportDecodeErrors fails if the ipc channel disconnected and succeeds if the channel is reconnected. - */ -TEST_F(RialtoClientMediaPipelineIpcSetReportDecodeErrorsTest, ReconnectChannel) -{ - expectIpcApiCallReconnected(); - expectUnsubscribeEvents(); - expectSubscribeEvents(); - - EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("setReportDecodeErrors"), _, _, _, _)); - - EXPECT_TRUE(m_mediaPipelineIpc->setReportDecodeErrors(m_kSourceId, true)); -} - -/** - * Test that setReportDecodeErrors fails when ipc fails. - */ -TEST_F(RialtoClientMediaPipelineIpcSetReportDecodeErrorsTest, SetReportDecodeErrorsFailure) -{ - expectIpcApiCallFailure(); - - EXPECT_CALL(*m_channelMock, CallMethod(methodMatcher("setReportDecodeErrors"), _, _, _, _)); - - EXPECT_FALSE(m_mediaPipelineIpc->setReportDecodeErrors(m_kSourceId, true)); -} diff --git a/tests/unittests/media/client/main/CMakeLists.txt b/tests/unittests/media/client/main/CMakeLists.txt index 36ef5cd7e..7518c2e2a 100644 --- a/tests/unittests/media/client/main/CMakeLists.txt +++ b/tests/unittests/media/client/main/CMakeLists.txt @@ -35,8 +35,6 @@ add_gtests ( mediaPipeline/GetDurationTest.cpp mediaPipeline/SetImmediateOutputTest.cpp mediaPipeline/GetImmediateOutputTest.cpp - mediaPipeline/SetReportDecodeErrorsTest.cpp - mediaPipeline/GetQueuedFramesTest.cpp mediaPipeline/GetStatsTest.cpp mediaPipeline/RenderFrameTest.cpp mediaPipeline/SetVolumeTest.cpp diff --git a/tests/unittests/media/client/main/mediaPipeline/GetQueuedFramesTest.cpp b/tests/unittests/media/client/main/mediaPipeline/GetQueuedFramesTest.cpp deleted file mode 100644 index 277b46872..000000000 --- a/tests/unittests/media/client/main/mediaPipeline/GetQueuedFramesTest.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2025 Sky UK - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "MediaPipelineTestBase.h" - -class RialtoClientMediaPipelineGetQueuedFramesTest : public MediaPipelineTestBase -{ -protected: - const int32_t m_kSourceId{1}; - - virtual void SetUp() - { - MediaPipelineTestBase::SetUp(); - - createMediaPipeline(); - } - - virtual void TearDown() - { - destroyMediaPipeline(); - - MediaPipelineTestBase::TearDown(); - } -}; - -/** - * Test that getQueuedFrames returns success if the IPC API succeeds. - */ -TEST_F(RialtoClientMediaPipelineGetQueuedFramesTest, GetQueuedFramesSuccess) -{ - constexpr uint32_t kExpectedQueuedFrames{123}; - EXPECT_CALL(*m_mediaPipelineIpcMock, getQueuedFrames(m_kSourceId, _)) - .WillOnce(DoAll(SetArgReferee<1>(123), Return(true))); - uint32_t queuedFrames; - EXPECT_TRUE(m_mediaPipeline->getQueuedFrames(m_kSourceId, queuedFrames)); - EXPECT_EQ(kExpectedQueuedFrames, queuedFrames); -} - -/** - * Test that getQueuedFrames returns failure if the IPC API fails. - */ -TEST_F(RialtoClientMediaPipelineGetQueuedFramesTest, GetQueuedFramesFailure) -{ - EXPECT_CALL(*m_mediaPipelineIpcMock, getQueuedFrames(m_kSourceId, _)).WillOnce(Return(false)); - uint32_t queuedFrames; - EXPECT_FALSE(m_mediaPipeline->getQueuedFrames(m_kSourceId, queuedFrames)); -} diff --git a/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp b/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp index 563291b6b..ec5e31896 100644 --- a/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp +++ b/tests/unittests/media/client/main/mediaPipeline/MediaPipelineProxyTest.cpp @@ -173,21 +173,6 @@ TEST_F(RialtoClientMediaPipelineProxyTest, TestPassthrough) ///////////////////////////////////////////// - EXPECT_CALL(*mediaPipelineMock, setReportDecodeErrors(kSourceId, true)).WillOnce(Return(true)); - EXPECT_TRUE(proxy->setReportDecodeErrors(kSourceId, true)); - - ///////////////////////////////////////////// - - { - const uint32_t kQueuedFrames{123}; - uint32_t returnQueuedFrames; - EXPECT_CALL(*mediaPipelineMock, getQueuedFrames(kSourceId, _)) - .WillOnce(DoAll(SetArgReferee<1>(kQueuedFrames), Return(true))); - EXPECT_TRUE(proxy->getQueuedFrames(kSourceId, returnQueuedFrames)); - EXPECT_EQ(returnQueuedFrames, kQueuedFrames); - } - ///////////////////////////////////////////// - EXPECT_CALL(*mediaPipelineMock, setVideoWindow(1, 2, 3, 4)).WillOnce(Return(true)); EXPECT_TRUE(proxy->setVideoWindow(1, 2, 3, 4)); diff --git a/tests/unittests/media/client/main/mediaPipeline/SetReportDecodeErrorsTest.cpp b/tests/unittests/media/client/main/mediaPipeline/SetReportDecodeErrorsTest.cpp deleted file mode 100644 index 29f10533d..000000000 --- a/tests/unittests/media/client/main/mediaPipeline/SetReportDecodeErrorsTest.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2025 Sky UK - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "MediaPipelineTestBase.h" - -class RialtoClientMediaPipelineSetReportDecodeErrorsTest : public MediaPipelineTestBase -{ -protected: - const int32_t m_kSourceId{1}; - - virtual void SetUp() - { - MediaPipelineTestBase::SetUp(); - - createMediaPipeline(); - } - - virtual void TearDown() - { - destroyMediaPipeline(); - - MediaPipelineTestBase::TearDown(); - } -}; - -/** - * Test that setReportDecodeErrors returns success if the IPC API succeeds. - */ -TEST_F(RialtoClientMediaPipelineSetReportDecodeErrorsTest, SetReportDecodeErrorsSuccess) -{ - EXPECT_CALL(*m_mediaPipelineIpcMock, setReportDecodeErrors(m_kSourceId, _)).WillOnce(Return(true)); - EXPECT_TRUE(m_mediaPipeline->setReportDecodeErrors(m_kSourceId, true)); -} - -/** - * Test that setReportDecodeErrors returns failure if the IPC API fails. - */ -TEST_F(RialtoClientMediaPipelineSetReportDecodeErrorsTest, SetReportDecodeErrorsFailure) -{ - EXPECT_CALL(*m_mediaPipelineIpcMock, setReportDecodeErrors(m_kSourceId, _)).WillOnce(Return(false)); - EXPECT_FALSE(m_mediaPipeline->setReportDecodeErrors(m_kSourceId, true)); -} diff --git a/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h b/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h index 2d90eb922..b955fce2a 100644 --- a/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h +++ b/tests/unittests/media/client/mocks/ipc/MediaPipelineIpcMock.h @@ -48,8 +48,6 @@ class MediaPipelineIpcMock : public IMediaPipelineIpc MOCK_METHOD(bool, getPosition, (int64_t & position), (override)); MOCK_METHOD(bool, setImmediateOutput, (int32_t sourceId, bool immediateOutput), (override)); MOCK_METHOD(bool, getImmediateOutput, (int32_t sourceId, bool &immediateOutput), (override)); - MOCK_METHOD(bool, setReportDecodeErrors, (int32_t sourceId, bool reportDecodeErrors), (override)); - MOCK_METHOD(bool, getQueuedFrames, (int32_t sourceId, uint32_t &queuedFrames), (override)); MOCK_METHOD(bool, getStats, (int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames), (override)); MOCK_METHOD(bool, setPlaybackRate, (double rate), (override)); MOCK_METHOD(bool, renderFrame, (), (override)); diff --git a/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h b/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h index c6fd26247..54349ba06 100644 --- a/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h +++ b/tests/unittests/media/client/mocks/main/MediaPipelineAndControlClientMock.h @@ -51,8 +51,6 @@ class MediaPipelineAndControlClientMock : public IMediaPipelineAndIControlClient MOCK_METHOD(bool, getPosition, (int64_t & position), (override)); MOCK_METHOD(bool, setImmediateOutput, (int32_t sourceId, bool immediateOutput), (override)); MOCK_METHOD(bool, getImmediateOutput, (int32_t sourceId, bool &immediateOutput), (override)); - MOCK_METHOD(bool, setReportDecodeErrors, (int32_t sourceId, bool reportDecodeErrors), (override)); - MOCK_METHOD(bool, getQueuedFrames, (int32_t sourceId, uint32_t &queuedFrames), (override)); MOCK_METHOD(bool, getStats, (int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames), (override)); MOCK_METHOD(bool, setVideoWindow, (uint32_t x, uint32_t y, uint32_t width, uint32_t height), (override)); diff --git a/tests/unittests/media/server/gstplayer/CMakeLists.txt b/tests/unittests/media/server/gstplayer/CMakeLists.txt index 5bfdf7f05..c2a1c1e02 100644 --- a/tests/unittests/media/server/gstplayer/CMakeLists.txt +++ b/tests/unittests/media/server/gstplayer/CMakeLists.txt @@ -49,7 +49,6 @@ add_gtests(RialtoServerGstPlayerUnitTests genericPlayer/tasksTests/SetBufferingLimitTest.cpp genericPlayer/tasksTests/SetLowLatencyTest.cpp genericPlayer/tasksTests/SetImmediateOutputTest.cpp - genericPlayer/tasksTests/SetReportDecodeErrorsTest.cpp genericPlayer/tasksTests/SetMuteTest.cpp genericPlayer/tasksTests/SetPlaybackRateTest.cpp genericPlayer/tasksTests/SetPositionTest.cpp diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp index 5e34f24b6..4544074a8 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp @@ -72,7 +72,6 @@ const std::string kAutoVideoSinkTypeName{"GstAutoVideoSink"}; const std::string kAutoAudioSinkTypeName{"GstAutoAudioSink"}; constexpr bool kResetTime{true}; const std::string kImmediateOutputStr{"immediate-output"}; -const std::string kReportDecodeErrorsStr{"report-decode-errors"}; const std::string kLowLatencyStr{"low-latency"}; const std::string kSyncStr{"sync"}; const std::string kSyncOffStr{"sync-off"}; @@ -357,35 +356,6 @@ TEST_F(GstGenericPlayerPrivateTest, shouldSetImmediateOutput) EXPECT_TRUE(m_sut->setImmediateOutput()); } -TEST_F(GstGenericPlayerPrivateTest, shouldFailToSetReportDecodeErrorsIfPropertyDoesntExist) -{ - modifyContext([&](GenericPlayerContext &context) { context.pendingReportDecodeErrorsForVideo = true; }); - - expectGetVideoDecoder(m_realElement); - - expectPropertyDoesntExist(m_glibWrapperMock, m_gstWrapperMock, m_realElement, kReportDecodeErrorsStr); - EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_realElement)).Times(1); - EXPECT_FALSE(m_sut->setReportDecodeErrors()); -} - -TEST_F(GstGenericPlayerPrivateTest, shouldSetReportDecodeErrors) -{ - modifyContext([&](GenericPlayerContext &context) { context.pendingReportDecodeErrorsForVideo = true; }); - - expectGetVideoDecoder(m_realElement); - - GParamSpec gParamSpec{}; - - EXPECT_CALL(*m_glibWrapperMock, - gObjectClassFindProperty(G_OBJECT_GET_CLASS(m_realElement), StrEq(kReportDecodeErrorsStr))) - .WillOnce(Return(&gParamSpec)); - - EXPECT_CALL(*m_glibWrapperMock, gObjectSetStub(m_realElement, StrEq(kReportDecodeErrorsStr))).Times(1); - - EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_realElement)).Times(1); - EXPECT_TRUE(m_sut->setReportDecodeErrors()); -} - TEST_F(GstGenericPlayerPrivateTest, shouldFailToSetLowLatencyIfSinkIsNull) { modifyContext([&](GenericPlayerContext &context) { context.pendingLowLatency = true; }); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp index 2352dacf9..e5f26e6db 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp @@ -453,43 +453,6 @@ TEST_F(GstGenericPlayerTest, shouldFailToGetImmediateOutputInPlayingStateIfPrope EXPECT_FALSE(m_sut->getImmediateOutput(MediaSourceType::VIDEO, immediateOutputState)); } -TEST_F(GstGenericPlayerTest, shouldSetReportDecodeErrors) -{ - std::unique_ptr task{std::make_unique>()}; - EXPECT_CALL(dynamic_cast &>(*task), execute()); - EXPECT_CALL(m_taskFactoryMock, createSetReportDecodeErrors(_, _, MediaSourceType::VIDEO, true)) - .WillOnce(Return(ByMove(std::move(task)))); - - EXPECT_TRUE(m_sut->setReportDecodeErrors(MediaSourceType::VIDEO, true)); -} - -TEST_F(GstGenericPlayerTest, shouldGetQueuedFramesInPlayingState) -{ - setPipelineState(GST_STATE_PLAYING); - const uint32_t kTestQueuedFramesValue{123}; - const std::string kPropertyStr{"queued-frames"}; - - expectGetVideoDecoder(m_element); - willGetElementProperty(kPropertyStr, kTestQueuedFramesValue); - - uint32_t queuedFrames; - EXPECT_TRUE(m_sut->getQueuedFrames(queuedFrames)); - EXPECT_EQ(queuedFrames, kTestQueuedFramesValue); -} - -TEST_F(GstGenericPlayerTest, shouldFailToGetQueuedFramesInPlayingStateIfPropertyDoesntExist) -{ - setPipelineState(GST_STATE_PLAYING); - - expectGetVideoDecoder(m_element); - - EXPECT_CALL(*m_glibWrapperMock, gObjectClassFindProperty(_, StrEq("queued-frames"))).WillOnce(Return(nullptr)); - EXPECT_CALL(*m_gstWrapperMock, gstObjectUnref(m_element)).Times(1); - - uint32_t queuedFrames; - EXPECT_FALSE(m_sut->getQueuedFrames(queuedFrames)); -} - TEST_F(GstGenericPlayerTest, shouldGetStatsInPlayingState) { constexpr guint64 kRenderedFrames{1234}; diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp index c7a6f39e6..5e26811cc 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp @@ -46,7 +46,6 @@ #include "tasks/generic/SetMute.h" #include "tasks/generic/SetPlaybackRate.h" #include "tasks/generic/SetPosition.h" -#include "tasks/generic/SetReportDecodeErrors.h" #include "tasks/generic/SetSourcePosition.h" #include "tasks/generic/SetStreamSyncMode.h" #include "tasks/generic/SetSubtitleOffset.h" @@ -3359,20 +3358,6 @@ void GenericTasksTestsBase::triggerSetImmediateOutput() EXPECT_EQ(testContext->m_context.pendingImmediateOutputForVideo, true); } -void GenericTasksTestsBase::shouldSetReportDecodeErrors() -{ - EXPECT_CALL(testContext->m_gstPlayer, setReportDecodeErrors()).WillOnce(Return(true)); -} - -void GenericTasksTestsBase::triggerSetReportDecodeErrors() -{ - firebolt::rialto::server::tasks::generic::SetReportDecodeErrors task{testContext->m_context, testContext->m_gstPlayer, - MediaSourceType::VIDEO, true}; - task.execute(); - - EXPECT_EQ(testContext->m_context.pendingReportDecodeErrorsForVideo, true); -} - void GenericTasksTestsBase::shouldSetLowLatency() { EXPECT_CALL(testContext->m_gstPlayer, setLowLatency()).WillOnce(Return(true)); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h index c62fbdd23..7d212b97d 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h @@ -280,10 +280,6 @@ class GenericTasksTestsBase : public ::testing::Test void shouldSetVideoMute(); void shouldSetSubtitleMute(); - // report-decode-errors decoder property test method - void shouldSetReportDecodeErrors(); - void triggerSetReportDecodeErrors(); - // immediate-output sink property test methods void shouldSetImmediateOutput(); void triggerSetImmediateOutput(); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp index 3384ef99e..02faa219f 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.cpp @@ -219,20 +219,6 @@ void GstGenericPlayerTestCommon::expectGetDecoder(GstElement *element) EXPECT_CALL(*m_gstWrapperMock, gstIteratorFree(&m_it)); } -void GstGenericPlayerTestCommon::expectGetVideoDecoder(GstElement *element) -{ - EXPECT_CALL(*m_gstWrapperMock, gstBinIterateRecurse(GST_BIN(&m_pipeline))).WillOnce(Return(&m_it)); - EXPECT_CALL(*m_gstWrapperMock, gstIteratorNext(&m_it, _)).WillOnce(Return(GST_ITERATOR_OK)); - EXPECT_CALL(*m_glibWrapperMock, gValueGetObject(_)).WillOnce(Return(element)); - EXPECT_CALL(*m_gstWrapperMock, gstElementGetFactory(element)).WillOnce(Return(m_factory)); - EXPECT_CALL(*m_gstWrapperMock, gstElementFactoryListIsType(m_factory, (GST_ELEMENT_FACTORY_TYPE_DECODER | - GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO))) - .WillOnce(Return(TRUE)); - EXPECT_CALL(*m_gstWrapperMock, gstObjectRef(element)).WillOnce(Return(element)); - EXPECT_CALL(*m_glibWrapperMock, gValueUnset(_)); - EXPECT_CALL(*m_gstWrapperMock, gstIteratorFree(&m_it)); -} - void GstGenericPlayerTestCommon::expectGetVideoParser(GstElement *element) { EXPECT_CALL(*m_gstWrapperMock, gstBinIterateRecurse(GST_BIN(&m_pipeline))).WillOnce(Return(&m_it)); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h index 6b3c97c2d..a3415bb7e 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GstGenericPlayerTestCommon.h @@ -126,7 +126,6 @@ class GstGenericPlayerTestCommon : public ::testing::Test void expectCheckPlaySink(); void expectSetMessageCallback(); void expectGetDecoder(GstElement *element); - void expectGetVideoDecoder(GstElement *element); void expectGetVideoParser(GstElement *element); void expectGetAVSink(const std::string &sinkName, GstElement *elementObj); void expectGetSink(const std::string &sinkName, GstElement *elementObj); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp index d25627cda..70c305e22 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp @@ -53,7 +53,6 @@ #include "tasks/generic/SetMute.h" #include "tasks/generic/SetPlaybackRate.h" #include "tasks/generic/SetPosition.h" -#include "tasks/generic/SetReportDecodeErrors.h" #include "tasks/generic/SetSourcePosition.h" #include "tasks/generic/SetStreamSyncMode.h" #include "tasks/generic/SetSync.h" @@ -345,13 +344,6 @@ TEST_F(GenericPlayerTaskFactoryTest, ShouldCreateSetImmediateOutput) EXPECT_NO_THROW(dynamic_cast(*task)); } -TEST_F(GenericPlayerTaskFactoryTest, ShouldCreateSetReportDecodeErrors) -{ - auto task = m_sut.createSetReportDecodeErrors(m_context, m_gstPlayer, firebolt::rialto::MediaSourceType::VIDEO, true); - EXPECT_NE(task, nullptr); - EXPECT_NO_THROW(dynamic_cast(*task)); -} - TEST_F(GenericPlayerTaskFactoryTest, ShouldCreateSetTextTrackIdentifier) { auto task = m_sut.createSetTextTrackIdentifier(m_context, ""); diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/SetReportDecodeErrorsTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/SetReportDecodeErrorsTest.cpp deleted file mode 100644 index 3d3f89f96..000000000 --- a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/SetReportDecodeErrorsTest.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2025 Sky UK - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "GenericTasksTestsBase.h" - -class SetReportDecodeErrorsTest : public GenericTasksTestsBase -{ -}; - -TEST_F(SetReportDecodeErrorsTest, shouldSetReportDecodeErrors) -{ - shouldSetReportDecodeErrors(); - triggerSetReportDecodeErrors(); -} diff --git a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTests.cpp b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTests.cpp index e4776c7c3..472dff026 100644 --- a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTests.cpp +++ b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTests.cpp @@ -297,38 +297,6 @@ TEST_F(MediaPipelineModuleServiceTests, shouldFailToGetImmediateOutput) sendGetImmediateOutputRequestAndReceiveFail(); } -TEST_F(MediaPipelineModuleServiceTests, shouldSetReportDecodeErrors) -{ - mediaPipelineServiceWillCreateSession(); - sendCreateSessionRequestAndReceiveResponse(); - mediaPipelineServiceWillSetReportDecodeErrors(); - sendSetReportDecodeErrorsRequestAndReceiveResponse(); -} - -TEST_F(MediaPipelineModuleServiceTests, shouldFailToSetReportDecodeErrors) -{ - mediaPipelineServiceWillCreateSession(); - sendCreateSessionRequestAndReceiveResponse(); - mediaPipelineServiceWillFailToSetReportDecodeErrors(); - sendSetReportDecodeErrorsRequestAndReceiveFail(); -} - -TEST_F(MediaPipelineModuleServiceTests, shouldGetQueuedFrames) -{ - mediaPipelineServiceWillCreateSession(); - sendCreateSessionRequestAndReceiveResponse(); - mediaPipelineServiceWillGetQueuedFrames(); - sendGetQueuedFramesRequestAndReceiveResponse(); -} - -TEST_F(MediaPipelineModuleServiceTests, shouldFailToGetQueuedFrames) -{ - mediaPipelineServiceWillCreateSession(); - sendCreateSessionRequestAndReceiveResponse(); - mediaPipelineServiceWillFailToGetQueuedFrames(); - sendGetQueuedFramesRequestAndReceiveFail(); -} - TEST_F(MediaPipelineModuleServiceTests, shouldGetStats) { mediaPipelineServiceWillCreateSession(); diff --git a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp index 656f84864..27cd7bd40 100644 --- a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp +++ b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.cpp @@ -86,8 +86,6 @@ constexpr uint64_t kDroppedFrames{321}; constexpr uint32_t kDuration{30}; constexpr bool kImmediateOutputVal1{false}; constexpr bool kImmediateOutputVal2{true}; -constexpr bool kReportDecodeErrorsVal{false}; -constexpr uint32_t kQueuedFramesVal{123}; constexpr int64_t kDiscontinuityGap{1}; constexpr bool kIsAudioAac{false}; constexpr uint32_t kBufferingLimit{12341}; @@ -541,33 +539,6 @@ void MediaPipelineModuleServiceTests::mediaPipelineServiceWillFailToGetImmediate EXPECT_CALL(m_mediaPipelineServiceMock, getImmediateOutput(kHardcodedSessionId, _, _)).WillOnce(Return(false)); } -void MediaPipelineModuleServiceTests::mediaPipelineServiceWillSetReportDecodeErrors() -{ - expectRequestSuccess(); - EXPECT_CALL(m_mediaPipelineServiceMock, setReportDecodeErrors(kHardcodedSessionId, _, kReportDecodeErrorsVal)) - .WillOnce(Return(true)); -} - -void MediaPipelineModuleServiceTests::mediaPipelineServiceWillFailToSetReportDecodeErrors() -{ - expectRequestFailure(); - EXPECT_CALL(m_mediaPipelineServiceMock, setReportDecodeErrors(kHardcodedSessionId, _, kReportDecodeErrorsVal)) - .WillOnce(Return(false)); -} - -void MediaPipelineModuleServiceTests::mediaPipelineServiceWillGetQueuedFrames() -{ - expectRequestSuccess(); - EXPECT_CALL(m_mediaPipelineServiceMock, getQueuedFrames(kHardcodedSessionId, _, _)) - .WillOnce(DoAll(SetArgReferee<2>(kQueuedFramesVal), Return(true))); -} - -void MediaPipelineModuleServiceTests::mediaPipelineServiceWillFailToGetQueuedFrames() -{ - expectRequestFailure(); - EXPECT_CALL(m_mediaPipelineServiceMock, getQueuedFrames(kHardcodedSessionId, _, _)).WillOnce(Return(false)); -} - void MediaPipelineModuleServiceTests::mediaPipelineServiceWillGetStats() { expectRequestSuccess(); @@ -1221,50 +1192,6 @@ void MediaPipelineModuleServiceTests::sendGetImmediateOutputRequestAndReceiveFai m_service->getImmediateOutput(m_controllerMock.get(), &request, &response, m_closureMock.get()); } -void MediaPipelineModuleServiceTests::sendSetReportDecodeErrorsRequestAndReceiveResponse() -{ - firebolt::rialto::SetReportDecodeErrorsRequest request; - firebolt::rialto::SetReportDecodeErrorsResponse response; - - request.set_session_id(kHardcodedSessionId); - request.set_report_decode_errors(kReportDecodeErrorsVal); - - m_service->setReportDecodeErrors(m_controllerMock.get(), &request, &response, m_closureMock.get()); -} - -void MediaPipelineModuleServiceTests::sendSetReportDecodeErrorsRequestAndReceiveFail() -{ - firebolt::rialto::SetReportDecodeErrorsRequest request; - firebolt::rialto::SetReportDecodeErrorsResponse response; - - request.set_session_id(kHardcodedSessionId); - request.set_report_decode_errors(kReportDecodeErrorsVal); - - m_service->setReportDecodeErrors(m_controllerMock.get(), &request, &response, m_closureMock.get()); -} - -void MediaPipelineModuleServiceTests::sendGetQueuedFramesRequestAndReceiveResponse() -{ - firebolt::rialto::GetQueuedFramesRequest request; - firebolt::rialto::GetQueuedFramesResponse response; - - request.set_session_id(kHardcodedSessionId); - - m_service->getQueuedFrames(m_controllerMock.get(), &request, &response, m_closureMock.get()); - - EXPECT_EQ(response.queued_frames(), kQueuedFramesVal); -} - -void MediaPipelineModuleServiceTests::sendGetQueuedFramesRequestAndReceiveFail() -{ - firebolt::rialto::GetQueuedFramesRequest request; - firebolt::rialto::GetQueuedFramesResponse response; - - request.set_session_id(kHardcodedSessionId); - - m_service->getQueuedFrames(m_controllerMock.get(), &request, &response, m_closureMock.get()); -} - void MediaPipelineModuleServiceTests::sendGetStatsRequestAndReceiveResponse() { firebolt::rialto::GetStatsRequest request; diff --git a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.h b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.h index f71a7f8c5..fe66ac0e1 100644 --- a/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.h +++ b/tests/unittests/media/server/ipc/mediaPipelineModuleService/MediaPipelineModuleServiceTestsFixture.h @@ -80,10 +80,6 @@ class MediaPipelineModuleServiceTests : public testing::Test void mediaPipelineServiceWillFailToSetImmediateOutput(); void mediaPipelineServiceWillGetImmediateOutput(); void mediaPipelineServiceWillFailToGetImmediateOutput(); - void mediaPipelineServiceWillSetReportDecodeErrors(); - void mediaPipelineServiceWillFailToSetReportDecodeErrors(); - void mediaPipelineServiceWillGetQueuedFrames(); - void mediaPipelineServiceWillFailToGetQueuedFrames(); void mediaPipelineServiceWillGetStats(); void mediaPipelineServiceWillFailToGetStats(); void mediaPipelineServiceWillRenderFrame(); @@ -162,10 +158,6 @@ class MediaPipelineModuleServiceTests : public testing::Test void sendSetImmediateOutputRequestAndReceiveFail(); void sendGetImmediateOutputRequestAndReceiveResponse(); void sendGetImmediateOutputRequestAndReceiveFail(); - void sendSetReportDecodeErrorsRequestAndReceiveResponse(); - void sendSetReportDecodeErrorsRequestAndReceiveFail(); - void sendGetQueuedFramesRequestAndReceiveResponse(); - void sendGetQueuedFramesRequestAndReceiveFail(); void sendGetStatsRequestAndReceiveResponse(); void sendGetStatsRequestAndReceiveResponseWithoutStatsMatch(); void sendHaveDataRequestAndReceiveResponse(); diff --git a/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp b/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp index c503ddb0f..6a05a2129 100644 --- a/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp +++ b/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp @@ -366,112 +366,6 @@ TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetImmediateOutputSu EXPECT_EQ(immediateOutputState, false); } -/** - * Test that SetReportDecodeErrors returns failure if the gstreamer player is not initialized - */ -TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, SetReportDecodeErrorsFailureDueToUninitializedPlayer) -{ - mainThreadWillEnqueueTaskAndWait(); - EXPECT_FALSE(m_mediaPipeline->setReportDecodeErrors(m_kDummySourceId, true)); -} - -/** - * Test that SetReportDecodeErrors returns failure if the gstreamer API fails - */ -TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, SetReportDecodeErrorsFailure) -{ - loadGstPlayer(); - int videoSourceId = attachSource(firebolt::rialto::MediaSourceType::VIDEO, "video/h264"); - mainThreadWillEnqueueTaskAndWait(); - EXPECT_CALL(*m_gstPlayerMock, setReportDecodeErrors(_, _)).WillOnce(Return(false)); - EXPECT_FALSE(m_mediaPipeline->setReportDecodeErrors(videoSourceId, true)); -} - -/** - * Test that SetReportDecodeErrors fails if source is not present. - */ -TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, SetReportDecodeErrorsNoSourcePresent) -{ - loadGstPlayer(); - // No attachment of source - mainThreadWillEnqueueTaskAndWait(); - - EXPECT_FALSE(m_mediaPipeline->setReportDecodeErrors(m_kDummySourceId, true)); -} - -/** - * Test that SetReportDecodeErrors returns success if the gstreamer API succeeds - */ -TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, SetReportDecodeErrorsSuccess) -{ - loadGstPlayer(); - int videoSourceId = attachSource(firebolt::rialto::MediaSourceType::VIDEO, "video/h264"); - - mainThreadWillEnqueueTaskAndWait(); - EXPECT_CALL(*m_gstPlayerMock, setReportDecodeErrors(_, true)).WillOnce(Return(true)); - EXPECT_TRUE(m_mediaPipeline->setReportDecodeErrors(videoSourceId, true)); - - mainThreadWillEnqueueTaskAndWait(); - EXPECT_CALL(*m_gstPlayerMock, setReportDecodeErrors(_, false)).WillOnce(Return(true)); - EXPECT_TRUE(m_mediaPipeline->setReportDecodeErrors(videoSourceId, false)); -} - -/** - * Test that GetQueuedFrames returns failure if the gstreamer player is not initialized - */ -TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetQueuedFramesFailureDueToUninitializedPlayer) -{ - mainThreadWillEnqueueTaskAndWait(); - uint32_t queuedFrames; - EXPECT_FALSE(m_mediaPipeline->getQueuedFrames(m_kDummySourceId, queuedFrames)); -} - -/** - * Test that GetQueuedFrames returns failure if the gstreamer API fails - */ -TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetQueuedFramesFailure) -{ - loadGstPlayer(); - int videoSourceId = attachSource(firebolt::rialto::MediaSourceType::VIDEO, "video/h264"); - mainThreadWillEnqueueTaskAndWait(); - EXPECT_CALL(*m_gstPlayerMock, getQueuedFrames(_)).WillOnce(Return(false)); - uint32_t queuedFrames; - EXPECT_FALSE(m_mediaPipeline->getQueuedFrames(videoSourceId, queuedFrames)); -} - -/** - * Test that GetQueuedFrames fails if source is not present. - */ -TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetQueuedFramesNoSourcePresent) -{ - loadGstPlayer(); - // No attachment of source - mainThreadWillEnqueueTaskAndWait(); - - uint32_t queuedFrames; - EXPECT_FALSE(m_mediaPipeline->getQueuedFrames(m_kDummySourceId, queuedFrames)); -} - -/** - * Test that GetQueuedFrames returns success if the gstreamer API succeeds - */ -TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, GetQueuedFramesSuccess) -{ - loadGstPlayer(); - int videoSourceId = attachSource(firebolt::rialto::MediaSourceType::VIDEO, "video/h264"); - - uint32_t queuedFrames; - mainThreadWillEnqueueTaskAndWait(); - EXPECT_CALL(*m_gstPlayerMock, getQueuedFrames(_)).WillOnce(DoAll(SetArgReferee<0>(123), Return(true))); - EXPECT_TRUE(m_mediaPipeline->getQueuedFrames(videoSourceId, queuedFrames)); - EXPECT_EQ(queuedFrames, 123); - - mainThreadWillEnqueueTaskAndWait(); - EXPECT_CALL(*m_gstPlayerMock, getQueuedFrames(_)).WillOnce(DoAll(SetArgReferee<0>(456), Return(true))); - EXPECT_TRUE(m_mediaPipeline->getQueuedFrames(videoSourceId, queuedFrames)); - EXPECT_EQ(queuedFrames, 456); -} - /** * Test that GetStats returns failure if the gstreamer player is not initialized */ diff --git a/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h b/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h index b3fca43e0..2cb7f0a35 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h @@ -133,10 +133,6 @@ class GenericPlayerTaskFactoryMock : public IGenericPlayerTaskFactory (GenericPlayerContext & context, IGstGenericPlayerPrivate &player, const firebolt::rialto::MediaSourceType &type, bool immediateOutput), (const, override)); - MOCK_METHOD(std::unique_ptr, createSetReportDecodeErrors, - (GenericPlayerContext & context, IGstGenericPlayerPrivate &player, - const firebolt::rialto::MediaSourceType &type, bool reportDecodeErrors), - (const, override)); MOCK_METHOD(std::unique_ptr, createSetBufferingLimit, (GenericPlayerContext & context, IGstGenericPlayerPrivate &player, std::uint32_t limit), (const, override)); diff --git a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h index 087cd452e..35aa3d004 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h @@ -45,9 +45,6 @@ class GstGenericPlayerMock : public IGstGenericPlayer MOCK_METHOD(bool, getDuration, (std::int64_t & duration), (override)); MOCK_METHOD(bool, setImmediateOutput, (const MediaSourceType &mediaSourceType, bool immediateOutput), (override)); MOCK_METHOD(bool, getImmediateOutput, (const MediaSourceType &mediaSourceType, bool &immediateOutput), (override)); - MOCK_METHOD(bool, setReportDecodeErrors, (const MediaSourceType &mediaSourceType, bool reportDecodeErrors), - (override)); - MOCK_METHOD(bool, getQueuedFrames, (uint32_t & queuedFrames), (override)); MOCK_METHOD(bool, getStats, (const MediaSourceType &mediaSourceType, uint64_t &renderedFrames, uint64_t &droppedFrames), (override)); MOCK_METHOD(void, setVideoGeometry, (int x, int y, int width, int height), (override)); diff --git a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h index d0b018a87..e28c70d8d 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h @@ -39,7 +39,6 @@ class GstGenericPlayerPrivateMock : public IGstGenericPlayerPrivate MOCK_METHOD(void, scheduleAllSourcesAttached, (), (override)); MOCK_METHOD(bool, setVideoSinkRectangle, (), (override)); MOCK_METHOD(bool, setImmediateOutput, (), (override)); - MOCK_METHOD(bool, setReportDecodeErrors, (), (override)); MOCK_METHOD(bool, setLowLatency, (), (override)); MOCK_METHOD(bool, setSync, (), (override)); MOCK_METHOD(bool, setSyncOff, (), (override)); diff --git a/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h b/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h index 8e7db9fe2..fccee93d9 100644 --- a/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h +++ b/tests/unittests/media/server/mocks/main/MediaPipelineServerInternalMock.h @@ -45,8 +45,6 @@ class MediaPipelineServerInternalMock : public IMediaPipelineServerInternal MOCK_METHOD(bool, getPosition, (std::int64_t & position), (override)); MOCK_METHOD(bool, setImmediateOutput, (int32_t sourceId, bool immediateOutput), (override)); MOCK_METHOD(bool, getImmediateOutput, (int32_t sourceId, bool &immediateOutput), (override)); - MOCK_METHOD(bool, setReportDecodeErrors, (int32_t sourceId, bool reportDecodeErrors), (override)); - MOCK_METHOD(bool, getQueuedFrames, (int32_t sourceId, uint32_t &queuedFrames), (override)); MOCK_METHOD(bool, getStats, (int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames), (override)); MOCK_METHOD(bool, setVideoWindow, (uint32_t x, uint32_t y, uint32_t width, uint32_t height), (override)); MOCK_METHOD(bool, haveData, (MediaSourceStatus status, uint32_t numFrames, uint32_t needDataRequestId), (override)); diff --git a/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h b/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h index 7c9179031..3273b14a0 100644 --- a/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h +++ b/tests/unittests/media/server/mocks/service/MediaPipelineServiceMock.h @@ -47,8 +47,6 @@ class MediaPipelineServiceMock : public IMediaPipelineService MOCK_METHOD(bool, getDuration, (int sessionId, int64_t &duration), (override)); MOCK_METHOD(bool, setImmediateOutput, (int sessionId, int32_t sourceId, bool immediateOutput), (override)); MOCK_METHOD(bool, getImmediateOutput, (int sessionId, int32_t sourceId, bool &immediateOutput), (override)); - MOCK_METHOD(bool, setReportDecodeErrors, (int sessionId, int32_t sourceId, bool reportDecodeErrors), (override)); - MOCK_METHOD(bool, getQueuedFrames, (int sessionId, int32_t sourceId, uint32_t &queuedFrames), (override)); MOCK_METHOD(bool, getStats, (int sessionId, int32_t sourceId, uint64_t &renderedFrames, uint64_t &droppedFrames), (override)); MOCK_METHOD(bool, setVideoWindow, (int, std::uint32_t, std::uint32_t, std::uint32_t, std::uint32_t), (override)); diff --git a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTests.cpp b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTests.cpp index 838928736..56643a42c 100644 --- a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTests.cpp +++ b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTests.cpp @@ -382,46 +382,6 @@ TEST_F(MediaPipelineServiceTests, shouldGetImmediateOutput) getImmediateOutputShouldSucceed(); } -TEST_F(MediaPipelineServiceTests, shouldFailToSetReportDecodeErrorsForNotExistingSession) -{ - createMediaPipelineShouldSuccess(); - setReportDecodeErrorsShouldFail(); -} - -TEST_F(MediaPipelineServiceTests, shouldFailToGetQueuedFramesForNotExistingSession) -{ - createMediaPipelineShouldSuccess(); - getQueuedFramesShouldFail(); -} - -TEST_F(MediaPipelineServiceTests, shouldFailToSetReportDecodeErrors) -{ - initSession(); - mediaPipelineWillFailToSetReportDecodeErrors(); - setReportDecodeErrorsShouldFail(); -} - -TEST_F(MediaPipelineServiceTests, shouldFailToGetQueuedFrames) -{ - initSession(); - mediaPipelineWillFailToGetQueuedFrames(); - getQueuedFramesShouldFail(); -} - -TEST_F(MediaPipelineServiceTests, shouldSetReportDecodeErrors) -{ - initSession(); - mediaPipelineWillSetReportDecodeErrors(); - setReportDecodeErrorsShouldSucceed(); -} - -TEST_F(MediaPipelineServiceTests, shouldGetQueuedFrames) -{ - initSession(); - mediaPipelineWillGetQueuedFrames(); - getQueuedFramesShouldSucceed(); -} - TEST_F(MediaPipelineServiceTests, shouldFailToGetStatsForNotExistingSession) { createMediaPipelineShouldSuccess(); diff --git a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp index 0c43860d9..ca43df569 100644 --- a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp +++ b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.cpp @@ -70,7 +70,6 @@ constexpr uint32_t kBufferingLimit{4324}; constexpr bool kUseBuffering{true}; constexpr uint64_t kStopPosition{23412}; constexpr bool kIsLive{false}; -constexpr uint32_t kQueuedFrames{123}; } // namespace namespace firebolt::rialto @@ -260,26 +259,6 @@ void MediaPipelineServiceTests::mediaPipelineWillFailToGetImmediateOutput() EXPECT_CALL(m_mediaPipelineMock, getImmediateOutput(_, _)).WillOnce(Return(false)); } -void MediaPipelineServiceTests::mediaPipelineWillSetReportDecodeErrors() -{ - EXPECT_CALL(m_mediaPipelineMock, setReportDecodeErrors(_, _)).WillOnce(Return(true)); -} - -void MediaPipelineServiceTests::mediaPipelineWillFailToSetReportDecodeErrors() -{ - EXPECT_CALL(m_mediaPipelineMock, setReportDecodeErrors(_, _)).WillOnce(Return(false)); -} - -void MediaPipelineServiceTests::mediaPipelineWillGetQueuedFrames() -{ - EXPECT_CALL(m_mediaPipelineMock, getQueuedFrames(_, _)).WillOnce(DoAll(SetArgReferee<1>(123), Return(true))); -} - -void MediaPipelineServiceTests::mediaPipelineWillFailToGetQueuedFrames() -{ - EXPECT_CALL(m_mediaPipelineMock, getQueuedFrames(_, _)).WillOnce(Return(false)); -} - void MediaPipelineServiceTests::mediaPipelineWillGetStats() { EXPECT_CALL(m_mediaPipelineMock, getStats(_, _, _)) @@ -797,29 +776,6 @@ void MediaPipelineServiceTests::getImmediateOutputShouldFail() EXPECT_FALSE(m_sut->getImmediateOutput(kSessionId, kSourceId, immOp)); } -void MediaPipelineServiceTests::setReportDecodeErrorsShouldSucceed() -{ - EXPECT_TRUE(m_sut->setReportDecodeErrors(kSessionId, kSourceId, true)); -} - -void MediaPipelineServiceTests::setReportDecodeErrorsShouldFail() -{ - EXPECT_FALSE(m_sut->setReportDecodeErrors(kSessionId, kSourceId, true)); -} - -void MediaPipelineServiceTests::getQueuedFramesShouldSucceed() -{ - uint32_t queuedFr; - EXPECT_TRUE(m_sut->getQueuedFrames(kSessionId, kSourceId, queuedFr)); - EXPECT_EQ(queuedFr, kQueuedFrames); -} - -void MediaPipelineServiceTests::getQueuedFramesShouldFail() -{ - uint32_t queuedFr; - EXPECT_FALSE(m_sut->getQueuedFrames(kSessionId, kSourceId, queuedFr)); -} - void MediaPipelineServiceTests::getSupportedMimeTypesSucceed() { MediaSourceType type = MediaSourceType::VIDEO; diff --git a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.h b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.h index d76ca55a3..347d64216 100644 --- a/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.h +++ b/tests/unittests/media/server/service/mediaPipelineService/MediaPipelineServiceTestsFixture.h @@ -68,12 +68,8 @@ class MediaPipelineServiceTests : public testing::Test void mediaPipelineWillFailToGetDuration(); void mediaPipelineWillSetImmediateOutput(); void mediaPipelineWillFailToSetImmediateOutput(); - void mediaPipelineWillSetReportDecodeErrors(); - void mediaPipelineWillFailToSetReportDecodeErrors(); void mediaPipelineWillGetImmediateOutput(); void mediaPipelineWillFailToGetImmediateOutput(); - void mediaPipelineWillGetQueuedFrames(); - void mediaPipelineWillFailToGetQueuedFrames(); void mediaPipelineWillGetStats(); void mediaPipelineWillFailToGetStats(); void mediaPipelineWillRenderFrame(); @@ -164,14 +160,10 @@ class MediaPipelineServiceTests : public testing::Test void getPositionShouldFail(); void getDurationShouldSucceed(); void getDurationShouldFail(); - void setReportDecodeErrorsShouldSucceed(); - void setReportDecodeErrorsShouldFail(); void setImmediateOutputShouldSucceed(); void setImmediateOutputShouldFail(); void getImmediateOutputShouldSucceed(); void getImmediateOutputShouldFail(); - void getQueuedFramesShouldSucceed(); - void getQueuedFramesShouldFail(); void getStatsShouldSucceed(); void getStatsShouldFail(); void getSupportedMimeTypesSucceed(); From 0a716fcf7bfec03827af89b05fd1fa2a05c454bf Mon Sep 17 00:00:00 2001 From: Marcin Wojciechowski <105790697+skywojciechowskim@users.noreply.github.com> Date: Fri, 8 May 2026 13:51:28 +0200 Subject: [PATCH 24/29] Do not treat server as deadlocked when outdated acks are received (#493) Summary: Do not treat server as deadlocked when outdated acks are received Type: Fix Test Plan: UT/CT, Fullstack Jira: RDKEMW-18258 --- .../gstplayer/source/tasks/generic/Flush.cpp | 1 + .../common/source/HealthcheckService.cpp | 30 +++++++++++++------ .../common/source/HealthcheckService.h | 2 +- .../common/HealthcheckServiceTests.cpp | 19 ++++++++++++ 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/media/server/gstplayer/source/tasks/generic/Flush.cpp b/media/server/gstplayer/source/tasks/generic/Flush.cpp index ed76e7576..f4551487a 100644 --- a/media/server/gstplayer/source/tasks/generic/Flush.cpp +++ b/media/server/gstplayer/source/tasks/generic/Flush.cpp @@ -78,6 +78,7 @@ void Flush::execute() const { m_context.flushOnPrerollController->waitIfRequired(m_type); + RIALTO_SERVER_LOG_MIL("Sending flush event for %s source.", common::convertMediaSourceType(m_type)); // Flush source GstEvent *flushStart = m_gstWrapper->gstEventNewFlushStart(); if (!m_gstWrapper->gstElementSendEvent(source, flushStart)) diff --git a/serverManager/common/source/HealthcheckService.cpp b/serverManager/common/source/HealthcheckService.cpp index 6a478a64d..7abc8e205 100644 --- a/serverManager/common/source/HealthcheckService.cpp +++ b/serverManager/common/source/HealthcheckService.cpp @@ -65,7 +65,7 @@ void HealthcheckService::onPingSent(int serverId, int pingId) return; } m_remainingPings.insert(serverId); - m_failedPings.try_emplace(serverId, 0); + m_failedPings.try_emplace(serverId, std::set{}); } void HealthcheckService::onPingFailed(int serverId, int pingId) @@ -86,7 +86,7 @@ void HealthcheckService::onPingFailed(int serverId, int pingId) { m_sessionServerAppManager.onSessionServerStateChanged(serverId, firebolt::rialto::common::SessionServerState::ERROR); - m_failedPings.emplace(serverId, 1); + m_failedPings.emplace(serverId, std::set{pingId}); } } @@ -95,15 +95,26 @@ void HealthcheckService::onAckReceived(int serverId, int pingId, bool success) std::unique_lock 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); + } return; } m_remainingPings.erase(serverId); if (success) { - m_failedPings[serverId] = 0; + m_failedPings[serverId].clear(); } else { @@ -136,12 +147,13 @@ void HealthcheckService::sendPing() void HealthcheckService::handleError(int serverId) { m_sessionServerAppManager.onSessionServerStateChanged(serverId, firebolt::rialto::common::SessionServerState::ERROR); - unsigned &failedPingsNum{m_failedPings[serverId]}; - if (++failedPingsNum >= m_kNumOfFailedPingsBeforeRecovery) + auto &failedPings{m_failedPings[serverId]}; + failedPings.insert(m_currentPingId); + if (failedPings.size() >= m_kNumOfFailedPingsBeforeRecovery) { RIALTO_SERVER_MANAGER_LOG_WARN( "Max num of failed pings reached for server with id: %d. Starting recovery action", serverId); - failedPingsNum = 0; + failedPings.clear(); m_sessionServerAppManager.restartServer(serverId); } } diff --git a/serverManager/common/source/HealthcheckService.h b/serverManager/common/source/HealthcheckService.h index d0517bdb4..b3649ac0b 100644 --- a/serverManager/common/source/HealthcheckService.h +++ b/serverManager/common/source/HealthcheckService.h @@ -53,7 +53,7 @@ class HealthcheckService : public IHealthcheckService std::mutex m_mutex; int m_currentPingId; std::set m_remainingPings; - std::map m_failedPings; + std::map> m_failedPings; }; } // namespace rialto::servermanager::common diff --git a/tests/unittests/serverManager/unittests/common/HealthcheckServiceTests.cpp b/tests/unittests/serverManager/unittests/common/HealthcheckServiceTests.cpp index 4289aa15b..70d6509c8 100644 --- a/tests/unittests/serverManager/unittests/common/HealthcheckServiceTests.cpp +++ b/tests/unittests/serverManager/unittests/common/HealthcheckServiceTests.cpp @@ -285,3 +285,22 @@ TEST_F(HealthcheckServiceTests, WillFailToFailPingWithWrongId) // There should be no error indication here. triggerOnPingFailed(pingId + 1); } + +TEST_F(HealthcheckServiceTests, WillSkipRestartingServerWhenAcksAreReceivedLater) +{ + int pingId{-1}; + timerWillBeCreated(); + createSut(); + pingWillBeSent(pingId); + triggerPingTimeout(); + const int firstPingId{pingId}; + triggerOnPingSent(pingId); + errorIndicationWillBeSent(); + pingWillBeSent(pingId); + triggerPingTimeout(); + triggerOnAckReceived(kServerId, firstPingId, kSuccess); + triggerOnPingSent(pingId); + errorIndicationWillBeSent(); + pingWillBeSent(pingId); + triggerPingTimeout(); +} From 3f49c6e55f62c5b96e73386fe51d22a560857a91 Mon Sep 17 00:00:00 2001 From: Marcin Wojciechowski <105790697+skywojciechowskim@users.noreply.github.com> Date: Fri, 8 May 2026 14:31:40 +0200 Subject: [PATCH 25/29] Disable synchronous play in RialtoServer as it caused some race conditions (#494) Summary: Disable synchronous play in RialtoServer as it caused some race conditions Type: Fix Test Plan: UT/CT, Fullstack Jira: RDKEMW-18286 --- .../gstplayer/include/GstGenericPlayer.h | 5 ---- .../gstplayer/source/GstGenericPlayer.cpp | 24 +++--------------- .../source/MediaPipelineServerInternal.cpp | 2 +- .../genericPlayer/GstGenericPlayerTest.cpp | 25 ------------------- .../MiscellaneousFunctionsTest.cpp | 4 +-- .../base/MediaPipelineTestBase.cpp | 7 ------ .../base/MediaPipelineTestBase.h | 1 - 7 files changed, 6 insertions(+), 62 deletions(-) diff --git a/media/server/gstplayer/include/GstGenericPlayer.h b/media/server/gstplayer/include/GstGenericPlayer.h index bab6fbc87..03436d67b 100644 --- a/media/server/gstplayer/include/GstGenericPlayer.h +++ b/media/server/gstplayer/include/GstGenericPlayer.h @@ -492,11 +492,6 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva * @brief The object used to check flushing state for all sources */ std::unique_ptr m_flushWatcher; - - /** - * @brief The ongoing state change operations counter - */ - std::atomic m_ongoingStateChangesNumber{0}; }; } // namespace firebolt::rialto::server diff --git a/media/server/gstplayer/source/GstGenericPlayer.cpp b/media/server/gstplayer/source/GstGenericPlayer.cpp index 099b1e62b..b3bc8fa99 100644 --- a/media/server/gstplayer/source/GstGenericPlayer.cpp +++ b/media/server/gstplayer/source/GstGenericPlayer.cpp @@ -1697,30 +1697,15 @@ void GstGenericPlayer::cancelUnderflow(firebolt::rialto::MediaSourceType mediaSo void GstGenericPlayer::play(bool &async) { - if (0 == m_ongoingStateChangesNumber) - { - // Operation called on main thread, because PAUSED->PLAYING change is synchronous and needs to be done fast. - // - // m_context.pipeline can be used, because it's modified only in GstGenericPlayer - // constructor and destructor. GstGenericPlayer is created/destructed on main thread, so we won't have a crash here. - ++m_ongoingStateChangesNumber; - async = (changePipelineState(GST_STATE_PLAYING) == GST_STATE_CHANGE_ASYNC); - RIALTO_SERVER_LOG_MIL("State change to PLAYING requested"); - } - else + async = true; + if (m_workerThread) { - ++m_ongoingStateChangesNumber; - async = true; - if (m_workerThread) - { - m_workerThread->enqueueTask(m_taskFactory->createPlay(*this)); - } + m_workerThread->enqueueTask(m_taskFactory->createPlay(*this)); } } void GstGenericPlayer::pause() { - ++m_ongoingStateChangesNumber; if (m_workerThread) { m_workerThread->enqueueTask(m_taskFactory->createPause(m_context, *this)); @@ -1729,7 +1714,6 @@ void GstGenericPlayer::pause() void GstGenericPlayer::stop() { - ++m_ongoingStateChangesNumber; if (m_workerThread) { m_workerThread->enqueueTask(m_taskFactory->createStop(m_context, *this)); @@ -1743,7 +1727,6 @@ GstStateChangeReturn GstGenericPlayer::changePipelineState(GstState newState) RIALTO_SERVER_LOG_ERROR("Change state failed - pipeline is nullptr"); if (m_gstPlayerClient) m_gstPlayerClient->notifyPlaybackState(PlaybackState::FAILURE); - --m_ongoingStateChangesNumber; return GST_STATE_CHANGE_FAILURE; } m_context.flushOnPrerollController->setTargetState(newState); @@ -1754,7 +1737,6 @@ GstStateChangeReturn GstGenericPlayer::changePipelineState(GstState newState) if (m_gstPlayerClient) m_gstPlayerClient->notifyPlaybackState(PlaybackState::FAILURE); } - --m_ongoingStateChangesNumber; return result; } diff --git a/media/server/main/source/MediaPipelineServerInternal.cpp b/media/server/main/source/MediaPipelineServerInternal.cpp index 50660642a..6476c9504 100644 --- a/media/server/main/source/MediaPipelineServerInternal.cpp +++ b/media/server/main/source/MediaPipelineServerInternal.cpp @@ -347,7 +347,7 @@ bool MediaPipelineServerInternal::play(bool &async) bool result; auto task = [&]() { result = playInternal(async); }; - m_mainThread->enqueuePriorityTaskAndWait(m_mainThreadClientId, task); + m_mainThread->enqueueTaskAndWait(m_mainThreadClientId, task); return result; } diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp index e5f26e6db..f4f65086a 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp @@ -158,15 +158,6 @@ TEST_F(GstGenericPlayerTest, shouldAllSourcesAttached) TEST_F(GstGenericPlayerTest, shouldPlayOnWorkerThread) { - // Pause first - std::unique_ptr pauseTask{std::make_unique>()}; - EXPECT_CALL(dynamic_cast &>(*pauseTask), execute()); - EXPECT_CALL(m_taskFactoryMock, createPause(_, _)).WillOnce(Return(ByMove(std::move(pauseTask)))); - - m_sut->pause(); - - // ... - bool async = false; std::unique_ptr task{std::make_unique>()}; EXPECT_CALL(dynamic_cast &>(*task), execute()); @@ -176,22 +167,6 @@ TEST_F(GstGenericPlayerTest, shouldPlayOnWorkerThread) EXPECT_TRUE(async); } -TEST_F(GstGenericPlayerTest, shouldPlayImmediatelySynchronously) -{ - bool async = false; - EXPECT_CALL(*m_gstWrapperMock, gstElementSetState(_, GST_STATE_PLAYING)).WillOnce(Return(GST_STATE_CHANGE_SUCCESS)); - m_sut->play(async); - EXPECT_FALSE(async); -} - -TEST_F(GstGenericPlayerTest, shouldPlayImmediatelyAsynchronously) -{ - bool async = false; - EXPECT_CALL(*m_gstWrapperMock, gstElementSetState(_, GST_STATE_PLAYING)).WillOnce(Return(GST_STATE_CHANGE_ASYNC)); - m_sut->play(async); - EXPECT_TRUE(async); -} - TEST_F(GstGenericPlayerTest, shouldPause) { std::unique_ptr task{std::make_unique>()}; diff --git a/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp b/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp index 6a05a2129..59289b9d3 100644 --- a/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp +++ b/tests/unittests/media/server/main/mediaPipeline/MiscellaneousFunctionsTest.cpp @@ -46,7 +46,7 @@ TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, PlaySuccess) { bool async{false}; loadGstPlayer(); - mainThreadWillEnqueuePriorityTaskAndWait(); + mainThreadWillEnqueueTaskAndWait(); EXPECT_CALL(*m_gstPlayerMock, play(_)); EXPECT_TRUE(m_mediaPipeline->play(async)); @@ -58,7 +58,7 @@ TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, PlaySuccess) TEST_F(RialtoServerMediaPipelineMiscellaneousFunctionsTest, PlayFailureDueToUninitializedPlayer) { bool async{false}; - mainThreadWillEnqueuePriorityTaskAndWait(); + mainThreadWillEnqueueTaskAndWait(); EXPECT_FALSE(m_mediaPipeline->play(async)); } diff --git a/tests/unittests/media/server/main/mediaPipeline/base/MediaPipelineTestBase.cpp b/tests/unittests/media/server/main/mediaPipeline/base/MediaPipelineTestBase.cpp index cdcad0ab5..398e7ea68 100644 --- a/tests/unittests/media/server/main/mediaPipeline/base/MediaPipelineTestBase.cpp +++ b/tests/unittests/media/server/main/mediaPipeline/base/MediaPipelineTestBase.cpp @@ -82,13 +82,6 @@ void MediaPipelineTestBase::mainThreadWillEnqueueTaskAndWait() .RetiresOnSaturation(); } -void MediaPipelineTestBase::mainThreadWillEnqueuePriorityTaskAndWait() -{ - EXPECT_CALL(*m_mainThreadMock, enqueuePriorityTaskAndWait(m_kMainThreadClientId, _)) - .WillOnce(Invoke([](uint32_t clientId, firebolt::rialto::server::IMainThread::Task task) { task(); })) - .RetiresOnSaturation(); -} - void MediaPipelineTestBase::loadGstPlayer() { mainThreadWillEnqueueTaskAndWait(); diff --git a/tests/unittests/media/server/main/mediaPipeline/base/MediaPipelineTestBase.h b/tests/unittests/media/server/main/mediaPipeline/base/MediaPipelineTestBase.h index 893353141..d31474d53 100644 --- a/tests/unittests/media/server/main/mediaPipeline/base/MediaPipelineTestBase.h +++ b/tests/unittests/media/server/main/mediaPipeline/base/MediaPipelineTestBase.h @@ -90,7 +90,6 @@ class MediaPipelineTestBase : public ::testing::Test void destroyMediaPipeline(); void mainThreadWillEnqueueTask(); void mainThreadWillEnqueueTaskAndWait(); - void mainThreadWillEnqueuePriorityTaskAndWait(); void loadGstPlayer(); int attachSource(MediaSourceType sourceType, const std::string &mimeType); void setEos(MediaSourceType sourceType); From 1e0ba7b29171ec5db0461e9dbce39456fb6ed15c Mon Sep 17 00:00:00 2001 From: "Adler, Douglas" Date: Sun, 17 May 2026 15:48:18 -0500 Subject: [PATCH 26/29] RDKEMW-18668 : Add retry loop to ocdm decrypt 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 --- .../main/source/MediaKeysServerInternal.cpp | 21 ++++++- wrappers/include/OcdmSession.h | 2 + wrappers/source/OcdmSession.cpp | 57 +++++++++++++++++++ 3 files changed, 77 insertions(+), 3 deletions(-) diff --git a/media/server/main/source/MediaKeysServerInternal.cpp b/media/server/main/source/MediaKeysServerInternal.cpp index 72240c3fc..74fd75f45 100644 --- a/media/server/main/source/MediaKeysServerInternal.cpp +++ b/media/server/main/source/MediaKeysServerInternal.cpp @@ -18,6 +18,8 @@ */ #include +#include +#include #include "MediaKeysServerInternal.h" #include "RialtoServerLogging.h" @@ -51,6 +53,9 @@ std::shared_ptr 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}; @@ -573,10 +578,20 @@ MediaKeyErrorStatus MediaKeysServerInternal::decrypt(int32_t keySessionId, GstBu { RIALTO_SERVER_LOG_DEBUG("entry:"); - 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); + 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); - m_mainThread->enqueueTaskAndWait(m_mainThreadClientId, task); return status; } 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..0c5d801f4 100644 --- a/wrappers/source/OcdmSession.cpp +++ b/wrappers/source/OcdmSession.cpp @@ -23,6 +23,7 @@ #include "opencdm/open_cdm_ext.h" #include #include +#include namespace { @@ -109,6 +110,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) @@ -121,6 +123,8 @@ 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"); }); } @@ -195,6 +199,59 @@ MediaKeyErrorStatus OcdmSession::decryptBuffer(GstBuffer *encrypted, GstCaps *ca 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) + { + return MediaKeyErrorStatus::OUTPUT_RESTRICTED; + } + } + + 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) + { + 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); } From d45e77d2b75b0c651eb3e8e6fef803bd264d66a6 Mon Sep 17 00:00:00 2001 From: Sasa Mudri Date: Mon, 18 May 2026 10:31:28 +0200 Subject: [PATCH 27/29] Fix build issues. --- media/client/ipc/source/MediaKeysIpc.cpp | 4 ++++ media/public/include/MediaCommon.h | 3 ++- media/server/ipc/source/MediaKeysModuleService.cpp | 4 ++++ media/server/main/source/MediaKeysCapabilities.cpp | 2 ++ wrappers/source/OcdmSession.cpp | 4 ++-- 5 files changed, 14 insertions(+), 3 deletions(-) diff --git a/media/client/ipc/source/MediaKeysIpc.cpp b/media/client/ipc/source/MediaKeysIpc.cpp index 761566218..41e3b1b17 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"; } diff --git a/media/public/include/MediaCommon.h b/media/public/include/MediaCommon.h index 0dd829f6a..16e1025ee 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 }; /** diff --git a/media/server/ipc/source/MediaKeysModuleService.cpp b/media/server/ipc/source/MediaKeysModuleService.cpp index 6f69d69dd..6368f93ec 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; } diff --git a/media/server/main/source/MediaKeysCapabilities.cpp b/media/server/main/source/MediaKeysCapabilities.cpp index b22836fb5..56123ce91 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/wrappers/source/OcdmSession.cpp b/wrappers/source/OcdmSession.cpp index 0c5d801f4..e407ca78b 100644 --- a/wrappers/source/OcdmSession.cpp +++ b/wrappers/source/OcdmSession.cpp @@ -226,7 +226,7 @@ MediaKeyErrorStatus OcdmSession::decryptBuffer(GstBuffer *encrypted, GstCaps *ca // the caller (MediaKeysServerInternal::decrypt) can retry from the GStreamer thread. if (!keyId.empty()) { - const KeyStatus preStatus = + const ::KeyStatus preStatus = opencdm_session_status(m_session, keyId.data(), static_cast(keyId.size())); if (preStatus == OutputRestricted || preStatus == OutputRestrictedHDCP22) { @@ -240,7 +240,7 @@ MediaKeyErrorStatus OcdmSession::decryptBuffer(GstBuffer *encrypted, GstCaps *ca // specific error code, so confirm via key status before signalling the caller to retry. if (result != ERROR_NONE && !keyId.empty()) { - const KeyStatus postStatus = + const ::KeyStatus postStatus = opencdm_session_status(m_session, keyId.data(), static_cast(keyId.size())); if (postStatus == OutputRestricted || postStatus == OutputRestrictedHDCP22) { From 5eee279616b3f35a5afdf513131aa830543d1e28 Mon Sep 17 00:00:00 2001 From: LekshmiMR Date: Mon, 18 May 2026 18:07:45 +0000 Subject: [PATCH 28/29] DecryptLoop adding logs --- wrappers/source/OcdmSession.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/wrappers/source/OcdmSession.cpp b/wrappers/source/OcdmSession.cpp index e407ca78b..37fd134a9 100644 --- a/wrappers/source/OcdmSession.cpp +++ b/wrappers/source/OcdmSession.cpp @@ -125,6 +125,9 @@ OcdmSession::OcdmSession(struct OpenCDMSystem *systemHandle, IOcdmSessionClient (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){ + printf("DEBUG PURPOSE : m_ocdmGstSessionDecryptBufferOnce exists\n"); + } }); } @@ -194,6 +197,7 @@ MediaKeyErrorStatus OcdmSession::update(const uint8_t response[], uint32_t respo MediaKeyErrorStatus OcdmSession::decryptBuffer(GstBuffer *encrypted, GstCaps *caps) { + printf("DEBUG PURPOSE : OcdmSession::decryptBuffer()\n"); if (!m_session) { return MediaKeyErrorStatus::FAIL; @@ -230,6 +234,8 @@ MediaKeyErrorStatus OcdmSession::decryptBuffer(GstBuffer *encrypted, GstCaps *ca opencdm_session_status(m_session, keyId.data(), static_cast(keyId.size())); if (preStatus == OutputRestricted || preStatus == OutputRestrictedHDCP22) { + + printf("DEBUG PURPOSE : OcdmSession::decryptBuffer() : returning MediaKeyErrorStatus::OUTPUT_RESTRICTED(Pre decrypt)\n"); return MediaKeyErrorStatus::OUTPUT_RESTRICTED; } } @@ -244,6 +250,7 @@ MediaKeyErrorStatus OcdmSession::decryptBuffer(GstBuffer *encrypted, GstCaps *ca opencdm_session_status(m_session, keyId.data(), static_cast(keyId.size())); if (postStatus == OutputRestricted || postStatus == OutputRestrictedHDCP22) { + printf("DEBUG PURPOSE : OcdmSession::decryptBuffer() : returning MediaKeyErrorStatus::OUTPUT_RESTRICTED(Post decrypt)\n"); return MediaKeyErrorStatus::OUTPUT_RESTRICTED; } } From a6011f89f1dc5e8ead0d86d61018ee80f75faccd Mon Sep 17 00:00:00 2001 From: LekshmiMR Date: Wed, 20 May 2026 00:41:04 +0000 Subject: [PATCH 29/29] Add logging --- .../main/source/MediaKeysServerInternal.cpp | 59 ++++++++++++++++++- wrappers/CMakeLists.txt | 2 + wrappers/source/OcdmSession.cpp | 9 +-- 3 files changed, 65 insertions(+), 5 deletions(-) diff --git a/media/server/main/source/MediaKeysServerInternal.cpp b/media/server/main/source/MediaKeysServerInternal.cpp index 74fd75f45..8f6ca5a2f 100644 --- a/media/server/main/source/MediaKeysServerInternal.cpp +++ b/media/server/main/source/MediaKeysServerInternal.cpp @@ -24,6 +24,34 @@ #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) @@ -576,7 +604,7 @@ 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::FAIL}; const auto deadline = std::chrono::steady_clock::now() + kOutputRestrictedRetryTimeout; @@ -584,6 +612,35 @@ MediaKeyErrorStatus MediaKeysServerInternal::decrypt(int32_t keySessionId, GstBu { 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; 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/source/OcdmSession.cpp b/wrappers/source/OcdmSession.cpp index 37fd134a9..821cd3043 100644 --- a/wrappers/source/OcdmSession.cpp +++ b/wrappers/source/OcdmSession.cpp @@ -24,6 +24,7 @@ #include #include #include +#include "RialtoCommonLogging.h" namespace { @@ -126,7 +127,7 @@ OcdmSession::OcdmSession(struct OpenCDMSystem *systemHandle, IOcdmSessionClient m_ocdmGstSessionDecryptBufferOnce = (OcdmGstSessionDecryptBufferOnceFn)dlsym(RTLD_DEFAULT, "opencdm_gstreamer_session_decrypt_buffer_once"); if(m_ocdmGstSessionDecryptBufferOnce != NULL){ - printf("DEBUG PURPOSE : m_ocdmGstSessionDecryptBufferOnce exists\n"); + RIALTO_COMMON_LOG_ERROR("DEBUG PURPOSE : m_ocdmGstSessionDecryptBufferOnce exists\n"); } }); } @@ -197,7 +198,7 @@ MediaKeyErrorStatus OcdmSession::update(const uint8_t response[], uint32_t respo MediaKeyErrorStatus OcdmSession::decryptBuffer(GstBuffer *encrypted, GstCaps *caps) { - printf("DEBUG PURPOSE : OcdmSession::decryptBuffer()\n"); + RIALTO_COMMON_LOG_ERROR("DEBUG PURPOSE : OcdmSession::decryptBuffer()\n"); if (!m_session) { return MediaKeyErrorStatus::FAIL; @@ -235,7 +236,7 @@ MediaKeyErrorStatus OcdmSession::decryptBuffer(GstBuffer *encrypted, GstCaps *ca if (preStatus == OutputRestricted || preStatus == OutputRestrictedHDCP22) { - printf("DEBUG PURPOSE : OcdmSession::decryptBuffer() : returning MediaKeyErrorStatus::OUTPUT_RESTRICTED(Pre decrypt)\n"); + RIALTO_COMMON_LOG_ERROR("DEBUG PURPOSE : OcdmSession::decryptBuffer() : returning MediaKeyErrorStatus::OUTPUT_RESTRICTED(Pre decrypt)\n"); return MediaKeyErrorStatus::OUTPUT_RESTRICTED; } } @@ -250,7 +251,7 @@ MediaKeyErrorStatus OcdmSession::decryptBuffer(GstBuffer *encrypted, GstCaps *ca opencdm_session_status(m_session, keyId.data(), static_cast(keyId.size())); if (postStatus == OutputRestricted || postStatus == OutputRestrictedHDCP22) { - printf("DEBUG PURPOSE : OcdmSession::decryptBuffer() : returning MediaKeyErrorStatus::OUTPUT_RESTRICTED(Post decrypt)\n"); + RIALTO_COMMON_LOG_ERROR("DEBUG PURPOSE : OcdmSession::decryptBuffer() : returning MediaKeyErrorStatus::OUTPUT_RESTRICTED(Post decrypt)\n"); return MediaKeyErrorStatus::OUTPUT_RESTRICTED; } }