LCOV - code coverage report
Current view: top level - media/server/gstplayer/source - GstGenericPlayer.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 84.5 % 1548 1308
Test Date: 2026-07-31 13:30:47 Functions: 95.3 % 128 122

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

Generated by: LCOV version 2.0-1