LCOV - code coverage report
Current view: top level - source - RialtoGStreamerWebAudioSink.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 80.2 % 131 105
Test Date: 2026-07-31 11:15:42 Functions: 100.0 % 16 16

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2023 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              : #include <gst/gst.h>
      20              : 
      21              : #include "Constants.h"
      22              : #include "PushModeAudioPlaybackDelegate.h"
      23              : #include "RialtoGStreamerWebAudioSink.h"
      24              : 
      25              : using namespace firebolt::rialto::client;
      26              : 
      27              : GST_DEBUG_CATEGORY_STATIC(RialtoWebAudioSinkDebug);
      28              : #define GST_CAT_DEFAULT RialtoWebAudioSinkDebug
      29              : 
      30              : #define rialto_web_audio_sink_parent_class parent_class
      31          232 : G_DEFINE_TYPE_WITH_CODE(RialtoWebAudioSink, rialto_web_audio_sink, GST_TYPE_ELEMENT,
      32              :                         G_ADD_PRIVATE(RialtoWebAudioSink)
      33              :                             GST_DEBUG_CATEGORY_INIT(RialtoWebAudioSinkDebug, "rialtowebaudiosink", 0,
      34              :                                                     "rialto web audio sink"));
      35              : enum
      36              : {
      37              :     PROP_0,
      38              :     PROP_TS_OFFSET,
      39              :     PROP_VOLUME,
      40              :     PROP_LAST
      41              : };
      42              : 
      43           25 : void rialto_web_audio_sink_initialise_delegate(RialtoWebAudioSink *sink,
      44              :                                                const std::shared_ptr<IPlaybackDelegate> &delegate)
      45              : {
      46           25 :     std::unique_lock lock{sink->priv->m_sinkMutex};
      47           25 :     sink->priv->m_delegate = delegate;
      48              : 
      49           25 :     for (auto &[type, value] : sink->priv->m_queuedProperties)
      50              :     {
      51            0 :         delegate->setProperty(type, &value);
      52            0 :         g_value_unset(&value);
      53              :     }
      54           25 :     sink->priv->m_queuedProperties.clear();
      55              : }
      56              : 
      57          154 : static std::shared_ptr<IPlaybackDelegate> rialto_web_audio_sink_get_delegate(RialtoWebAudioSink *sink)
      58              : {
      59          154 :     std::unique_lock lock{sink->priv->m_sinkMutex};
      60          154 :     if (!sink->priv->m_delegate)
      61              :     {
      62            0 :         GST_ERROR_OBJECT(sink, "Sink delegate not initialized");
      63              :     }
      64          308 :     return sink->priv->m_delegate;
      65          154 : }
      66              : 
      67            6 : static void rialto_web_audio_sink_handle_get_property(RialtoWebAudioSink *sink,
      68              :                                                       const IPlaybackDelegate::Property &property, GValue *value)
      69              : {
      70            6 :     if (auto delegate = rialto_web_audio_sink_get_delegate(sink))
      71              :     {
      72            6 :         delegate->getProperty(property, value);
      73              :     }
      74              :     else // Copy queued value if present
      75              :     {
      76            0 :         std::unique_lock lock{sink->priv->m_sinkMutex};
      77            0 :         if (sink->priv->m_queuedProperties.find(property) != sink->priv->m_queuedProperties.end())
      78              :         {
      79            0 :             g_value_copy(&sink->priv->m_queuedProperties[property], value);
      80              :         }
      81            6 :     }
      82              : }
      83              : 
      84            6 : static void rialto_web_audio_sink_handle_set_property(RialtoWebAudioSink *sink,
      85              :                                                       const IPlaybackDelegate::Property &property, const GValue *value)
      86              : {
      87            6 :     if (auto delegate = rialto_web_audio_sink_get_delegate(sink))
      88              :     {
      89            6 :         delegate->setProperty(property, value);
      90              :     }
      91              :     else
      92              :     {
      93            0 :         std::unique_lock lock{sink->priv->m_sinkMutex};
      94            0 :         sink->priv->m_queuedProperties[property] = G_VALUE_INIT;
      95            0 :         g_value_init(&(sink->priv->m_queuedProperties[property]), G_VALUE_TYPE(value));
      96            0 :         g_value_copy(value, &(sink->priv->m_queuedProperties[property]));
      97            6 :     }
      98              : }
      99              : 
     100           16 : static gboolean rialto_web_audio_sink_send_event(GstElement *element, GstEvent *event)
     101              : {
     102           16 :     RialtoWebAudioSink *sink = RIALTO_WEB_AUDIO_SINK(element);
     103           16 :     if (auto delegate = rialto_web_audio_sink_get_delegate(sink))
     104              :     {
     105           16 :         return delegate->handleSendEvent(event);
     106              :     }
     107            0 :     gst_event_unref(event);
     108            0 :     return FALSE;
     109              : }
     110              : 
     111          105 : static GstStateChangeReturn rialto_web_audio_sink_change_state(GstElement *element, GstStateChange transition)
     112              : {
     113          105 :     RialtoWebAudioSink *sink = RIALTO_WEB_AUDIO_SINK(element);
     114          105 :     if (GST_STATE_CHANGE_NULL_TO_READY == transition)
     115              :     {
     116           25 :         GST_INFO_OBJECT(sink, "RialtoWebAudioSink state change to READY. Initializing delegate");
     117           25 :         rialto_web_audio_sink_initialise_delegate(sink, std::make_shared<PushModeAudioPlaybackDelegate>(element));
     118              :     }
     119          105 :     if (auto delegate = rialto_web_audio_sink_get_delegate(sink))
     120              :     {
     121          105 :         GstStateChangeReturn status = delegate->changeState(transition);
     122          105 :         if (GST_STATE_CHANGE_FAILURE != status)
     123              :         {
     124          102 :             GstStateChangeReturn result = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
     125          102 :             if (G_UNLIKELY(result == GST_STATE_CHANGE_FAILURE))
     126              :             {
     127            0 :                 GST_WARNING_OBJECT(sink, "State change failed");
     128            0 :                 return result;
     129              :             }
     130          102 :             else if (result == GST_STATE_CHANGE_ASYNC)
     131              :             {
     132            0 :                 return GST_STATE_CHANGE_ASYNC;
     133              :             }
     134              :         }
     135          105 :         return status;
     136              :     }
     137            0 :     return GST_STATE_CHANGE_FAILURE;
     138              : }
     139              : 
     140           20 : static gboolean rialto_web_audio_sink_event(GstPad *pad, GstObject *parent, GstEvent *event)
     141              : {
     142           20 :     if (auto delegate = rialto_web_audio_sink_get_delegate(RIALTO_WEB_AUDIO_SINK(parent)))
     143              :     {
     144           20 :         return delegate->handleEvent(pad, parent, event);
     145              :     }
     146            0 :     gst_event_unref(event);
     147            0 :     return FALSE;
     148              : }
     149              : 
     150            7 : static void rialto_web_audio_sink_get_property(GObject *object, guint propId, GValue *value, GParamSpec *pspec)
     151              : {
     152            7 :     switch (propId)
     153              :     {
     154            1 :     case PROP_TS_OFFSET:
     155              :     {
     156            1 :         rialto_web_audio_sink_handle_get_property(RIALTO_WEB_AUDIO_SINK(object), IPlaybackDelegate::Property::TsOffset,
     157              :                                                   value);
     158            1 :         break;
     159              :     }
     160              : 
     161            5 :     case PROP_VOLUME:
     162              :     {
     163            5 :         g_value_set_double(value, kDefaultVolume);
     164            5 :         rialto_web_audio_sink_handle_get_property(RIALTO_WEB_AUDIO_SINK(object), IPlaybackDelegate::Property::Volume,
     165              :                                                   value);
     166            5 :         break;
     167              :     }
     168              : 
     169            1 :     default:
     170              :     {
     171            1 :         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
     172            1 :         break;
     173              :     }
     174              :     }
     175            7 : }
     176              : 
     177            7 : static void rialto_web_audio_sink_set_property(GObject *object, guint propId, const GValue *value, GParamSpec *pspec)
     178              : {
     179            7 :     switch (propId)
     180              :     {
     181            1 :     case PROP_TS_OFFSET:
     182              :     {
     183            1 :         rialto_web_audio_sink_handle_set_property(RIALTO_WEB_AUDIO_SINK(object), IPlaybackDelegate::Property::TsOffset,
     184              :                                                   value);
     185            1 :         break;
     186              :     }
     187              : 
     188            5 :     case PROP_VOLUME:
     189              :     {
     190            5 :         rialto_web_audio_sink_handle_set_property(RIALTO_WEB_AUDIO_SINK(object), IPlaybackDelegate::Property::Volume,
     191              :                                                   value);
     192            5 :         break;
     193              :     }
     194              : 
     195            1 :     default:
     196              :     {
     197            1 :         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
     198            1 :         break;
     199              :     }
     200              :     }
     201            7 : }
     202              : 
     203            1 : static GstFlowReturn rialto_web_audio_sink_chain(GstPad *pad, GstObject *parent, GstBuffer *buf)
     204              : {
     205            1 :     if (auto delegate = rialto_web_audio_sink_get_delegate(RIALTO_WEB_AUDIO_SINK(parent)))
     206              :     {
     207            1 :         return delegate->handleBuffer(buf);
     208              :     }
     209            0 :     gst_buffer_unref(buf);
     210            0 :     return GST_FLOW_ERROR;
     211              : }
     212              : 
     213            1 : static void rialto_web_audio_sink_setup_supported_caps(GstElementClass *elementClass)
     214              : {
     215            1 :     GstCaps *caps = gst_caps_from_string("audio/x-raw");
     216            1 :     GstPadTemplate *sinktempl = gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps);
     217            1 :     gst_element_class_add_pad_template(elementClass, sinktempl);
     218            1 :     gst_caps_unref(caps);
     219              : }
     220              : 
     221           25 : static bool rialto_web_audio_sink_initialise_sinkpad(RialtoWebAudioSink *sink)
     222              : {
     223              :     GstPadTemplate *pad_template =
     224           25 :         gst_element_class_get_pad_template(GST_ELEMENT_CLASS(G_OBJECT_GET_CLASS(sink)), "sink");
     225           25 :     if (!pad_template)
     226              :     {
     227            0 :         GST_ERROR_OBJECT(sink, "Could not find sink pad template");
     228            0 :         return false;
     229              :     }
     230              : 
     231           25 :     GstPad *sinkPad = gst_pad_new_from_template(pad_template, "sink");
     232           25 :     if (!sinkPad)
     233              :     {
     234            0 :         GST_ERROR_OBJECT(sink, "Could not create sinkpad");
     235            0 :         return false;
     236              :     }
     237              : 
     238           25 :     gst_element_add_pad(GST_ELEMENT_CAST(sink), sinkPad);
     239              : 
     240           25 :     gst_pad_set_event_function(sinkPad, rialto_web_audio_sink_event);
     241           25 :     gst_pad_set_chain_function(sinkPad, rialto_web_audio_sink_chain);
     242              : 
     243           25 :     return true;
     244              : }
     245              : 
     246           25 : static void rialto_web_audio_sink_init(RialtoWebAudioSink *sink)
     247              : {
     248           25 :     GST_INFO_OBJECT(sink, "Init: %" GST_PTR_FORMAT, sink);
     249           25 :     sink->priv = static_cast<RialtoWebAudioSinkPrivate *>(rialto_web_audio_sink_get_instance_private(sink));
     250           25 :     new (sink->priv) RialtoWebAudioSinkPrivate();
     251              : 
     252           25 :     GST_OBJECT_FLAG_SET(sink, GST_ELEMENT_FLAG_SINK);
     253           25 :     if (!rialto_web_audio_sink_initialise_sinkpad(sink))
     254              :     {
     255            0 :         GST_ERROR_OBJECT(sink, "Failed to initialise AUDIO sink. Sink pad initialisation failed.");
     256            0 :         return;
     257              :     }
     258              : }
     259              : 
     260           25 : static void rialto_web_audio_sink_finalize(GObject *object)
     261              : {
     262           25 :     RialtoWebAudioSink *sink = RIALTO_WEB_AUDIO_SINK(object);
     263           25 :     RialtoWebAudioSinkPrivate *priv = sink->priv;
     264           25 :     GST_INFO_OBJECT(sink, "Finalize: %" GST_PTR_FORMAT " %" GST_PTR_FORMAT, sink, priv);
     265              : 
     266           25 :     priv->~RialtoWebAudioSinkPrivate();
     267              : 
     268           25 :     GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
     269              : }
     270              : 
     271            1 : static void rialto_web_audio_sink_class_init(RialtoWebAudioSinkClass *klass)
     272              : {
     273            1 :     GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
     274            1 :     GstElementClass *elementClass = GST_ELEMENT_CLASS(klass);
     275              : 
     276            1 :     gst_element_class_set_metadata(elementClass, "Rialto Web Audio sink", "Generic", "A sink for Rialto Web Audio",
     277              :                                    "Sky");
     278              : 
     279            1 :     gobjectClass->finalize = rialto_web_audio_sink_finalize;
     280            1 :     gobjectClass->get_property = rialto_web_audio_sink_get_property;
     281            1 :     gobjectClass->set_property = rialto_web_audio_sink_set_property;
     282              : 
     283            1 :     elementClass->change_state = rialto_web_audio_sink_change_state;
     284            1 :     elementClass->send_event = rialto_web_audio_sink_send_event;
     285              : 
     286            1 :     g_object_class_install_property(gobjectClass, PROP_TS_OFFSET,
     287              :                                     g_param_spec_int64("ts-offset",
     288              :                                                        "ts-offset", "Not supported, RialtoWebAudioSink does not require the synchronisation of sources",
     289              :                                                        G_MININT64, G_MAXINT64, 0,
     290              :                                                        GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
     291              : 
     292            1 :     g_object_class_install_property(gobjectClass, PROP_VOLUME,
     293              :                                     g_param_spec_double("volume", "Volume", "Volume of this stream", 0, 1.0,
     294              :                                                         kDefaultVolume,
     295              :                                                         GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
     296              : 
     297            1 :     rialto_web_audio_sink_setup_supported_caps(elementClass);
     298              : 
     299            1 :     gst_element_class_set_details_simple(elementClass, "Rialto Web Audio Sink", "Decoder/Audio/Sink/Audio",
     300              :                                          "Communicates with Rialto Server", "Sky");
     301              : }
        

Generated by: LCOV version 2.0-1