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

Generated by: LCOV version 2.0-1