LCOV - code coverage report
Current view: top level - media/server/gstplayer/source - GstGenericPlayer.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 82.3 % 1625 1337
Test Date: 2026-09-29 14:28:21 Functions: 94.0 % 134 126

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

Generated by: LCOV version 2.0-1