LCOV - code coverage report
Current view: top level - media/server/gstplayer/source - GstGenericPlayer.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 96.4 % 1054 1016
Test Date: 2025-09-15 11:52:04 Functions: 99.0 % 103 102

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

Generated by: LCOV version 2.0-1