LCOV - code coverage report
Current view: top level - media/server/gstplayer/source - GstGenericPlayer.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 84.1 % 1466 1233
Test Date: 2026-07-07 06:27:16 Functions: 95.0 % 121 115

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

Generated by: LCOV version 2.0-1