LCOV - code coverage report
Current view: top level - source - RialtoGStreamerWebAudioSink.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 82.8 % 128 106
Test Date: 2025-08-11 12:19:57 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          242 : 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           26 : void rialto_web_audio_sink_initialise_delegate(RialtoWebAudioSink *sink,
      44              :                                                const std::shared_ptr<IPlaybackDelegate> &delegate)
      45              : {
      46           26 :     std::unique_lock lock{sink->priv->m_sinkMutex};
      47           26 :     sink->priv->m_delegate = delegate;
      48              : 
      49           26 :     for (auto &[type, value] : sink->priv->m_queuedProperties)
      50              :     {
      51            0 :         delegate->setProperty(type, &value);
      52            0 :         g_value_unset(&value);
      53              :     }
      54           26 :     sink->priv->m_queuedProperties.clear();
      55              : }
      56              : 
      57          161 : static std::shared_ptr<IPlaybackDelegate> rialto_web_audio_sink_get_delegate(RialtoWebAudioSink *sink)
      58              : {
      59          161 :     std::unique_lock lock{sink->priv->m_sinkMutex};
      60          161 :     if (!sink->priv->m_delegate)
      61              :     {
      62            0 :         GST_ERROR_OBJECT(sink, "Sink delegate not initialized");
      63              :     }
      64          322 :     return sink->priv->m_delegate;
      65          161 : }
      66              : 
      67            7 : static void rialto_web_audio_sink_handle_get_property(RialtoWebAudioSink *sink,
      68              :                                                       const IPlaybackDelegate::Property &property, GValue *value)
      69              : {
      70            7 :     if (auto delegate = rialto_web_audio_sink_get_delegate(sink))
      71              :     {
      72            7 :         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            7 :     }
      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           17 : static gboolean rialto_web_audio_sink_send_event(GstElement *element, GstEvent *event)
     101              : {
     102           17 :     RialtoWebAudioSink *sink = RIALTO_WEB_AUDIO_SINK(element);
     103           17 :     if (auto delegate = rialto_web_audio_sink_get_delegate(sink))
     104              :     {
     105           17 :         delegate->handleSendEvent(event);
     106              :     }
     107           17 :     GstElement *parent = GST_ELEMENT(&sink->parent);
     108           17 :     return GST_ELEMENT_CLASS(parent_class)->send_event(parent, event);
     109              : }
     110              : 
     111          109 : static GstStateChangeReturn rialto_web_audio_sink_change_state(GstElement *element, GstStateChange transition)
     112              : {
     113          109 :     RialtoWebAudioSink *sink = RIALTO_WEB_AUDIO_SINK(element);
     114          109 :     if (GST_STATE_CHANGE_NULL_TO_READY == transition)
     115              :     {
     116           26 :         GST_INFO_OBJECT(sink, "RialtoWebAudioSink state change to READY. Initializing delegate");
     117           26 :         rialto_web_audio_sink_initialise_delegate(sink, std::make_shared<PushModeAudioPlaybackDelegate>(element));
     118              :     }
     119          109 :     if (auto delegate = rialto_web_audio_sink_get_delegate(sink))
     120              :     {
     121          109 :         GstStateChangeReturn status = delegate->changeState(transition);
     122          109 :         if (GST_STATE_CHANGE_FAILURE != status)
     123              :         {
     124          106 :             GstStateChangeReturn result = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
     125          106 :             if (G_UNLIKELY(result == GST_STATE_CHANGE_FAILURE))
     126              :             {
     127            0 :                 GST_WARNING_OBJECT(sink, "State change failed");
     128            0 :                 return result;
     129              :             }
     130          106 :             else if (result == GST_STATE_CHANGE_ASYNC)
     131              :             {
     132            0 :                 return GST_STATE_CHANGE_ASYNC;
     133              :             }
     134              :         }
     135          109 :         return status;
     136              :     }
     137            0 :     return GST_STATE_CHANGE_FAILURE;
     138              : }
     139              : 
     140           21 : static gboolean rialto_web_audio_sink_event(GstPad *pad, GstObject *parent, GstEvent *event)
     141              : {
     142           21 :     if (auto delegate = rialto_web_audio_sink_get_delegate(RIALTO_WEB_AUDIO_SINK(parent)))
     143              :     {
     144           21 :         return delegate->handleEvent(pad, parent, event);
     145              :     }
     146            0 :     return FALSE;
     147              : }
     148              : 
     149            7 : static void rialto_web_audio_sink_get_property(GObject *object, guint propId, GValue *value, GParamSpec *pspec)
     150              : {
     151            7 :     switch (propId)
     152              :     {
     153            1 :     case PROP_TS_OFFSET:
     154              :     {
     155            1 :         rialto_web_audio_sink_handle_get_property(RIALTO_WEB_AUDIO_SINK(object), IPlaybackDelegate::Property::TsOffset,
     156              :                                                   value);
     157              :     }
     158              : 
     159            6 :     case PROP_VOLUME:
     160              :     {
     161            6 :         g_value_set_double(value, kDefaultVolume);
     162            6 :         rialto_web_audio_sink_handle_get_property(RIALTO_WEB_AUDIO_SINK(object), IPlaybackDelegate::Property::Volume,
     163              :                                                   value);
     164            6 :         break;
     165              :     }
     166              : 
     167            1 :     default:
     168              :     {
     169            1 :         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
     170            1 :         break;
     171              :     }
     172              :     }
     173            7 : }
     174              : 
     175            7 : static void rialto_web_audio_sink_set_property(GObject *object, guint propId, const GValue *value, GParamSpec *pspec)
     176              : {
     177            7 :     switch (propId)
     178              :     {
     179            1 :     case PROP_TS_OFFSET:
     180              :     {
     181            1 :         rialto_web_audio_sink_handle_set_property(RIALTO_WEB_AUDIO_SINK(object), IPlaybackDelegate::Property::TsOffset,
     182              :                                                   value);
     183            1 :         break;
     184              :     }
     185              : 
     186            5 :     case PROP_VOLUME:
     187              :     {
     188            5 :         rialto_web_audio_sink_handle_set_property(RIALTO_WEB_AUDIO_SINK(object), IPlaybackDelegate::Property::Volume,
     189              :                                                   value);
     190            5 :         break;
     191              :     }
     192              : 
     193            1 :     default:
     194              :     {
     195            1 :         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
     196            1 :         break;
     197              :     }
     198              :     }
     199            7 : }
     200              : 
     201            1 : static GstFlowReturn rialto_web_audio_sink_chain(GstPad *pad, GstObject *parent, GstBuffer *buf)
     202              : {
     203            1 :     if (auto delegate = rialto_web_audio_sink_get_delegate(RIALTO_WEB_AUDIO_SINK(parent)))
     204              :     {
     205            1 :         return delegate->handleBuffer(buf);
     206              :     }
     207            0 :     return GST_FLOW_ERROR;
     208              : }
     209              : 
     210            1 : static void rialto_web_audio_sink_setup_supported_caps(GstElementClass *elementClass)
     211              : {
     212            1 :     GstCaps *caps = gst_caps_from_string("audio/x-raw");
     213            1 :     GstPadTemplate *sinktempl = gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps);
     214            1 :     gst_element_class_add_pad_template(elementClass, sinktempl);
     215            1 :     gst_caps_unref(caps);
     216              : }
     217              : 
     218           26 : static bool rialto_web_audio_sink_initialise_sinkpad(RialtoWebAudioSink *sink)
     219              : {
     220              :     GstPadTemplate *pad_template =
     221           26 :         gst_element_class_get_pad_template(GST_ELEMENT_CLASS(G_OBJECT_GET_CLASS(sink)), "sink");
     222           26 :     if (!pad_template)
     223              :     {
     224            0 :         GST_ERROR_OBJECT(sink, "Could not find sink pad template");
     225            0 :         return false;
     226              :     }
     227              : 
     228           26 :     GstPad *sinkPad = gst_pad_new_from_template(pad_template, "sink");
     229           26 :     if (!sinkPad)
     230              :     {
     231            0 :         GST_ERROR_OBJECT(sink, "Could not create sinkpad");
     232            0 :         return false;
     233              :     }
     234              : 
     235           26 :     gst_element_add_pad(GST_ELEMENT_CAST(sink), sinkPad);
     236              : 
     237           26 :     gst_pad_set_event_function(sinkPad, rialto_web_audio_sink_event);
     238           26 :     gst_pad_set_chain_function(sinkPad, rialto_web_audio_sink_chain);
     239              : 
     240           26 :     return true;
     241              : }
     242              : 
     243           26 : static void rialto_web_audio_sink_init(RialtoWebAudioSink *sink)
     244              : {
     245           26 :     GST_INFO_OBJECT(sink, "Init: %" GST_PTR_FORMAT, sink);
     246           26 :     sink->priv = static_cast<RialtoWebAudioSinkPrivate *>(rialto_web_audio_sink_get_instance_private(sink));
     247           26 :     new (sink->priv) RialtoWebAudioSinkPrivate();
     248              : 
     249           26 :     GST_OBJECT_FLAG_SET(sink, GST_ELEMENT_FLAG_SINK);
     250           26 :     if (!rialto_web_audio_sink_initialise_sinkpad(sink))
     251              :     {
     252            0 :         GST_ERROR_OBJECT(sink, "Failed to initialise AUDIO sink. Sink pad initialisation failed.");
     253            0 :         return;
     254              :     }
     255              : }
     256              : 
     257           26 : static void rialto_web_audio_sink_finalize(GObject *object)
     258              : {
     259           26 :     RialtoWebAudioSink *sink = RIALTO_WEB_AUDIO_SINK(object);
     260           26 :     RialtoWebAudioSinkPrivate *priv = sink->priv;
     261           26 :     GST_INFO_OBJECT(sink, "Finalize: %" GST_PTR_FORMAT " %" GST_PTR_FORMAT, sink, priv);
     262              : 
     263           26 :     priv->~RialtoWebAudioSinkPrivate();
     264              : 
     265           26 :     GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
     266              : }
     267              : 
     268            1 : static void rialto_web_audio_sink_class_init(RialtoWebAudioSinkClass *klass)
     269              : {
     270            1 :     GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
     271            1 :     GstElementClass *elementClass = GST_ELEMENT_CLASS(klass);
     272              : 
     273            1 :     gst_element_class_set_metadata(elementClass, "Rialto Web Audio sink", "Generic", "A sink for Rialto Web Audio",
     274              :                                    "Sky");
     275              : 
     276            1 :     gobjectClass->finalize = rialto_web_audio_sink_finalize;
     277            1 :     gobjectClass->get_property = rialto_web_audio_sink_get_property;
     278            1 :     gobjectClass->set_property = rialto_web_audio_sink_set_property;
     279              : 
     280            1 :     elementClass->change_state = rialto_web_audio_sink_change_state;
     281            1 :     elementClass->send_event = rialto_web_audio_sink_send_event;
     282              : 
     283            1 :     g_object_class_install_property(gobjectClass, PROP_TS_OFFSET,
     284              :                                     g_param_spec_int64("ts-offset",
     285              :                                                        "ts-offset", "Not supported, RialtoWebAudioSink does not require the synchronisation of sources",
     286              :                                                        G_MININT64, G_MAXINT64, 0,
     287              :                                                        GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
     288              : 
     289            1 :     g_object_class_install_property(gobjectClass, PROP_VOLUME,
     290              :                                     g_param_spec_double("volume", "Volume", "Volume of this stream", 0, 1.0,
     291              :                                                         kDefaultVolume,
     292              :                                                         GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
     293              : 
     294            1 :     rialto_web_audio_sink_setup_supported_caps(elementClass);
     295              : 
     296            1 :     gst_element_class_set_details_simple(elementClass, "Rialto Web Audio Sink", "Decoder/Audio/Sink/Audio",
     297              :                                          "Communicates with Rialto Server", "Sky");
     298              : }
        

Generated by: LCOV version 2.0-1