From 3d50076c3310f7aaf1bbf94cb343e05ef1107152 Mon Sep 17 00:00:00 2001 From: deepikasri Date: Mon, 8 Jun 2026 18:36:41 +0530 Subject: [PATCH 1/3] RDKEMW-19686: [Develop]Bringing Parental Pin changes/vipa container crash & LLAMA freeze issue Reason for change : Addressing the above changes in middleware-player-interface Test Steps : E2E testing to check regression . Signed-off by: Deepikasri Natarajan --- InterfacePlayerPriv.h | 1 + InterfacePlayerRDK.cpp | 194 ++++++++++++++++-- SocUtils.cpp | 25 ++- SocUtils.h | 7 + test/utests/fakes/FakeSocUtils.cpp | 4 + .../InterfacePlayerFunctionTests.cpp | 45 ++++ 6 files changed, 256 insertions(+), 20 deletions(-) diff --git a/InterfacePlayerPriv.h b/InterfacePlayerPriv.h index 296c5431..97798d78 100755 --- a/InterfacePlayerPriv.h +++ b/InterfacePlayerPriv.h @@ -206,6 +206,7 @@ struct GstPlayerPriv gboolean buffering_in_progress; /**< buffering is in progress */ guint buffering_timeout_cnt; /**< make sure buffering_timeout doesn't get stuck */ GstState buffering_target_state; /**< the target state after buffering */ + bool seekPausedState; /** < true when seek with keepPaused is active — guards buffering_timeout from setting PLAYING */ gint64 lastKnownPTS; /**< To store the PTS of last displayed video */ long long ptsUpdatedTimeMS; /**< Timestamp when PTS was last updated */ guint ptsCheckForEosOnUnderflowIdleTaskId; /**< ID of task to ensure video PTS is not moving before notifying EOS on underflow. */ diff --git a/InterfacePlayerRDK.cpp b/InterfacePlayerRDK.cpp index f648fa50..0b57fa7c 100644 --- a/InterfacePlayerRDK.cpp +++ b/InterfacePlayerRDK.cpp @@ -148,7 +148,7 @@ firstFrameCallbackIdleTaskId(GST_TASK_ID_INVALID), firstFrameCallbackIdleTaskPen using_westerossink(false), usingRialtoSink(false), usingClosedCaptionsControl(false), pauseOnStartPlayback(false), eosSignalled(false), buffering_enabled(FALSE), buffering_in_progress(FALSE), buffering_timeout_cnt(0), buffering_target_state(GST_STATE_NULL), -lastKnownPTS(0), ptsUpdatedTimeMS(0), ptsCheckForEosOnUnderflowIdleTaskId(GST_TASK_ID_INVALID), +seekPausedState(false),lastKnownPTS(0), ptsUpdatedTimeMS(0), ptsCheckForEosOnUnderflowIdleTaskId(GST_TASK_ID_INVALID), numberOfVideoBuffersSent(0), segmentStart(0), positionQuery(NULL), paused(false), pipelineState(GST_STATE_NULL), firstVideoFrameDisplayedCallbackTask("FirstVideoFrameDisplayedCallback"), @@ -470,6 +470,15 @@ void InterfacePlayerRDK::ConfigurePipeline(int format, int audioFormat, int subF gboolean videoOnly = (audioFormat == GST_FORMAT_INVALID); MW_LOG_INFO("Setting single-path-stream to %d", videoOnly); g_object_set(vidsink, "single-path-stream", videoOnly, NULL); + // RDKEMW-18286: Reinforce show-video-window before pipeline state change + if (interfacePlayerPriv->gstPrivateContext->videoMuted) + { + MW_LOG_MIL("InterfacePlayerRDK - ConfigurePipeline: reinforcing " + "show-video-window=FALSE on %s (videoMuted=%d)", + GST_ELEMENT_NAME(vidsink), + interfacePlayerPriv->gstPrivateContext->videoMuted); + g_object_set(vidsink, "show-video-window", FALSE, NULL); + } } else { @@ -512,6 +521,12 @@ void InterfacePlayerRDK::ConfigurePipeline(int format, int audioFormat, int subF interfacePlayerPriv->gstPrivateContext->buffering_in_progress = true; interfacePlayerPriv->gstPrivateContext->buffering_timeout_cnt = DEFAULT_BUFFERING_MAX_CNT; + // buffering_timeout will handle the PLAYING transition, so seekPausedState must not block it. + if (interfacePlayerPriv->gstPrivateContext->seekPausedState) + { + MW_LOG_WARN("ConfigurePipeline: clearing seekPausedState — buffering will drive PLAYING transition"); + interfacePlayerPriv->gstPrivateContext->seekPausedState = false; + } if (SetStateWithWarnings(interfacePlayerPriv->gstPrivateContext->pipeline, GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) { MW_LOG_ERR("InterfacePlayerRDK_Configure GST_STATE_PAUSED failed"); @@ -522,12 +537,31 @@ void InterfacePlayerRDK::ConfigurePipeline(int format, int audioFormat, int subF else { MW_LOG_INFO("Setting state to GST_STATE_PLAYING"); - if (SetStateWithWarnings(interfacePlayerPriv->gstPrivateContext->pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) + /* If a seek-with-keepPaused is active we must not race into PLAYING. + * Defer the PLAYING transition and leave pipeline in PAUSED until + * an explicit resume (Pause(false)) clears `seekPausedState`. + */ + if (interfacePlayerPriv->gstPrivateContext->seekPausedState) { - MW_LOG_ERR("InterfacePlayerRDK: GST_STATE_PLAYING failed"); + MW_LOG_WARN("seekPausedState active - deferring transition to PLAYING, marking pendingPlayState"); + interfacePlayerPriv->gstPrivateContext->buffering_target_state = GST_STATE_PLAYING; + interfacePlayerPriv->gstPrivateContext->pendingPlayState = true; + /* Ensure pipeline is left/returned to PAUSED to avoid accidental play */ + if (SetStateWithWarnings(interfacePlayerPriv->gstPrivateContext->pipeline, GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) + { + MW_LOG_ERR("InterfacePlayerRDK: GST_STATE_PAUSED failed while deferring PLAYING"); + } + interfacePlayerPriv->gstPrivateContext->paused = true; + } + else + { + if (SetStateWithWarnings(interfacePlayerPriv->gstPrivateContext->pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) + { + MW_LOG_ERR("InterfacePlayerRDK: GST_STATE_PLAYING failed"); + } + interfacePlayerPriv->gstPrivateContext->pendingPlayState = false; + interfacePlayerPriv->gstPrivateContext->paused = false; } - interfacePlayerPriv->gstPrivateContext->pendingPlayState = false; - interfacePlayerPriv->gstPrivateContext->paused = false; } interfacePlayerPriv->gstPrivateContext->eosSignalled = false; interfacePlayerPriv->gstPrivateContext->numberOfVideoBuffersSent = 0; @@ -1597,14 +1631,32 @@ bool InterfacePlayerRDK::Flush(double position, int rate, bool shouldTearDown, b interfacePlayerPriv->gstPrivateContext->ptsCheckForEosOnUnderflowIdleTaskId = PLAYER_TASK_ID_INVALID; } + + /* If pipeline is paused (seek with keepPaused), mark seekPausedState + * so that when ConfigurePipeline restarts buffering, the buffering_timeout callback + * won't race to set PLAYING before Pause(1) arrives */ + if (interfacePlayerPriv->gstPrivateContext->paused) + { + interfacePlayerPriv->gstPrivateContext->seekPausedState = true; + MW_LOG_MIL("InterfacePlayerRDK: Flush with paused state — setting seekPausedState"); + } if (interfacePlayerPriv->gstPrivateContext->bufferingTimeoutTimerId) { MW_LOG_MIL("InterfacePlayerRDK: Remove bufferingTimeoutTimerId %d", interfacePlayerPriv->gstPrivateContext->bufferingTimeoutTimerId); g_source_remove(interfacePlayerPriv->gstPrivateContext->bufferingTimeoutTimerId); interfacePlayerPriv->gstPrivateContext->bufferingTimeoutTimerId = PLAYER_TASK_ID_INVALID; + // Reset buffering state to prevent stale timeout_cnt from triggering error after seek + interfacePlayerPriv->gstPrivateContext->buffering_in_progress = false; + interfacePlayerPriv->gstPrivateContext->buffering_timeout_cnt = DEFAULT_BUFFERING_MAX_CNT; - } + } + // If rate indicates playback (not paused seek), clear seekPausedState + if (rate > 0 && !interfacePlayerPriv->gstPrivateContext->paused) + { + interfacePlayerPriv->gstPrivateContext->seekPausedState = false; + MW_LOG_MIL("InterfacePlayerRDK: rate indicates playback, clearing seekPausedState"); + } // If the pipeline is not setup, we will cache the value for later SetSeekPosition(position); @@ -1680,8 +1732,21 @@ bool InterfacePlayerRDK::Flush(double position, int rate, bool shouldTearDown, b { if ((interfacePlayerPriv->socInterface->IsSimulatorSink() || interfacePlayerPriv->gstPrivateContext->usingRialtoSink) && rate != GST_NORMAL_PLAY_RATE) { - MW_LOG_INFO("Resetting seek position to zero"); - position = 0; + const bool isTrickplay = (rate != GST_NORMAL_PLAY_RATE); + const bool isLiveMedia = (static_cast(m_gstConfigParam->media) == eGST_MEDIAFORMAT_OTA); + + if (isTrickplay) + { + if (isLiveMedia) + { + MW_LOG_WARN("Live trickplay active - preserving flush seek position %f", position); + } + else + { + MW_LOG_INFO("Resetting seek position to zero"); + position = 0; + } + } } } if (!gst_element_seek(interfacePlayerPriv->gstPrivateContext->pipeline, playRate, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, @@ -2343,6 +2408,21 @@ int InterfacePlayerRDK::SetupStream(int streamId, void *playerInstance, std::st g_object_set(vidsink, "has-drm", FALSE, NULL); } interfacePlayerPriv->gstPrivateContext->video_sink = vidsink; + + // RDKEMW-18286: Set show-video-window=FALSE at sink creation time. + // This is the EARLIEST possible point. The Rialto delegate will queue + // this (m_videoMuteQueued=true) and apply it server-side when the + // source attaches — which happens BEFORE the pipeline goes to PLAYING + // and BEFORE any frame can be decoded. + if (interfacePlayerPriv->gstPrivateContext->videoMuted) + { + MW_LOG_MIL("InterfacePlayerRDK - %s: setting show-video-window=FALSE " + "at creation time (videoMuted=%d)", + GST_ELEMENT_NAME(vidsink), + interfacePlayerPriv->gstPrivateContext->videoMuted); + g_object_set(vidsink, "show-video-window", FALSE, NULL); + } + } else { @@ -3385,6 +3465,12 @@ bool InterfacePlayerRDK::Pause(bool pause , bool forceStopGstreamerPreBuffering) GstState nextState = pause ? GST_STATE_PAUSED : GST_STATE_PLAYING; interfacePlayerPriv->gstPrivateContext->buffering_target_state = nextState; + /* Clear seekPausedState when explicitly resuming playback */ + if (!pause) + { + interfacePlayerPriv->gstPrivateContext->seekPausedState = false; + } + if (GST_STATE_PAUSED == nextState && forceStopGstreamerPreBuffering) { /* maybe in a timing case during the playback start, @@ -3404,7 +3490,30 @@ bool InterfacePlayerRDK::Pause(bool pause , bool forceStopGstreamerPreBuffering) /* wait a bit longer for the state change to conclude */ if (nextState != validateStateWithMsTimeout(this,nextState, 100)) { - MW_LOG_ERR("InterfacePlayerRDK_Pause - validateStateWithMsTimeout - FAILED GstState %d", nextState); + GstState current, pending; + MW_LOG_INFO("InterfacePlayerRDK_Pause - validateStateWithMsTimeout - FAILED expected %s", gst_element_state_get_name(nextState)); + + /* Recovery: retry the state change once before reporting failure */ + MW_LOG_INFO("InterfacePlayerRDK_Pause - retrying state change to GstState %d", nextState); + + // Wait for any in-flight transition to settle + gst_element_get_state(interfacePlayerPriv->gstPrivateContext->pipeline, ¤t, &pending, 0); + + // Single retry — no destructive NULL reset + GstStateChangeReturn rcRetry = SetStateWithWarnings(interfacePlayerPriv->gstPrivateContext->pipeline, nextState); + if (GST_STATE_CHANGE_ASYNC == rcRetry) + { + if (nextState != validateStateWithMsTimeout(this, nextState, 100)) + { + MW_LOG_ERR("Retry also failed — reporting error"); + retValue = false; + } + } + else if (GST_STATE_CHANGE_SUCCESS != rcRetry) + { + MW_LOG_ERR("Retry failed immediately with rc %d — reporting error", rcRetry); + retValue = false; + } } } else if (GST_STATE_CHANGE_SUCCESS != rc) @@ -4500,7 +4609,17 @@ static gboolean bus_message(GstBus * bus, GstMessage * msg, InterfacePlayerRDK * if(eGST_MEDIAFORMAT_DASH != static_cast(pInterfacePlayerRDK->m_gstConfigParam->media)) { SetStateWithWarnings(privatePlayer->gstPrivateContext->pipeline, GST_STATE_PAUSED); - SetStateWithWarnings(privatePlayer->gstPrivateContext->pipeline, GST_STATE_PLAYING); + /* Avoid forcing PLAYING if a seek-with-keepPaused is active */ + if (!privatePlayer->gstPrivateContext->seekPausedState) + { + SetStateWithWarnings(privatePlayer->gstPrivateContext->pipeline, GST_STATE_PLAYING); + } + else + { + MW_LOG_WARN("GST_MESSAGE_CLOCK_LOST: seekPausedState active - skipping PLAYING"); + privatePlayer->gstPrivateContext->pendingPlayState = true; + privatePlayer->gstPrivateContext->buffering_target_state = GST_STATE_PLAYING; + } } break; @@ -4566,8 +4685,34 @@ bool InterfacePlayerRDK::SetPlayBackRate(double rate) sources.push_back(interfacePlayerPriv->gstPrivateContext->stream[iTrack].source); } } - ret = interfacePlayerPriv->socInterface->SetPlaybackRate(sources, interfacePlayerPriv->gstPrivateContext->pipeline, rate, interfacePlayerPriv->gstPrivateContext->video_dec,interfacePlayerPriv->gstPrivateContext->audio_dec); - return ret; + ret = interfacePlayerPriv->socInterface->SetPlaybackRate(sources, interfacePlayerPriv->gstPrivateContext->pipeline, rate, interfacePlayerPriv->gstPrivateContext->video_dec,interfacePlayerPriv->gstPrivateContext->audio_dec); + + /* If application requested resume via rate change but middleware's + * seek-paused protection left the pipeline in PAUSED, ensure we clear + * `seekPausedState` here at middleware level. This handles cases where + * higher-level callers may retry or skip setting rate — forcing an + * explicit resume in the middleware prevents the pipeline from being + * stuck in PAUSED. */ + if (rate != 0.0 && interfacePlayerPriv->gstPrivateContext->seekPausedState && interfacePlayerPriv->gstPrivateContext->paused) + { + MW_LOG_WARN("InterfacePlayerRDK: SetPlayBackRate detected resume while seekPausedState active — forcing resume"); + /* Pause(false) clears seekPausedState in Pause implementation. */ + bool pauseResult = Pause(false, false); + if (pauseResult) + { + interfacePlayerPriv->gstPrivateContext->seekPausedState = false; + interfacePlayerPriv->gstPrivateContext->pendingPlayState = false; + /* After explicit resume we consider operation successful */ + ret = true; + } + else + { + MW_LOG_ERR("SetPlayBackRate: Pause(false) failed — cannot resume"); + ret = false; + } + } + + return ret; } /** @@ -4686,6 +4831,20 @@ static gboolean buffering_timeout (gpointer data) } else if (frames == -1 || frames >= pInterfacePlayerRDK->m_gstConfigParam->framesToQueue || (privatePlayer->gstPrivateContext->buffering_timeout_cnt > 0 && --privatePlayer->gstPrivateContext->buffering_timeout_cnt == 0)) { + /* Do not set PLAYING if a seek-with-keepPaused is in progress. + * The buffering_timeout timer may fire after ConfigurePipeline restarts buffering + * but BEFORE the Pause(1) from keepPaused logic arrives — causing a race. */ + if (privatePlayer->gstPrivateContext->seekPausedState) + { + MW_LOG_WARN("buffering_timeout: skipping PLAYING — seekPausedState active (cnt %u, frames %d)", privatePlayer->gstPrivateContext->buffering_timeout_cnt, frames); + if (privatePlayer->gstPrivateContext->buffering_timeout_cnt == 0) + { + MW_LOG_ERR("buffering_timeout: seekPausedState still active after timeout exhausted — clearing to unblock"); + privatePlayer->gstPrivateContext->seekPausedState = false; + } + return privatePlayer->gstPrivateContext->buffering_in_progress; + } + uint32_t original_buffering_timeout_cnt = privatePlayer->gstPrivateContext->buffering_timeout_cnt; MW_LOG_MIL("Set pipeline state to %s - buffering_timeout_cnt %u frames %i", gst_element_state_get_name(privatePlayer->gstPrivateContext->buffering_target_state), original_buffering_timeout_cnt, frames); @@ -5097,6 +5256,16 @@ void InterfacePlayerRDK::NotifyFragmentCachingComplete() { if(interfacePlayerPriv->gstPrivateContext->pendingPlayState) { + /* If a seek-with-keepPaused is active, do not transition to PLAYING here. + * Leave pendingPlayState set so the explicit resume will perform the transition. + */ + if (interfacePlayerPriv->gstPrivateContext->seekPausedState) + { + MW_LOG_WARN("NotifyFragmentCachingComplete: seekPausedState active - deferring PLAYING"); + interfacePlayerPriv->gstPrivateContext->buffering_target_state = GST_STATE_PLAYING; + return; + } + MW_LOG_MIL("InterfacePlayer: Setting pipeline to PLAYING state "); interfacePlayerPriv->gstPrivateContext->buffering_target_state = GST_STATE_PLAYING; if (SetStateWithWarnings(interfacePlayerPriv->gstPrivateContext->pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) @@ -5266,6 +5435,7 @@ void InterfacePlayerRDK::InitializePlayerGstreamerPlugins() if (!gst_init_check(nullptr, nullptr, nullptr)) { MW_LOG_ERR("gst_init_check() failed"); } + SocUtils::Init(); #define PLUGINS_TO_LOWER_RANK_MAX 2 static const char *plugins_to_lower_rank[PLUGINS_TO_LOWER_RANK_MAX] = { diff --git a/SocUtils.cpp b/SocUtils.cpp index e6967a80..5449cb86 100644 --- a/SocUtils.cpp +++ b/SocUtils.cpp @@ -27,7 +27,16 @@ namespace SocUtils { - static std::shared_ptr socInterface = SocInterface::CreateSocInterface(); + static std::shared_ptr GetSocInterface() + { + static std::shared_ptr socInterface = SocInterface::CreateSocInterface(); + return socInterface; + } + + void Init() + { + (void)GetSocInterface(); + } /** * @brief Checks if AppSrc should be used for progressive playback. * @@ -38,7 +47,7 @@ namespace SocUtils */ bool UseAppSrcForProgressivePlayback( void ) { - return socInterface->UseAppSrc(); + return GetSocInterface()->UseAppSrc(); } /** @@ -51,7 +60,7 @@ namespace SocUtils */ bool UseWesterosSink( void ) { - return socInterface->UseWesterosSink(); + return GetSocInterface()->UseWesterosSink(); } /** @@ -63,7 +72,7 @@ namespace SocUtils */ bool IsAudioFragmentSyncSupported( void ) { - return socInterface->IsAudioFragmentSyncSupported(); + return GetSocInterface()->IsAudioFragmentSyncSupported(); } /** @@ -76,7 +85,7 @@ namespace SocUtils */ bool EnableLiveLatencyCorrection( void ) { - return socInterface->EnableLiveLatencyCorrection(); + return GetSocInterface()->EnableLiveLatencyCorrection(); } /** @@ -89,7 +98,7 @@ namespace SocUtils */ int RequiredQueuedFrames( void ) { - return socInterface->RequiredQueuedFrames(); + return GetSocInterface()->RequiredQueuedFrames(); } /** @@ -102,7 +111,7 @@ namespace SocUtils */ bool EnablePTSRestamp(void) { - return socInterface->EnablePTSRestamp(); + return GetSocInterface()->EnablePTSRestamp(); } /** * @brief Resets segment event flags during trickplay transitions. @@ -111,7 +120,7 @@ namespace SocUtils */ bool ResetNewSegmentEvent() { - return socInterface->ResetNewSegmentEvent(); + return GetSocInterface()->ResetNewSegmentEvent(); } /** * @brief Check if GST Subtec is enabled diff --git a/SocUtils.h b/SocUtils.h index 892eb3d4..d1aed318 100644 --- a/SocUtils.h +++ b/SocUtils.h @@ -26,6 +26,13 @@ namespace SocUtils { + /** + * @brief Initializes access to SOC-specific runtime capabilities. + * + * This function can be used to perform eager initialization during startup. + * SocUtils accessors also perform lazy initialization when needed. + */ + void Init(); /** * @brief Checks if AppSrc should be used for progressive playback. * diff --git a/test/utests/fakes/FakeSocUtils.cpp b/test/utests/fakes/FakeSocUtils.cpp index c278cd65..13c8137e 100644 --- a/test/utests/fakes/FakeSocUtils.cpp +++ b/test/utests/fakes/FakeSocUtils.cpp @@ -21,6 +21,10 @@ namespace SocUtils { + void Init() + { + } + void InitializePlatformConfigs() { } diff --git a/test/utests/tests/InterfacePlayerTests/InterfacePlayerFunctionTests.cpp b/test/utests/tests/InterfacePlayerTests/InterfacePlayerFunctionTests.cpp index 95585061..de08b409 100644 --- a/test/utests/tests/InterfacePlayerTests/InterfacePlayerFunctionTests.cpp +++ b/test/utests/tests/InterfacePlayerTests/InterfacePlayerFunctionTests.cpp @@ -3298,3 +3298,48 @@ TEST_F(InterfacePlayerTests, GetVideoPTS_PropertyProbeOnceAtCreation) EXPECT_EQ(mInterfaceGstPlayer->GetVideoPTS(), ptsValue); EXPECT_EQ(mInterfaceGstPlayer->GetVideoPTS(), ptsValue); } +/** + * @brief Test that NotifyFragmentCachingComplete defers PLAYING while seekPausedState is active. + * + * This matches the middleware behavior required for seek-with-keepPaused flows, + * where fragment caching completion should not force playback transition until + * explicit resume is requested. + */ +TEST_F(InterfacePlayerTests, NotifyFragmentCachingComplete_DefersPlayingWhenSeekPaused) +{ + // Arrange: Create a pending play state and protect it with seekPausedState + mPlayerContext->pendingPlayState = true; + mPlayerContext->seekPausedState = true; + mPlayerContext->buffering_target_state = GST_STATE_PAUSED; + + // Act: Notify fragment caching complete + mInterfaceGstPlayer->NotifyFragmentCachingComplete(); + + + // Assert: Pending state should remain until explicit resume, and target stays PLAYING + EXPECT_EQ(mPlayerContext->pendingPlayState, true); + EXPECT_EQ(mPlayerContext->seekPausedState, true); + EXPECT_EQ(mPlayerContext->buffering_target_state, GST_STATE_PLAYING); +} + +/** + * @brief Test that SetPlayBackRate clears seekPausedState when resuming from a seek-paused state. + * + * When a non-zero rate arrives while seekPausedState is active and the pipeline is still paused, + * the middleware should force resume and clear the protection state. + */ +TEST_F(InterfacePlayerTests, SetPlayBackRate_ForceResumeClearsSeekPausedState) +{ + // Arrange: Simulate a paused pipeline in seek-paused state + mPlayerContext->paused = true; + mPlayerContext->seekPausedState = true; + mPlayerContext->pendingPlayState = true; + mPlayerContext->pipeline = nullptr; + + // Act: Request a resume rate + bool result = mInterfaceGstPlayer->SetPlayBackRate(1.0); + // Assert: Middleware should clear seekPausedState and pendingPlayState, and return true + EXPECT_TRUE(result); + EXPECT_EQ(mPlayerContext->seekPausedState, false); + EXPECT_EQ(mPlayerContext->pendingPlayState, false); +} From d6d3e90425684f4bd2e497ae2507b58bb5005d9e Mon Sep 17 00:00:00 2001 From: rkandh015 Date: Tue, 9 Jun 2026 12:34:14 +0530 Subject: [PATCH 2/3] resolve l1 failure --- .../InterfacePlayerFunctionTests.cpp | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/test/utests/tests/InterfacePlayerTests/InterfacePlayerFunctionTests.cpp b/test/utests/tests/InterfacePlayerTests/InterfacePlayerFunctionTests.cpp index de08b409..3798a840 100644 --- a/test/utests/tests/InterfacePlayerTests/InterfacePlayerFunctionTests.cpp +++ b/test/utests/tests/InterfacePlayerTests/InterfacePlayerFunctionTests.cpp @@ -1985,7 +1985,10 @@ TEST_F(InterfacePlayerTests, Pause_Success) mPlayerContext->pipeline = &gst_element_pipeline; EXPECT_CALL(*g_mockGStreamer, gst_element_get_state(_, NotNull(), NotNull(), _)) - .WillRepeatedly(Return(GST_STATE_CHANGE_SUCCESS)); + .WillRepeatedly(DoAll( + SetArgPointee<1>(GST_STATE_PAUSED), + SetArgPointee<2>(GST_STATE_NULL), + Return(GST_STATE_CHANGE_SUCCESS))); EXPECT_CALL(*g_mockGStreamer, gst_element_set_state(_, GST_STATE_PAUSED)) .WillOnce(Return(GST_STATE_CHANGE_ASYNC)); @@ -3334,7 +3337,23 @@ TEST_F(InterfacePlayerTests, SetPlayBackRate_ForceResumeClearsSeekPausedState) mPlayerContext->paused = true; mPlayerContext->seekPausedState = true; mPlayerContext->pendingPlayState = true; - mPlayerContext->pipeline = nullptr; + mPlayerContext->pipeline = &gst_element_pipeline; + + // Mock the socInterface->SetPlaybackRate call + auto mockSocInterface = std::make_shared>(); + mInterfacePrivatePlayer->socInterface = mockSocInterface; + ON_CALL(*mockSocInterface, SetPlaybackRate).WillByDefault(Return(true)); + + // Mock gst_element_set_state to return PLAYING state change + EXPECT_CALL(*g_mockGStreamer, gst_element_set_state(&gst_element_pipeline, GST_STATE_PLAYING)) + .WillOnce(Return(GST_STATE_CHANGE_SUCCESS)); + + // Mock gst_element_get_state for both the initial call and validateStateWithMsTimeout + EXPECT_CALL(*g_mockGStreamer, gst_element_get_state(&gst_element_pipeline, NotNull(), NotNull(), _)) + .WillRepeatedly(DoAll( + SetArgPointee<1>(GST_STATE_PLAYING), + SetArgPointee<2>(GST_STATE_NULL), + Return(GST_STATE_CHANGE_SUCCESS))); // Act: Request a resume rate bool result = mInterfaceGstPlayer->SetPlayBackRate(1.0); From b1c8a2d3c93b2a3ae8318b06f44332c68d9c4489 Mon Sep 17 00:00:00 2001 From: nrames759 Date: Thu, 11 Jun 2026 19:53:30 +0530 Subject: [PATCH 3/3] utest fix --- .../InterfacePlayerFunctionTests.cpp | 274 ------------------ 1 file changed, 274 deletions(-) diff --git a/test/utests/tests/InterfacePlayerTests/InterfacePlayerFunctionTests.cpp b/test/utests/tests/InterfacePlayerTests/InterfacePlayerFunctionTests.cpp index 1e487443..d619a343 100644 --- a/test/utests/tests/InterfacePlayerTests/InterfacePlayerFunctionTests.cpp +++ b/test/utests/tests/InterfacePlayerTests/InterfacePlayerFunctionTests.cpp @@ -3057,277 +3057,3 @@ TEST_F(InterfacePlayerTests, SetStreamCaps_EncryptedAudioCodecFormat) delete g_mockGstUtils; } -/** - * @test GetVideoPosition_PipelineNull - * @brief Verify GetVideoPosition returns 0 when pipeline pointer is NULL. - */ -TEST_F(InterfacePlayerTests, GetVideoPosition_PipelineNull) -{ - mPlayerContext->pipeline = nullptr; - mPlayerContext->stream[eGST_MEDIATYPE_VIDEO].sinkbin = &gst_element_pipeline; - - EXPECT_CALL(*g_mockGStreamer, gst_element_get_state(_, _, _, _)).Times(0); - EXPECT_CALL(*g_mockGStreamer, gst_element_query_position(_, _, _)).Times(0); - - EXPECT_EQ(mInterfaceGstPlayer->GetVideoPosition(), 0); -} - -/** - * @test GetVideoPosition_VideoSinkbinNull - * @brief Verify GetVideoPosition returns 0 when the video sinkbin is NULL. - */ -TEST_F(InterfacePlayerTests, GetVideoPosition_VideoSinkbinNull) -{ - mPlayerContext->pipeline = &gst_element_pipeline; - mPlayerContext->stream[eGST_MEDIATYPE_VIDEO].sinkbin = nullptr; - - EXPECT_CALL(*g_mockGStreamer, gst_element_get_state(_, _, _, _)).Times(0); - EXPECT_CALL(*g_mockGStreamer, gst_element_query_position(_, _, _)).Times(0); - - EXPECT_EQ(mInterfaceGstPlayer->GetVideoPosition(), 0); -} - -/** - * @test GetVideoPosition_StateNotPlayingOrPaused - * @brief When the pipeline state is neither PLAYING nor PAUSED, the position - * query must be skipped and 0 returned. - */ -TEST_F(InterfacePlayerTests, GetVideoPosition_StateNotPlayingOrPaused) -{ - GstElement video_sinkbin = {.object = {.name = (gchar *)"video_sinkbin"}}; - mPlayerContext->pipeline = &gst_element_pipeline; - mPlayerContext->stream[eGST_MEDIATYPE_VIDEO].sinkbin = &video_sinkbin; - - EXPECT_CALL(*g_mockGStreamer, gst_element_get_state(&gst_element_pipeline, _, _, _)) - .WillOnce(DoAll( - SetArgPointee<1>(GST_STATE_READY), - SetArgPointee<2>(GST_STATE_READY), - Return(GST_STATE_CHANGE_SUCCESS))); - EXPECT_CALL(*g_mockGStreamer, gst_element_query_position(_, _, _)).Times(0); - - EXPECT_EQ(mInterfaceGstPlayer->GetVideoPosition(), 0); -} - -/** - * @test GetVideoPosition_GetStateFails - * @brief When gst_element_get_state does not return SUCCESS the position - * query must be skipped and 0 returned. - */ -TEST_F(InterfacePlayerTests, GetVideoPosition_GetStateFails) -{ - GstElement video_sinkbin = {.object = {.name = (gchar *)"video_sinkbin"}}; - mPlayerContext->pipeline = &gst_element_pipeline; - mPlayerContext->stream[eGST_MEDIATYPE_VIDEO].sinkbin = &video_sinkbin; - - EXPECT_CALL(*g_mockGStreamer, gst_element_get_state(&gst_element_pipeline, _, _, _)) - .WillOnce(DoAll( - SetArgPointee<1>(GST_STATE_PLAYING), - SetArgPointee<2>(GST_STATE_PLAYING), - Return(GST_STATE_CHANGE_FAILURE))); - EXPECT_CALL(*g_mockGStreamer, gst_element_query_position(_, _, _)).Times(0); - - EXPECT_EQ(mInterfaceGstPlayer->GetVideoPosition(), 0); -} - -/** - * @test GetVideoPosition_QueryFails - * @brief When state is PLAYING but gst_element_query_position fails, return 0. - */ -TEST_F(InterfacePlayerTests, GetVideoPosition_QueryFails) -{ - GstElement video_sinkbin = {.object = {.name = (gchar *)"video_sinkbin"}}; - mPlayerContext->pipeline = &gst_element_pipeline; - mPlayerContext->stream[eGST_MEDIATYPE_VIDEO].sinkbin = &video_sinkbin; - - EXPECT_CALL(*g_mockGStreamer, gst_element_get_state(&gst_element_pipeline, _, _, _)) - .WillOnce(DoAll( - SetArgPointee<1>(GST_STATE_PLAYING), - SetArgPointee<2>(GST_STATE_PLAYING), - Return(GST_STATE_CHANGE_SUCCESS))); - EXPECT_CALL(*g_mockGStreamer, - gst_element_query_position(&video_sinkbin, GST_FORMAT_TIME, _)) - .WillOnce(Return(FALSE)); - - EXPECT_EQ(mInterfaceGstPlayer->GetVideoPosition(), 0); -} - -/** - * @test GetVideoPosition_QuerySucceedsPlaying - * @brief Position is returned in milliseconds (ns -> ms) when state is PLAYING - * and the position query succeeds. - */ -TEST_F(InterfacePlayerTests, GetVideoPosition_QuerySucceedsPlaying) -{ - GstElement video_sinkbin = {.object = {.name = (gchar *)"video_sinkbin"}}; - mPlayerContext->pipeline = &gst_element_pipeline; - mPlayerContext->stream[eGST_MEDIATYPE_VIDEO].sinkbin = &video_sinkbin; - - const gint64 positionNs = 7000000; /* 7 ms in nanoseconds */ - EXPECT_CALL(*g_mockGStreamer, gst_element_get_state(&gst_element_pipeline, _, _, _)) - .WillOnce(DoAll( - SetArgPointee<1>(GST_STATE_PLAYING), - SetArgPointee<2>(GST_STATE_PLAYING), - Return(GST_STATE_CHANGE_SUCCESS))); - EXPECT_CALL(*g_mockGStreamer, - gst_element_query_position(&video_sinkbin, GST_FORMAT_TIME, _)) - .WillOnce(DoAll(SetArgPointee<2>(positionNs), Return(TRUE))); - - EXPECT_EQ(mInterfaceGstPlayer->GetVideoPosition(), 7); -} - -/** - * @test GetVideoPosition_QuerySucceedsPaused - * @brief Position is also queried in PAUSED state. - */ -TEST_F(InterfacePlayerTests, GetVideoPosition_QuerySucceedsPaused) -{ - GstElement video_sinkbin = {.object = {.name = (gchar *)"video_sinkbin"}}; - mPlayerContext->pipeline = &gst_element_pipeline; - mPlayerContext->stream[eGST_MEDIATYPE_VIDEO].sinkbin = &video_sinkbin; - - const gint64 positionNs = 12000000; /* 12 ms in nanoseconds */ - EXPECT_CALL(*g_mockGStreamer, gst_element_get_state(&gst_element_pipeline, _, _, _)) - .WillOnce(DoAll( - SetArgPointee<1>(GST_STATE_PAUSED), - SetArgPointee<2>(GST_STATE_PAUSED), - Return(GST_STATE_CHANGE_SUCCESS))); - EXPECT_CALL(*g_mockGStreamer, - gst_element_query_position(&video_sinkbin, GST_FORMAT_TIME, _)) - .WillOnce(DoAll(SetArgPointee<2>(positionNs), Return(TRUE))); - - EXPECT_EQ(mInterfaceGstPlayer->GetVideoPosition(), 12); -} - -/** - * @test GetVideoPTS_PropertySupported - * @brief When SocInterface::GetVideoPts returns a valid PTS, GetVideoPTS() - * returns it directly without falling back to the position query. - */ -TEST_F(InterfacePlayerTests, GetVideoPTS_PropertySupported) -{ - auto mockSoc = std::make_shared(); - mInterfacePrivatePlayer->socInterface = mockSoc; - - const long long ptsValue = 4500; - EXPECT_CALL(*mockSoc, GetVideoPts(_, _, _)) - .WillOnce(Return(ptsValue)); - - /* Position fallback must NOT be used when a valid PTS is returned. */ - EXPECT_CALL(*g_mockGStreamer, gst_element_get_state(_, _, _, _)).Times(0); - EXPECT_CALL(*g_mockGStreamer, gst_element_query_position(_, _, _)).Times(0); - - EXPECT_EQ(mInterfaceGstPlayer->GetVideoPTS(), ptsValue); -} - -/** - * @test GetVideoPTS_PropertyNotSupportedFallsBackToPosition - * @brief When SocInterface::GetVideoPts returns -1 (property not supported), - * InterfacePlayerRDK::GetVideoPTS falls back to - * 90 * GetVideoPosition() (90 kHz ticks per millisecond). - */ -TEST_F(InterfacePlayerTests, GetVideoPTS_PropertyNotSupportedFallsBackToPosition) -{ - auto mockSoc = std::make_shared(); - mInterfacePrivatePlayer->socInterface = mockSoc; - - GstElement video_sinkbin = {.object = {.name = (gchar *)"video_sinkbin"}}; - mPlayerContext->pipeline = &gst_element_pipeline; - mPlayerContext->stream[eGST_MEDIATYPE_VIDEO].sinkbin = &video_sinkbin; - - /* SocInterface reports property not supported. */ - EXPECT_CALL(*mockSoc, GetVideoPts(_, _, _)) - .WillOnce(Return(-1LL)); - - const gint64 positionNs = 10000000; /* 10 ms */ - EXPECT_CALL(*g_mockGStreamer, gst_element_get_state(&gst_element_pipeline, _, _, _)) - .WillOnce(DoAll( - SetArgPointee<1>(GST_STATE_PLAYING), - SetArgPointee<2>(GST_STATE_PLAYING), - Return(GST_STATE_CHANGE_SUCCESS))); - EXPECT_CALL(*g_mockGStreamer, - gst_element_query_position(&video_sinkbin, GST_FORMAT_TIME, _)) - .WillOnce(DoAll(SetArgPointee<2>(positionNs), Return(TRUE))); - - /* Expected: 90 * 10 ms = 900 (90 kHz ticks per ms). */ - EXPECT_EQ(mInterfaceGstPlayer->GetVideoPTS(), 900); -} - -/** - * @test GetVideoPTS_PropertyProbeOnceAtCreation - * @brief GetVideoPTS() delegates to SocInterface::GetVideoPts on every call - * and returns the same value consistently across multiple calls. - */ -TEST_F(InterfacePlayerTests, GetVideoPTS_PropertyProbeOnceAtCreation) -{ - auto mockSoc = std::make_shared(); - mInterfacePrivatePlayer->socInterface = mockSoc; - - const long long ptsValue = 1234; - EXPECT_CALL(*mockSoc, GetVideoPts(_, _, _)) - .Times(2) - .WillRepeatedly(Return(ptsValue)); - - EXPECT_EQ(mInterfaceGstPlayer->GetVideoPTS(), ptsValue); - EXPECT_EQ(mInterfaceGstPlayer->GetVideoPTS(), ptsValue); -} -/** - * @brief Test that NotifyFragmentCachingComplete defers PLAYING while seekPausedState is active. - * - * This matches the middleware behavior required for seek-with-keepPaused flows, - * where fragment caching completion should not force playback transition until - * explicit resume is requested. - */ -TEST_F(InterfacePlayerTests, NotifyFragmentCachingComplete_DefersPlayingWhenSeekPaused) -{ - // Arrange: Create a pending play state and protect it with seekPausedState - mPlayerContext->pendingPlayState = true; - mPlayerContext->seekPausedState = true; - mPlayerContext->buffering_target_state = GST_STATE_PAUSED; - - // Act: Notify fragment caching complete - mInterfaceGstPlayer->NotifyFragmentCachingComplete(); - - - // Assert: Pending state should remain until explicit resume, and target stays PLAYING - EXPECT_EQ(mPlayerContext->pendingPlayState, true); - EXPECT_EQ(mPlayerContext->seekPausedState, true); - EXPECT_EQ(mPlayerContext->buffering_target_state, GST_STATE_PLAYING); -} - -/** - * @brief Test that SetPlayBackRate clears seekPausedState when resuming from a seek-paused state. - * - * When a non-zero rate arrives while seekPausedState is active and the pipeline is still paused, - * the middleware should force resume and clear the protection state. - */ -TEST_F(InterfacePlayerTests, SetPlayBackRate_ForceResumeClearsSeekPausedState) -{ - // Arrange: Simulate a paused pipeline in seek-paused state - mPlayerContext->paused = true; - mPlayerContext->seekPausedState = true; - mPlayerContext->pendingPlayState = true; - mPlayerContext->pipeline = &gst_element_pipeline; - - // Mock the socInterface->SetPlaybackRate call - auto mockSocInterface = std::make_shared>(); - mInterfacePrivatePlayer->socInterface = mockSocInterface; - ON_CALL(*mockSocInterface, SetPlaybackRate).WillByDefault(Return(true)); - - // Mock gst_element_set_state to return PLAYING state change - EXPECT_CALL(*g_mockGStreamer, gst_element_set_state(&gst_element_pipeline, GST_STATE_PLAYING)) - .WillOnce(Return(GST_STATE_CHANGE_SUCCESS)); - - // Mock gst_element_get_state for both the initial call and validateStateWithMsTimeout - EXPECT_CALL(*g_mockGStreamer, gst_element_get_state(&gst_element_pipeline, NotNull(), NotNull(), _)) - .WillRepeatedly(DoAll( - SetArgPointee<1>(GST_STATE_PLAYING), - SetArgPointee<2>(GST_STATE_NULL), - Return(GST_STATE_CHANGE_SUCCESS))); - - // Act: Request a resume rate - bool result = mInterfaceGstPlayer->SetPlayBackRate(1.0); - // Assert: Middleware should clear seekPausedState and pendingPlayState, and return true - EXPECT_TRUE(result); - EXPECT_EQ(mPlayerContext->seekPausedState, false); - EXPECT_EQ(mPlayerContext->pendingPlayState, false); -}