LCOV - code coverage report
Current view: top level - media/server/gstplayer/source - GstGenericPlayer.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 94.9 % 1145 1087
Test Date: 2026-03-04 13:20:25 Functions: 94.7 % 114 108

            Line data    Source code
       1              : /*
       2              :  * If not stated otherwise in this file or this component's LICENSE file the
       3              :  * following copyright and licenses apply:
       4              :  *
       5              :  * Copyright 2022 Sky UK
       6              :  *
       7              :  * Licensed under the Apache License, Version 2.0 (the "License");
       8              :  * you may not use this file except in compliance with the License.
       9              :  * You may obtain a copy of the License at
      10              :  *
      11              :  * http://www.apache.org/licenses/LICENSE-2.0
      12              :  *
      13              :  * Unless required by applicable law or agreed to in writing, software
      14              :  * distributed under the License is distributed on an "AS IS" BASIS,
      15              :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      16              :  * See the License for the specific language governing permissions and
      17              :  * limitations under the License.
      18              :  */
      19              : 
      20              : #include <chrono>
      21              : #include <cinttypes>
      22              : #include <stdexcept>
      23              : 
      24              : #include "FlushWatcher.h"
      25              : #include "GstDispatcherThread.h"
      26              : #include "GstGenericPlayer.h"
      27              : #include "GstProtectionMetadata.h"
      28              : #include "IGstTextTrackSinkFactory.h"
      29              : #include "IMediaPipeline.h"
      30              : #include "ITimer.h"
      31              : #include "RialtoServerLogging.h"
      32              : #include "TypeConverters.h"
      33              : #include "Utils.h"
      34              : #include "WorkerThread.h"
      35              : #include "tasks/generic/GenericPlayerTaskFactory.h"
      36              : 
      37              : namespace
      38              : {
      39              : /**
      40              :  * @brief Report position interval in ms.
      41              :  *        The position reporting timer should be started whenever the PLAYING state is entered and stopped
      42              :  *        whenever the session moves to another playback state.
      43              :  */
      44              : constexpr std::chrono::milliseconds kPositionReportTimerMs{250};
      45              : constexpr std::chrono::seconds kSubtitleClockResyncInterval{10};
      46              : 
      47            1 : bool operator==(const firebolt::rialto::server::SegmentData &lhs, const firebolt::rialto::server::SegmentData &rhs)
      48              : {
      49            2 :     return (lhs.position == rhs.position) && (lhs.resetTime == rhs.resetTime) && (lhs.appliedRate == rhs.appliedRate) &&
      50            2 :            (lhs.stopPosition == rhs.stopPosition);
      51              : }
      52              : } // namespace
      53              : 
      54              : namespace firebolt::rialto::server
      55              : {
      56              : std::weak_ptr<IGstGenericPlayerFactory> GstGenericPlayerFactory::m_factory;
      57              : 
      58            3 : std::shared_ptr<IGstGenericPlayerFactory> IGstGenericPlayerFactory::getFactory()
      59              : {
      60            3 :     std::shared_ptr<IGstGenericPlayerFactory> factory = GstGenericPlayerFactory::m_factory.lock();
      61              : 
      62            3 :     if (!factory)
      63              :     {
      64              :         try
      65              :         {
      66            3 :             factory = std::make_shared<GstGenericPlayerFactory>();
      67              :         }
      68            0 :         catch (const std::exception &e)
      69              :         {
      70            0 :             RIALTO_SERVER_LOG_ERROR("Failed to create the gstreamer player factory, reason: %s", e.what());
      71              :         }
      72              : 
      73            3 :         GstGenericPlayerFactory::m_factory = factory;
      74              :     }
      75              : 
      76            3 :     return factory;
      77              : }
      78              : 
      79            1 : std::unique_ptr<IGstGenericPlayer> GstGenericPlayerFactory::createGstGenericPlayer(
      80              :     IGstGenericPlayerClient *client, IDecryptionService &decryptionService, MediaType type,
      81              :     const VideoRequirements &videoRequirements,
      82              :     const std::shared_ptr<firebolt::rialto::wrappers::IRdkGstreamerUtilsWrapperFactory> &rdkGstreamerUtilsWrapperFactory)
      83              : {
      84            1 :     std::unique_ptr<IGstGenericPlayer> gstPlayer;
      85              : 
      86              :     try
      87              :     {
      88            1 :         auto gstWrapperFactory = firebolt::rialto::wrappers::IGstWrapperFactory::getFactory();
      89            1 :         auto glibWrapperFactory = firebolt::rialto::wrappers::IGlibWrapperFactory::getFactory();
      90            1 :         std::shared_ptr<firebolt::rialto::wrappers::IGstWrapper> gstWrapper;
      91            1 :         std::shared_ptr<firebolt::rialto::wrappers::IGlibWrapper> glibWrapper;
      92            1 :         std::shared_ptr<firebolt::rialto::wrappers::IRdkGstreamerUtilsWrapper> rdkGstreamerUtilsWrapper;
      93            1 :         if ((!gstWrapperFactory) || (!(gstWrapper = gstWrapperFactory->getGstWrapper())))
      94              :         {
      95            0 :             throw std::runtime_error("Cannot create GstWrapper");
      96              :         }
      97            1 :         if ((!glibWrapperFactory) || (!(glibWrapper = glibWrapperFactory->getGlibWrapper())))
      98              :         {
      99            0 :             throw std::runtime_error("Cannot create GlibWrapper");
     100              :         }
     101            2 :         if ((!rdkGstreamerUtilsWrapperFactory) ||
     102            2 :             (!(rdkGstreamerUtilsWrapper = rdkGstreamerUtilsWrapperFactory->createRdkGstreamerUtilsWrapper())))
     103              :         {
     104            0 :             throw std::runtime_error("Cannot create RdkGstreamerUtilsWrapper");
     105              :         }
     106              :         gstPlayer = std::make_unique<
     107            2 :             GstGenericPlayer>(client, decryptionService, type, videoRequirements, gstWrapper, glibWrapper,
     108            2 :                               rdkGstreamerUtilsWrapper, IGstInitialiser::instance(), std::make_unique<FlushWatcher>(),
     109            2 :                               IGstSrcFactory::getFactory(), common::ITimerFactory::getFactory(),
     110            2 :                               std::make_unique<GenericPlayerTaskFactory>(client, gstWrapper, glibWrapper,
     111              :                                                                          rdkGstreamerUtilsWrapper,
     112            2 :                                                                          IGstTextTrackSinkFactory::createFactory()),
     113            2 :                               std::make_unique<WorkerThreadFactory>(), std::make_unique<GstDispatcherThreadFactory>(),
     114            3 :                               IGstProtectionMetadataHelperFactory::createFactory());
     115            1 :     }
     116            0 :     catch (const std::exception &e)
     117              :     {
     118            0 :         RIALTO_SERVER_LOG_ERROR("Failed to create the gstreamer player, reason: %s", e.what());
     119              :     }
     120              : 
     121            1 :     return gstPlayer;
     122              : }
     123              : 
     124          222 : GstGenericPlayer::GstGenericPlayer(
     125              :     IGstGenericPlayerClient *client, IDecryptionService &decryptionService, MediaType type,
     126              :     const VideoRequirements &videoRequirements,
     127              :     const std::shared_ptr<firebolt::rialto::wrappers::IGstWrapper> &gstWrapper,
     128              :     const std::shared_ptr<firebolt::rialto::wrappers::IGlibWrapper> &glibWrapper,
     129              :     const std::shared_ptr<firebolt::rialto::wrappers::IRdkGstreamerUtilsWrapper> &rdkGstreamerUtilsWrapper,
     130              :     const IGstInitialiser &gstInitialiser, std::unique_ptr<IFlushWatcher> &&flushWatcher,
     131              :     const std::shared_ptr<IGstSrcFactory> &gstSrcFactory, std::shared_ptr<common::ITimerFactory> timerFactory,
     132              :     std::unique_ptr<IGenericPlayerTaskFactory> taskFactory, std::unique_ptr<IWorkerThreadFactory> workerThreadFactory,
     133              :     std::unique_ptr<IGstDispatcherThreadFactory> gstDispatcherThreadFactory,
     134          222 :     std::shared_ptr<IGstProtectionMetadataHelperFactory> gstProtectionMetadataFactory)
     135          222 :     : m_gstPlayerClient(client), m_gstWrapper{gstWrapper}, m_glibWrapper{glibWrapper},
     136          444 :       m_rdkGstreamerUtilsWrapper{rdkGstreamerUtilsWrapper}, m_timerFactory{timerFactory},
     137          666 :       m_taskFactory{std::move(taskFactory)}, m_flushWatcher{std::move(flushWatcher)}
     138              : {
     139          222 :     RIALTO_SERVER_LOG_DEBUG("GstGenericPlayer is constructed.");
     140              : 
     141          222 :     gstInitialiser.waitForInitialisation();
     142              : 
     143          222 :     m_context.decryptionService = &decryptionService;
     144              : 
     145          222 :     if ((!gstSrcFactory) || (!(m_context.gstSrc = gstSrcFactory->getGstSrc())))
     146              :     {
     147            2 :         throw std::runtime_error("Cannot create GstSrc");
     148              :     }
     149              : 
     150          220 :     if (!timerFactory)
     151              :     {
     152            1 :         throw std::runtime_error("TimeFactory is invalid");
     153              :     }
     154              : 
     155          438 :     if ((!gstProtectionMetadataFactory) ||
     156          438 :         (!(m_protectionMetadataWrapper = gstProtectionMetadataFactory->createProtectionMetadataWrapper(m_gstWrapper))))
     157              :     {
     158            0 :         throw std::runtime_error("Cannot create protection metadata wrapper");
     159              :     }
     160              : 
     161              :     // Ensure that rialtosrc has been initalised
     162          219 :     m_context.gstSrc->initSrc();
     163              : 
     164              :     // Start task thread
     165          219 :     if ((!workerThreadFactory) || (!(m_workerThread = workerThreadFactory->createWorkerThread())))
     166              :     {
     167            0 :         throw std::runtime_error("Failed to create the worker thread");
     168              :     }
     169              : 
     170              :     // Initialise pipeline
     171          219 :     switch (type)
     172              :     {
     173          218 :     case MediaType::MSE:
     174              :     {
     175          218 :         initMsePipeline();
     176          218 :         break;
     177              :     }
     178            1 :     default:
     179              :     {
     180            1 :         resetWorkerThread();
     181            1 :         throw std::runtime_error("Media type not supported");
     182              :     }
     183              :     }
     184              : 
     185              :     // Check the video requirements for a limited video.
     186              :     // If the video requirements are set to anything lower than the minimum, this playback is assumed to be a secondary
     187              :     // video in a dual video scenario.
     188          218 :     if ((kMinPrimaryVideoWidth > videoRequirements.maxWidth) || (kMinPrimaryVideoHeight > videoRequirements.maxHeight))
     189              :     {
     190            8 :         RIALTO_SERVER_LOG_MIL("Secondary video playback selected");
     191            8 :         bool westerossinkSecondaryVideoResult = setWesterossinkSecondaryVideo();
     192            8 :         bool ermContextResult = setErmContext();
     193            8 :         if (!westerossinkSecondaryVideoResult && !ermContextResult)
     194              :         {
     195            1 :             resetWorkerThread();
     196            1 :             termPipeline();
     197            1 :             throw std::runtime_error("Could not set secondary video");
     198              :         }
     199            7 :     }
     200              :     else
     201              :     {
     202          210 :         RIALTO_SERVER_LOG_MIL("Primary video playback selected");
     203              :     }
     204              : 
     205          434 :     m_gstDispatcherThread = gstDispatcherThreadFactory->createGstDispatcherThread(*this, m_context.pipeline,
     206          217 :                                                                                   m_context.flushOnPrerollController,
     207          217 :                                                                                   m_gstWrapper);
     208          302 : }
     209              : 
     210          434 : GstGenericPlayer::~GstGenericPlayer()
     211              : {
     212          217 :     RIALTO_SERVER_LOG_DEBUG("GstGenericPlayer is destructed.");
     213          217 :     m_gstDispatcherThread.reset();
     214              : 
     215          217 :     resetWorkerThread();
     216              : 
     217          217 :     termPipeline();
     218          434 : }
     219              : 
     220          218 : void GstGenericPlayer::initMsePipeline()
     221              : {
     222              :     // Make playbin
     223          218 :     m_context.pipeline = m_gstWrapper->gstElementFactoryMake("playbin", "media_pipeline");
     224              :     // Set pipeline flags
     225          218 :     setPlaybinFlags(true);
     226              : 
     227              :     // Set callbacks
     228          218 :     m_glibWrapper->gSignalConnect(m_context.pipeline, "source-setup", G_CALLBACK(&GstGenericPlayer::setupSource), this);
     229          218 :     m_glibWrapper->gSignalConnect(m_context.pipeline, "element-setup", G_CALLBACK(&GstGenericPlayer::setupElement), this);
     230          218 :     m_glibWrapper->gSignalConnect(m_context.pipeline, "deep-element-added",
     231              :                                   G_CALLBACK(&GstGenericPlayer::deepElementAdded), this);
     232              : 
     233              :     // Set uri
     234          218 :     m_glibWrapper->gObjectSet(m_context.pipeline, "uri", "rialto://", nullptr);
     235              : 
     236              :     // Check playsink
     237          218 :     GstElement *playsink = (m_gstWrapper->gstBinGetByName(GST_BIN(m_context.pipeline), "playsink"));
     238          218 :     if (playsink)
     239              :     {
     240          217 :         m_glibWrapper->gObjectSet(G_OBJECT(playsink), "send-event-mode", 0, nullptr);
     241          217 :         m_gstWrapper->gstObjectUnref(playsink);
     242              :     }
     243              :     else
     244              :     {
     245            1 :         GST_WARNING("No playsink ?!?!?");
     246              :     }
     247          218 :     if (GST_STATE_CHANGE_FAILURE == m_gstWrapper->gstElementSetState(m_context.pipeline, GST_STATE_READY))
     248              :     {
     249            1 :         GST_WARNING("Failed to set pipeline to READY state");
     250              :     }
     251          218 :     RIALTO_SERVER_LOG_MIL("New RialtoServer's pipeline created");
     252              : }
     253              : 
     254          219 : void GstGenericPlayer::resetWorkerThread()
     255              : {
     256              :     // Shutdown task thread
     257          219 :     m_workerThread->enqueueTask(m_taskFactory->createShutdown(*this));
     258          219 :     m_workerThread->join();
     259          219 :     m_workerThread.reset();
     260              : }
     261              : 
     262          218 : void GstGenericPlayer::termPipeline()
     263              : {
     264          218 :     if (m_finishSourceSetupTimer && m_finishSourceSetupTimer->isActive())
     265              :     {
     266            0 :         m_finishSourceSetupTimer->cancel();
     267              :     }
     268              : 
     269          218 :     m_finishSourceSetupTimer.reset();
     270              : 
     271          267 :     for (auto &elem : m_context.streamInfo)
     272              :     {
     273           49 :         StreamInfo &streamInfo = elem.second;
     274           51 :         for (auto &buffer : streamInfo.buffers)
     275              :         {
     276            2 :             m_gstWrapper->gstBufferUnref(buffer);
     277              :         }
     278              : 
     279           49 :         streamInfo.buffers.clear();
     280              :     }
     281              : 
     282          218 :     m_taskFactory->createStop(m_context, *this)->execute();
     283          218 :     GstBus *bus = m_gstWrapper->gstPipelineGetBus(GST_PIPELINE(m_context.pipeline));
     284          218 :     m_gstWrapper->gstBusSetSyncHandler(bus, nullptr, nullptr, nullptr);
     285          218 :     m_gstWrapper->gstObjectUnref(bus);
     286              : 
     287          218 :     if (m_context.source)
     288              :     {
     289            1 :         m_gstWrapper->gstObjectUnref(m_context.source);
     290              :     }
     291          218 :     if (m_context.subtitleSink)
     292              :     {
     293            4 :         m_gstWrapper->gstObjectUnref(m_context.subtitleSink);
     294            4 :         m_context.subtitleSink = nullptr;
     295              :     }
     296              : 
     297          218 :     if (m_context.videoSink)
     298              :     {
     299            0 :         m_gstWrapper->gstObjectUnref(m_context.videoSink);
     300            0 :         m_context.videoSink = nullptr;
     301              :     }
     302              : 
     303              :     // Delete the pipeline
     304          218 :     m_gstWrapper->gstObjectUnref(m_context.pipeline);
     305              : 
     306          218 :     RIALTO_SERVER_LOG_MIL("RialtoServer's pipeline terminated");
     307              : }
     308              : 
     309          873 : unsigned GstGenericPlayer::getGstPlayFlag(const char *nick)
     310              : {
     311              :     GFlagsClass *flagsClass =
     312          873 :         static_cast<GFlagsClass *>(m_glibWrapper->gTypeClassRef(m_glibWrapper->gTypeFromName("GstPlayFlags")));
     313          873 :     GFlagsValue *flag = m_glibWrapper->gFlagsGetValueByNick(flagsClass, nick);
     314          873 :     return flag ? flag->value : 0;
     315              : }
     316              : 
     317            1 : void GstGenericPlayer::setupSource(GstElement *pipeline, GstElement *source, GstGenericPlayer *self)
     318              : {
     319            1 :     self->m_gstWrapper->gstObjectRef(source);
     320            1 :     if (self->m_workerThread)
     321              :     {
     322            1 :         self->m_workerThread->enqueueTask(self->m_taskFactory->createSetupSource(self->m_context, *self, source));
     323              :     }
     324              : }
     325              : 
     326            1 : void GstGenericPlayer::setupElement(GstElement *pipeline, GstElement *element, GstGenericPlayer *self)
     327              : {
     328            1 :     RIALTO_SERVER_LOG_DEBUG("Element %s added to the pipeline", GST_ELEMENT_NAME(element));
     329            1 :     self->m_gstWrapper->gstObjectRef(element);
     330            1 :     if (self->m_workerThread)
     331              :     {
     332            1 :         self->m_workerThread->enqueueTask(self->m_taskFactory->createSetupElement(self->m_context, *self, element));
     333              :     }
     334              : }
     335              : 
     336            1 : void GstGenericPlayer::deepElementAdded(GstBin *pipeline, GstBin *bin, GstElement *element, GstGenericPlayer *self)
     337              : {
     338            1 :     RIALTO_SERVER_LOG_DEBUG("Deep element %s added to the pipeline", GST_ELEMENT_NAME(element));
     339            1 :     if (self->m_workerThread)
     340              :     {
     341            2 :         self->m_workerThread->enqueueTask(
     342            2 :             self->m_taskFactory->createDeepElementAdded(self->m_context, *self, pipeline, bin, element));
     343              :     }
     344            1 : }
     345              : 
     346            1 : void GstGenericPlayer::attachSource(const std::unique_ptr<IMediaPipeline::MediaSource> &attachedSource)
     347              : {
     348            1 :     if (m_workerThread)
     349              :     {
     350            1 :         m_workerThread->enqueueTask(m_taskFactory->createAttachSource(m_context, *this, attachedSource));
     351              :     }
     352              : }
     353              : 
     354            2 : void GstGenericPlayer::allSourcesAttached()
     355              : {
     356            2 :     if (m_workerThread)
     357              :     {
     358            2 :         m_workerThread->enqueueTask(m_taskFactory->createFinishSetupSource(m_context, *this));
     359              :     }
     360              : }
     361              : 
     362            1 : void GstGenericPlayer::attachSamples(const IMediaPipeline::MediaSegmentVector &mediaSegments)
     363              : {
     364            1 :     if (m_workerThread)
     365              :     {
     366            1 :         m_workerThread->enqueueTask(m_taskFactory->createAttachSamples(m_context, *this, mediaSegments));
     367              :     }
     368              : }
     369              : 
     370            1 : void GstGenericPlayer::attachSamples(const std::shared_ptr<IDataReader> &dataReader)
     371              : {
     372            1 :     if (m_workerThread)
     373              :     {
     374            1 :         m_workerThread->enqueueTask(m_taskFactory->createReadShmDataAndAttachSamples(m_context, *this, dataReader));
     375              :     }
     376              : }
     377              : 
     378            1 : void GstGenericPlayer::setPosition(std::int64_t position)
     379              : {
     380            1 :     if (m_workerThread)
     381              :     {
     382            1 :         m_workerThread->enqueueTask(m_taskFactory->createSetPosition(m_context, *this, position));
     383              :     }
     384              : }
     385              : 
     386            1 : void GstGenericPlayer::setPlaybackRate(double rate)
     387              : {
     388            1 :     if (m_workerThread)
     389              :     {
     390            1 :         m_workerThread->enqueueTask(m_taskFactory->createSetPlaybackRate(m_context, rate));
     391              :     }
     392              : }
     393              : 
     394           11 : bool GstGenericPlayer::getPosition(std::int64_t &position)
     395              : {
     396              :     // We are on main thread here, but m_context.pipeline can be used, because it's modified only in GstGenericPlayer
     397              :     // constructor and destructor. GstGenericPlayer is created/destructed on main thread, so we won't have a crash here.
     398           11 :     position = getPosition(m_context.pipeline);
     399           11 :     if (position == -1)
     400              :     {
     401            3 :         return false;
     402              :     }
     403              : 
     404            8 :     return true;
     405              : }
     406              : 
     407           45 : GstElement *GstGenericPlayer::getSink(const MediaSourceType &mediaSourceType) const
     408              : {
     409           45 :     const char *kSinkName{nullptr};
     410           45 :     GstElement *sink{nullptr};
     411           45 :     switch (mediaSourceType)
     412              :     {
     413           24 :     case MediaSourceType::AUDIO:
     414           24 :         kSinkName = "audio-sink";
     415           24 :         break;
     416           18 :     case MediaSourceType::VIDEO:
     417           18 :         kSinkName = "video-sink";
     418           18 :         break;
     419            1 :     case MediaSourceType::SUBTITLE:
     420            1 :         kSinkName = "text-sink";
     421            1 :         break;
     422            2 :     default:
     423            2 :         break;
     424              :     }
     425           45 :     if (!kSinkName)
     426              :     {
     427            2 :         RIALTO_SERVER_LOG_WARN("mediaSourceType not supported %d", static_cast<int>(mediaSourceType));
     428              :     }
     429              :     else
     430              :     {
     431           43 :         if (m_context.pipeline == nullptr)
     432              :         {
     433            0 :             RIALTO_SERVER_LOG_WARN("Pipeline is NULL!");
     434              :         }
     435              :         else
     436              :         {
     437           43 :             RIALTO_SERVER_LOG_DEBUG("Pipeline is valid: %p", m_context.pipeline);
     438              :         }
     439           43 :         m_glibWrapper->gObjectGet(m_context.pipeline, kSinkName, &sink, nullptr);
     440           43 :         if (sink && firebolt::rialto::MediaSourceType::SUBTITLE != mediaSourceType)
     441              :         {
     442           25 :             GstElement *autoSink{sink};
     443           25 :             if (firebolt::rialto::MediaSourceType::VIDEO == mediaSourceType)
     444           14 :                 autoSink = getSinkChildIfAutoVideoSink(sink);
     445           11 :             else if (firebolt::rialto::MediaSourceType::AUDIO == mediaSourceType)
     446           11 :                 autoSink = getSinkChildIfAutoAudioSink(sink);
     447              : 
     448              :             // Is this an auto-sink?...
     449           25 :             if (autoSink != sink)
     450              :             {
     451            2 :                 m_gstWrapper->gstObjectUnref(GST_OBJECT(sink));
     452              : 
     453              :                 // increase the reference count of the auto sink
     454            2 :                 sink = GST_ELEMENT(m_gstWrapper->gstObjectRef(GST_OBJECT(autoSink)));
     455              :             }
     456              :         }
     457              :     }
     458           45 :     return sink;
     459              : }
     460              : 
     461            1 : void GstGenericPlayer::setSourceFlushed(const MediaSourceType &mediaSourceType)
     462              : {
     463            1 :     m_flushWatcher->setFlushed(mediaSourceType);
     464              : }
     465              : 
     466            6 : void GstGenericPlayer::notifyPlaybackInfo()
     467              : {
     468            6 :     PlaybackInfo info;
     469            6 :     getPosition(info.currentPosition);
     470            6 :     getVolume(info.volume);
     471            6 :     m_gstPlayerClient->notifyPlaybackInfo(info);
     472              : }
     473              : 
     474           23 : GstElement *GstGenericPlayer::getDecoder(const MediaSourceType &mediaSourceType)
     475              : {
     476           23 :     GstIterator *it = m_gstWrapper->gstBinIterateRecurse(GST_BIN(m_context.pipeline));
     477           23 :     GValue item = G_VALUE_INIT;
     478           23 :     gboolean done = FALSE;
     479              : 
     480           32 :     while (!done)
     481              :     {
     482           25 :         switch (m_gstWrapper->gstIteratorNext(it, &item))
     483              :         {
     484           16 :         case GST_ITERATOR_OK:
     485              :         {
     486           16 :             GstElement *element = GST_ELEMENT(m_glibWrapper->gValueGetObject(&item));
     487           16 :             GstElementFactory *factory = m_gstWrapper->gstElementGetFactory(element);
     488              : 
     489           16 :             if (factory)
     490              :             {
     491           16 :                 GstElementFactoryListType type = GST_ELEMENT_FACTORY_TYPE_DECODER;
     492           16 :                 if (mediaSourceType == MediaSourceType::AUDIO)
     493              :                 {
     494           12 :                     type |= GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO;
     495              :                 }
     496            4 :                 else if (mediaSourceType == MediaSourceType::VIDEO)
     497              :                 {
     498            4 :                     type |= GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO;
     499              :                 }
     500              : 
     501           16 :                 if (m_gstWrapper->gstElementFactoryListIsType(factory, type))
     502              :                 {
     503           16 :                     m_glibWrapper->gValueUnset(&item);
     504           16 :                     m_gstWrapper->gstIteratorFree(it);
     505           16 :                     return GST_ELEMENT(m_gstWrapper->gstObjectRef(element));
     506              :                 }
     507              :             }
     508              : 
     509            0 :             m_glibWrapper->gValueUnset(&item);
     510            0 :             break;
     511              :         }
     512            2 :         case GST_ITERATOR_RESYNC:
     513            2 :             m_gstWrapper->gstIteratorResync(it);
     514            2 :             break;
     515            7 :         case GST_ITERATOR_ERROR:
     516              :         case GST_ITERATOR_DONE:
     517            7 :             done = TRUE;
     518            7 :             break;
     519              :         }
     520              :     }
     521              : 
     522            7 :     RIALTO_SERVER_LOG_WARN("Could not find decoder");
     523              : 
     524            7 :     m_glibWrapper->gValueUnset(&item);
     525            7 :     m_gstWrapper->gstIteratorFree(it);
     526              : 
     527            7 :     return nullptr;
     528              : }
     529              : 
     530            3 : GstElement *GstGenericPlayer::getParser(const MediaSourceType &mediaSourceType)
     531              : {
     532            3 :     GstIterator *it = m_gstWrapper->gstBinIterateRecurse(GST_BIN(m_context.pipeline));
     533            3 :     GValue item = G_VALUE_INIT;
     534            3 :     gboolean done = FALSE;
     535              : 
     536            4 :     while (!done)
     537              :     {
     538            3 :         switch (m_gstWrapper->gstIteratorNext(it, &item))
     539              :         {
     540            2 :         case GST_ITERATOR_OK:
     541              :         {
     542            2 :             GstElement *element = GST_ELEMENT(m_glibWrapper->gValueGetObject(&item));
     543            2 :             GstElementFactory *factory = m_gstWrapper->gstElementGetFactory(element);
     544              : 
     545            2 :             if (factory)
     546              :             {
     547            2 :                 GstElementFactoryListType type = GST_ELEMENT_FACTORY_TYPE_PARSER;
     548            2 :                 if (mediaSourceType == MediaSourceType::AUDIO)
     549              :                 {
     550            0 :                     type |= GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO;
     551              :                 }
     552            2 :                 else if (mediaSourceType == MediaSourceType::VIDEO)
     553              :                 {
     554            2 :                     type |= GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO;
     555              :                 }
     556              : 
     557            2 :                 if (m_gstWrapper->gstElementFactoryListIsType(factory, type))
     558              :                 {
     559            2 :                     m_glibWrapper->gValueUnset(&item);
     560            2 :                     m_gstWrapper->gstIteratorFree(it);
     561            2 :                     return GST_ELEMENT(m_gstWrapper->gstObjectRef(element));
     562              :                 }
     563              :             }
     564              : 
     565            0 :             m_glibWrapper->gValueUnset(&item);
     566            0 :             break;
     567              :         }
     568            0 :         case GST_ITERATOR_RESYNC:
     569            0 :             m_gstWrapper->gstIteratorResync(it);
     570            0 :             break;
     571            1 :         case GST_ITERATOR_ERROR:
     572              :         case GST_ITERATOR_DONE:
     573            1 :             done = TRUE;
     574            1 :             break;
     575              :         }
     576              :     }
     577              : 
     578            1 :     RIALTO_SERVER_LOG_WARN("Could not find parser");
     579              : 
     580            1 :     m_glibWrapper->gValueUnset(&item);
     581            1 :     m_gstWrapper->gstIteratorFree(it);
     582              : 
     583            1 :     return nullptr;
     584              : }
     585              : 
     586              : std::optional<firebolt::rialto::wrappers::AudioAttributesPrivate>
     587            5 : GstGenericPlayer::createAudioAttributes(const std::unique_ptr<IMediaPipeline::MediaSource> &source) const
     588              : {
     589            5 :     std::optional<firebolt::rialto::wrappers::AudioAttributesPrivate> audioAttributes;
     590            5 :     const IMediaPipeline::MediaSourceAudio *kSource = dynamic_cast<IMediaPipeline::MediaSourceAudio *>(source.get());
     591            5 :     if (kSource)
     592              :     {
     593            4 :         firebolt::rialto::AudioConfig audioConfig = kSource->getAudioConfig();
     594              :         audioAttributes =
     595           12 :             firebolt::rialto::wrappers::AudioAttributesPrivate{"", // param set below.
     596            4 :                                                                audioConfig.numberOfChannels, audioConfig.sampleRate,
     597              :                                                                0, // used only in one of logs in rdk_gstreamer_utils, no
     598              :                                                                   // need to set this param.
     599              :                                                                0, // used only in one of logs in rdk_gstreamer_utils, no
     600              :                                                                   // need to set this param.
     601            4 :                                                                audioConfig.codecSpecificConfig.data(),
     602              :                                                                static_cast<std::uint32_t>(
     603            4 :                                                                    audioConfig.codecSpecificConfig.size())};
     604            4 :         if (source->getMimeType() == "audio/mp4" || source->getMimeType() == "audio/aac")
     605              :         {
     606            2 :             audioAttributes->m_codecParam = "mp4a";
     607              :         }
     608            2 :         else if (source->getMimeType() == "audio/x-eac3")
     609              :         {
     610            1 :             audioAttributes->m_codecParam = "ec-3";
     611              :         }
     612            1 :         else if (source->getMimeType() == "audio/b-wav" || source->getMimeType() == "audio/x-raw")
     613              :         {
     614            1 :             audioAttributes->m_codecParam = "lpcm";
     615              :         }
     616            4 :     }
     617              :     else
     618              :     {
     619            1 :         RIALTO_SERVER_LOG_ERROR("Failed to cast source");
     620              :     }
     621              : 
     622            5 :     return audioAttributes;
     623              : }
     624              : 
     625            1 : bool GstGenericPlayer::setImmediateOutput(const MediaSourceType &mediaSourceType, bool immediateOutputParam)
     626              : {
     627            1 :     if (!m_workerThread)
     628            0 :         return false;
     629              : 
     630            2 :     m_workerThread->enqueueTask(
     631            2 :         m_taskFactory->createSetImmediateOutput(m_context, *this, mediaSourceType, immediateOutputParam));
     632            1 :     return true;
     633              : }
     634              : 
     635            1 : bool GstGenericPlayer::setReportDecodeErrors(const MediaSourceType &mediaSourceType, bool reportDecodeErrors)
     636              : {
     637            1 :     if (!m_workerThread)
     638            0 :         return false;
     639              : 
     640            2 :     m_workerThread->enqueueTask(
     641            2 :         m_taskFactory->createSetReportDecodeErrors(m_context, *this, mediaSourceType, reportDecodeErrors));
     642            1 :     return true;
     643              : }
     644              : 
     645            2 : bool GstGenericPlayer::getQueuedFrames(uint32_t &queuedFrames)
     646              : {
     647            2 :     bool returnValue{false};
     648            2 :     GstElement *decoder{getDecoder(MediaSourceType::VIDEO)};
     649            2 :     if (decoder)
     650              :     {
     651            2 :         if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(decoder), "queued-frames"))
     652              :         {
     653            1 :             m_glibWrapper->gObjectGet(decoder, "queued-frames", &queuedFrames, nullptr);
     654            1 :             returnValue = true;
     655              :         }
     656              :         else
     657              :         {
     658            1 :             RIALTO_SERVER_LOG_ERROR("queued-frames not supported in element %s", GST_ELEMENT_NAME(decoder));
     659              :         }
     660            2 :         m_gstWrapper->gstObjectUnref(decoder);
     661              :     }
     662              :     else
     663              :     {
     664            0 :         RIALTO_SERVER_LOG_ERROR("Failed to get queued-frames property, decoder is NULL");
     665              :     }
     666              : 
     667            2 :     return returnValue;
     668              : }
     669              : 
     670            5 : bool GstGenericPlayer::getImmediateOutput(const MediaSourceType &mediaSourceType, bool &immediateOutputRef)
     671              : {
     672            5 :     bool returnValue{false};
     673            5 :     GstElement *sink{getSink(mediaSourceType)};
     674            5 :     if (sink)
     675              :     {
     676            3 :         if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(sink), "immediate-output"))
     677              :         {
     678            2 :             m_glibWrapper->gObjectGet(sink, "immediate-output", &immediateOutputRef, nullptr);
     679            2 :             returnValue = true;
     680              :         }
     681              :         else
     682              :         {
     683            1 :             RIALTO_SERVER_LOG_ERROR("immediate-output not supported in element %s", GST_ELEMENT_NAME(sink));
     684              :         }
     685            3 :         m_gstWrapper->gstObjectUnref(sink);
     686              :     }
     687              :     else
     688              :     {
     689            2 :         RIALTO_SERVER_LOG_ERROR("Failed to set immediate-output property, sink is NULL");
     690              :     }
     691              : 
     692            5 :     return returnValue;
     693              : }
     694              : 
     695            5 : bool GstGenericPlayer::getStats(const MediaSourceType &mediaSourceType, uint64_t &renderedFrames, uint64_t &droppedFrames)
     696              : {
     697            5 :     bool returnValue{false};
     698            5 :     GstElement *sink{getSink(mediaSourceType)};
     699            5 :     if (sink)
     700              :     {
     701            3 :         GstStructure *stats{nullptr};
     702            3 :         m_glibWrapper->gObjectGet(sink, "stats", &stats, nullptr);
     703            3 :         if (!stats)
     704              :         {
     705            1 :             RIALTO_SERVER_LOG_ERROR("failed to get stats from '%s'", GST_ELEMENT_NAME(sink));
     706              :         }
     707              :         else
     708              :         {
     709              :             guint64 renderedFramesTmp;
     710              :             guint64 droppedFramesTmp;
     711            3 :             if (m_gstWrapper->gstStructureGetUint64(stats, "rendered", &renderedFramesTmp) &&
     712            1 :                 m_gstWrapper->gstStructureGetUint64(stats, "dropped", &droppedFramesTmp))
     713              :             {
     714            1 :                 renderedFrames = renderedFramesTmp;
     715            1 :                 droppedFrames = droppedFramesTmp;
     716            1 :                 returnValue = true;
     717              :             }
     718              :             else
     719              :             {
     720            1 :                 RIALTO_SERVER_LOG_ERROR("failed to get 'rendered' or 'dropped' from structure (%s)",
     721              :                                         GST_ELEMENT_NAME(sink));
     722              :             }
     723            2 :             m_gstWrapper->gstStructureFree(stats);
     724              :         }
     725            3 :         m_gstWrapper->gstObjectUnref(sink);
     726              :     }
     727              :     else
     728              :     {
     729            2 :         RIALTO_SERVER_LOG_ERROR("Failed to get stats, sink is NULL");
     730              :     }
     731              : 
     732            5 :     return returnValue;
     733              : }
     734              : 
     735            4 : GstBuffer *GstGenericPlayer::createBuffer(const IMediaPipeline::MediaSegment &mediaSegment) const
     736              : {
     737            4 :     GstBuffer *gstBuffer = m_gstWrapper->gstBufferNewAllocate(nullptr, mediaSegment.getDataLength(), nullptr);
     738            4 :     m_gstWrapper->gstBufferFill(gstBuffer, 0, mediaSegment.getData(), mediaSegment.getDataLength());
     739              : 
     740            4 :     if (mediaSegment.isEncrypted())
     741              :     {
     742            3 :         GstBuffer *keyId = m_gstWrapper->gstBufferNewAllocate(nullptr, mediaSegment.getKeyId().size(), nullptr);
     743            3 :         m_gstWrapper->gstBufferFill(keyId, 0, mediaSegment.getKeyId().data(), mediaSegment.getKeyId().size());
     744              : 
     745            3 :         GstBuffer *initVector = m_gstWrapper->gstBufferNewAllocate(nullptr, mediaSegment.getInitVector().size(), nullptr);
     746            6 :         m_gstWrapper->gstBufferFill(initVector, 0, mediaSegment.getInitVector().data(),
     747            3 :                                     mediaSegment.getInitVector().size());
     748            3 :         GstBuffer *subsamples{nullptr};
     749            3 :         if (!mediaSegment.getSubSamples().empty())
     750              :         {
     751            3 :             auto subsamplesRawSize = mediaSegment.getSubSamples().size() * (sizeof(guint16) + sizeof(guint32));
     752            3 :             guint8 *subsamplesRaw = static_cast<guint8 *>(m_glibWrapper->gMalloc(subsamplesRawSize));
     753              :             GstByteWriter writer;
     754            3 :             m_gstWrapper->gstByteWriterInitWithData(&writer, subsamplesRaw, subsamplesRawSize, FALSE);
     755              : 
     756            6 :             for (const auto &subSample : mediaSegment.getSubSamples())
     757              :             {
     758            3 :                 m_gstWrapper->gstByteWriterPutUint16Be(&writer, subSample.numClearBytes);
     759            3 :                 m_gstWrapper->gstByteWriterPutUint32Be(&writer, subSample.numEncryptedBytes);
     760              :             }
     761            3 :             subsamples = m_gstWrapper->gstBufferNewWrapped(subsamplesRaw, subsamplesRawSize);
     762              :         }
     763              : 
     764            3 :         uint32_t crypt = 0;
     765            3 :         uint32_t skip = 0;
     766            3 :         bool encryptionPatternSet = mediaSegment.getEncryptionPattern(crypt, skip);
     767              : 
     768            3 :         GstRialtoProtectionData data = {mediaSegment.getMediaKeySessionId(),
     769            3 :                                         static_cast<uint32_t>(mediaSegment.getSubSamples().size()),
     770            3 :                                         mediaSegment.getInitWithLast15(),
     771              :                                         keyId,
     772              :                                         initVector,
     773              :                                         subsamples,
     774            6 :                                         mediaSegment.getCipherMode(),
     775              :                                         crypt,
     776              :                                         skip,
     777              :                                         encryptionPatternSet,
     778            6 :                                         m_context.decryptionService};
     779              : 
     780            3 :         if (!m_protectionMetadataWrapper->addProtectionMetadata(gstBuffer, data))
     781              :         {
     782            1 :             RIALTO_SERVER_LOG_ERROR("Failed to add protection metadata");
     783            1 :             if (keyId)
     784              :             {
     785            1 :                 m_gstWrapper->gstBufferUnref(keyId);
     786              :             }
     787            1 :             if (initVector)
     788              :             {
     789            1 :                 m_gstWrapper->gstBufferUnref(initVector);
     790              :             }
     791            1 :             if (subsamples)
     792              :             {
     793            1 :                 m_gstWrapper->gstBufferUnref(subsamples);
     794              :             }
     795              :         }
     796              :     }
     797              : 
     798            4 :     GST_BUFFER_TIMESTAMP(gstBuffer) = mediaSegment.getTimeStamp();
     799            4 :     GST_BUFFER_DURATION(gstBuffer) = mediaSegment.getDuration();
     800            4 :     return gstBuffer;
     801              : }
     802              : 
     803            4 : void GstGenericPlayer::notifyNeedMediaData(const MediaSourceType mediaSource)
     804              : {
     805            4 :     auto elem = m_context.streamInfo.find(mediaSource);
     806            4 :     if (elem != m_context.streamInfo.end())
     807              :     {
     808            2 :         StreamInfo &streamInfo = elem->second;
     809            2 :         streamInfo.isNeedDataPending = false;
     810              : 
     811              :         // Send new NeedMediaData if we still need it
     812            2 :         if (m_gstPlayerClient && streamInfo.isDataNeeded)
     813              :         {
     814            2 :             streamInfo.isNeedDataPending = m_gstPlayerClient->notifyNeedMediaData(mediaSource);
     815              :         }
     816              :     }
     817              :     else
     818              :     {
     819            2 :         RIALTO_SERVER_LOG_WARN("Media type %s could not be found", common::convertMediaSourceType(mediaSource));
     820              :     }
     821            4 : }
     822              : 
     823           19 : void GstGenericPlayer::attachData(const firebolt::rialto::MediaSourceType mediaType)
     824              : {
     825           19 :     auto elem = m_context.streamInfo.find(mediaType);
     826           19 :     if (elem != m_context.streamInfo.end())
     827              :     {
     828           16 :         StreamInfo &streamInfo = elem->second;
     829           16 :         if (streamInfo.buffers.empty() || !streamInfo.isDataNeeded)
     830              :         {
     831            2 :             return;
     832              :         }
     833              : 
     834           14 :         if (firebolt::rialto::MediaSourceType::SUBTITLE == mediaType)
     835              :         {
     836            2 :             setTextTrackPositionIfRequired(streamInfo.appSrc);
     837              :         }
     838              :         else
     839              :         {
     840           36 :             pushSampleIfRequired(streamInfo.appSrc, common::convertMediaSourceType(mediaType));
     841              :         }
     842           14 :         if (mediaType == firebolt::rialto::MediaSourceType::AUDIO)
     843              :         {
     844              :             // This needs to be done before gstAppSrcPushBuffer() is
     845              :             // called because it can free the memory
     846            7 :             m_context.lastAudioSampleTimestamps = static_cast<int64_t>(GST_BUFFER_PTS(streamInfo.buffers.back()));
     847              :         }
     848              : 
     849           28 :         for (GstBuffer *buffer : streamInfo.buffers)
     850              :         {
     851           14 :             m_gstWrapper->gstAppSrcPushBuffer(GST_APP_SRC(streamInfo.appSrc), buffer);
     852              :         }
     853           14 :         streamInfo.buffers.clear();
     854           14 :         streamInfo.isDataPushed = true;
     855              : 
     856           14 :         const bool kIsSingle = m_context.streamInfo.size() == 1;
     857           14 :         bool allOtherStreamsPushed = std::all_of(m_context.streamInfo.begin(), m_context.streamInfo.end(),
     858           15 :                                                  [](const auto &entry) { return entry.second.isDataPushed; });
     859              : 
     860           14 :         if (!m_context.bufferedNotificationSent && (allOtherStreamsPushed || kIsSingle) && m_gstPlayerClient)
     861              :         {
     862            1 :             m_context.bufferedNotificationSent = true;
     863            1 :             m_gstPlayerClient->notifyNetworkState(NetworkState::BUFFERED);
     864            1 :             RIALTO_SERVER_LOG_MIL("Buffered NetworkState reached");
     865              :         }
     866           14 :         cancelUnderflow(mediaType);
     867              : 
     868           14 :         const auto eosInfoIt = m_context.endOfStreamInfo.find(mediaType);
     869           14 :         if (eosInfoIt != m_context.endOfStreamInfo.end() && eosInfoIt->second == EosState::PENDING)
     870              :         {
     871            0 :             setEos(mediaType);
     872              :         }
     873              :     }
     874              : }
     875              : 
     876            7 : void GstGenericPlayer::updateAudioCaps(int32_t rate, int32_t channels, const std::shared_ptr<CodecData> &codecData)
     877              : {
     878            7 :     auto elem = m_context.streamInfo.find(firebolt::rialto::MediaSourceType::AUDIO);
     879            7 :     if (elem != m_context.streamInfo.end())
     880              :     {
     881            6 :         StreamInfo &streamInfo = elem->second;
     882              : 
     883            6 :         constexpr int kInvalidRate{0}, kInvalidChannels{0};
     884            6 :         GstCaps *currentCaps = m_gstWrapper->gstAppSrcGetCaps(GST_APP_SRC(streamInfo.appSrc));
     885            6 :         GstCaps *newCaps = m_gstWrapper->gstCapsCopy(currentCaps);
     886              : 
     887            6 :         if (rate != kInvalidRate)
     888              :         {
     889            3 :             m_gstWrapper->gstCapsSetSimple(newCaps, "rate", G_TYPE_INT, rate, NULL);
     890              :         }
     891              : 
     892            6 :         if (channels != kInvalidChannels)
     893              :         {
     894            3 :             m_gstWrapper->gstCapsSetSimple(newCaps, "channels", G_TYPE_INT, channels, NULL);
     895              :         }
     896              : 
     897            6 :         setCodecData(newCaps, codecData);
     898              : 
     899            6 :         if (!m_gstWrapper->gstCapsIsEqual(currentCaps, newCaps))
     900              :         {
     901            5 :             m_gstWrapper->gstAppSrcSetCaps(GST_APP_SRC(streamInfo.appSrc), newCaps);
     902              :         }
     903              : 
     904            6 :         m_gstWrapper->gstCapsUnref(newCaps);
     905            6 :         m_gstWrapper->gstCapsUnref(currentCaps);
     906              :     }
     907            7 : }
     908              : 
     909            8 : void GstGenericPlayer::updateVideoCaps(int32_t width, int32_t height, Fraction frameRate,
     910              :                                        const std::shared_ptr<CodecData> &codecData)
     911              : {
     912            8 :     auto elem = m_context.streamInfo.find(firebolt::rialto::MediaSourceType::VIDEO);
     913            8 :     if (elem != m_context.streamInfo.end())
     914              :     {
     915            7 :         StreamInfo &streamInfo = elem->second;
     916              : 
     917            7 :         GstCaps *currentCaps = m_gstWrapper->gstAppSrcGetCaps(GST_APP_SRC(streamInfo.appSrc));
     918            7 :         GstCaps *newCaps = m_gstWrapper->gstCapsCopy(currentCaps);
     919              : 
     920            7 :         if (width > 0)
     921              :         {
     922            6 :             m_gstWrapper->gstCapsSetSimple(newCaps, "width", G_TYPE_INT, width, NULL);
     923              :         }
     924              : 
     925            7 :         if (height > 0)
     926              :         {
     927            6 :             m_gstWrapper->gstCapsSetSimple(newCaps, "height", G_TYPE_INT, height, NULL);
     928              :         }
     929              : 
     930            7 :         if ((kUndefinedSize != frameRate.numerator) && (kUndefinedSize != frameRate.denominator))
     931              :         {
     932            6 :             m_gstWrapper->gstCapsSetSimple(newCaps, "framerate", GST_TYPE_FRACTION, frameRate.numerator,
     933              :                                            frameRate.denominator, NULL);
     934              :         }
     935              : 
     936            7 :         setCodecData(newCaps, codecData);
     937              : 
     938            7 :         if (!m_gstWrapper->gstCapsIsEqual(currentCaps, newCaps))
     939              :         {
     940            6 :             m_gstWrapper->gstAppSrcSetCaps(GST_APP_SRC(streamInfo.appSrc), newCaps);
     941              :         }
     942              : 
     943            7 :         m_gstWrapper->gstCapsUnref(currentCaps);
     944            7 :         m_gstWrapper->gstCapsUnref(newCaps);
     945              :     }
     946            8 : }
     947              : 
     948            5 : void GstGenericPlayer::addAudioClippingToBuffer(GstBuffer *buffer, uint64_t clippingStart, uint64_t clippingEnd) const
     949              : {
     950            5 :     if (clippingStart || clippingEnd)
     951              :     {
     952            4 :         if (m_gstWrapper->gstBufferAddAudioClippingMeta(buffer, GST_FORMAT_TIME, clippingStart, clippingEnd))
     953              :         {
     954            3 :             RIALTO_SERVER_LOG_DEBUG("Added audio clipping to buffer %p, start: %" PRIu64 ", end %" PRIu64, buffer,
     955              :                                     clippingStart, clippingEnd);
     956              :         }
     957              :         else
     958              :         {
     959            1 :             RIALTO_SERVER_LOG_WARN("Failed to add audio clipping to buffer %p, start: %" PRIu64 ", end %" PRIu64,
     960              :                                    buffer, clippingStart, clippingEnd);
     961              :         }
     962              :     }
     963            5 : }
     964              : 
     965           13 : bool GstGenericPlayer::setCodecData(GstCaps *caps, const std::shared_ptr<CodecData> &codecData) const
     966              : {
     967           13 :     if (codecData && CodecDataType::BUFFER == codecData->type)
     968              :     {
     969            7 :         gpointer memory = m_glibWrapper->gMemdup(codecData->data.data(), codecData->data.size());
     970            7 :         GstBuffer *buf = m_gstWrapper->gstBufferNewWrapped(memory, codecData->data.size());
     971            7 :         m_gstWrapper->gstCapsSetSimple(caps, "codec_data", GST_TYPE_BUFFER, buf, nullptr);
     972            7 :         m_gstWrapper->gstBufferUnref(buf);
     973            7 :         return true;
     974              :     }
     975            6 :     if (codecData && CodecDataType::STRING == codecData->type)
     976              :     {
     977            2 :         std::string codecDataStr(codecData->data.begin(), codecData->data.end());
     978            2 :         m_gstWrapper->gstCapsSetSimple(caps, "codec_data", G_TYPE_STRING, codecDataStr.c_str(), nullptr);
     979            2 :         return true;
     980              :     }
     981            4 :     return false;
     982              : }
     983              : 
     984           12 : void GstGenericPlayer::pushSampleIfRequired(GstElement *source, const std::string &typeStr)
     985              : {
     986           12 :     auto initialPosition = m_context.initialPositions.find(source);
     987           12 :     if (m_context.initialPositions.end() == initialPosition)
     988              :     {
     989              :         // Sending initial sample not needed
     990            7 :         return;
     991              :     }
     992              :     // GstAppSrc does not replace segment, if it's the same as previous one.
     993              :     // It causes problems with position reporing in amlogic devices, so we need to push
     994              :     // two segments with different reset time value.
     995            5 :     pushAdditionalSegmentIfRequired(source);
     996              : 
     997           10 :     for (const auto &[position, resetTime, appliedRate, stopPosition] : initialPosition->second)
     998              :     {
     999            6 :         GstSeekFlags seekFlag = resetTime ? GST_SEEK_FLAG_FLUSH : GST_SEEK_FLAG_NONE;
    1000            6 :         RIALTO_SERVER_LOG_DEBUG("Pushing new %s sample...", typeStr.c_str());
    1001            6 :         GstSegment *segment{m_gstWrapper->gstSegmentNew()};
    1002            6 :         m_gstWrapper->gstSegmentInit(segment, GST_FORMAT_TIME);
    1003            6 :         if (!m_gstWrapper->gstSegmentDoSeek(segment, m_context.playbackRate, GST_FORMAT_TIME, seekFlag,
    1004              :                                             GST_SEEK_TYPE_SET, position, GST_SEEK_TYPE_SET, stopPosition, nullptr))
    1005              :         {
    1006            1 :             RIALTO_SERVER_LOG_WARN("Segment seek failed.");
    1007            1 :             m_gstWrapper->gstSegmentFree(segment);
    1008            1 :             m_context.initialPositions.erase(initialPosition);
    1009            1 :             return;
    1010              :         }
    1011            5 :         segment->applied_rate = appliedRate;
    1012            5 :         RIALTO_SERVER_LOG_MIL("New %s segment: [%" GST_TIME_FORMAT ", %" GST_TIME_FORMAT
    1013              :                               "], rate: %f, appliedRate %f, reset_time: %d\n",
    1014              :                               typeStr.c_str(), GST_TIME_ARGS(segment->start), GST_TIME_ARGS(segment->stop),
    1015              :                               segment->rate, segment->applied_rate, resetTime);
    1016              : 
    1017            5 :         GstCaps *currentCaps = m_gstWrapper->gstAppSrcGetCaps(GST_APP_SRC(source));
    1018              :         // We can't pass buffer in GstSample, because implementation of gst_app_src_push_sample
    1019              :         // uses gst_buffer_copy, which loses RialtoProtectionMeta (that causes problems with EME
    1020              :         // for first frame).
    1021            5 :         GstSample *sample = m_gstWrapper->gstSampleNew(nullptr, currentCaps, segment, nullptr);
    1022            5 :         m_gstWrapper->gstAppSrcPushSample(GST_APP_SRC(source), sample);
    1023            5 :         m_gstWrapper->gstSampleUnref(sample);
    1024            5 :         m_gstWrapper->gstCapsUnref(currentCaps);
    1025              : 
    1026            5 :         m_gstWrapper->gstSegmentFree(segment);
    1027              :     }
    1028            4 :     m_context.currentPosition[source] = initialPosition->second.back();
    1029            4 :     m_context.initialPositions.erase(initialPosition);
    1030            4 :     return;
    1031              : }
    1032              : 
    1033            5 : void GstGenericPlayer::pushAdditionalSegmentIfRequired(GstElement *source)
    1034              : {
    1035            5 :     auto currentPosition = m_context.currentPosition.find(source);
    1036            5 :     if (m_context.currentPosition.end() == currentPosition)
    1037              :     {
    1038            4 :         return;
    1039              :     }
    1040            1 :     auto initialPosition = m_context.initialPositions.find(source);
    1041            1 :     if (m_context.initialPositions.end() == initialPosition)
    1042              :     {
    1043            0 :         return;
    1044              :     }
    1045            2 :     if (initialPosition->second.size() == 1 && initialPosition->second.back().resetTime &&
    1046            1 :         currentPosition->second == initialPosition->second.back())
    1047              :     {
    1048            1 :         RIALTO_SERVER_LOG_INFO("Adding additional segment with reset_time = false");
    1049            1 :         SegmentData additionalSegment = initialPosition->second.back();
    1050            1 :         additionalSegment.resetTime = false;
    1051            1 :         initialPosition->second.push_back(additionalSegment);
    1052              :     }
    1053              : }
    1054              : 
    1055            2 : void GstGenericPlayer::setTextTrackPositionIfRequired(GstElement *source)
    1056              : {
    1057            2 :     auto initialPosition = m_context.initialPositions.find(source);
    1058            2 :     if (m_context.initialPositions.end() == initialPosition)
    1059              :     {
    1060              :         // Sending initial sample not needed
    1061            1 :         return;
    1062              :     }
    1063              : 
    1064            1 :     RIALTO_SERVER_LOG_MIL("New subtitle position set %" GST_TIME_FORMAT,
    1065              :                           GST_TIME_ARGS(initialPosition->second.back().position));
    1066            1 :     m_glibWrapper->gObjectSet(m_context.subtitleSink, "position",
    1067            1 :                               static_cast<guint64>(initialPosition->second.back().position), nullptr);
    1068              : 
    1069            1 :     m_context.initialPositions.erase(initialPosition);
    1070              : }
    1071              : 
    1072            7 : bool GstGenericPlayer::reattachSource(const std::unique_ptr<IMediaPipeline::MediaSource> &source)
    1073              : {
    1074            7 :     if (m_context.streamInfo.find(source->getType()) == m_context.streamInfo.end())
    1075              :     {
    1076            1 :         RIALTO_SERVER_LOG_ERROR("Unable to switch source, type does not exist");
    1077            1 :         return false;
    1078              :     }
    1079            6 :     if (source->getMimeType().empty())
    1080              :     {
    1081            1 :         RIALTO_SERVER_LOG_WARN("Skip switch audio source. Unknown mime type");
    1082            1 :         return false;
    1083              :     }
    1084            5 :     std::optional<firebolt::rialto::wrappers::AudioAttributesPrivate> audioAttributes{createAudioAttributes(source)};
    1085            5 :     if (!audioAttributes)
    1086              :     {
    1087            1 :         RIALTO_SERVER_LOG_ERROR("Failed to create audio attributes");
    1088            1 :         return false;
    1089              :     }
    1090              : 
    1091            4 :     long long currentDispPts = getPosition(m_context.pipeline); // NOLINT(runtime/int)
    1092            4 :     GstCaps *caps{createCapsFromMediaSource(m_gstWrapper, m_glibWrapper, source)};
    1093            4 :     GstAppSrc *appSrc{GST_APP_SRC(m_context.streamInfo[source->getType()].appSrc)};
    1094            4 :     GstCaps *oldCaps = m_gstWrapper->gstAppSrcGetCaps(appSrc);
    1095            4 :     if ((!oldCaps) || (!m_gstWrapper->gstCapsIsEqual(caps, oldCaps)))
    1096              :     {
    1097            3 :         RIALTO_SERVER_LOG_DEBUG("Caps not equal. Perform audio track codec channel switch.");
    1098            3 :         int sampleAttributes{
    1099              :             0}; // rdk_gstreamer_utils::performAudioTrackCodecChannelSwitch checks if this param != NULL only.
    1100            3 :         std::uint32_t status{0};   // must be 0 to make rdk_gstreamer_utils::performAudioTrackCodecChannelSwitch work
    1101            3 :         unsigned int ui32Delay{0}; // output param
    1102            3 :         long long audioChangeTargetPts{-1}; // NOLINT(runtime/int) output param. Set audioChangeTargetPts =
    1103              :                                             // currentDispPts in rdk_gstreamer_utils function stub
    1104            3 :         unsigned int audioChangeStage{0};   // Output param. Set to AUDCHG_ALIGN in rdk_gstreamer_utils function stub
    1105            3 :         gchar *oldCapsCStr = m_gstWrapper->gstCapsToString(oldCaps);
    1106            3 :         std::string oldCapsStr = std::string(oldCapsCStr);
    1107            3 :         m_glibWrapper->gFree(oldCapsCStr);
    1108            3 :         bool audioAac{oldCapsStr.find("audio/mpeg") != std::string::npos};
    1109            3 :         bool svpEnabled{true}; // assume always true
    1110            3 :         bool retVal{false};    // Output param. Set to TRUE in rdk_gstreamer_utils function stub
    1111              :         bool result =
    1112            3 :             m_rdkGstreamerUtilsWrapper
    1113            6 :                 ->performAudioTrackCodecChannelSwitch(&m_context.playbackGroup, &sampleAttributes, &(*audioAttributes),
    1114              :                                                       &status, &ui32Delay, &audioChangeTargetPts, &currentDispPts,
    1115              :                                                       &audioChangeStage,
    1116              :                                                       &caps, // may fail for amlogic - that implementation changes
    1117              :                                                              // this parameter, it's probably used by Netflix later
    1118            3 :                                                       &audioAac, svpEnabled, GST_ELEMENT(appSrc), &retVal);
    1119              : 
    1120            3 :         if (!result || !retVal)
    1121              :         {
    1122            3 :             RIALTO_SERVER_LOG_WARN("performAudioTrackCodecChannelSwitch failed! Result: %d, retval %d", result, retVal);
    1123              :         }
    1124              :     }
    1125              :     else
    1126              :     {
    1127            1 :         RIALTO_SERVER_LOG_DEBUG("Skip switching audio source - caps are the same.");
    1128              :     }
    1129              : 
    1130            4 :     m_context.lastAudioSampleTimestamps = currentDispPts;
    1131            4 :     if (caps)
    1132            4 :         m_gstWrapper->gstCapsUnref(caps);
    1133            4 :     if (oldCaps)
    1134            4 :         m_gstWrapper->gstCapsUnref(oldCaps);
    1135              : 
    1136            4 :     return true;
    1137            5 : }
    1138              : 
    1139            0 : bool GstGenericPlayer::hasSourceType(const MediaSourceType &mediaSourceType) const
    1140              : {
    1141            0 :     return m_context.streamInfo.find(mediaSourceType) != m_context.streamInfo.end();
    1142              : }
    1143              : 
    1144           89 : void GstGenericPlayer::scheduleNeedMediaData(GstAppSrc *src)
    1145              : {
    1146           89 :     if (m_workerThread)
    1147              :     {
    1148           89 :         m_workerThread->enqueueTask(m_taskFactory->createNeedData(m_context, *this, src));
    1149              :     }
    1150              : }
    1151              : 
    1152            1 : void GstGenericPlayer::scheduleEnoughData(GstAppSrc *src)
    1153              : {
    1154            1 :     if (m_workerThread)
    1155              :     {
    1156            1 :         m_workerThread->enqueueTask(m_taskFactory->createEnoughData(m_context, src));
    1157              :     }
    1158              : }
    1159              : 
    1160            2 : void GstGenericPlayer::scheduleAudioUnderflow()
    1161              : {
    1162            2 :     if (m_workerThread)
    1163              :     {
    1164            2 :         bool underflowEnabled = m_context.isPlaying;
    1165            4 :         m_workerThread->enqueueTask(
    1166            4 :             m_taskFactory->createUnderflow(m_context, *this, underflowEnabled, MediaSourceType::AUDIO));
    1167              :     }
    1168            2 : }
    1169              : 
    1170            2 : void GstGenericPlayer::scheduleVideoUnderflow()
    1171              : {
    1172            2 :     if (m_workerThread)
    1173              :     {
    1174            2 :         bool underflowEnabled = m_context.isPlaying;
    1175            4 :         m_workerThread->enqueueTask(
    1176            4 :             m_taskFactory->createUnderflow(m_context, *this, underflowEnabled, MediaSourceType::VIDEO));
    1177              :     }
    1178            2 : }
    1179              : 
    1180            1 : void GstGenericPlayer::scheduleAllSourcesAttached()
    1181              : {
    1182            1 :     allSourcesAttached();
    1183              : }
    1184              : 
    1185           14 : void GstGenericPlayer::cancelUnderflow(firebolt::rialto::MediaSourceType mediaSource)
    1186              : {
    1187           14 :     auto elem = m_context.streamInfo.find(mediaSource);
    1188           14 :     if (elem != m_context.streamInfo.end())
    1189              :     {
    1190           14 :         StreamInfo &streamInfo = elem->second;
    1191           14 :         if (!streamInfo.underflowOccured)
    1192              :         {
    1193           11 :             return;
    1194              :         }
    1195              : 
    1196            3 :         RIALTO_SERVER_LOG_DEBUG("Cancelling %s underflow", common::convertMediaSourceType(mediaSource));
    1197            3 :         streamInfo.underflowOccured = false;
    1198              :     }
    1199              : }
    1200              : 
    1201            3 : void GstGenericPlayer::play(bool &async)
    1202              : {
    1203            3 :     if (0 == m_ongoingStateChangesNumber)
    1204              :     {
    1205              :         // Operation called on main thread, because PAUSED->PLAYING change is synchronous and needs to be done fast.
    1206              :         //
    1207              :         // m_context.pipeline can be used, because it's modified only in GstGenericPlayer
    1208              :         // constructor and destructor. GstGenericPlayer is created/destructed on main thread, so we won't have a crash here.
    1209            2 :         ++m_ongoingStateChangesNumber;
    1210            2 :         async = (changePipelineState(GST_STATE_PLAYING) == GST_STATE_CHANGE_ASYNC);
    1211            2 :         RIALTO_SERVER_LOG_MIL("State change to PLAYING requested");
    1212              :     }
    1213              :     else
    1214              :     {
    1215            1 :         ++m_ongoingStateChangesNumber;
    1216            1 :         async = true;
    1217            1 :         if (m_workerThread)
    1218              :         {
    1219            1 :             m_workerThread->enqueueTask(m_taskFactory->createPlay(*this));
    1220              :         }
    1221              :     }
    1222            3 : }
    1223              : 
    1224            2 : void GstGenericPlayer::pause()
    1225              : {
    1226            2 :     ++m_ongoingStateChangesNumber;
    1227            2 :     if (m_workerThread)
    1228              :     {
    1229            2 :         m_workerThread->enqueueTask(m_taskFactory->createPause(m_context, *this));
    1230              :     }
    1231              : }
    1232              : 
    1233            1 : void GstGenericPlayer::stop()
    1234              : {
    1235            1 :     ++m_ongoingStateChangesNumber;
    1236            1 :     if (m_workerThread)
    1237              :     {
    1238            1 :         m_workerThread->enqueueTask(m_taskFactory->createStop(m_context, *this));
    1239              :     }
    1240              : }
    1241              : 
    1242            6 : GstStateChangeReturn GstGenericPlayer::changePipelineState(GstState newState)
    1243              : {
    1244            6 :     if (!m_context.pipeline)
    1245              :     {
    1246            1 :         RIALTO_SERVER_LOG_ERROR("Change state failed - pipeline is nullptr");
    1247            1 :         if (m_gstPlayerClient)
    1248            1 :             m_gstPlayerClient->notifyPlaybackState(PlaybackState::FAILURE);
    1249            1 :         --m_ongoingStateChangesNumber;
    1250            1 :         return GST_STATE_CHANGE_FAILURE;
    1251              :     }
    1252            5 :     m_context.flushOnPrerollController->setTargetState(newState);
    1253            5 :     const GstStateChangeReturn result{m_gstWrapper->gstElementSetState(m_context.pipeline, newState)};
    1254            5 :     if (result == GST_STATE_CHANGE_FAILURE)
    1255              :     {
    1256            1 :         RIALTO_SERVER_LOG_ERROR("Change state failed - Gstreamer returned an error");
    1257            1 :         if (m_gstPlayerClient)
    1258            1 :             m_gstPlayerClient->notifyPlaybackState(PlaybackState::FAILURE);
    1259              :     }
    1260            5 :     --m_ongoingStateChangesNumber;
    1261            5 :     return result;
    1262              : }
    1263              : 
    1264           15 : int64_t GstGenericPlayer::getPosition(GstElement *element)
    1265              : {
    1266           15 :     if (!element)
    1267              :     {
    1268            1 :         RIALTO_SERVER_LOG_WARN("Element is null");
    1269            1 :         return -1;
    1270              :     }
    1271              : 
    1272           14 :     m_gstWrapper->gstStateLock(element);
    1273              : 
    1274           28 :     if (m_gstWrapper->gstElementGetState(element) < GST_STATE_PAUSED ||
    1275           14 :         (m_gstWrapper->gstElementGetStateReturn(element) == GST_STATE_CHANGE_ASYNC &&
    1276            1 :          m_gstWrapper->gstElementGetStateNext(element) == GST_STATE_PAUSED))
    1277              :     {
    1278            1 :         RIALTO_SERVER_LOG_WARN("Element is prerolling or in invalid state - state: %s, return: %s, next: %s",
    1279              :                                m_gstWrapper->gstElementStateGetName(m_gstWrapper->gstElementGetState(element)),
    1280              :                                m_gstWrapper->gstElementStateChangeReturnGetName(
    1281              :                                    m_gstWrapper->gstElementGetStateReturn(element)),
    1282              :                                m_gstWrapper->gstElementStateGetName(m_gstWrapper->gstElementGetStateNext(element)));
    1283              : 
    1284            1 :         m_gstWrapper->gstStateUnlock(element);
    1285            1 :         return -1;
    1286              :     }
    1287           13 :     m_gstWrapper->gstStateUnlock(element);
    1288              : 
    1289           13 :     gint64 position = -1;
    1290           13 :     if (!m_gstWrapper->gstElementQueryPosition(m_context.pipeline, GST_FORMAT_TIME, &position))
    1291              :     {
    1292            1 :         RIALTO_SERVER_LOG_WARN("Failed to query position");
    1293            1 :         return -1;
    1294              :     }
    1295              : 
    1296           12 :     return position;
    1297              : }
    1298              : 
    1299            1 : void GstGenericPlayer::setVideoGeometry(int x, int y, int width, int height)
    1300              : {
    1301            1 :     if (m_workerThread)
    1302              :     {
    1303            2 :         m_workerThread->enqueueTask(
    1304            2 :             m_taskFactory->createSetVideoGeometry(m_context, *this, Rectangle{x, y, width, height}));
    1305              :     }
    1306            1 : }
    1307              : 
    1308            1 : void GstGenericPlayer::setEos(const firebolt::rialto::MediaSourceType &type)
    1309              : {
    1310            1 :     if (m_workerThread)
    1311              :     {
    1312            1 :         m_workerThread->enqueueTask(m_taskFactory->createEos(m_context, *this, type));
    1313              :     }
    1314              : }
    1315              : 
    1316            4 : bool GstGenericPlayer::setVideoSinkRectangle()
    1317              : {
    1318            4 :     bool result = false;
    1319            4 :     GstElement *videoSink{getSink(MediaSourceType::VIDEO)};
    1320            4 :     if (videoSink)
    1321              :     {
    1322            3 :         if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(videoSink), "rectangle"))
    1323              :         {
    1324              :             std::string rect =
    1325            4 :                 std::to_string(m_context.pendingGeometry.x) + ',' + std::to_string(m_context.pendingGeometry.y) + ',' +
    1326            6 :                 std::to_string(m_context.pendingGeometry.width) + ',' + std::to_string(m_context.pendingGeometry.height);
    1327            2 :             m_glibWrapper->gObjectSet(videoSink, "rectangle", rect.c_str(), nullptr);
    1328            2 :             m_context.pendingGeometry.clear();
    1329            2 :             result = true;
    1330              :         }
    1331              :         else
    1332              :         {
    1333            1 :             RIALTO_SERVER_LOG_ERROR("Failed to set the video rectangle");
    1334              :         }
    1335            3 :         m_gstWrapper->gstObjectUnref(videoSink);
    1336              :     }
    1337              :     else
    1338              :     {
    1339            1 :         RIALTO_SERVER_LOG_ERROR("Failed to set video rectangle, sink is NULL");
    1340              :     }
    1341              : 
    1342            4 :     return result;
    1343              : }
    1344              : 
    1345            3 : bool GstGenericPlayer::setImmediateOutput()
    1346              : {
    1347            3 :     bool result{false};
    1348            3 :     if (m_context.pendingImmediateOutputForVideo.has_value())
    1349              :     {
    1350            3 :         GstElement *sink{getSink(MediaSourceType::VIDEO)};
    1351            3 :         if (sink)
    1352              :         {
    1353            2 :             bool immediateOutput{m_context.pendingImmediateOutputForVideo.value()};
    1354            2 :             RIALTO_SERVER_LOG_DEBUG("Set immediate-output to %s", immediateOutput ? "TRUE" : "FALSE");
    1355              : 
    1356            2 :             if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(sink), "immediate-output"))
    1357              :             {
    1358            1 :                 gboolean immediateOutputGboolean{immediateOutput ? TRUE : FALSE};
    1359            1 :                 m_glibWrapper->gObjectSet(sink, "immediate-output", immediateOutputGboolean, nullptr);
    1360            1 :                 result = true;
    1361              :             }
    1362              :             else
    1363              :             {
    1364            1 :                 RIALTO_SERVER_LOG_ERROR("Failed to set immediate-output property on sink '%s'", GST_ELEMENT_NAME(sink));
    1365              :             }
    1366            2 :             m_context.pendingImmediateOutputForVideo.reset();
    1367            2 :             m_gstWrapper->gstObjectUnref(sink);
    1368              :         }
    1369              :         else
    1370              :         {
    1371            1 :             RIALTO_SERVER_LOG_DEBUG("Pending an immediate-output, sink is NULL");
    1372              :         }
    1373              :     }
    1374            3 :     return result;
    1375              : }
    1376              : 
    1377            2 : bool GstGenericPlayer::setReportDecodeErrors()
    1378              : {
    1379            2 :     bool result{false};
    1380            2 :     bool reportDecodeErrors{false};
    1381              : 
    1382              :     {
    1383            2 :         std::unique_lock lock{m_context.propertyMutex};
    1384            2 :         if (!m_context.pendingReportDecodeErrorsForVideo.has_value())
    1385              :         {
    1386            0 :             return false;
    1387              :         }
    1388            2 :         reportDecodeErrors = m_context.pendingReportDecodeErrorsForVideo.value();
    1389              :     }
    1390              : 
    1391            2 :     GstElement *decoder = getDecoder(MediaSourceType::VIDEO);
    1392            2 :     if (decoder)
    1393              :     {
    1394            2 :         RIALTO_SERVER_LOG_DEBUG("Set report decode errors to %s", reportDecodeErrors ? "TRUE" : "FALSE");
    1395              : 
    1396            2 :         if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(decoder), "report-decode-errors"))
    1397              :         {
    1398            1 :             gboolean reportDecodeErrorsGboolean{reportDecodeErrors ? TRUE : FALSE};
    1399            1 :             m_glibWrapper->gObjectSet(decoder, "report-decode-errors", reportDecodeErrorsGboolean, nullptr);
    1400            1 :             result = true;
    1401              :         }
    1402              :         else
    1403              :         {
    1404            1 :             RIALTO_SERVER_LOG_ERROR("Failed to set report-decode-errors property on decoder '%s'",
    1405              :                                     GST_ELEMENT_NAME(decoder));
    1406              :         }
    1407              : 
    1408            2 :         m_gstWrapper->gstObjectUnref(decoder);
    1409              : 
    1410              :         {
    1411            2 :             std::unique_lock lock{m_context.propertyMutex};
    1412            2 :             m_context.pendingReportDecodeErrorsForVideo.reset();
    1413              :         }
    1414              :     }
    1415              :     else
    1416              :     {
    1417            0 :         RIALTO_SERVER_LOG_DEBUG("Pending report-decode-errors, decoder is NULL");
    1418              :     }
    1419            2 :     return result;
    1420              : }
    1421              : 
    1422            4 : bool GstGenericPlayer::setShowVideoWindow()
    1423              : {
    1424            4 :     if (!m_context.pendingShowVideoWindow.has_value())
    1425              :     {
    1426            1 :         RIALTO_SERVER_LOG_WARN("No show video window value to be set. Aborting...");
    1427            1 :         return false;
    1428              :     }
    1429              : 
    1430            3 :     GstElement *videoSink{getSink(MediaSourceType::VIDEO)};
    1431            3 :     if (!videoSink)
    1432              :     {
    1433            1 :         RIALTO_SERVER_LOG_DEBUG("Setting show video window queued. Video sink is NULL");
    1434            1 :         return false;
    1435              :     }
    1436            2 :     bool result{false};
    1437            2 :     if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(videoSink), "show-video-window"))
    1438              :     {
    1439            1 :         m_glibWrapper->gObjectSet(videoSink, "show-video-window", m_context.pendingShowVideoWindow.value(), nullptr);
    1440            1 :         result = true;
    1441              :     }
    1442              :     else
    1443              :     {
    1444            1 :         RIALTO_SERVER_LOG_ERROR("Setting show video window failed. Property does not exist");
    1445              :     }
    1446            2 :     m_context.pendingShowVideoWindow.reset();
    1447            2 :     m_gstWrapper->gstObjectUnref(GST_OBJECT(videoSink));
    1448            2 :     return result;
    1449              : }
    1450              : 
    1451            4 : bool GstGenericPlayer::setLowLatency()
    1452              : {
    1453            4 :     bool result{false};
    1454            4 :     if (m_context.pendingLowLatency.has_value())
    1455              :     {
    1456            4 :         GstElement *sink{getSink(MediaSourceType::AUDIO)};
    1457            4 :         if (sink)
    1458              :         {
    1459            3 :             bool lowLatency{m_context.pendingLowLatency.value()};
    1460            3 :             RIALTO_SERVER_LOG_DEBUG("Set low-latency to %s", lowLatency ? "TRUE" : "FALSE");
    1461              : 
    1462            3 :             if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(sink), "low-latency"))
    1463              :             {
    1464            2 :                 gboolean lowLatencyGboolean{lowLatency ? TRUE : FALSE};
    1465            2 :                 m_glibWrapper->gObjectSet(sink, "low-latency", lowLatencyGboolean, nullptr);
    1466            2 :                 result = true;
    1467              :             }
    1468              :             else
    1469              :             {
    1470            1 :                 RIALTO_SERVER_LOG_ERROR("Failed to set low-latency property on sink '%s'", GST_ELEMENT_NAME(sink));
    1471              :             }
    1472            3 :             m_context.pendingLowLatency.reset();
    1473            3 :             m_gstWrapper->gstObjectUnref(sink);
    1474              :         }
    1475              :         else
    1476              :         {
    1477            1 :             RIALTO_SERVER_LOG_DEBUG("Pending low-latency, sink is NULL");
    1478              :         }
    1479              :     }
    1480            4 :     return result;
    1481              : }
    1482              : 
    1483            3 : bool GstGenericPlayer::setSync()
    1484              : {
    1485            3 :     bool result{false};
    1486            3 :     if (m_context.pendingSync.has_value())
    1487              :     {
    1488            3 :         GstElement *sink{getSink(MediaSourceType::AUDIO)};
    1489            3 :         if (sink)
    1490              :         {
    1491            2 :             bool sync{m_context.pendingSync.value()};
    1492            2 :             RIALTO_SERVER_LOG_DEBUG("Set sync to %s", sync ? "TRUE" : "FALSE");
    1493              : 
    1494            2 :             if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(sink), "sync"))
    1495              :             {
    1496            1 :                 gboolean syncGboolean{sync ? TRUE : FALSE};
    1497            1 :                 m_glibWrapper->gObjectSet(sink, "sync", syncGboolean, nullptr);
    1498            1 :                 result = true;
    1499              :             }
    1500              :             else
    1501              :             {
    1502            1 :                 RIALTO_SERVER_LOG_ERROR("Failed to set sync property on sink '%s'", GST_ELEMENT_NAME(sink));
    1503              :             }
    1504            2 :             m_context.pendingSync.reset();
    1505            2 :             m_gstWrapper->gstObjectUnref(sink);
    1506              :         }
    1507              :         else
    1508              :         {
    1509            1 :             RIALTO_SERVER_LOG_DEBUG("Pending sync, sink is NULL");
    1510              :         }
    1511              :     }
    1512            3 :     return result;
    1513              : }
    1514              : 
    1515            3 : bool GstGenericPlayer::setSyncOff()
    1516              : {
    1517            3 :     bool result{false};
    1518            3 :     if (m_context.pendingSyncOff.has_value())
    1519              :     {
    1520            3 :         GstElement *decoder = getDecoder(MediaSourceType::AUDIO);
    1521            3 :         if (decoder)
    1522              :         {
    1523            2 :             bool syncOff{m_context.pendingSyncOff.value()};
    1524            2 :             RIALTO_SERVER_LOG_DEBUG("Set sync-off to %s", syncOff ? "TRUE" : "FALSE");
    1525              : 
    1526            2 :             if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(decoder), "sync-off"))
    1527              :             {
    1528            1 :                 gboolean syncOffGboolean{decoder ? TRUE : FALSE};
    1529            1 :                 m_glibWrapper->gObjectSet(decoder, "sync-off", syncOffGboolean, nullptr);
    1530            1 :                 result = true;
    1531              :             }
    1532              :             else
    1533              :             {
    1534            1 :                 RIALTO_SERVER_LOG_ERROR("Failed to set sync-off property on decoder '%s'", GST_ELEMENT_NAME(decoder));
    1535              :             }
    1536            2 :             m_context.pendingSyncOff.reset();
    1537            2 :             m_gstWrapper->gstObjectUnref(decoder);
    1538              :         }
    1539              :         else
    1540              :         {
    1541            1 :             RIALTO_SERVER_LOG_DEBUG("Pending sync-off, decoder is NULL");
    1542              :         }
    1543              :     }
    1544            3 :     return result;
    1545              : }
    1546              : 
    1547            6 : bool GstGenericPlayer::setStreamSyncMode(const MediaSourceType &type)
    1548              : {
    1549            6 :     bool result{false};
    1550            6 :     int32_t streamSyncMode{0};
    1551              :     {
    1552            6 :         std::unique_lock lock{m_context.propertyMutex};
    1553            6 :         if (m_context.pendingStreamSyncMode.find(type) == m_context.pendingStreamSyncMode.end())
    1554              :         {
    1555            0 :             return false;
    1556              :         }
    1557            6 :         streamSyncMode = m_context.pendingStreamSyncMode[type];
    1558              :     }
    1559            6 :     if (MediaSourceType::AUDIO == type)
    1560              :     {
    1561            3 :         GstElement *decoder = getDecoder(MediaSourceType::AUDIO);
    1562            3 :         if (!decoder)
    1563              :         {
    1564            1 :             RIALTO_SERVER_LOG_DEBUG("Pending stream-sync-mode, decoder is NULL");
    1565            1 :             return false;
    1566              :         }
    1567              : 
    1568            2 :         RIALTO_SERVER_LOG_DEBUG("Set stream-sync-mode to %d", streamSyncMode);
    1569              : 
    1570            2 :         if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(decoder), "stream-sync-mode"))
    1571              :         {
    1572            1 :             gint streamSyncModeGint{static_cast<gint>(streamSyncMode)};
    1573            1 :             m_glibWrapper->gObjectSet(decoder, "stream-sync-mode", streamSyncModeGint, nullptr);
    1574            1 :             result = true;
    1575              :         }
    1576              :         else
    1577              :         {
    1578            1 :             RIALTO_SERVER_LOG_ERROR("Failed to set stream-sync-mode property on decoder '%s'", GST_ELEMENT_NAME(decoder));
    1579              :         }
    1580            2 :         m_gstWrapper->gstObjectUnref(decoder);
    1581            2 :         std::unique_lock lock{m_context.propertyMutex};
    1582            2 :         m_context.pendingStreamSyncMode.erase(type);
    1583              :     }
    1584            3 :     else if (MediaSourceType::VIDEO == type)
    1585              :     {
    1586            3 :         GstElement *parser = getParser(MediaSourceType::VIDEO);
    1587            3 :         if (!parser)
    1588              :         {
    1589            1 :             RIALTO_SERVER_LOG_DEBUG("Pending syncmode-streaming, parser is NULL");
    1590            1 :             return false;
    1591              :         }
    1592              : 
    1593            2 :         gboolean streamSyncModeBoolean{static_cast<gboolean>(streamSyncMode)};
    1594            2 :         RIALTO_SERVER_LOG_DEBUG("Set syncmode-streaming to %d", streamSyncMode);
    1595              : 
    1596            2 :         if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(parser), "syncmode-streaming"))
    1597              :         {
    1598            1 :             m_glibWrapper->gObjectSet(parser, "syncmode-streaming", streamSyncModeBoolean, nullptr);
    1599            1 :             result = true;
    1600              :         }
    1601              :         else
    1602              :         {
    1603            1 :             RIALTO_SERVER_LOG_ERROR("Failed to set syncmode-streaming property on parser '%s'", GST_ELEMENT_NAME(parser));
    1604              :         }
    1605            2 :         m_gstWrapper->gstObjectUnref(parser);
    1606            2 :         std::unique_lock lock{m_context.propertyMutex};
    1607            2 :         m_context.pendingStreamSyncMode.erase(type);
    1608              :     }
    1609            4 :     return result;
    1610              : }
    1611              : 
    1612            3 : bool GstGenericPlayer::setRenderFrame()
    1613              : {
    1614            3 :     bool result{false};
    1615            3 :     if (m_context.pendingRenderFrame)
    1616              :     {
    1617            5 :         static const std::string kStepOnPrerollPropertyName = "frame-step-on-preroll";
    1618            3 :         GstElement *sink{getSink(MediaSourceType::VIDEO)};
    1619            3 :         if (sink)
    1620              :         {
    1621            2 :             if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(sink), kStepOnPrerollPropertyName.c_str()))
    1622              :             {
    1623            1 :                 RIALTO_SERVER_LOG_INFO("Rendering preroll");
    1624              : 
    1625            1 :                 m_glibWrapper->gObjectSet(sink, kStepOnPrerollPropertyName.c_str(), 1, nullptr);
    1626            1 :                 m_gstWrapper->gstElementSendEvent(sink, m_gstWrapper->gstEventNewStep(GST_FORMAT_BUFFERS, 1, 1.0, true,
    1627              :                                                                                       false));
    1628            1 :                 m_glibWrapper->gObjectSet(sink, kStepOnPrerollPropertyName.c_str(), 0, nullptr);
    1629            1 :                 result = true;
    1630              :             }
    1631              :             else
    1632              :             {
    1633            1 :                 RIALTO_SERVER_LOG_ERROR("Video sink doesn't have property `%s`", kStepOnPrerollPropertyName.c_str());
    1634              :             }
    1635            2 :             m_gstWrapper->gstObjectUnref(sink);
    1636            2 :             m_context.pendingRenderFrame = false;
    1637              :         }
    1638              :         else
    1639              :         {
    1640            1 :             RIALTO_SERVER_LOG_DEBUG("Pending render frame, sink is NULL");
    1641              :         }
    1642              :     }
    1643            3 :     return result;
    1644              : }
    1645              : 
    1646            3 : bool GstGenericPlayer::setBufferingLimit()
    1647              : {
    1648            3 :     bool result{false};
    1649            3 :     guint bufferingLimit{0};
    1650              :     {
    1651            3 :         std::unique_lock lock{m_context.propertyMutex};
    1652            3 :         if (!m_context.pendingBufferingLimit.has_value())
    1653              :         {
    1654            0 :             return false;
    1655              :         }
    1656            3 :         bufferingLimit = static_cast<guint>(m_context.pendingBufferingLimit.value());
    1657              :     }
    1658              : 
    1659            3 :     GstElement *decoder{getDecoder(MediaSourceType::AUDIO)};
    1660            3 :     if (decoder)
    1661              :     {
    1662            2 :         RIALTO_SERVER_LOG_DEBUG("Set limit-buffering-ms to %u", bufferingLimit);
    1663              : 
    1664            2 :         if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(decoder), "limit-buffering-ms"))
    1665              :         {
    1666            1 :             m_glibWrapper->gObjectSet(decoder, "limit-buffering-ms", bufferingLimit, nullptr);
    1667            1 :             result = true;
    1668              :         }
    1669              :         else
    1670              :         {
    1671            1 :             RIALTO_SERVER_LOG_ERROR("Failed to set limit-buffering-ms property on decoder '%s'",
    1672              :                                     GST_ELEMENT_NAME(decoder));
    1673              :         }
    1674            2 :         m_gstWrapper->gstObjectUnref(decoder);
    1675            2 :         std::unique_lock lock{m_context.propertyMutex};
    1676            2 :         m_context.pendingBufferingLimit.reset();
    1677              :     }
    1678              :     else
    1679              :     {
    1680            1 :         RIALTO_SERVER_LOG_DEBUG("Pending limit-buffering-ms, decoder is NULL");
    1681              :     }
    1682            3 :     return result;
    1683              : }
    1684              : 
    1685            2 : bool GstGenericPlayer::setUseBuffering()
    1686              : {
    1687            2 :     std::unique_lock lock{m_context.propertyMutex};
    1688            2 :     if (m_context.pendingUseBuffering.has_value())
    1689              :     {
    1690            2 :         if (m_context.playbackGroup.m_curAudioDecodeBin)
    1691              :         {
    1692            1 :             gboolean useBufferingGboolean{m_context.pendingUseBuffering.value() ? TRUE : FALSE};
    1693            1 :             RIALTO_SERVER_LOG_DEBUG("Set use-buffering to %d", useBufferingGboolean);
    1694            1 :             m_glibWrapper->gObjectSet(m_context.playbackGroup.m_curAudioDecodeBin, "use-buffering",
    1695              :                                       useBufferingGboolean, nullptr);
    1696            1 :             m_context.pendingUseBuffering.reset();
    1697            1 :             return true;
    1698              :         }
    1699              :         else
    1700              :         {
    1701            1 :             RIALTO_SERVER_LOG_DEBUG("Pending use-buffering, decodebin is NULL");
    1702              :         }
    1703              :     }
    1704            1 :     return false;
    1705            2 : }
    1706              : 
    1707            8 : bool GstGenericPlayer::setWesterossinkSecondaryVideo()
    1708              : {
    1709            8 :     bool result = false;
    1710            8 :     GstElementFactory *factory = m_gstWrapper->gstElementFactoryFind("westerossink");
    1711            8 :     if (factory)
    1712              :     {
    1713            7 :         GstElement *videoSink = m_gstWrapper->gstElementFactoryCreate(factory, nullptr);
    1714            7 :         if (videoSink)
    1715              :         {
    1716            5 :             if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(videoSink), "res-usage"))
    1717              :             {
    1718            4 :                 m_glibWrapper->gObjectSet(videoSink, "res-usage", 0x0u, nullptr);
    1719            4 :                 m_glibWrapper->gObjectSet(m_context.pipeline, "video-sink", videoSink, nullptr);
    1720            4 :                 result = true;
    1721              :             }
    1722              :             else
    1723              :             {
    1724            1 :                 RIALTO_SERVER_LOG_ERROR("Failed to set the westerossink res-usage");
    1725            1 :                 m_gstWrapper->gstObjectUnref(GST_OBJECT(videoSink));
    1726              :             }
    1727              :         }
    1728              :         else
    1729              :         {
    1730            2 :             RIALTO_SERVER_LOG_ERROR("Failed to create the westerossink");
    1731              :         }
    1732              : 
    1733            7 :         m_gstWrapper->gstObjectUnref(GST_OBJECT(factory));
    1734              :     }
    1735              :     else
    1736              :     {
    1737              :         // No westeros sink
    1738            1 :         result = true;
    1739              :     }
    1740              : 
    1741            8 :     return result;
    1742              : }
    1743              : 
    1744            8 : bool GstGenericPlayer::setErmContext()
    1745              : {
    1746            8 :     bool result = false;
    1747            8 :     GstContext *context = m_gstWrapper->gstContextNew("erm", false);
    1748            8 :     if (context)
    1749              :     {
    1750            6 :         GstStructure *contextStructure = m_gstWrapper->gstContextWritableStructure(context);
    1751            6 :         if (contextStructure)
    1752              :         {
    1753            5 :             m_gstWrapper->gstStructureSet(contextStructure, "res-usage", G_TYPE_UINT, 0x0u, nullptr);
    1754            5 :             m_gstWrapper->gstElementSetContext(GST_ELEMENT(m_context.pipeline), context);
    1755            5 :             result = true;
    1756              :         }
    1757              :         else
    1758              :         {
    1759            1 :             RIALTO_SERVER_LOG_ERROR("Failed to create the erm structure");
    1760              :         }
    1761            6 :         m_gstWrapper->gstContextUnref(context);
    1762              :     }
    1763              :     else
    1764              :     {
    1765            2 :         RIALTO_SERVER_LOG_ERROR("Failed to create the erm context");
    1766              :     }
    1767              : 
    1768            8 :     return result;
    1769              : }
    1770              : 
    1771            6 : void GstGenericPlayer::startPositionReportingAndCheckAudioUnderflowTimer()
    1772              : {
    1773            6 :     if (m_positionReportingAndCheckAudioUnderflowTimer && m_positionReportingAndCheckAudioUnderflowTimer->isActive())
    1774              :     {
    1775            1 :         return;
    1776              :     }
    1777              : 
    1778           15 :     m_positionReportingAndCheckAudioUnderflowTimer = m_timerFactory->createTimer(
    1779              :         kPositionReportTimerMs,
    1780           10 :         [this]()
    1781              :         {
    1782            1 :             if (m_workerThread)
    1783              :             {
    1784            1 :                 m_workerThread->enqueueTask(m_taskFactory->createReportPosition(m_context, *this));
    1785            1 :                 m_workerThread->enqueueTask(m_taskFactory->createCheckAudioUnderflow(m_context, *this));
    1786              :             }
    1787            1 :         },
    1788            5 :         firebolt::rialto::common::TimerType::PERIODIC);
    1789              : }
    1790              : 
    1791            4 : void GstGenericPlayer::stopPositionReportingAndCheckAudioUnderflowTimer()
    1792              : {
    1793            4 :     if (m_positionReportingAndCheckAudioUnderflowTimer && m_positionReportingAndCheckAudioUnderflowTimer->isActive())
    1794              :     {
    1795            1 :         m_positionReportingAndCheckAudioUnderflowTimer->cancel();
    1796            1 :         m_positionReportingAndCheckAudioUnderflowTimer.reset();
    1797              :     }
    1798            4 : }
    1799              : 
    1800            6 : void GstGenericPlayer::startNotifyPlaybackInfoTimer()
    1801              : {
    1802              :     static constexpr std::chrono::milliseconds kPlaybackInfoTimerMs{32};
    1803            6 :     if (m_playbackInfoTimer && m_playbackInfoTimer->isActive())
    1804              :     {
    1805            1 :         return;
    1806              :     }
    1807              : 
    1808            5 :     notifyPlaybackInfo();
    1809              : 
    1810              :     m_playbackInfoTimer =
    1811            5 :         m_timerFactory
    1812            6 :             ->createTimer(kPlaybackInfoTimerMs, [this]() { notifyPlaybackInfo(); }, firebolt::rialto::common::TimerType::PERIODIC);
    1813              : }
    1814              : 
    1815            3 : void GstGenericPlayer::stopNotifyPlaybackInfoTimer()
    1816              : {
    1817            3 :     if (m_playbackInfoTimer && m_playbackInfoTimer->isActive())
    1818              :     {
    1819            1 :         m_playbackInfoTimer->cancel();
    1820            1 :         m_playbackInfoTimer.reset();
    1821              :     }
    1822            3 : }
    1823              : 
    1824            0 : void GstGenericPlayer::startSubtitleClockResyncTimer()
    1825              : {
    1826            0 :     if (m_subtitleClockResyncTimer && m_subtitleClockResyncTimer->isActive())
    1827              :     {
    1828            0 :         return;
    1829              :     }
    1830              : 
    1831            0 :     m_subtitleClockResyncTimer = m_timerFactory->createTimer(
    1832              :         kSubtitleClockResyncInterval,
    1833            0 :         [this]()
    1834              :         {
    1835            0 :             if (m_workerThread)
    1836              :             {
    1837            0 :                 m_workerThread->enqueueTask(m_taskFactory->createSynchroniseSubtitleClock(m_context, *this));
    1838              :             }
    1839            0 :         },
    1840            0 :         firebolt::rialto::common::TimerType::PERIODIC);
    1841              : }
    1842              : 
    1843            0 : void GstGenericPlayer::stopSubtitleClockResyncTimer()
    1844              : {
    1845            0 :     if (m_subtitleClockResyncTimer && m_subtitleClockResyncTimer->isActive())
    1846              :     {
    1847            0 :         m_subtitleClockResyncTimer->cancel();
    1848            0 :         m_subtitleClockResyncTimer.reset();
    1849              :     }
    1850              : }
    1851              : 
    1852            2 : void GstGenericPlayer::stopWorkerThread()
    1853              : {
    1854            2 :     if (m_workerThread)
    1855              :     {
    1856            2 :         m_workerThread->stop();
    1857              :     }
    1858              : }
    1859              : 
    1860            0 : void GstGenericPlayer::setPendingPlaybackRate()
    1861              : {
    1862            0 :     RIALTO_SERVER_LOG_INFO("Setting pending playback rate");
    1863            0 :     setPlaybackRate(m_context.pendingPlaybackRate);
    1864              : }
    1865              : 
    1866            1 : void GstGenericPlayer::renderFrame()
    1867              : {
    1868            1 :     if (m_workerThread)
    1869              :     {
    1870            1 :         m_workerThread->enqueueTask(m_taskFactory->createRenderFrame(m_context, *this));
    1871              :     }
    1872              : }
    1873              : 
    1874           18 : void GstGenericPlayer::setVolume(double targetVolume, uint32_t volumeDuration, firebolt::rialto::EaseType easeType)
    1875              : {
    1876           18 :     if (m_workerThread)
    1877              :     {
    1878           36 :         m_workerThread->enqueueTask(
    1879           36 :             m_taskFactory->createSetVolume(m_context, *this, targetVolume, volumeDuration, easeType));
    1880              :     }
    1881           18 : }
    1882              : 
    1883            9 : bool GstGenericPlayer::getVolume(double &currentVolume)
    1884              : {
    1885              :     // We are on main thread here, but m_context.pipeline can be used, because it's modified only in GstGenericPlayer
    1886              :     // constructor and destructor. GstGenericPlayer is created/destructed on main thread, so we won't have a crash here.
    1887            9 :     if (!m_context.pipeline)
    1888              :     {
    1889            0 :         return false;
    1890              :     }
    1891              : 
    1892              :     // NOTE: No gstreamer documentation for "fade-volume" could be found at the time this code was written.
    1893              :     // Therefore the author performed several tests on a supported platform (Flex2) to determine the behaviour of this property.
    1894              :     // The code has been written to be backwardly compatible on platforms that don't have this property.
    1895              :     // The observed behaviour was:
    1896              :     //    - if the returned fade volume is negative then audio-fade is not active. In this case the usual technique
    1897              :     //      to find volume in the pipeline works and is used.
    1898              :     //    - if the returned fade volume is positive then audio-fade is active. In this case the returned fade volume
    1899              :     //      directly returns the current volume level 0=min to 100=max (and the pipeline's current volume level is
    1900              :     //      meaningless and doesn't contribute in this case).
    1901            9 :     GstElement *sink{getSink(MediaSourceType::AUDIO)};
    1902           11 :     if (m_context.audioFadeEnabled && sink &&
    1903            2 :         m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(sink), "fade-volume"))
    1904              :     {
    1905            2 :         gint fadeVolume{-100};
    1906            2 :         m_glibWrapper->gObjectGet(sink, "fade-volume", &fadeVolume, NULL);
    1907            2 :         if (fadeVolume < 0)
    1908              :         {
    1909            1 :             currentVolume = m_gstWrapper->gstStreamVolumeGetVolume(GST_STREAM_VOLUME(m_context.pipeline),
    1910              :                                                                    GST_STREAM_VOLUME_FORMAT_LINEAR);
    1911            1 :             RIALTO_SERVER_LOG_INFO("Fade volume is negative, using volume from pipeline: %f", currentVolume);
    1912              :         }
    1913              :         else
    1914              :         {
    1915            1 :             currentVolume = static_cast<double>(fadeVolume) / 100.0;
    1916            1 :             RIALTO_SERVER_LOG_INFO("Fade volume is supported: %f", currentVolume);
    1917              :         }
    1918              :     }
    1919              :     else
    1920              :     {
    1921            7 :         currentVolume = m_gstWrapper->gstStreamVolumeGetVolume(GST_STREAM_VOLUME(m_context.pipeline),
    1922              :                                                                GST_STREAM_VOLUME_FORMAT_LINEAR);
    1923            7 :         RIALTO_SERVER_LOG_INFO("Fade volume is not supported, using volume from pipeline: %f", currentVolume);
    1924              :     }
    1925              : 
    1926            9 :     if (sink)
    1927            2 :         m_gstWrapper->gstObjectUnref(sink);
    1928              : 
    1929            9 :     return true;
    1930              : }
    1931              : 
    1932            1 : void GstGenericPlayer::setMute(const MediaSourceType &mediaSourceType, bool mute)
    1933              : {
    1934            1 :     if (m_workerThread)
    1935              :     {
    1936            1 :         m_workerThread->enqueueTask(m_taskFactory->createSetMute(m_context, *this, mediaSourceType, mute));
    1937              :     }
    1938              : }
    1939              : 
    1940            5 : bool GstGenericPlayer::getMute(const MediaSourceType &mediaSourceType, bool &mute)
    1941              : {
    1942              :     // We are on main thread here, but m_context.pipeline can be used, because it's modified only in GstGenericPlayer
    1943              :     // constructor and destructor. GstGenericPlayer is created/destructed on main thread, so we won't have a crash here.
    1944            5 :     if (mediaSourceType == MediaSourceType::SUBTITLE)
    1945              :     {
    1946            2 :         if (!m_context.subtitleSink)
    1947              :         {
    1948            1 :             RIALTO_SERVER_LOG_ERROR("There is no subtitle sink");
    1949            1 :             return false;
    1950              :         }
    1951            1 :         gboolean muteValue{FALSE};
    1952            1 :         m_glibWrapper->gObjectGet(m_context.subtitleSink, "mute", &muteValue, nullptr);
    1953            1 :         mute = muteValue;
    1954              :     }
    1955            3 :     else if (mediaSourceType == MediaSourceType::AUDIO)
    1956              :     {
    1957            2 :         if (!m_context.pipeline)
    1958              :         {
    1959            1 :             return false;
    1960              :         }
    1961            1 :         mute = m_gstWrapper->gstStreamVolumeGetMute(GST_STREAM_VOLUME(m_context.pipeline));
    1962              :     }
    1963              :     else
    1964              :     {
    1965            1 :         RIALTO_SERVER_LOG_ERROR("Getting mute for type %s unsupported", common::convertMediaSourceType(mediaSourceType));
    1966            1 :         return false;
    1967              :     }
    1968              : 
    1969            2 :     return true;
    1970              : }
    1971              : 
    1972            2 : bool GstGenericPlayer::isAsync(const MediaSourceType &mediaSourceType) const
    1973              : {
    1974            2 :     GstElement *sink = getSink(mediaSourceType);
    1975            2 :     if (!sink)
    1976              :     {
    1977            0 :         RIALTO_SERVER_LOG_WARN("Sink not found for %s", common::convertMediaSourceType(mediaSourceType));
    1978            0 :         return true; // Our sinks are async by default
    1979              :     }
    1980            2 :     gboolean returnValue{TRUE};
    1981            2 :     m_glibWrapper->gObjectGet(sink, "async", &returnValue, nullptr);
    1982            2 :     m_gstWrapper->gstObjectUnref(sink);
    1983            2 :     return returnValue == TRUE;
    1984              : }
    1985              : 
    1986            1 : void GstGenericPlayer::setTextTrackIdentifier(const std::string &textTrackIdentifier)
    1987              : {
    1988            1 :     if (m_workerThread)
    1989              :     {
    1990            1 :         m_workerThread->enqueueTask(m_taskFactory->createSetTextTrackIdentifier(m_context, textTrackIdentifier));
    1991              :     }
    1992              : }
    1993              : 
    1994            3 : bool GstGenericPlayer::getTextTrackIdentifier(std::string &textTrackIdentifier)
    1995              : {
    1996            3 :     if (!m_context.subtitleSink)
    1997              :     {
    1998            1 :         RIALTO_SERVER_LOG_ERROR("There is no subtitle sink");
    1999            1 :         return false;
    2000              :     }
    2001              : 
    2002            2 :     gchar *identifier = nullptr;
    2003            2 :     m_glibWrapper->gObjectGet(m_context.subtitleSink, "text-track-identifier", &identifier, nullptr);
    2004              : 
    2005            2 :     if (identifier)
    2006              :     {
    2007            1 :         textTrackIdentifier = identifier;
    2008            1 :         m_glibWrapper->gFree(identifier);
    2009            1 :         return true;
    2010              :     }
    2011              :     else
    2012              :     {
    2013            1 :         RIALTO_SERVER_LOG_ERROR("Failed to get text track identifier");
    2014            1 :         return false;
    2015              :     }
    2016              : }
    2017              : 
    2018            1 : bool GstGenericPlayer::setLowLatency(bool lowLatency)
    2019              : {
    2020            1 :     if (m_workerThread)
    2021              :     {
    2022            1 :         m_workerThread->enqueueTask(m_taskFactory->createSetLowLatency(m_context, *this, lowLatency));
    2023              :     }
    2024            1 :     return true;
    2025              : }
    2026              : 
    2027            1 : bool GstGenericPlayer::setSync(bool sync)
    2028              : {
    2029            1 :     if (m_workerThread)
    2030              :     {
    2031            1 :         m_workerThread->enqueueTask(m_taskFactory->createSetSync(m_context, *this, sync));
    2032              :     }
    2033            1 :     return true;
    2034              : }
    2035              : 
    2036            4 : bool GstGenericPlayer::getSync(bool &sync)
    2037              : {
    2038            4 :     bool returnValue{false};
    2039            4 :     GstElement *sink{getSink(MediaSourceType::AUDIO)};
    2040            4 :     if (sink)
    2041              :     {
    2042            2 :         if (m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(sink), "sync"))
    2043              :         {
    2044            1 :             m_glibWrapper->gObjectGet(sink, "sync", &sync, nullptr);
    2045            1 :             returnValue = true;
    2046              :         }
    2047              :         else
    2048              :         {
    2049            1 :             RIALTO_SERVER_LOG_ERROR("Sync not supported in sink '%s'", GST_ELEMENT_NAME(sink));
    2050              :         }
    2051            2 :         m_gstWrapper->gstObjectUnref(sink);
    2052              :     }
    2053            2 :     else if (m_context.pendingSync.has_value())
    2054              :     {
    2055            1 :         RIALTO_SERVER_LOG_DEBUG("Returning queued value");
    2056            1 :         sync = m_context.pendingSync.value();
    2057            1 :         returnValue = true;
    2058              :     }
    2059              :     else
    2060              :     {
    2061              :         // We dont know the default setting on the sync, so return failure here
    2062            1 :         RIALTO_SERVER_LOG_WARN("No audio sink attached or queued value");
    2063              :     }
    2064              : 
    2065            4 :     return returnValue;
    2066              : }
    2067              : 
    2068            1 : bool GstGenericPlayer::setSyncOff(bool syncOff)
    2069              : {
    2070            1 :     if (m_workerThread)
    2071              :     {
    2072            1 :         m_workerThread->enqueueTask(m_taskFactory->createSetSyncOff(m_context, *this, syncOff));
    2073              :     }
    2074            1 :     return true;
    2075              : }
    2076              : 
    2077            1 : bool GstGenericPlayer::setStreamSyncMode(const MediaSourceType &mediaSourceType, int32_t streamSyncMode)
    2078              : {
    2079            1 :     if (m_workerThread)
    2080              :     {
    2081            2 :         m_workerThread->enqueueTask(
    2082            2 :             m_taskFactory->createSetStreamSyncMode(m_context, *this, mediaSourceType, streamSyncMode));
    2083              :     }
    2084            1 :     return true;
    2085              : }
    2086              : 
    2087            5 : bool GstGenericPlayer::getStreamSyncMode(int32_t &streamSyncMode)
    2088              : {
    2089            5 :     bool returnValue{false};
    2090            5 :     GstElement *decoder = getDecoder(MediaSourceType::AUDIO);
    2091            5 :     if (decoder && m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(decoder), "stream-sync-mode"))
    2092              :     {
    2093            2 :         m_glibWrapper->gObjectGet(decoder, "stream-sync-mode", &streamSyncMode, nullptr);
    2094            2 :         returnValue = true;
    2095              :     }
    2096              :     else
    2097              :     {
    2098            3 :         std::unique_lock lock{m_context.propertyMutex};
    2099            3 :         if (m_context.pendingStreamSyncMode.find(MediaSourceType::AUDIO) != m_context.pendingStreamSyncMode.end())
    2100              :         {
    2101            1 :             RIALTO_SERVER_LOG_DEBUG("Returning queued value");
    2102            1 :             streamSyncMode = m_context.pendingStreamSyncMode[MediaSourceType::AUDIO];
    2103            1 :             returnValue = true;
    2104              :         }
    2105              :         else
    2106              :         {
    2107            2 :             RIALTO_SERVER_LOG_ERROR("Stream sync mode not supported in decoder '%s'",
    2108              :                                     (decoder ? GST_ELEMENT_NAME(decoder) : "null"));
    2109              :         }
    2110            3 :     }
    2111              : 
    2112            5 :     if (decoder)
    2113            3 :         m_gstWrapper->gstObjectUnref(GST_OBJECT(decoder));
    2114              : 
    2115            5 :     return returnValue;
    2116              : }
    2117              : 
    2118            1 : void GstGenericPlayer::ping(std::unique_ptr<IHeartbeatHandler> &&heartbeatHandler)
    2119              : {
    2120            1 :     if (m_workerThread)
    2121              :     {
    2122            1 :         m_workerThread->enqueueTask(m_taskFactory->createPing(std::move(heartbeatHandler)));
    2123              :     }
    2124              : }
    2125              : 
    2126            2 : void GstGenericPlayer::flush(const MediaSourceType &mediaSourceType, bool resetTime, bool &async)
    2127              : {
    2128            2 :     if (m_workerThread)
    2129              :     {
    2130            2 :         async = isAsync(mediaSourceType);
    2131            2 :         m_flushWatcher->setFlushing(mediaSourceType, async);
    2132            2 :         m_workerThread->enqueueTask(m_taskFactory->createFlush(m_context, *this, mediaSourceType, resetTime, async));
    2133              :     }
    2134              : }
    2135              : 
    2136            1 : void GstGenericPlayer::setSourcePosition(const MediaSourceType &mediaSourceType, int64_t position, bool resetTime,
    2137              :                                          double appliedRate, uint64_t stopPosition)
    2138              : {
    2139            1 :     if (m_workerThread)
    2140              :     {
    2141            1 :         m_workerThread->enqueueTask(m_taskFactory->createSetSourcePosition(m_context, mediaSourceType, position,
    2142              :                                                                            resetTime, appliedRate, stopPosition));
    2143              :     }
    2144              : }
    2145              : 
    2146            0 : void GstGenericPlayer::setSubtitleOffset(int64_t position)
    2147              : {
    2148            0 :     if (m_workerThread)
    2149              :     {
    2150            0 :         m_workerThread->enqueueTask(m_taskFactory->createSetSubtitleOffset(m_context, position));
    2151              :     }
    2152              : }
    2153              : 
    2154            1 : void GstGenericPlayer::processAudioGap(int64_t position, uint32_t duration, int64_t discontinuityGap, bool audioAac)
    2155              : {
    2156            1 :     if (m_workerThread)
    2157              :     {
    2158            2 :         m_workerThread->enqueueTask(
    2159            2 :             m_taskFactory->createProcessAudioGap(m_context, position, duration, discontinuityGap, audioAac));
    2160              :     }
    2161            1 : }
    2162              : 
    2163            1 : void GstGenericPlayer::setBufferingLimit(uint32_t limitBufferingMs)
    2164              : {
    2165            1 :     if (m_workerThread)
    2166              :     {
    2167            1 :         m_workerThread->enqueueTask(m_taskFactory->createSetBufferingLimit(m_context, *this, limitBufferingMs));
    2168              :     }
    2169              : }
    2170              : 
    2171            5 : bool GstGenericPlayer::getBufferingLimit(uint32_t &limitBufferingMs)
    2172              : {
    2173            5 :     bool returnValue{false};
    2174            5 :     GstElement *decoder = getDecoder(MediaSourceType::AUDIO);
    2175            5 :     if (decoder && m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(decoder), "limit-buffering-ms"))
    2176              :     {
    2177            2 :         m_glibWrapper->gObjectGet(decoder, "limit-buffering-ms", &limitBufferingMs, nullptr);
    2178            2 :         returnValue = true;
    2179              :     }
    2180              :     else
    2181              :     {
    2182            3 :         std::unique_lock lock{m_context.propertyMutex};
    2183            3 :         if (m_context.pendingBufferingLimit.has_value())
    2184              :         {
    2185            1 :             RIALTO_SERVER_LOG_DEBUG("Returning queued value");
    2186            1 :             limitBufferingMs = m_context.pendingBufferingLimit.value();
    2187            1 :             returnValue = true;
    2188              :         }
    2189              :         else
    2190              :         {
    2191            2 :             RIALTO_SERVER_LOG_ERROR("buffering limit not supported in decoder '%s'",
    2192              :                                     (decoder ? GST_ELEMENT_NAME(decoder) : "null"));
    2193              :         }
    2194            3 :     }
    2195              : 
    2196            5 :     if (decoder)
    2197            3 :         m_gstWrapper->gstObjectUnref(GST_OBJECT(decoder));
    2198              : 
    2199            5 :     return returnValue;
    2200              : }
    2201              : 
    2202            1 : void GstGenericPlayer::setUseBuffering(bool useBuffering)
    2203              : {
    2204            1 :     if (m_workerThread)
    2205              :     {
    2206            1 :         m_workerThread->enqueueTask(m_taskFactory->createSetUseBuffering(m_context, *this, useBuffering));
    2207              :     }
    2208              : }
    2209              : 
    2210            3 : bool GstGenericPlayer::getUseBuffering(bool &useBuffering)
    2211              : {
    2212            3 :     if (m_context.playbackGroup.m_curAudioDecodeBin)
    2213              :     {
    2214            1 :         m_glibWrapper->gObjectGet(m_context.playbackGroup.m_curAudioDecodeBin, "use-buffering", &useBuffering, nullptr);
    2215            1 :         return true;
    2216              :     }
    2217              :     else
    2218              :     {
    2219            2 :         std::unique_lock lock{m_context.propertyMutex};
    2220            2 :         if (m_context.pendingUseBuffering.has_value())
    2221              :         {
    2222            1 :             RIALTO_SERVER_LOG_DEBUG("Returning queued value");
    2223            1 :             useBuffering = m_context.pendingUseBuffering.value();
    2224            1 :             return true;
    2225              :         }
    2226            2 :     }
    2227            1 :     return false;
    2228              : }
    2229              : 
    2230            1 : void GstGenericPlayer::switchSource(const std::unique_ptr<IMediaPipeline::MediaSource> &mediaSource)
    2231              : {
    2232            1 :     if (m_workerThread)
    2233              :     {
    2234            1 :         m_workerThread->enqueueTask(m_taskFactory->createSwitchSource(*this, mediaSource));
    2235              :     }
    2236              : }
    2237              : 
    2238            1 : void GstGenericPlayer::handleBusMessage(GstMessage *message)
    2239              : {
    2240            1 :     m_workerThread->enqueueTask(m_taskFactory->createHandleBusMessage(m_context, *this, message, *m_flushWatcher));
    2241              : }
    2242              : 
    2243            1 : void GstGenericPlayer::updatePlaybackGroup(GstElement *typefind, const GstCaps *caps)
    2244              : {
    2245            1 :     m_workerThread->enqueueTask(m_taskFactory->createUpdatePlaybackGroup(m_context, *this, typefind, caps));
    2246              : }
    2247              : 
    2248            3 : void GstGenericPlayer::addAutoVideoSinkChild(GObject *object)
    2249              : {
    2250              :     // Only add children that are sinks
    2251            3 :     if (GST_OBJECT_FLAG_IS_SET(GST_ELEMENT(object), GST_ELEMENT_FLAG_SINK))
    2252              :     {
    2253            2 :         RIALTO_SERVER_LOG_DEBUG("Store AutoVideoSink child sink");
    2254              : 
    2255            2 :         if (m_context.autoVideoChildSink && m_context.autoVideoChildSink != GST_ELEMENT(object))
    2256              :         {
    2257            1 :             RIALTO_SERVER_LOG_MIL("AutoVideoSink child is been overwritten");
    2258              :         }
    2259            2 :         m_context.autoVideoChildSink = GST_ELEMENT(object);
    2260              :     }
    2261            3 : }
    2262              : 
    2263            3 : void GstGenericPlayer::addAutoAudioSinkChild(GObject *object)
    2264              : {
    2265              :     // Only add children that are sinks
    2266            3 :     if (GST_OBJECT_FLAG_IS_SET(GST_ELEMENT(object), GST_ELEMENT_FLAG_SINK))
    2267              :     {
    2268            2 :         RIALTO_SERVER_LOG_DEBUG("Store AutoAudioSink child sink");
    2269              : 
    2270            2 :         if (m_context.autoAudioChildSink && m_context.autoAudioChildSink != GST_ELEMENT(object))
    2271              :         {
    2272            1 :             RIALTO_SERVER_LOG_MIL("AutoAudioSink child is been overwritten");
    2273              :         }
    2274            2 :         m_context.autoAudioChildSink = GST_ELEMENT(object);
    2275              :     }
    2276            3 : }
    2277              : 
    2278            3 : void GstGenericPlayer::removeAutoVideoSinkChild(GObject *object)
    2279              : {
    2280            3 :     if (GST_OBJECT_FLAG_IS_SET(GST_ELEMENT(object), GST_ELEMENT_FLAG_SINK))
    2281              :     {
    2282            3 :         RIALTO_SERVER_LOG_DEBUG("Remove AutoVideoSink child sink");
    2283              : 
    2284            3 :         if (m_context.autoVideoChildSink && m_context.autoVideoChildSink != GST_ELEMENT(object))
    2285              :         {
    2286            1 :             RIALTO_SERVER_LOG_MIL("AutoVideoSink child sink is not the same as the one stored");
    2287            1 :             return;
    2288              :         }
    2289              : 
    2290            2 :         m_context.autoVideoChildSink = nullptr;
    2291              :     }
    2292              : }
    2293              : 
    2294            3 : void GstGenericPlayer::removeAutoAudioSinkChild(GObject *object)
    2295              : {
    2296            3 :     if (GST_OBJECT_FLAG_IS_SET(GST_ELEMENT(object), GST_ELEMENT_FLAG_SINK))
    2297              :     {
    2298            3 :         RIALTO_SERVER_LOG_DEBUG("Remove AutoAudioSink child sink");
    2299              : 
    2300            3 :         if (m_context.autoAudioChildSink && m_context.autoAudioChildSink != GST_ELEMENT(object))
    2301              :         {
    2302            1 :             RIALTO_SERVER_LOG_MIL("AutoAudioSink child sink is not the same as the one stored");
    2303            1 :             return;
    2304              :         }
    2305              : 
    2306            2 :         m_context.autoAudioChildSink = nullptr;
    2307              :     }
    2308              : }
    2309              : 
    2310           14 : GstElement *GstGenericPlayer::getSinkChildIfAutoVideoSink(GstElement *sink) const
    2311              : {
    2312           14 :     const gchar *kTmpName = m_glibWrapper->gTypeName(G_OBJECT_TYPE(sink));
    2313           14 :     if (!kTmpName)
    2314            0 :         return sink;
    2315              : 
    2316           28 :     const std::string kElementTypeName{kTmpName};
    2317           14 :     if (kElementTypeName == "GstAutoVideoSink")
    2318              :     {
    2319            1 :         if (!m_context.autoVideoChildSink)
    2320              :         {
    2321            0 :             RIALTO_SERVER_LOG_WARN("No child sink has been added to the autovideosink");
    2322              :         }
    2323              :         else
    2324              :         {
    2325            1 :             return m_context.autoVideoChildSink;
    2326              :         }
    2327              :     }
    2328           13 :     return sink;
    2329           14 : }
    2330              : 
    2331           11 : GstElement *GstGenericPlayer::getSinkChildIfAutoAudioSink(GstElement *sink) const
    2332              : {
    2333           11 :     const gchar *kTmpName = m_glibWrapper->gTypeName(G_OBJECT_TYPE(sink));
    2334           11 :     if (!kTmpName)
    2335            0 :         return sink;
    2336              : 
    2337           22 :     const std::string kElementTypeName{kTmpName};
    2338           11 :     if (kElementTypeName == "GstAutoAudioSink")
    2339              :     {
    2340            1 :         if (!m_context.autoAudioChildSink)
    2341              :         {
    2342            0 :             RIALTO_SERVER_LOG_WARN("No child sink has been added to the autoaudiosink");
    2343              :         }
    2344              :         else
    2345              :         {
    2346            1 :             return m_context.autoAudioChildSink;
    2347              :         }
    2348              :     }
    2349           10 :     return sink;
    2350           11 : }
    2351              : 
    2352          218 : void GstGenericPlayer::setPlaybinFlags(bool enableAudio)
    2353              : {
    2354          218 :     unsigned flags = getGstPlayFlag("video") | getGstPlayFlag("native-video") | getGstPlayFlag("text");
    2355              : 
    2356          218 :     if (enableAudio)
    2357              :     {
    2358          218 :         flags |= getGstPlayFlag("audio");
    2359          218 :         flags |= shouldEnableNativeAudio() ? getGstPlayFlag("native-audio") : 0;
    2360              :     }
    2361              : 
    2362          218 :     m_glibWrapper->gObjectSet(m_context.pipeline, "flags", flags, nullptr);
    2363              : }
    2364              : 
    2365          218 : bool GstGenericPlayer::shouldEnableNativeAudio()
    2366              : {
    2367          218 :     GstElementFactory *factory = m_gstWrapper->gstElementFactoryFind("brcmaudiosink");
    2368          218 :     if (factory)
    2369              :     {
    2370            1 :         m_gstWrapper->gstObjectUnref(GST_OBJECT(factory));
    2371            1 :         return true;
    2372              :     }
    2373          217 :     return false;
    2374              : }
    2375              : 
    2376              : }; // namespace firebolt::rialto::server
        

Generated by: LCOV version 2.0-1