diff --git a/media/server/gstplayer/CMakeLists.txt b/media/server/gstplayer/CMakeLists.txt index 0e147af6b..6dcdb9f5f 100644 --- a/media/server/gstplayer/CMakeLists.txt +++ b/media/server/gstplayer/CMakeLists.txt @@ -50,6 +50,7 @@ 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 da80a09e2..929f58e81 100644 --- a/media/server/gstplayer/include/GenericPlayerContext.h +++ b/media/server/gstplayer/include/GenericPlayerContext.h @@ -207,6 +207,13 @@ 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 bdd3d4a39..f11021a12 100644 --- a/media/server/gstplayer/include/GstGenericPlayer.h +++ b/media/server/gstplayer/include/GstGenericPlayer.h @@ -110,6 +110,7 @@ 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; diff --git a/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h b/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h index 86f89c876..227b0486f 100644 --- a/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h +++ b/media/server/gstplayer/include/tasks/IGenericPlayerTaskFactory.h @@ -176,6 +176,19 @@ 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/GenericPlayerTaskFactory.h b/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h index 22fd2d082..14e05b9ab 100644 --- a/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h +++ b/media/server/gstplayer/include/tasks/generic/GenericPlayerTaskFactory.h @@ -65,6 +65,8 @@ 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 new file mode 100644 index 000000000..3ffc4bd53 --- /dev/null +++ b/media/server/gstplayer/include/tasks/generic/RemoveSource.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 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, + const 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 f0d5111a1..ceb961bf7 100644 --- a/media/server/gstplayer/interface/IGstGenericPlayer.h +++ b/media/server/gstplayer/interface/IGstGenericPlayer.h @@ -89,6 +89,14 @@ class IGstGenericPlayer */ virtual void attachSource(const std::unique_ptr &mediaSource) = 0; + /** + * @brief Removes a source from gstreamer. + * + * @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 386c390d2..9c6a79595 100644 --- a/media/server/gstplayer/source/GstGenericPlayer.cpp +++ b/media/server/gstplayer/source/GstGenericPlayer.cpp @@ -403,6 +403,14 @@ void GstGenericPlayer::attachSource(const std::unique_ptrenqueueTask(m_taskFactory->createRemoveSource(m_context, *this, mediaSourceType)); + } +} + void GstGenericPlayer::allSourcesAttached() { if (m_workerThread) @@ -1687,7 +1695,7 @@ void GstGenericPlayer::scheduleAudioUnderflow() { if (m_workerThread) { - bool underflowEnabled = m_context.isPlaying; + bool underflowEnabled = m_context.isPlaying && !m_context.audioSourceRemoved; 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 8ca2b2405..1b3f9a03a 100644 --- a/media/server/gstplayer/source/tasks/generic/AttachSource.cpp +++ b/media/server/gstplayer/source/tasks/generic/AttachSource.cpp @@ -129,6 +129,11 @@ void AttachSource::reattachAudioSource() const RIALTO_SERVER_LOG_ERROR("Reattaching source failed!"); return; } + + 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 55e641304..84f9e0623 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; + bool underflowEnabled = m_context.isPlaying && !m_context.audioSourceRemoved; 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 1ad49eaef..be2f3d834 100644 --- a/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp +++ b/media/server/gstplayer/source/tasks/generic/GenericPlayerTaskFactory.cpp @@ -34,6 +34,7 @@ #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,6 +148,13 @@ 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 acf7417c5..ca6c1865e 100644 --- a/media/server/gstplayer/source/tasks/generic/NeedData.cpp +++ b/media/server/gstplayer/source/tasks/generic/NeedData.cpp @@ -59,6 +59,11 @@ 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 new file mode 100644 index 000000000..32ce2ccc4 --- /dev/null +++ b/media/server/gstplayer/source/tasks/generic/RemoveSource.cpp @@ -0,0 +1,75 @@ +/* + * 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, + const 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; + if (m_gstPlayerClient) + { + m_gstPlayerClient->invalidateActiveRequests(m_type); + } + auto sourceElem = m_context.streamInfo.find(m_type); + if (sourceElem == m_context.streamInfo.end()) + { + RIALTO_SERVER_LOG_WARN("Failed to remove source - streamInfo not found"); + return; + } + StreamInfo &streamInfo = sourceElem->second; + for (auto &buffer : streamInfo.buffers) + { + m_gstWrapper->gstBufferUnref(buffer); + } + streamInfo.buffers.clear(); + streamInfo.isDataNeeded = false; + streamInfo.isNeedDataPending = false; + m_context.initialPositions.erase(streamInfo.appSrc); + + // Reset Eos info + m_context.endOfStreamInfo.erase(m_type); + m_context.eosNotified = 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/MediaKeySession.cpp b/media/server/main/source/MediaKeySession.cpp index 61e6edcab..f2b11a924 100644 --- a/media/server/main/source/MediaKeySession.cpp +++ b/media/server/main/source/MediaKeySession.cpp @@ -276,6 +276,34 @@ MediaKeyErrorStatus MediaKeySession::decrypt(GstBuffer *encrypted, GstCaps *caps status = MediaKeyErrorStatus::FAIL; } + 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; + } + return status; } diff --git a/media/server/main/source/MediaPipelineServerInternal.cpp b/media/server/main/source/MediaPipelineServerInternal.cpp index c0c083c80..544169f89 100644 --- a/media/server/main/source/MediaPipelineServerInternal.cpp +++ b/media/server/main/source/MediaPipelineServerInternal.cpp @@ -302,6 +302,7 @@ bool MediaPipelineServerInternal::removeSourceInternal(int32_t id) MediaSourceType type = sourceIter->first; + m_gstPlayer->removeSource(type); m_needMediaDataTimers.erase(type); m_noAvailableSamplesCounter.erase(type); m_isMediaTypeEosMap.erase(type); diff --git a/tests/unittests/media/server/gstplayer/CMakeLists.txt b/tests/unittests/media/server/gstplayer/CMakeLists.txt index 7ceaaf650..ea5b7b2cb 100644 --- a/tests/unittests/media/server/gstplayer/CMakeLists.txt +++ b/tests/unittests/media/server/gstplayer/CMakeLists.txt @@ -45,6 +45,7 @@ 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 65a87a57a..9ab8eebab 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerPrivateTest.cpp @@ -255,7 +255,12 @@ TEST_F(GstGenericPlayerPrivateTest, shouldScheduleEnoughDataData) TEST_F(GstGenericPlayerPrivateTest, shouldScheduleAudioUnderflowWithUnderflowEnabled) { - modifyContext([&](GenericPlayerContext &context) { context.isPlaying = true; }); + modifyContext( + [&](GenericPlayerContext &context) + { + context.isPlaying = true; + context.audioSourceRemoved = false; + }); std::unique_ptr task{std::make_unique>()}; EXPECT_CALL(dynamic_cast &>(*task), execute()); @@ -267,7 +272,29 @@ TEST_F(GstGenericPlayerPrivateTest, shouldScheduleAudioUnderflowWithUnderflowEna TEST_F(GstGenericPlayerPrivateTest, shouldScheduleAudioUnderflowWithUnderflowDisabledNotPlaying) { - modifyContext([&](GenericPlayerContext &context) { context.isPlaying = false; }); + 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; + }); 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 f4f65086a..6096a5b3d 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/GstGenericPlayerTest.cpp @@ -147,6 +147,16 @@ 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 c9d971378..4a8a28600 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.cpp @@ -38,6 +38,7 @@ #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,16 +387,35 @@ 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; } +void GenericTasksTestsBase::setContextAudioInitialPosition() +{ + testContext->m_context.initialPositions[&testContext->m_appSrcAudio].emplace_back( + firebolt::rialto::server::SegmentData{kPosition, kResetTime, kAppliedRate, kStopPosition}); +} + void GenericTasksTestsBase::expectVideoUnderflowSignalConnection() { EXPECT_CALL(*testContext->m_glibWrapper, gObjectType(testContext->m_element)).WillRepeatedly(Return(G_TYPE_PARAM)); @@ -1965,6 +1985,11 @@ void GenericTasksTestsBase::shouldReattachAudioSource() EXPECT_CALL(testContext->m_gstPlayer, reattachSource(_)).WillOnce(Return(true)); } +void GenericTasksTestsBase::shouldRequestAudioData() +{ + EXPECT_CALL(testContext->m_gstPlayer, notifyNeedMediaData(MediaSourceType::AUDIO)); +} + void GenericTasksTestsBase::shouldFailToReattachAudioSource() { EXPECT_CALL(testContext->m_gstPlayer, reattachSource(_)).WillOnce(Return(false)); @@ -1975,6 +2000,15 @@ 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)); @@ -3137,6 +3171,44 @@ void GenericTasksTestsBase::triggerRenderFrame() task.execute(); } +void GenericTasksTestsBase::shouldInvalidateActiveAudioRequests() +{ + EXPECT_CALL(testContext->m_gstPlayerClient, invalidateActiveRequests(firebolt::rialto::MediaSourceType::AUDIO)); +} + +void GenericTasksTestsBase::shouldUnrefAudioBuffer() +{ + EXPECT_CALL(*testContext->m_gstWrapper, gstBufferUnref(&testContext->m_audioBuffer)); +} + +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 4aca17fa7..17ff78e43 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h +++ b/tests/unittests/media/server/gstplayer/genericPlayer/common/GenericTasksTestsBase.h @@ -72,8 +72,11 @@ class GenericTasksTestsBase : public ::testing::Test void setContextVideoBuffer(); void setContextPlaybackRate(); void setContextSourceNull(); + void setContextAudioSourceRemoved(); void setContextStreamInfoEmpty(); + void setContextNeedDataAudioOnly(); void setContextSetupSourceFinished(); + void setContextAudioInitialPosition(); // SetupElement test methods void shouldSetupVideoSinkElementOnly(); @@ -194,6 +197,7 @@ class GenericTasksTestsBase : public ::testing::Test void shouldReattachAudioSource(); void shouldFailToReattachAudioSource(); void triggerReattachAudioSource(); + void checkNewAudioSourceAttached(); void triggerFailToCastAudioSource(); void triggerFailToCastVideoSource(); void triggerFailToCastDolbyVisionSource(); @@ -402,6 +406,15 @@ class GenericTasksTestsBase : public ::testing::Test void triggerReadShmDataAndAttachSamplesVideo(); void triggerReadShmDataAndAttachSamples(); + // RemoveSource test methods + void shouldInvalidateActiveAudioRequests(); + void shouldUnrefAudioBuffer(); + void shouldRequestAudioData(); + void triggerRemoveSourceAudio(); + void triggerRemoveSourceVideo(); + void checkAudioSourceRemoved(); + void checkAudioSourceNotRemoved(); + // 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 746827b33..8b2775fc0 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/AttachSourceTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/AttachSourceTest.cpp @@ -161,16 +161,20 @@ TEST_F(AttachSourceTest, shouldFailToAttachUnknownSource) triggerAttachUnknownSource(); } -TEST_F(AttachSourceTest, shouldSwitchAudioSourceWhenSourceIsReattached) +TEST_F(AttachSourceTest, shouldReattachAudioSource) { setContextStreamInfo(firebolt::rialto::MediaSourceType::AUDIO); + setContextAudioSourceRemoved(); shouldReattachAudioSource(); + shouldRequestAudioData(); triggerReattachAudioSource(); + checkNewAudioSourceAttached(); } -TEST_F(AttachSourceTest, shouldFailToSwitchAudioSourceWhenSourceIsReattached) +TEST_F(AttachSourceTest, shouldFailToReattachAudioSource) { setContextStreamInfo(firebolt::rialto::MediaSourceType::AUDIO); + setContextAudioSourceRemoved(); shouldFailToReattachAudioSource(); triggerReattachAudioSource(); } diff --git a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp index 9b4911e15..d1d1b67ec 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/GenericPlayerTaskFactoryTest.cpp @@ -46,6 +46,7 @@ #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,6 +182,13 @@ 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 46e67cd0e..af3ccd199 100644 --- a/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/NeedDataTest.cpp +++ b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/NeedDataTest.cpp @@ -70,6 +70,16 @@ 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 new file mode 100644 index 000000000..3288a7d43 --- /dev/null +++ b/tests/unittests/media/server/gstplayer/genericPlayer/tasksTests/RemoveSourceTest.cpp @@ -0,0 +1,72 @@ +/* + * 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) +{ + setContextAudioInitialPosition(); + shouldInvalidateActiveAudioRequests(); + shouldUnrefAudioBuffer(); + triggerRemoveSourceAudio(); + checkNoMoreNeedData(); + checkNoNeedDataPendingForBothSources(); + checkAudioSourceRemoved(); + checkBuffersEmpty(); + checkInitialPositionNotSet(firebolt::rialto::MediaSourceType::AUDIO); +} + +TEST_F(RemoveSourceTest, shouldRemoveAudioSourceFlushEventError) +{ + shouldInvalidateActiveAudioRequests(); + shouldUnrefAudioBuffer(); + 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 56eae34ee..18717e95b 100644 --- a/tests/unittests/media/server/main/mediaPipeline/SourceTest.cpp +++ b/tests/unittests/media/server/main/mediaPipeline/SourceTest.cpp @@ -97,6 +97,7 @@ 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); } @@ -136,6 +137,7 @@ 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 2226cfb3c..77fac21c3 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GenericPlayerTaskFactoryMock.h @@ -65,6 +65,10 @@ 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 35aa3d004..1f1fd37de 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerMock.h @@ -34,6 +34,7 @@ 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 9fbf62ebc..5de619935 100644 --- a/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h +++ b/tests/unittests/media/server/mocks/gstplayer/GstGenericPlayerPrivateMock.h @@ -71,7 +71,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, addAudioClippingToBuffer, (GstBuffer * buffer, uint64_t clippingStart, uint64_t clippingEnd), (const, override)); MOCK_METHOD(void, pushSampleIfRequired, (GstElement * source, const std::string &typeStr), (override)); 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 5ea989b6c..10b6f4b37 100644 --- a/wrappers/source/OcdmSession.cpp +++ b/wrappers/source/OcdmSession.cpp @@ -21,6 +21,9 @@ #include "OcdmCommon.h" #include "opencdm/open_cdm_adapter.h" #include "opencdm/open_cdm_ext.h" +#include "RialtoCommonLogging.h" +#include +#include #include #include #include @@ -43,8 +46,22 @@ LicenseType convertLicenseType(const firebolt::rialto::KeySessionType &sessionTy } } } -const char *convertInitDataType(const firebolt::rialto::InitDataType &initDataType) +std::string bytesToHex(const std::vector &bytes) { + std::ostringstream oss; + for (size_t i = 0; i < bytes.size(); ++i) + { + oss << std::hex << std::setw(2) << std::setfill('0') << static_cast(bytes[i]); + if (i + 1 < bytes.size()) + { + oss << ' '; + } + } + return oss.str(); +} + +const char *convertInitDataType(const firebolt::rialto::InitDataType &initDataType) + switch (initDataType) { case firebolt::rialto::InitDataType::CENC: @@ -236,8 +253,10 @@ 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())); + RIALTO_COMMON_LOG_MIL("OCDM pre status casted to int is %d", static_cast(preStatus)); + RIALTO_COMMON_LOG_MIL("keyId (%zu bytes): %s", keyId.size(), bytesToHex(keyId).c_str()); if (preStatus == OutputRestricted || preStatus == OutputRestrictedHDCP22) { return MediaKeyErrorStatus::OUTPUT_RESTRICTED; @@ -252,6 +271,7 @@ MediaKeyErrorStatus OcdmSession::decryptBuffer(GstBuffer *encrypted, GstCaps *ca { const ::KeyStatus postStatus = opencdm_session_status(m_session, keyId.data(), static_cast(keyId.size())); + RIALTO_COMMON_LOG_MIL("OCDM post status casted to int is %d", static_cast(postStatus)); if (postStatus == OutputRestricted || postStatus == OutputRestrictedHDCP22) { return MediaKeyErrorStatus::OUTPUT_RESTRICTED;