LCOV - code coverage report
Current view: top level - source - RialtoGStreamerMSEAudioSink.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 96.8 % 158 153
Test Date: 2025-10-17 10:59:19 Functions: 100.0 % 6 6

            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              : #include <mutex>
      19              : 
      20              : #include <gst/audio/audio.h>
      21              : #include <gst/gst.h>
      22              : 
      23              : #include "GStreamerMSEUtils.h"
      24              : #include "IMediaPipelineCapabilities.h"
      25              : #include "PullModeAudioPlaybackDelegate.h"
      26              : #include "PushModeAudioPlaybackDelegate.h"
      27              : #include "RialtoGStreamerMSEAudioSink.h"
      28              : #include "RialtoGStreamerMSEBaseSinkPrivate.h"
      29              : 
      30              : using namespace firebolt::rialto::client;
      31              : 
      32              : GST_DEBUG_CATEGORY_STATIC(RialtoMSEAudioSinkDebug);
      33              : #define GST_CAT_DEFAULT RialtoMSEAudioSinkDebug
      34              : 
      35              : #define rialto_mse_audio_sink_parent_class parent_class
      36            3 : G_DEFINE_TYPE_WITH_CODE(RialtoMSEAudioSink, rialto_mse_audio_sink, RIALTO_TYPE_MSE_BASE_SINK,
      37              :                         G_IMPLEMENT_INTERFACE(GST_TYPE_STREAM_VOLUME, NULL)
      38              :                             GST_DEBUG_CATEGORY_INIT(RialtoMSEAudioSinkDebug, "rialtomseaudiosink", 0,
      39              :                                                     "rialto mse audio sink"));
      40              : 
      41              : enum
      42              : {
      43              :     PROP_0,
      44              :     PROP_VOLUME,
      45              :     PROP_MUTE,
      46              :     PROP_GAP,
      47              :     PROP_LOW_LATENCY,
      48              :     PROP_SYNC,
      49              :     PROP_SYNC_OFF,
      50              :     PROP_STREAM_SYNC_MODE,
      51              :     PROP_AUDIO_FADE,
      52              :     PROP_FADE_VOLUME,
      53              :     PROP_LIMIT_BUFFERING_MS,
      54              :     PROP_USE_BUFFERING,
      55              :     PROP_ASYNC,
      56              :     PROP_WEBAUDIO,
      57              :     PROP_LAST
      58              : };
      59              : 
      60          710 : static GstStateChangeReturn rialto_mse_audio_sink_change_state(GstElement *element, GstStateChange transition)
      61              : {
      62          710 :     RialtoMSEBaseSink *sink = RIALTO_MSE_BASE_SINK(element);
      63          710 :     if (GST_STATE_CHANGE_NULL_TO_READY == transition)
      64              :     {
      65          210 :         if (PlaybackMode::Pull == sink->priv->m_playbackMode)
      66              :         {
      67          188 :             GST_INFO_OBJECT(sink, "RialtoMSEAudioSink state change to READY. Initializing Pull Mode delegate");
      68          188 :             rialto_mse_base_sink_initialise_delegate(sink, std::make_shared<PullModeAudioPlaybackDelegate>(element));
      69              :         }
      70              :         else // Push playback mode
      71              :         {
      72           22 :             GST_INFO_OBJECT(sink, "RialtoMSEAudioSink state change to READY. Initializing Push Mode delegate");
      73           22 :             rialto_mse_base_sink_initialise_delegate(sink, std::make_shared<PushModeAudioPlaybackDelegate>(element));
      74              :         }
      75              :     }
      76              : 
      77          710 :     GstStateChangeReturn result = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
      78          710 :     if (G_UNLIKELY(result == GST_STATE_CHANGE_FAILURE))
      79              :     {
      80            6 :         GST_WARNING_OBJECT(sink, "State change failed");
      81            6 :         return result;
      82              :     }
      83              : 
      84          704 :     return result;
      85              : }
      86              : 
      87           32 : static void rialto_mse_audio_sink_get_property(GObject *object, guint propId, GValue *value, GParamSpec *pspec)
      88              : {
      89           32 :     RialtoMSEBaseSink *sink = RIALTO_MSE_BASE_SINK(object);
      90           32 :     switch (propId)
      91              :     {
      92           10 :     case PROP_VOLUME:
      93              :     {
      94           10 :         g_value_set_double(value, kDefaultVolume);
      95           10 :         rialto_mse_base_sink_handle_get_property(sink, IPlaybackDelegate::Property::Volume, value);
      96           10 :         break;
      97              :     }
      98            3 :     case PROP_MUTE:
      99              :     {
     100            3 :         g_value_set_boolean(value, kDefaultMute);
     101            3 :         rialto_mse_base_sink_handle_get_property(sink, IPlaybackDelegate::Property::Mute, value);
     102            3 :         break;
     103              :     }
     104            4 :     case PROP_SYNC:
     105              :     {
     106            4 :         g_value_set_boolean(value, kDefaultSync);
     107            4 :         rialto_mse_base_sink_handle_get_property(sink, IPlaybackDelegate::Property::Sync, value);
     108            4 :         break;
     109              :     }
     110            4 :     case PROP_STREAM_SYNC_MODE:
     111              :     {
     112            4 :         g_value_set_int(value, kDefaultStreamSyncMode);
     113            4 :         rialto_mse_base_sink_handle_get_property(sink, IPlaybackDelegate::Property::StreamSyncMode, value);
     114            4 :         break;
     115              :     }
     116            2 :     case PROP_FADE_VOLUME:
     117              :     {
     118            2 :         g_value_set_uint(value, kDefaultFadeVolume);
     119            2 :         rialto_mse_base_sink_handle_get_property(sink, IPlaybackDelegate::Property::FadeVolume, value);
     120            2 :         break;
     121              :     }
     122            3 :     case PROP_LIMIT_BUFFERING_MS:
     123              :     {
     124            3 :         g_value_set_uint(value, kDefaultBufferingLimit);
     125            3 :         rialto_mse_base_sink_handle_get_property(sink, IPlaybackDelegate::Property::LimitBufferingMs, value);
     126            3 :         break;
     127              :     }
     128            3 :     case PROP_USE_BUFFERING:
     129              :     {
     130            3 :         g_value_set_boolean(value, kDefaultUseBuffering);
     131            3 :         rialto_mse_base_sink_handle_get_property(sink, IPlaybackDelegate::Property::UseBuffering, value);
     132            3 :         break;
     133              :     }
     134            1 :     case PROP_ASYNC:
     135              :     {
     136            1 :         g_value_set_boolean(value, TRUE);
     137            1 :         rialto_mse_base_sink_handle_get_property(sink, IPlaybackDelegate::Property::Async, value);
     138            1 :         break;
     139              :     }
     140            1 :     case PROP_WEBAUDIO:
     141              :     {
     142            1 :         g_value_set_boolean(value, (sink->priv->m_playbackMode == PlaybackMode::Push));
     143            1 :         break;
     144              :     }
     145            1 :     default:
     146              :     {
     147            1 :         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
     148            1 :         break;
     149              :     }
     150              :     }
     151           32 : }
     152              : 
     153           68 : static void rialto_mse_audio_sink_set_property(GObject *object, guint propId, const GValue *value, GParamSpec *pspec)
     154              : {
     155           68 :     RialtoMSEBaseSink *sink = RIALTO_MSE_BASE_SINK(object);
     156           68 :     switch (propId)
     157              :     {
     158            8 :     case PROP_VOLUME:
     159              :     {
     160            8 :         rialto_mse_base_sink_handle_set_property(sink, IPlaybackDelegate::Property::Volume, value);
     161            8 :         break;
     162              :     }
     163            3 :     case PROP_MUTE:
     164              :     {
     165            3 :         rialto_mse_base_sink_handle_set_property(sink, IPlaybackDelegate::Property::Mute, value);
     166            3 :         break;
     167              :     }
     168            2 :     case PROP_GAP:
     169              :     {
     170            2 :         rialto_mse_base_sink_handle_set_property(sink, IPlaybackDelegate::Property::Gap, value);
     171            2 :         break;
     172              :     }
     173            5 :     case PROP_LOW_LATENCY:
     174              :     {
     175            5 :         rialto_mse_base_sink_handle_set_property(sink, IPlaybackDelegate::Property::LowLatency, value);
     176            5 :         break;
     177              :     }
     178            5 :     case PROP_SYNC:
     179              :     {
     180            5 :         rialto_mse_base_sink_handle_set_property(sink, IPlaybackDelegate::Property::Sync, value);
     181            5 :         break;
     182              :     }
     183            5 :     case PROP_SYNC_OFF:
     184              :     {
     185            5 :         rialto_mse_base_sink_handle_set_property(sink, IPlaybackDelegate::Property::SyncOff, value);
     186            5 :         break;
     187              :     }
     188            5 :     case PROP_STREAM_SYNC_MODE:
     189              :     {
     190            5 :         rialto_mse_base_sink_handle_set_property(sink, IPlaybackDelegate::Property::StreamSyncMode, value);
     191            5 :         break;
     192              :     }
     193            4 :     case PROP_AUDIO_FADE:
     194              :     {
     195            4 :         rialto_mse_base_sink_handle_set_property(sink, IPlaybackDelegate::Property::AudioFade, value);
     196            4 :         break;
     197              :     }
     198            3 :     case PROP_LIMIT_BUFFERING_MS:
     199              :     {
     200            3 :         rialto_mse_base_sink_handle_set_property(sink, IPlaybackDelegate::Property::LimitBufferingMs, value);
     201            3 :         break;
     202              :     }
     203            3 :     case PROP_USE_BUFFERING:
     204              :     {
     205            3 :         rialto_mse_base_sink_handle_set_property(sink, IPlaybackDelegate::Property::UseBuffering, value);
     206            3 :         break;
     207              :     }
     208            1 :     case PROP_ASYNC:
     209              :     {
     210            1 :         rialto_mse_base_sink_handle_set_property(sink, IPlaybackDelegate::Property::Async, value);
     211            1 :         break;
     212              :     }
     213           23 :     case PROP_WEBAUDIO:
     214              :     {
     215           23 :         if (GST_STATE(sink) > GST_STATE_NULL)
     216              :         {
     217            1 :             GST_ERROR_OBJECT(object, "Playback mode set too late - sink is not in NULL state");
     218            1 :             break;
     219              :         }
     220           22 :         if (TRUE == g_value_get_boolean(value))
     221              :         {
     222           22 :             sink->priv->m_playbackMode = PlaybackMode::Push;
     223              :         }
     224              :         else
     225              :         {
     226            0 :             sink->priv->m_playbackMode = PlaybackMode::Pull;
     227              :         }
     228           22 :         break;
     229              :     }
     230            1 :     default:
     231              :     {
     232            1 :         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
     233            1 :         break;
     234              :     }
     235              :     }
     236           68 : }
     237              : 
     238          215 : static void rialto_mse_audio_sink_init(RialtoMSEAudioSink *sink)
     239              : {
     240          215 :     RialtoMSEBaseSinkPrivate *priv = sink->parent.priv;
     241              : 
     242          215 :     if (!rialto_mse_base_sink_initialise_sinkpad(RIALTO_MSE_BASE_SINK(sink)))
     243              :     {
     244            0 :         GST_ERROR_OBJECT(sink, "Failed to initialise AUDIO sink. Sink pad initialisation failed.");
     245            0 :         return;
     246              :     }
     247              : 
     248          215 :     gst_pad_set_chain_function(priv->m_sinkPad, rialto_mse_base_sink_chain);
     249          215 :     gst_pad_set_event_function(priv->m_sinkPad, rialto_mse_base_sink_event);
     250              : }
     251              : 
     252            1 : static void rialto_mse_audio_sink_class_init(RialtoMSEAudioSinkClass *klass)
     253              : {
     254            1 :     GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
     255            1 :     GstElementClass *elementClass = GST_ELEMENT_CLASS(klass);
     256            1 :     gobjectClass->get_property = rialto_mse_audio_sink_get_property;
     257            1 :     gobjectClass->set_property = rialto_mse_audio_sink_set_property;
     258            1 :     elementClass->change_state = rialto_mse_audio_sink_change_state;
     259              : 
     260            1 :     g_object_class_install_property(gobjectClass, PROP_VOLUME,
     261              :                                     g_param_spec_double("volume", "Volume", "Volume of this stream", 0, 1.0,
     262              :                                                         kDefaultVolume,
     263              :                                                         GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
     264              : 
     265            1 :     g_object_class_install_property(gobjectClass, PROP_MUTE,
     266              :                                     g_param_spec_boolean("mute", "Mute", "Mute status of this stream", kDefaultMute,
     267              :                                                          GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
     268              : 
     269            1 :     g_object_class_install_property(gobjectClass, PROP_GAP,
     270              :                                     g_param_spec_boxed("gap", "Gap", "Audio Gap", GST_TYPE_STRUCTURE,
     271              :                                                        (GParamFlags)(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)));
     272              : 
     273            1 :     g_object_class_install_property(gobjectClass, PROP_USE_BUFFERING,
     274              :                                     g_param_spec_boolean("use-buffering",
     275              :                                                          "Use buffering", "Emit GST_MESSAGE_BUFFERING based on low-/high-percent thresholds",
     276              :                                                          kDefaultUseBuffering, G_PARAM_READWRITE));
     277            1 :     g_object_class_install_property(gobjectClass, PROP_ASYNC,
     278              :                                     g_param_spec_boolean("async", "Async", "Asynchronous mode", FALSE, G_PARAM_READWRITE));
     279            1 :     g_object_class_install_property(gobjectClass, PROP_WEBAUDIO,
     280              :                                     g_param_spec_boolean("web-audio",
     281              :                                                          "Webaudio mode", "Enable webaudio mode. Property should be set before NULL->READY transition",
     282              :                                                          FALSE, G_PARAM_READWRITE));
     283              : 
     284              :     std::unique_ptr<firebolt::rialto::IMediaPipelineCapabilities> mediaPlayerCapabilities =
     285            1 :         firebolt::rialto::IMediaPipelineCapabilitiesFactory::createFactory()->createMediaPipelineCapabilities();
     286            1 :     if (mediaPlayerCapabilities)
     287              :     {
     288              :         std::vector<std::string> supportedMimeTypes =
     289            1 :             mediaPlayerCapabilities->getSupportedMimeTypes(firebolt::rialto::MediaSourceType::AUDIO);
     290              : 
     291            1 :         rialto_mse_sink_setup_supported_caps(elementClass, supportedMimeTypes);
     292              : 
     293            2 :         const std::string kLowLatencyPropertyName{"low-latency"};
     294            2 :         const std::string kSyncPropertyName{"sync"};
     295            2 :         const std::string kSyncOffPropertyName{"sync-off"};
     296            2 :         const std::string kStreamSyncModePropertyName{"stream-sync-mode"};
     297            2 :         const std::string kAudioFadePropertyName{"audio-fade"};
     298            2 :         const std::string kFadeVolumePropertyName{"fade-volume"};
     299            1 :         const std::string kBufferingLimitPropertyName{"limit-buffering-ms"};
     300              :         const std::vector<std::string> kPropertyNamesToSearch{kLowLatencyPropertyName,     kSyncPropertyName,
     301              :                                                               kSyncOffPropertyName,        kStreamSyncModePropertyName,
     302              :                                                               kBufferingLimitPropertyName, kAudioFadePropertyName,
     303            9 :                                                               kFadeVolumePropertyName};
     304              :         std::vector<std::string> supportedProperties{
     305            1 :             mediaPlayerCapabilities->getSupportedProperties(firebolt::rialto::MediaSourceType::AUDIO,
     306            1 :                                                             kPropertyNamesToSearch)};
     307              : 
     308            8 :         for (auto it = supportedProperties.begin(); it != supportedProperties.end(); ++it)
     309              :         {
     310            7 :             if (kLowLatencyPropertyName == *it)
     311              :             {
     312            1 :                 g_object_class_install_property(gobjectClass, PROP_LOW_LATENCY,
     313              :                                                 g_param_spec_boolean(kLowLatencyPropertyName.c_str(),
     314              :                                                                      "low latency", "Turn on low latency mode, for use with gaming (no audio decoding, no a/v sync)",
     315              :                                                                      kDefaultLowLatency, GParamFlags(G_PARAM_WRITABLE)));
     316              :             }
     317            6 :             else if (kSyncPropertyName == *it)
     318              :             {
     319            1 :                 g_object_class_install_property(gobjectClass, PROP_SYNC,
     320              :                                                 g_param_spec_boolean(kSyncPropertyName.c_str(), "sync", "Clock sync",
     321              :                                                                      kDefaultSync, GParamFlags(G_PARAM_READWRITE)));
     322              :             }
     323            5 :             else if (kSyncOffPropertyName == *it)
     324              :             {
     325            1 :                 g_object_class_install_property(gobjectClass, PROP_SYNC_OFF,
     326              :                                                 g_param_spec_boolean(kSyncOffPropertyName.c_str(),
     327              :                                                                      "sync off", "Turn on free running audio. Must be set before pipeline is PLAYING state.",
     328              :                                                                      kDefaultSyncOff, GParamFlags(G_PARAM_WRITABLE)));
     329              :             }
     330            4 :             else if (kStreamSyncModePropertyName == *it)
     331              :             {
     332            1 :                 g_object_class_install_property(gobjectClass, PROP_STREAM_SYNC_MODE,
     333              :                                                 g_param_spec_int(kStreamSyncModePropertyName.c_str(),
     334              :                                                                  "stream sync mode", "1 - Frame to decode frame will immediately proceed next frame sync, 0 - Frame decoded with no frame sync",
     335              :                                                                  0, G_MAXINT, kDefaultStreamSyncMode,
     336              :                                                                  GParamFlags(G_PARAM_READWRITE)));
     337              :             }
     338            3 :             else if (kAudioFadePropertyName == *it)
     339              :             {
     340            1 :                 g_object_class_install_property(gobjectClass, PROP_AUDIO_FADE,
     341              :                                                 g_param_spec_string(kAudioFadePropertyName.c_str(),
     342              :                                                                     "audio fade", "Start audio fade (vol[0-100],duration ms,easetype[(L)inear,Cubic(I)n,Cubic(O)ut])",
     343              :                                                                     kDefaultAudioFade, GParamFlags(G_PARAM_WRITABLE)));
     344              :             }
     345            2 :             else if (kFadeVolumePropertyName == *it)
     346              :             {
     347            1 :                 g_object_class_install_property(gobjectClass, PROP_FADE_VOLUME,
     348              :                                                 g_param_spec_uint(kFadeVolumePropertyName.c_str(), "fade volume",
     349              :                                                                   "Get current fade volume", 0, 100, kDefaultFadeVolume,
     350              :                                                                   G_PARAM_READABLE));
     351              :             }
     352            1 :             else if (kBufferingLimitPropertyName == *it)
     353              :             {
     354            1 :                 constexpr uint32_t kMaxValue{20000};
     355            1 :                 g_object_class_install_property(gobjectClass, PROP_LIMIT_BUFFERING_MS,
     356              :                                                 g_param_spec_uint("limit-buffering-ms",
     357              :                                                                   "limit buffering ms", "Set millisecond threshold used if limit_buffering is set. Changing this value does not enable/disable limit_buffering",
     358              :                                                                   0, kMaxValue, kDefaultBufferingLimit,
     359              :                                                                   G_PARAM_READWRITE));
     360              :             }
     361              :             else
     362              :             {
     363            0 :                 GST_ERROR("Unexpected property %s returned from rialto", it->c_str());
     364              :             }
     365              :         }
     366            1 :     }
     367              :     else
     368              :     {
     369            0 :         GST_ERROR("Failed to get supported mime types for AUDIO");
     370              :     }
     371              : 
     372            1 :     gst_element_class_set_details_simple(elementClass, "Rialto Audio Sink", "Decoder/Audio/Sink/Audio",
     373              :                                          "Communicates with Rialto Server", "Sky");
     374            2 : }
        

Generated by: LCOV version 2.0-1