LCOV - code coverage report
Current view: top level - media/server/gstplayer/source - GstGenericPlayer.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 84.3 % 1436 1211
Test Date: 2026-06-02 05:31:17 Functions: 94.9 % 118 112

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

Generated by: LCOV version 2.0-1