LCOV - code coverage report
Current view: top level - source - RialtoGStreamerMSEBaseSink.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 87.6 % 169 148
Test Date: 2026-06-10 12:08:30 Functions: 100.0 % 18 18

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2022 Sky UK
       3              :  *
       4              :  * This library is free software; you can redistribute it and/or
       5              :  * modify it under the terms of the GNU Lesser General Public
       6              :  * License as published by the Free Software Foundation;
       7              :  * version 2.1 of the License.
       8              :  *
       9              :  * This library is distributed in the hope that it will be useful,
      10              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12              :  * Lesser General Public License for more details.
      13              :  *
      14              :  * You should have received a copy of the GNU Lesser General Public
      15              :  * License along with this library; if not, write to the Free Software
      16              :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
      17              :  */
      18              : 
      19              : #define USE_GLIB 1
      20              : 
      21              : #include <cstring>
      22              : #include <limits>
      23              : 
      24              : #include <gst/gst.h>
      25              : 
      26              : #include "ControlBackend.h"
      27              : #include "GStreamerUtils.h"
      28              : #include "IClientLogControl.h"
      29              : #include "IMediaPipeline.h"
      30              : #include "LogToGstHandler.h"
      31              : #include "RialtoGStreamerMSEBaseSink.h"
      32              : #include "RialtoGStreamerMSEBaseSinkPrivate.h"
      33              : 
      34              : GST_DEBUG_CATEGORY_STATIC(RialtoMSEBaseSinkDebug);
      35              : #define GST_CAT_DEFAULT RialtoMSEBaseSinkDebug
      36              : 
      37              : #define rialto_mse_base_sink_parent_class parent_class
      38         4373 : G_DEFINE_TYPE_WITH_CODE(RialtoMSEBaseSink, rialto_mse_base_sink, GST_TYPE_ELEMENT,
      39              :                         G_ADD_PRIVATE(RialtoMSEBaseSink)
      40              :                             GST_DEBUG_CATEGORY_INIT(RialtoMSEBaseSinkDebug, "rialtomsebasesink", 0,
      41              :                                                     "rialto mse base sink"));
      42              : 
      43              : enum
      44              : {
      45              :     PROP_0,
      46              :     PROP_IS_SINGLE_PATH_STREAM,
      47              :     PROP_N_STREAMS,
      48              :     PROP_HAS_DRM,
      49              :     PROP_STATS,
      50              :     PROP_LAST_SAMPLE,
      51              :     PROP_ENABLE_LAST_SAMPLE,
      52              :     PROP_LAST
      53              : };
      54              : 
      55              : enum
      56              : {
      57              :     SIGNAL_UNDERFLOW,
      58              :     SIGNAL_FIRST_VIDEO_FRAME_RECEIVED,
      59              :     SIGNAL_LAST
      60              : };
      61              : 
      62              : static guint g_signals[SIGNAL_LAST] = {0};
      63              : 
      64          311 : void rialto_mse_base_sink_initialise_delegate(RialtoMSEBaseSink *sink, const std::shared_ptr<IPlaybackDelegate> &delegate)
      65              : {
      66          311 :     std::unique_lock lock{sink->priv->m_sinkMutex};
      67          311 :     sink->priv->m_delegate = delegate;
      68              : 
      69          313 :     for (auto &[type, value] : sink->priv->m_queuedProperties)
      70              :     {
      71            2 :         delegate->setProperty(type, &value);
      72            2 :         g_value_unset(&value);
      73              :     }
      74          311 :     sink->priv->m_queuedProperties.clear();
      75              : }
      76              : 
      77         1844 : static std::shared_ptr<IPlaybackDelegate> rialto_mse_base_sink_get_delegate(RialtoMSEBaseSink *sink)
      78              : {
      79         1844 :     std::unique_lock lock{sink->priv->m_sinkMutex};
      80         1844 :     if (!sink->priv->m_delegate)
      81              :     {
      82            3 :         GST_ERROR_OBJECT(sink, "Sink delegate not initialized");
      83              :     }
      84         3688 :     return sink->priv->m_delegate;
      85         1844 : }
      86              : 
      87           45 : static gboolean rialto_mse_base_sink_send_event(GstElement *element, GstEvent *event)
      88              : {
      89           45 :     if (auto delegate = rialto_mse_base_sink_get_delegate(RIALTO_MSE_BASE_SINK(element)))
      90              :     {
      91           45 :         return delegate->handleSendEvent(event);
      92              :     }
      93            0 :     return FALSE;
      94              : }
      95              : 
      96          225 : gboolean rialto_mse_base_sink_event(GstPad *pad, GstObject *parent, GstEvent *event)
      97              : {
      98          225 :     if (auto delegate = rialto_mse_base_sink_get_delegate(RIALTO_MSE_BASE_SINK(parent)))
      99              :     {
     100          225 :         return delegate->handleEvent(pad, parent, event);
     101              :     }
     102            0 :     return FALSE;
     103              : }
     104              : 
     105           36 : GstFlowReturn rialto_mse_base_sink_chain(GstPad *pad, GstObject *parent, GstBuffer *buf)
     106              : {
     107           36 :     if (auto delegate = rialto_mse_base_sink_get_delegate(RIALTO_MSE_BASE_SINK(parent)))
     108              :     {
     109           36 :         return delegate->handleBuffer(buf);
     110              :     }
     111            0 :     return GST_FLOW_ERROR;
     112              : }
     113              : 
     114           42 : static gboolean rialto_mse_base_sink_query(GstElement *element, GstQuery *query)
     115              : {
     116           42 :     RialtoMSEBaseSink *sink = RIALTO_MSE_BASE_SINK(element);
     117           42 :     if (auto delegate = rialto_mse_base_sink_get_delegate(sink))
     118              :     {
     119           42 :         std::optional<gboolean> result{delegate->handleQuery(query)};
     120           42 :         if (result.has_value())
     121              :         {
     122           12 :             return result.value();
     123              :         }
     124           30 :         GstElement *parent = GST_ELEMENT(&sink->parent);
     125           30 :         return GST_ELEMENT_CLASS(parent_class)->query(parent, query);
     126           42 :     }
     127            0 :     return FALSE;
     128              : }
     129              : 
     130          994 : static GstStateChangeReturn rialto_mse_base_sink_change_state(GstElement *element, GstStateChange transition)
     131              : {
     132          994 :     RialtoMSEBaseSink *sink = RIALTO_MSE_BASE_SINK(element);
     133          994 :     if (auto delegate = rialto_mse_base_sink_get_delegate(sink))
     134              :     {
     135          993 :         GstStateChangeReturn status = delegate->changeState(transition);
     136          993 :         if (GST_STATE_CHANGE_FAILURE != status)
     137              :         {
     138          986 :             if (GST_STATE_CHANGE_READY_TO_NULL == transition)
     139              :             {
     140          304 :                 sink->priv->m_delegate.reset();
     141              :             }
     142          986 :             GstStateChangeReturn result = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
     143          986 :             if (G_UNLIKELY(result == GST_STATE_CHANGE_FAILURE))
     144              :             {
     145            0 :                 GST_WARNING_OBJECT(sink, "State change failed");
     146            0 :                 return result;
     147              :             }
     148          986 :             else if (result == GST_STATE_CHANGE_ASYNC)
     149              :             {
     150            0 :                 return GST_STATE_CHANGE_ASYNC;
     151              :             }
     152              :         }
     153          993 :         return status;
     154          994 :     }
     155            1 :     return GST_STATE_CHANGE_FAILURE;
     156              : }
     157              : 
     158           60 : void rialto_mse_base_sink_handle_get_property(RialtoMSEBaseSink *sink, const IPlaybackDelegate::Property &property,
     159              :                                               GValue *value)
     160              : {
     161           60 :     if (auto delegate = rialto_mse_base_sink_get_delegate(sink))
     162              :     {
     163           60 :         delegate->getProperty(property, value);
     164              :     }
     165              :     else // Copy queued value if present
     166              :     {
     167            0 :         std::unique_lock lock{sink->priv->m_sinkMutex};
     168            0 :         if (sink->priv->m_queuedProperties.find(property) != sink->priv->m_queuedProperties.end())
     169              :         {
     170            0 :             g_value_copy(&sink->priv->m_queuedProperties[property], value);
     171              :         }
     172           60 :     }
     173              : }
     174              : 
     175          442 : void rialto_mse_base_sink_handle_set_property(RialtoMSEBaseSink *sink, const IPlaybackDelegate::Property &property,
     176              :                                               const GValue *value)
     177              : {
     178          442 :     if (auto delegate = rialto_mse_base_sink_get_delegate(sink))
     179              :     {
     180          440 :         delegate->setProperty(property, value);
     181              :     }
     182              :     else
     183              :     {
     184            2 :         std::unique_lock lock{sink->priv->m_sinkMutex};
     185            2 :         sink->priv->m_queuedProperties[property] = G_VALUE_INIT;
     186            2 :         g_value_init(&(sink->priv->m_queuedProperties[property]), G_VALUE_TYPE(value));
     187            2 :         g_value_copy(value, &(sink->priv->m_queuedProperties[property]));
     188          444 :     }
     189          442 : }
     190              : 
     191           11 : static void rialto_mse_base_sink_get_property(GObject *object, guint propId, GValue *value, GParamSpec *pspec)
     192              : {
     193           11 :     switch (propId)
     194              :     {
     195            1 :     case PROP_IS_SINGLE_PATH_STREAM:
     196              :         // Set default value if it can't be acquired
     197            1 :         g_value_set_boolean(value, FALSE);
     198            1 :         rialto_mse_base_sink_handle_get_property(RIALTO_MSE_BASE_SINK(object),
     199            1 :                                                  IPlaybackDelegate::Property::IsSinglePathStream, value);
     200            1 :         break;
     201            1 :     case PROP_N_STREAMS:
     202              :         // Set default value if it can't be acquired
     203            1 :         g_value_set_int(value, 1);
     204            1 :         rialto_mse_base_sink_handle_get_property(RIALTO_MSE_BASE_SINK(object),
     205            1 :                                                  IPlaybackDelegate::Property::NumberOfStreams, value);
     206            1 :         break;
     207            1 :     case PROP_HAS_DRM:
     208              :         // Set default value if it can't be acquired
     209            1 :         g_value_set_boolean(value, TRUE);
     210            1 :         rialto_mse_base_sink_handle_get_property(RIALTO_MSE_BASE_SINK(object), IPlaybackDelegate::Property::HasDrm,
     211              :                                                  value);
     212            1 :         break;
     213            2 :     case PROP_STATS:
     214            2 :         rialto_mse_base_sink_handle_get_property(RIALTO_MSE_BASE_SINK(object), IPlaybackDelegate::Property::Stats, value);
     215            2 :         break;
     216            2 :     case PROP_ENABLE_LAST_SAMPLE:
     217            2 :         rialto_mse_base_sink_handle_get_property(RIALTO_MSE_BASE_SINK(object),
     218            2 :                                                  IPlaybackDelegate::Property::EnableLastSample, value);
     219            2 :         break;
     220            4 :     case PROP_LAST_SAMPLE:
     221            4 :         rialto_mse_base_sink_handle_get_property(RIALTO_MSE_BASE_SINK(object), IPlaybackDelegate::Property::LastSample,
     222              :                                                  value);
     223            4 :         break;
     224            0 :     default:
     225            0 :         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
     226            0 :         break;
     227              :     }
     228           11 : }
     229              : 
     230          366 : static void rialto_mse_base_sink_set_property(GObject *object, guint propId, const GValue *value, GParamSpec *pspec)
     231              : {
     232          366 :     switch (propId)
     233              :     {
     234          182 :     case PROP_IS_SINGLE_PATH_STREAM:
     235          182 :         rialto_mse_base_sink_handle_set_property(RIALTO_MSE_BASE_SINK(object),
     236          182 :                                                  IPlaybackDelegate::Property::IsSinglePathStream, value);
     237          182 :         break;
     238          182 :     case PROP_N_STREAMS:
     239          182 :         rialto_mse_base_sink_handle_set_property(RIALTO_MSE_BASE_SINK(object),
     240          182 :                                                  IPlaybackDelegate::Property::NumberOfStreams, value);
     241          182 :         break;
     242            1 :     case PROP_HAS_DRM:
     243            1 :         rialto_mse_base_sink_handle_set_property(RIALTO_MSE_BASE_SINK(object), IPlaybackDelegate::Property::HasDrm,
     244              :                                                  value);
     245            1 :         break;
     246            1 :     case PROP_ENABLE_LAST_SAMPLE:
     247            1 :         rialto_mse_base_sink_handle_set_property(RIALTO_MSE_BASE_SINK(object),
     248            1 :                                                  IPlaybackDelegate::Property::EnableLastSample, value);
     249            1 :         break;
     250            0 :     default:
     251            0 :         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
     252            0 :         break;
     253              :     }
     254          366 : }
     255              : 
     256            1 : void rialto_mse_base_handle_rialto_server_sent_buffer_underflow(RialtoMSEBaseSink *sink)
     257              : {
     258            1 :     GST_WARNING_OBJECT(sink, "Sending underflow signal");
     259              :     // send 2 last parameters just to be compatible with RDK's buffer-underflow-callback signal signature
     260            1 :     g_signal_emit(G_OBJECT(sink), g_signals[SIGNAL_UNDERFLOW], 0, 0, nullptr);
     261              : }
     262              : 
     263            1 : void rialto_mse_base_handle_rialto_server_sent_first_video_frame_received(RialtoMSEBaseSink *sink)
     264              : {
     265            1 :     GST_INFO_OBJECT(sink, "Sending first frame received signal");
     266            1 :     g_signal_emit(G_OBJECT(sink), g_signals[SIGNAL_FIRST_VIDEO_FRAME_RECEIVED], 0, 0, nullptr);
     267              : }
     268              : 
     269          310 : bool rialto_mse_base_sink_initialise_sinkpad(RialtoMSEBaseSink *sink)
     270              : {
     271              :     GstPadTemplate *pad_template =
     272          310 :         gst_element_class_get_pad_template(GST_ELEMENT_CLASS(G_OBJECT_GET_CLASS(sink)), "sink");
     273          310 :     if (!pad_template)
     274              :     {
     275            0 :         GST_ERROR_OBJECT(sink, "Could not find sink pad template");
     276            0 :         return false;
     277              :     }
     278              : 
     279          310 :     GstPad *sinkPad = gst_pad_new_from_template(pad_template, "sink");
     280          310 :     if (!sinkPad)
     281              :     {
     282            0 :         GST_ERROR_OBJECT(sink, "Could not create sinkpad");
     283            0 :         return false;
     284              :     }
     285              : 
     286          310 :     gst_element_add_pad(GST_ELEMENT_CAST(sink), sinkPad);
     287          310 :     sink->priv->m_sinkPad = sinkPad;
     288              : 
     289          310 :     return true;
     290              : }
     291              : 
     292          310 : static void rialto_mse_base_sink_init(RialtoMSEBaseSink *sink)
     293              : {
     294          310 :     GST_INFO_OBJECT(sink, "Init: %" GST_PTR_FORMAT, sink);
     295          310 :     sink->priv = static_cast<RialtoMSEBaseSinkPrivate *>(rialto_mse_base_sink_get_instance_private(sink));
     296          310 :     new (sink->priv) RialtoMSEBaseSinkPrivate();
     297              : 
     298          310 :     GST_OBJECT_FLAG_SET(sink, GST_ELEMENT_FLAG_SINK);
     299              : }
     300              : 
     301          310 : static void rialto_mse_base_sink_finalize(GObject *object)
     302              : {
     303          310 :     RialtoMSEBaseSink *sink = RIALTO_MSE_BASE_SINK(object);
     304          310 :     RialtoMSEBaseSinkPrivate *priv = sink->priv;
     305          310 :     GST_INFO_OBJECT(sink, "Finalize: %" GST_PTR_FORMAT " %" GST_PTR_FORMAT, sink, priv);
     306              : 
     307          310 :     priv->~RialtoMSEBaseSinkPrivate();
     308          310 :     GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
     309              : }
     310              : 
     311            1 : static void rialto_mse_base_sink_class_init(RialtoMSEBaseSinkClass *klass)
     312              : {
     313              :     std::shared_ptr<firebolt::rialto::IClientLogHandler> logToGstHandler =
     314            1 :         std::make_shared<firebolt::rialto::LogToGstHandler>();
     315            1 :     if (!firebolt::rialto::IClientLogControlFactory::createFactory()->createClientLogControl().registerLogHandler(logToGstHandler,
     316              :                                                                                                                   true))
     317              :     {
     318            0 :         GST_ERROR("Unable to preRegister log handler");
     319              :     }
     320              : 
     321            1 :     GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
     322            1 :     GstElementClass *elementClass = GST_ELEMENT_CLASS(klass);
     323              : 
     324            1 :     gst_element_class_set_metadata(elementClass, "Rialto MSE base sink", "Generic", "A sink for Rialto", "Sky");
     325              : 
     326            1 :     gobjectClass->finalize = rialto_mse_base_sink_finalize;
     327            1 :     gobjectClass->get_property = rialto_mse_base_sink_get_property;
     328            1 :     gobjectClass->set_property = rialto_mse_base_sink_set_property;
     329            1 :     elementClass->query = rialto_mse_base_sink_query;
     330            1 :     elementClass->send_event = rialto_mse_base_sink_send_event;
     331            1 :     elementClass->change_state = rialto_mse_base_sink_change_state;
     332              : 
     333            1 :     g_signals[SIGNAL_UNDERFLOW] = g_signal_new("buffer-underflow-callback", G_TYPE_FROM_CLASS(klass),
     334              :                                                (GSignalFlags)(G_SIGNAL_RUN_LAST), 0, nullptr, nullptr,
     335              :                                                g_cclosure_marshal_VOID__UINT_POINTER, G_TYPE_NONE, 2, G_TYPE_UINT,
     336              :                                                G_TYPE_POINTER);
     337            1 :     g_signals[SIGNAL_FIRST_VIDEO_FRAME_RECEIVED] = g_signal_new("first-video-frame-callback", G_TYPE_FROM_CLASS(klass),
     338              :                                                                 (GSignalFlags)(G_SIGNAL_RUN_LAST), 0, nullptr, nullptr,
     339              :                                                                 g_cclosure_marshal_VOID__UINT_POINTER, G_TYPE_NONE, 2,
     340              :                                                                 G_TYPE_UINT, G_TYPE_POINTER);
     341            1 :     g_object_class_install_property(gobjectClass, PROP_IS_SINGLE_PATH_STREAM,
     342              :                                     g_param_spec_boolean("single-path-stream", "single path stream",
     343              :                                                          "is single path stream", FALSE, GParamFlags(G_PARAM_READWRITE)));
     344              : 
     345            1 :     g_object_class_install_property(gobjectClass, PROP_N_STREAMS,
     346              :                                     g_param_spec_int("streams-number", "streams number", "streams number", 1, G_MAXINT,
     347              :                                                      1, GParamFlags(G_PARAM_READWRITE)));
     348              : 
     349            1 :     g_object_class_install_property(gobjectClass, PROP_HAS_DRM,
     350              :                                     g_param_spec_boolean("has-drm", "has drm", "has drm", TRUE,
     351              :                                                          GParamFlags(G_PARAM_READWRITE)));
     352            1 :     g_object_class_install_property(gobjectClass, PROP_STATS,
     353              :                                     g_param_spec_pointer("stats", NULL, "pointer to a gst_structure",
     354              :                                                          GParamFlags(G_PARAM_READABLE)));
     355              : 
     356            1 :     g_object_class_install_property(gobjectClass, PROP_ENABLE_LAST_SAMPLE,
     357              :                                     g_param_spec_boolean("enable-last-sample", "Enable Last Buffer",
     358              :                                                          "Enable the last-sample property", FALSE,
     359              :                                                          GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
     360              : 
     361            1 :     g_object_class_install_property(gobjectClass, PROP_LAST_SAMPLE,
     362              :                                     g_param_spec_boxed("last-sample", "Last Sample",
     363              :                                                        "The last sample received in the sink", GST_TYPE_SAMPLE,
     364              :                                                        GParamFlags(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
     365              : }
        

Generated by: LCOV version 2.0-1