diff --git a/aampgstplayer.cpp b/aampgstplayer.cpp index b1b5b92c77..6ec4777045 100644 --- a/aampgstplayer.cpp +++ b/aampgstplayer.cpp @@ -842,12 +842,14 @@ void AAMPGstPlayer::EndOfStreamReached(AampMediaType type) */ void AAMPGstPlayer::Stop(bool keepLastFrame) { + aamp->SyncBegin(); AAMPLOG_MIL("entering AAMPGstPlayer_Stop keepLastFrame %d", keepLastFrame); StopMonitorAvTimer(); playerInstance->Stop(keepLastFrame); aamp->seiTimecode.assign(""); AAMPLOG_MIL("exiting AAMPGstPlayer_Stop"); + aamp->SyncEnd(); } @@ -928,21 +930,29 @@ long long AAMPGstPlayer::GetPositionMilliseconds(void) */ bool AAMPGstPlayer::Pause( bool pause, bool forceStopGstreamerPreBuffering ) { - aamp->SyncBegin(); /* Obtains a mutex lock */ + bool res = false; - AAMPLOG_MIL("entering AAMPGstPlayer_Pause - pause(%d) stop-pre-buffering(%d)", pause, forceStopGstreamerPreBuffering); + aamp->SyncBegin(); /* Obtains a mutex lock */ - bool res = this->playerInstance->Pause(pause, forceStopGstreamerPreBuffering); - if(res) + if (!playerInstance) + { + AAMPLOG_WARN("AAMPGstPlayer_Pause called but playerInstance is null"); + } + else { - if(!aamp->IsGstreamerSubsEnabled()) - aamp->PauseSubtitleParser(pause); + AAMPLOG_MIL("entering AAMPGstPlayer_Pause - pause(%d) stop-pre-buffering(%d)", pause, forceStopGstreamerPreBuffering); + + res = this->playerInstance->Pause(pause, forceStopGstreamerPreBuffering); + if(res) + { + if(!aamp->IsGstreamerSubsEnabled()) + aamp->PauseSubtitleParser(pause); + } } aamp->SyncEnd(); /* Releases the mutex */ return res; - //return retValue; } /** diff --git a/test/utests/fakes/FakePrivateInstanceAAMP.cpp b/test/utests/fakes/FakePrivateInstanceAAMP.cpp index 21673bfdbb..9292a8deac 100644 --- a/test/utests/fakes/FakePrivateInstanceAAMP.cpp +++ b/test/utests/fakes/FakePrivateInstanceAAMP.cpp @@ -951,10 +951,18 @@ void PrivateInstanceAAMP::StopTrackInjection(AampMediaType type) void PrivateInstanceAAMP::SyncBegin(void) { + if (g_mockPrivateInstanceAAMP != nullptr) + { + g_mockPrivateInstanceAAMP->SyncBegin(); + } } void PrivateInstanceAAMP::SyncEnd(void) { + if (g_mockPrivateInstanceAAMP != nullptr) + { + g_mockPrivateInstanceAAMP->SyncEnd(); + } } void PrivateInstanceAAMP::UpdateCullingState(double culledSecs) diff --git a/test/utests/mocks/MockPrivateInstanceAAMP.h b/test/utests/mocks/MockPrivateInstanceAAMP.h index c5583d4a26..64cb739a69 100644 --- a/test/utests/mocks/MockPrivateInstanceAAMP.h +++ b/test/utests/mocks/MockPrivateInstanceAAMP.h @@ -95,6 +95,8 @@ class MockPrivateInstanceAAMP MOCK_METHOD(bool, IsLiveStream, ()); MOCK_METHOD(void, Individualization, (const std::string &payload)); MOCK_METHOD(void, UpdateUseSinglePipeline, ()); + MOCK_METHOD(void, SyncBegin, ()); + MOCK_METHOD(void, SyncEnd, ()); }; extern MockPrivateInstanceAAMP *g_mockPrivateInstanceAAMP; diff --git a/test/utests/tests/AampGstPlayer/FunctionalTests.cpp b/test/utests/tests/AampGstPlayer/FunctionalTests.cpp index 3c1e476450..917889b554 100644 --- a/test/utests/tests/AampGstPlayer/FunctionalTests.cpp +++ b/test/utests/tests/AampGstPlayer/FunctionalTests.cpp @@ -640,4 +640,50 @@ TEST_F(AAMPGstPlayerTests, MonitorAV ) DestroyAMPGstPlayer(); } +TEST_F(AAMPGstPlayerTests, Pause_NullPlayerInstance) +{ + // Setup + ConstructAMPGstPlayer(); + + // Simulate playerInstance being null (as could happen during a race with Stop) + InterfacePlayerRDK *savedPlayerInstance = mAAMPGstPlayer->playerInstance; + mAAMPGstPlayer->playerInstance = nullptr; + + // Expect SyncBegin/SyncEnd to be called even when playerInstance is null + EXPECT_CALL(*g_mockPrivateInstanceAAMP, SyncBegin()).Times(2); + EXPECT_CALL(*g_mockPrivateInstanceAAMP, SyncEnd()).Times(2); + + // Code under test - should return false without crashing + bool result = mAAMPGstPlayer->Pause(true, false); + EXPECT_FALSE(result); + + result = mAAMPGstPlayer->Pause(false, false); + EXPECT_FALSE(result); + + // Restore playerInstance for proper cleanup + mAAMPGstPlayer->playerInstance = savedPlayerInstance; + + // Tidy Up + DestroyAMPGstPlayer(); +} +TEST_F(AAMPGstPlayerTests, Stop_WithPipeline) +{ + // Setup + ConstructAMPGstPlayer(); + SetupPipeline(&tbl[0]); + + // Expect SyncBegin/SyncEnd to be called during Stop + EXPECT_CALL(*g_mockPrivateInstanceAAMP, SyncBegin()).Times(1); + EXPECT_CALL(*g_mockPrivateInstanceAAMP, SyncEnd()).Times(1); + + // Code under test - Stop should execute with SyncBegin/SyncEnd without issues + mAAMPGstPlayer->Stop(false); + + // After Stop, playerInstance->Stop() should have been called (pipeline torn down) + // The DestroyAMPGstPlayer expectations for pipeline teardown are no longer needed + isPipelineSetup = false; + + // Tidy Up + DestroyAMPGstPlayer(); +}