LCOV - code coverage report
Current view: top level - media/server/gstplayer/source/tasks/generic - SetupElement.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 137 137
Test Date: 2026-06-17 06:35:35 Functions: 100.0 % 10 10

            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 "tasks/generic/SetupElement.h"
      21              : #include "GenericPlayerContext.h"
      22              : #include "IGlibWrapper.h"
      23              : #include "IGstGenericPlayerPrivate.h"
      24              : #include "IGstWrapper.h"
      25              : #include "RialtoServerLogging.h"
      26              : #include "Utils.h"
      27              : 
      28              : namespace
      29              : {
      30              : /**
      31              :  * @brief Callback for audio underflow event from sink. Called by the Gstreamer thread.
      32              :  *
      33              :  * @param[in] object     : the object that emitted the signal
      34              :  * @param[in] fifoDepth  : the fifo depth (may be 0)
      35              :  * @param[in] queueDepth : the queue depth (may be NULL)
      36              :  * @param[in] self       : The pointer to IGstGenericPlayerPrivate
      37              :  *
      38              :  * @retval true if the handling of the message is successful, false otherwise.
      39              :  */
      40            1 : void audioUnderflowCallback(GstElement *object, guint fifoDepth, gpointer queueDepth, gpointer self)
      41              : {
      42            1 :     firebolt::rialto::server::IGstGenericPlayerPrivate *player =
      43              :         static_cast<firebolt::rialto::server::IGstGenericPlayerPrivate *>(self);
      44            1 :     player->scheduleAudioUnderflow();
      45              : }
      46              : 
      47              : /**
      48              :  * @brief Callback for video underflow event from sink. Called by the Gstreamer thread.
      49              :  *
      50              :  * @param[in] object     : the object that emitted the signal
      51              :  * @param[in] fifoDepth  : the fifo depth (may be 0)
      52              :  * @param[in] queueDepth : the queue depth (may be NULL)
      53              :  * @param[in] self       : The pointer to IGstGenericPlayerPrivate
      54              :  *
      55              :  * @retval true if the handling of the message is successful, false otherwise.
      56              :  */
      57            1 : void videoUnderflowCallback(GstElement *object, guint fifoDepth, gpointer queueDepth, gpointer self)
      58              : {
      59            1 :     firebolt::rialto::server::IGstGenericPlayerPrivate *player =
      60              :         static_cast<firebolt::rialto::server::IGstGenericPlayerPrivate *>(self);
      61            1 :     player->scheduleVideoUnderflow();
      62              : }
      63              : 
      64              : /**
      65              :  * @brief Callback for first video frame event from the emitting video element. Called by the Gstreamer thread.
      66              :  *
      67              :  * @param[in] object     : the object that emitted the signal
      68              :  * @param[in] fifoDepth  : the fifo depth (may be 0)
      69              :  * @param[in] queueDepth : the queue depth (may be NULL)
      70              :  * @param[in] self       : The pointer to IGstGenericPlayerPrivate
      71              :  */
      72            1 : void firstVideoFrameCallback(GstElement *object, guint fifoDepth, gpointer queueDepth, gpointer self)
      73              : {
      74            1 :     firebolt::rialto::server::IGstGenericPlayerPrivate *player =
      75              :         static_cast<firebolt::rialto::server::IGstGenericPlayerPrivate *>(self);
      76            1 :     player->scheduleFirstVideoFrameReceived();
      77              : }
      78              : 
      79              : /**
      80              :  * @brief Callback for a autovideosink when a child has been added to the sink.
      81              :  *
      82              :  * @param[in] obj        : the parent element (autovideosink)
      83              :  * @param[in] object     : the child element
      84              :  * @param[in] name       : the name of the child element
      85              :  * @param[in] self       : The pointer to IGstGenericPlayerPrivate
      86              :  */
      87            1 : void autoVideoSinkChildAddedCallback(GstChildProxy *obj, GObject *object, gchar *name, gpointer self)
      88              : {
      89            1 :     RIALTO_SERVER_LOG_DEBUG("AutoVideoSink added element %s", name);
      90            1 :     firebolt::rialto::server::IGstGenericPlayerPrivate *player =
      91              :         static_cast<firebolt::rialto::server::IGstGenericPlayerPrivate *>(self);
      92            1 :     player->addAutoVideoSinkChild(object);
      93              : }
      94              : 
      95              : /**
      96              :  * @brief Callback for a autoaudiosink when a child has been added to the sink.
      97              :  *
      98              :  * @param[in] obj        : the parent element (autoaudiosink)
      99              :  * @param[in] object     : the child element
     100              :  * @param[in] name       : the name of the child element
     101              :  * @param[in] self       : The pointer to IGstGenericPlayerPrivate
     102              :  */
     103            1 : void autoAudioSinkChildAddedCallback(GstChildProxy *obj, GObject *object, gchar *name, gpointer self)
     104              : {
     105            1 :     RIALTO_SERVER_LOG_DEBUG("AutoAudioSink added element %s", name);
     106            1 :     firebolt::rialto::server::IGstGenericPlayerPrivate *player =
     107              :         static_cast<firebolt::rialto::server::IGstGenericPlayerPrivate *>(self);
     108            1 :     player->addAutoAudioSinkChild(object);
     109              : }
     110              : 
     111              : /**
     112              :  * @brief Callback for a autovideosink when a child has been removed from the sink.
     113              :  *
     114              :  * @param[in] obj        : the parent element (autovideosink)
     115              :  * @param[in] object     : the child element
     116              :  * @param[in] name       : the name of the child element
     117              :  * @param[in] self       : The pointer to IGstGenericPlayerPrivate
     118              :  */
     119            1 : void autoVideoSinkChildRemovedCallback(GstChildProxy *obj, GObject *object, gchar *name, gpointer self)
     120              : {
     121            1 :     RIALTO_SERVER_LOG_DEBUG("AutoVideoSink removed element %s", name);
     122            1 :     firebolt::rialto::server::IGstGenericPlayerPrivate *player =
     123              :         static_cast<firebolt::rialto::server::IGstGenericPlayerPrivate *>(self);
     124            1 :     player->removeAutoVideoSinkChild(object);
     125              : }
     126              : 
     127              : /**
     128              :  * @brief Callback for a autoaudiosink when a child has been removed from the sink.
     129              :  *
     130              :  * @param[in] obj        : the parent element (autoaudiosink)
     131              :  * @param[in] object     : the child element
     132              :  * @param[in] name       : the name of the child element
     133              :  * @param[in] self       : The pointer to IGstGenericPlayerPrivate
     134              :  */
     135            1 : void autoAudioSinkChildRemovedCallback(GstChildProxy *obj, GObject *object, gchar *name, gpointer self)
     136              : {
     137            1 :     RIALTO_SERVER_LOG_DEBUG("AutoAudioSink removed element %s", name);
     138            1 :     firebolt::rialto::server::IGstGenericPlayerPrivate *player =
     139              :         static_cast<firebolt::rialto::server::IGstGenericPlayerPrivate *>(self);
     140            1 :     player->removeAutoAudioSinkChild(object);
     141              : }
     142              : } // namespace
     143              : 
     144              : namespace firebolt::rialto::server::tasks::generic
     145              : {
     146           35 : SetupElement::SetupElement(GenericPlayerContext &context,
     147              :                            const std::shared_ptr<firebolt::rialto::wrappers::IGstWrapper> &gstWrapper,
     148              :                            const std::shared_ptr<firebolt::rialto::wrappers::IGlibWrapper> &glibWrapper,
     149           35 :                            IGstGenericPlayerPrivate &player, GstElement *element)
     150           35 :     : m_context{context}, m_gstWrapper{gstWrapper}, m_glibWrapper{glibWrapper}, m_player{player}, m_element{element}
     151              : {
     152           35 :     RIALTO_SERVER_LOG_DEBUG("Constructing SetupElement");
     153              : }
     154              : 
     155           36 : SetupElement::~SetupElement()
     156              : {
     157           35 :     RIALTO_SERVER_LOG_DEBUG("SetupElement finished");
     158           36 : }
     159              : 
     160           34 : void SetupElement::execute() const
     161              : {
     162           34 :     RIALTO_SERVER_LOG_DEBUG("Executing SetupElement");
     163              : 
     164           34 :     const std::string kElementTypeName = m_glibWrapper->gTypeName(G_OBJECT_TYPE(m_element));
     165           34 :     if (kElementTypeName == "GstAutoVideoSink")
     166              :     {
     167              :         // Check and store child sink so we can set underlying properties
     168            5 :         m_glibWrapper->gSignalConnect(m_element, "child-added", G_CALLBACK(autoVideoSinkChildAddedCallback), &m_player);
     169            5 :         m_glibWrapper->gSignalConnect(m_element, "child-removed", G_CALLBACK(autoVideoSinkChildRemovedCallback),
     170            5 :                                       &m_player);
     171              : 
     172              :         // AutoVideoSink sets child before it is setup on the pipeline, so check for children here
     173            5 :         GstIterator *sinks = m_gstWrapper->gstBinIterateSinks(GST_BIN(m_element));
     174            5 :         if (sinks && sinks->size > 1)
     175              :         {
     176            1 :             RIALTO_SERVER_LOG_WARN("More than one child sink attached");
     177              :         }
     178              : 
     179            5 :         GValue elem = G_VALUE_INIT;
     180            5 :         if (m_gstWrapper->gstIteratorNext(sinks, &elem) == GST_ITERATOR_OK)
     181              :         {
     182            2 :             m_player.addAutoVideoSinkChild(G_OBJECT(m_glibWrapper->gValueGetObject(&elem)));
     183              :         }
     184            5 :         m_glibWrapper->gValueUnset(&elem);
     185              : 
     186            5 :         if (sinks)
     187            5 :             m_gstWrapper->gstIteratorFree(sinks);
     188              :     }
     189           29 :     else if (kElementTypeName == "GstAutoAudioSink")
     190              :     {
     191              :         // Check and store child sink so we can set underlying properties
     192            5 :         m_glibWrapper->gSignalConnect(m_element, "child-added", G_CALLBACK(autoAudioSinkChildAddedCallback), &m_player);
     193            5 :         m_glibWrapper->gSignalConnect(m_element, "child-removed", G_CALLBACK(autoAudioSinkChildRemovedCallback),
     194            5 :                                       &m_player);
     195              : 
     196              :         // AutoAudioSink sets child before it is setup on the pipeline, so check for children here
     197            5 :         GstIterator *sinks = m_gstWrapper->gstBinIterateSinks(GST_BIN(m_element));
     198            5 :         if (sinks && sinks->size > 1)
     199              :         {
     200            1 :             RIALTO_SERVER_LOG_WARN("More than one child sink attached");
     201              :         }
     202              : 
     203            5 :         GValue elem = G_VALUE_INIT;
     204            5 :         if (m_gstWrapper->gstIteratorNext(sinks, &elem) == GST_ITERATOR_OK)
     205              :         {
     206            2 :             m_player.addAutoAudioSinkChild(G_OBJECT(m_glibWrapper->gValueGetObject(&elem)));
     207              :         }
     208            5 :         m_glibWrapper->gValueUnset(&elem);
     209              : 
     210            5 :         if (sinks)
     211            5 :             m_gstWrapper->gstIteratorFree(sinks);
     212              :     }
     213              : 
     214           34 :     if (m_glibWrapper->gStrHasPrefix(GST_ELEMENT_NAME(m_element), "amlhalasink"))
     215              :     {
     216            2 :         if (m_context.streamInfo.find(MediaSourceType::VIDEO) != m_context.streamInfo.end())
     217              :         {
     218              :             // Wait for video so that the audio aligns at the starting point with timeout of 4000ms.
     219            1 :             m_glibWrapper->gObjectSet(m_element, "wait-video", TRUE, "a-wait-timeout", 4000, nullptr);
     220              :         }
     221              : 
     222              :         // Xrun occasionally pauses the underlying sink due to unstable playback, but the rest of the pipeline
     223              :         // remains in the playing state. This causes problems with the synchronization of gst element and rialto
     224              :         // ultimately hangs waiting for pipeline termination.
     225            2 :         m_glibWrapper->gObjectSet(m_element, "disable-xrun", TRUE, nullptr);
     226              :     }
     227           32 :     else if (m_glibWrapper->gStrHasPrefix(GST_ELEMENT_NAME(m_element), "brcmaudiosink"))
     228              :     {
     229            1 :         m_glibWrapper->gObjectSet(m_element, "async", TRUE, nullptr);
     230              :     }
     231           31 :     else if (m_glibWrapper->gStrHasPrefix(GST_ELEMENT_NAME(m_element), "rialtotexttracksink"))
     232              :     {
     233              :         // in cannot be set during construction, because playsink overwrites "sync" value of text-sink during setup
     234            1 :         m_glibWrapper->gObjectSet(m_element, "sync", FALSE, nullptr);
     235              :     }
     236              : 
     237           34 :     if (m_context.subtitleSink)
     238              :     {
     239            3 :         if (!m_context.isVideoHandleSet && isVideoDecoder(*m_gstWrapper, m_element))
     240              :         {
     241            3 :             if (m_glibWrapper->gStrHasPrefix(GST_ELEMENT_NAME(m_element), "westerossink"))
     242              :             {
     243            2 :                 gpointer decoder{nullptr};
     244            2 :                 m_glibWrapper->gObjectGet(m_element, "videodecoder", &decoder, nullptr);
     245            2 :                 if (decoder)
     246              :                 {
     247            1 :                     m_glibWrapper->gObjectSet(m_context.subtitleSink, "video-decoder", decoder, nullptr);
     248            1 :                     RIALTO_SERVER_LOG_INFO("Setting video decoder handle for subtitle sink: %p", decoder);
     249            1 :                     m_context.isVideoHandleSet = true;
     250              :                 }
     251              :                 else
     252              :                 {
     253            1 :                     m_glibWrapper->gObjectSet(m_context.subtitleSink, "video-decoder", m_element, nullptr);
     254            1 :                     RIALTO_SERVER_LOG_INFO("Setting video decoder handle for subtitle sink: %p", m_element);
     255            1 :                     m_context.isVideoHandleSet = true;
     256              :                 }
     257              :             }
     258            1 :             else if (m_glibWrapper->gStrHasPrefix(GST_ELEMENT_NAME(m_element), "omx"))
     259              :             {
     260            1 :                 m_glibWrapper->gObjectSet(m_context.subtitleSink, "video-decoder", m_element, nullptr);
     261            1 :                 RIALTO_SERVER_LOG_INFO("Setting video decoder handle for subtitle sink: %p", m_element);
     262            1 :                 m_context.isVideoHandleSet = true;
     263              :             }
     264              :         }
     265              :     }
     266              : 
     267           34 :     if (isDecoder(*m_gstWrapper, m_element) || isSink(*m_gstWrapper, m_element))
     268              :     {
     269           28 :         std::optional<std::string> underflowSignalName = getUnderflowSignalName(*m_glibWrapper, m_element);
     270           28 :         if (underflowSignalName)
     271              :         {
     272           28 :             if (isAudio(*m_gstWrapper, m_element))
     273              :             {
     274           16 :                 RIALTO_SERVER_LOG_INFO("Connecting audio underflow callback for signal: %s",
     275              :                                        underflowSignalName.value().c_str());
     276           32 :                 m_glibWrapper->gSignalConnect(m_element, underflowSignalName.value().c_str(),
     277           16 :                                               G_CALLBACK(audioUnderflowCallback), &m_player);
     278              :             }
     279           12 :             else if (isVideo(*m_gstWrapper, m_element))
     280              :             {
     281           12 :                 RIALTO_SERVER_LOG_INFO("Connecting video underflow callback for signal: %s",
     282              :                                        underflowSignalName.value().c_str());
     283           24 :                 m_glibWrapper->gSignalConnect(m_element, underflowSignalName.value().c_str(),
     284           12 :                                               G_CALLBACK(videoUnderflowCallback), &m_player);
     285              :             }
     286              :         }
     287              : 
     288           28 :         std::optional<std::string> firstFrameSignalName = getFirstFrameSignalName(*m_glibWrapper, m_element);
     289           28 :         if (firstFrameSignalName)
     290              :         {
     291            1 :             if (isVideo(*m_gstWrapper, m_element))
     292              :             {
     293            1 :                 RIALTO_SERVER_LOG_INFO("Connecting first video frame callback for signal: %s",
     294              :                                        firstFrameSignalName.value().c_str());
     295            2 :                 m_glibWrapper->gSignalConnect(m_element, firstFrameSignalName.value().c_str(),
     296            1 :                                               G_CALLBACK(firstVideoFrameCallback), &m_player);
     297              :             }
     298              :         }
     299           28 :     }
     300              : 
     301           34 :     if (isVideoSink(*m_gstWrapper, m_element))
     302              :     {
     303           10 :         if (!m_context.videoSink)
     304              :         {
     305           10 :             m_gstWrapper->gstObjectRef(m_element);
     306           10 :             m_context.videoSink = m_element;
     307              :         }
     308           10 :         if (!m_context.pendingGeometry.empty())
     309              :         {
     310            1 :             m_player.setVideoSinkRectangle();
     311              :         }
     312           10 :         if (m_context.pendingImmediateOutputForVideo.has_value())
     313              :         {
     314            1 :             m_player.setImmediateOutput();
     315              :         }
     316           10 :         if (m_context.pendingRenderFrame)
     317              :         {
     318            1 :             m_player.setRenderFrame();
     319              :         }
     320           10 :         if (m_context.pendingShowVideoWindow.has_value())
     321              :         {
     322            1 :             m_player.setShowVideoWindow();
     323              :         }
     324              :     }
     325           24 :     else if (isAudioDecoder(*m_gstWrapper, m_element))
     326              :     {
     327            6 :         if (m_context.pendingSyncOff.has_value())
     328              :         {
     329            1 :             m_player.setSyncOff();
     330              :         }
     331            6 :         if (m_context.pendingStreamSyncMode.find(MediaSourceType::AUDIO) != m_context.pendingStreamSyncMode.end())
     332              :         {
     333            1 :             m_player.setStreamSyncMode(MediaSourceType::AUDIO);
     334              :         }
     335            6 :         if (m_context.pendingBufferingLimit.has_value())
     336              :         {
     337            1 :             m_player.setBufferingLimit();
     338              :         }
     339            7 :         if (m_context.isLive &&
     340            1 :             m_glibWrapper->gObjectClassFindProperty(G_OBJECT_GET_CLASS(m_element), "enable-rate-correction"))
     341              :         {
     342            1 :             RIALTO_SERVER_LOG_INFO("Enabling rate correction for broadcom decoder.");
     343            1 :             m_glibWrapper->gObjectSet(m_element, "enable-rate-correction", TRUE, nullptr);
     344              :         }
     345              :     }
     346           18 :     else if (isAudioSink(*m_gstWrapper, m_element))
     347              :     {
     348           10 :         if (m_context.pendingLowLatency.has_value())
     349              :         {
     350            1 :             m_player.setLowLatency();
     351              :         }
     352           10 :         if (m_context.pendingSync.has_value())
     353              :         {
     354            1 :             m_player.setSync();
     355              :         }
     356              :     }
     357            8 :     else if (isVideoParser(*m_gstWrapper, m_element))
     358              :     {
     359            1 :         if (m_context.pendingStreamSyncMode.find(MediaSourceType::VIDEO) != m_context.pendingStreamSyncMode.end())
     360              :         {
     361            1 :             m_player.setStreamSyncMode(MediaSourceType::VIDEO);
     362              :         }
     363              :     }
     364              : 
     365           34 :     if (m_gstWrapper->gstIsBaseParse(m_element))
     366              :     {
     367            1 :         m_gstWrapper->gstBaseParseSetPtsInterpolation(GST_BASE_PARSE(m_element), FALSE);
     368              :     }
     369              : 
     370           34 :     m_gstWrapper->gstObjectUnref(m_element);
     371              : }
     372              : } // namespace firebolt::rialto::server::tasks::generic
        

Generated by: LCOV version 2.0-1