LCOV - code coverage report
Current view: top level - source - PullModeVideoPlaybackDelegate.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 95.7 % 211 202
Test Date: 2026-05-12 09:11:14 Functions: 100.0 % 7 7

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2025 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 "PullModeVideoPlaybackDelegate.h"
      20              : #include "GStreamerEMEUtils.h"
      21              : #include "GStreamerMSEUtils.h"
      22              : #include "GstreamerCatLog.h"
      23              : 
      24              : #define GST_CAT_DEFAULT rialtoGStreamerCat
      25              : 
      26          118 : PullModeVideoPlaybackDelegate::PullModeVideoPlaybackDelegate(GstElement *sink) : PullModePlaybackDelegate(sink)
      27              : {
      28           59 :     m_mediaSourceType = firebolt::rialto::MediaSourceType::VIDEO;
      29           59 :     m_isAsync = true;
      30              : }
      31              : 
      32          178 : GstStateChangeReturn PullModeVideoPlaybackDelegate::changeState(GstStateChange transition)
      33              : {
      34          178 :     switch (transition)
      35              :     {
      36           30 :     case GST_STATE_CHANGE_READY_TO_PAUSED:
      37              :     {
      38           30 :         if (!attachToMediaClientAndSetStreamsNumber(m_maxWidth, m_maxHeight))
      39              :         {
      40            1 :             return GST_STATE_CHANGE_FAILURE;
      41              :         }
      42              : 
      43           29 :         std::shared_ptr<GStreamerMSEMediaPlayerClient> client = m_mediaPlayerManager.getMediaPlayerClient();
      44           29 :         if (!client)
      45              :         {
      46            0 :             GST_ERROR_OBJECT(m_sink, "MediaPlayerClient is nullptr");
      47            0 :             return GST_STATE_CHANGE_FAILURE;
      48              :         }
      49              : 
      50           29 :         std::unique_lock lock{m_propertyMutex};
      51           29 :         if (m_rectangleSettingQueued)
      52              :         {
      53            1 :             GST_DEBUG_OBJECT(m_sink, "Set queued video rectangle");
      54            1 :             m_rectangleSettingQueued = false;
      55            1 :             client->setVideoRectangle(m_videoRectangle);
      56              :         }
      57           29 :         break;
      58           58 :     }
      59          148 :     default:
      60          148 :         break;
      61              :     }
      62          177 :     return PullModePlaybackDelegate::changeState(transition);
      63              : }
      64              : 
      65           31 : gboolean PullModeVideoPlaybackDelegate::handleEvent(GstPad *pad, GstObject *parent, GstEvent *event)
      66              : {
      67           31 :     switch (GST_EVENT_TYPE(event))
      68              :     {
      69           30 :     case GST_EVENT_CAPS:
      70              :     {
      71           30 :         GstCaps *caps{nullptr};
      72           30 :         gst_event_parse_caps(event, &caps);
      73           30 :         if (m_sourceAttached)
      74              :         {
      75            1 :             GST_INFO_OBJECT(m_sink, "Source already attached. Skip calling attachSource");
      76            1 :             break;
      77              :         }
      78              : 
      79           29 :         GST_INFO_OBJECT(m_sink, "Attaching VIDEO source with caps %" GST_PTR_FORMAT, caps);
      80              : 
      81           29 :         std::unique_ptr<firebolt::rialto::IMediaPipeline::MediaSource> vsource = createMediaSource(caps);
      82           29 :         if (vsource)
      83              :         {
      84           29 :             std::shared_ptr<GStreamerMSEMediaPlayerClient> client = m_mediaPlayerManager.getMediaPlayerClient();
      85           29 :             if ((!client) || (!client->attachSource(vsource, RIALTO_MSE_BASE_SINK(m_sink), shared_from_this())))
      86              :             {
      87            1 :                 GST_ERROR_OBJECT(m_sink, "Failed to attach VIDEO source");
      88              :             }
      89              :             else
      90              :             {
      91           28 :                 m_sourceAttached = true;
      92              : 
      93              :                 // check if READY -> PAUSED was requested before source was attached
      94           28 :                 if (GST_STATE_NEXT(m_sink) == GST_STATE_PAUSED)
      95              :                 {
      96           28 :                     client->pause(m_sourceId);
      97              :                 }
      98           28 :                 std::unique_lock lock{m_propertyMutex};
      99           28 :                 if (m_immediateOutputQueued)
     100              :                 {
     101            1 :                     GST_DEBUG_OBJECT(m_sink, "Set queued immediate-output");
     102            1 :                     m_immediateOutputQueued = false;
     103            1 :                     if (!client->setImmediateOutput(m_sourceId, m_immediateOutput))
     104              :                     {
     105            0 :                         GST_ERROR_OBJECT(m_sink, "Could not set immediate-output");
     106              :                     }
     107              :                 }
     108           28 :                 if (m_syncmodeStreamingQueued)
     109              :                 {
     110            2 :                     GST_DEBUG_OBJECT(m_sink, "Set queued syncmode-streaming");
     111            2 :                     m_syncmodeStreamingQueued = false;
     112            2 :                     if (!client->setStreamSyncMode(m_sourceId, m_syncmodeStreaming))
     113              :                     {
     114            1 :                         GST_ERROR_OBJECT(m_sink, "Could not set syncmode-streaming");
     115              :                     }
     116              :                 }
     117           28 :                 if (m_videoMuteQueued)
     118              :                 {
     119            1 :                     GST_DEBUG_OBJECT(m_sink, "Set queued show-video-window");
     120            1 :                     m_videoMuteQueued = false;
     121            1 :                     client->setMute(m_videoMute, m_sourceId);
     122              :                 }
     123           28 :             }
     124           29 :         }
     125              :         else
     126              :         {
     127            0 :             GST_ERROR_OBJECT(m_sink, "Failed to create VIDEO source");
     128              :         }
     129              : 
     130           29 :         break;
     131              :     }
     132            1 :     default:
     133            1 :         break;
     134              :     }
     135           31 :     return PullModePlaybackDelegate::handleEvent(pad, parent, event);
     136              : }
     137              : 
     138           15 : void PullModeVideoPlaybackDelegate::getProperty(const Property &type, GValue *value)
     139              : {
     140           15 :     switch (type)
     141              :     {
     142            5 :     case Property::WindowSet:
     143              :     {
     144            5 :         std::unique_lock lock{m_propertyMutex};
     145            5 :         auto client = m_mediaPlayerManager.getMediaPlayerClient();
     146            5 :         if (!client)
     147              :         {
     148              :             // Return the default value and
     149              :             // queue a setting event (for the default value) so that it will become true when
     150              :             // the client connects...
     151            2 :             GST_DEBUG_OBJECT(m_sink, "Return default rectangle setting, and queue an event to set the default upon "
     152              :                                      "client connect");
     153            2 :             m_rectangleSettingQueued = true;
     154            2 :             g_value_set_string(value, m_videoRectangle.c_str());
     155              :         }
     156              :         else
     157              :         {
     158            3 :             lock.unlock();
     159            3 :             g_value_set_string(value, client->getVideoRectangle().c_str());
     160              :         }
     161            5 :         break;
     162              :     }
     163            2 :     case Property::MaxVideoWidth:
     164              :     {
     165            2 :         g_value_set_uint(value, m_maxWidth);
     166            2 :         break;
     167              :     }
     168            2 :     case Property::MaxVideoHeight:
     169              :     {
     170            2 :         g_value_set_uint(value, m_maxHeight);
     171            2 :         break;
     172              :     }
     173            1 :     case Property::FrameStepOnPreroll:
     174              :     {
     175            1 :         g_value_set_boolean(value, m_stepOnPrerollEnabled);
     176            1 :         break;
     177              :     }
     178            3 :     case Property::ImmediateOutput:
     179              :     {
     180            3 :         std::unique_lock lock{m_propertyMutex};
     181            3 :         auto client = m_mediaPlayerManager.getMediaPlayerClient();
     182            3 :         if (!client)
     183              :         {
     184              :             // Return the default value and
     185              :             // queue a setting event (for the default value) so that it will become true when
     186              :             // the client connects...
     187            1 :             GST_DEBUG_OBJECT(m_sink, "Return default immediate-output setting, and queue an event to set the default "
     188              :                                      "upon client connect");
     189            1 :             m_immediateOutputQueued = true;
     190            1 :             g_value_set_boolean(value, m_immediateOutput);
     191              :         }
     192              :         else
     193              :         {
     194            2 :             bool immediateOutput{m_immediateOutput};
     195            2 :             lock.unlock();
     196            2 :             if (!client->getImmediateOutput(m_sourceId, immediateOutput))
     197              :             {
     198            1 :                 GST_ERROR_OBJECT(m_sink, "Could not get immediate-output");
     199              :             }
     200            2 :             g_value_set_boolean(value, immediateOutput);
     201              :         }
     202            3 :         break;
     203              :     }
     204            2 :     case Property::VideoPts:
     205              :     {
     206            2 :         int64_t videoPts = 0;
     207              : 
     208            2 :         std::unique_lock lock{m_propertyMutex};
     209            2 :         auto client = m_mediaPlayerManager.getMediaPlayerClient();
     210            2 :         if (!client)
     211              :         {
     212            1 :             GST_DEBUG_OBJECT(m_sink, "Getting video PTS: no client, returning 0");
     213              :         }
     214              :         else
     215              :         {
     216            1 :             lock.unlock();
     217            1 :             gint64 position = client->getPosition(m_sourceId);
     218            1 :             videoPts = ((position / 100000) * 9LL); // 90Khz PTS
     219              :         }
     220              : 
     221            2 :         g_value_set_int64(value, videoPts);
     222            2 :         break;
     223              :     }
     224            0 :     default:
     225              :     {
     226            0 :         PullModePlaybackDelegate::getProperty(type, value);
     227            0 :         break;
     228              :     }
     229              :     }
     230           15 : }
     231              : 
     232           83 : void PullModeVideoPlaybackDelegate::setProperty(const Property &type, const GValue *value)
     233              : {
     234           83 :     std::shared_ptr<GStreamerMSEMediaPlayerClient> client = m_mediaPlayerManager.getMediaPlayerClient();
     235           83 :     switch (type)
     236              :     {
     237            4 :     case Property::WindowSet:
     238              :     {
     239            4 :         const gchar *rectangle = g_value_get_string(value);
     240            4 :         if (!rectangle)
     241              :         {
     242            1 :             GST_WARNING_OBJECT(m_sink, "Rectangle string not valid");
     243            1 :             break;
     244              :         }
     245            6 :         std::string videoRectangle{rectangle};
     246            3 :         std::unique_lock lock{m_propertyMutex};
     247            3 :         m_videoRectangle = videoRectangle;
     248            3 :         if (!client)
     249              :         {
     250            2 :             GST_DEBUG_OBJECT(m_sink, "Rectangle setting enqueued");
     251            2 :             m_rectangleSettingQueued = true;
     252              :         }
     253              :         else
     254              :         {
     255            1 :             lock.unlock();
     256            1 :             client->setVideoRectangle(videoRectangle);
     257              :         }
     258            3 :         break;
     259              :     }
     260            2 :     case Property::MaxVideoWidth:
     261            2 :         m_maxWidth = g_value_get_uint(value);
     262            2 :         break;
     263            2 :     case Property::MaxVideoHeight:
     264            2 :         m_maxHeight = g_value_get_uint(value);
     265            2 :         break;
     266            5 :     case Property::FrameStepOnPreroll:
     267              :     {
     268            5 :         bool stepOnPrerollEnabled = g_value_get_boolean(value);
     269            5 :         if (client && stepOnPrerollEnabled && !m_stepOnPrerollEnabled)
     270              :         {
     271            2 :             GST_INFO_OBJECT(m_sink, "Frame stepping on preroll");
     272            2 :             client->renderFrame(m_sourceId);
     273              :         }
     274            5 :         m_stepOnPrerollEnabled = stepOnPrerollEnabled;
     275            5 :         break;
     276              :     }
     277            4 :     case Property::ImmediateOutput:
     278              :     {
     279            4 :         bool immediateOutput = (g_value_get_boolean(value) != FALSE);
     280            4 :         std::unique_lock lock{m_propertyMutex};
     281            4 :         m_immediateOutput = immediateOutput;
     282            4 :         if (!client)
     283              :         {
     284            2 :             GST_DEBUG_OBJECT(m_sink, "Immediate output setting enqueued");
     285            2 :             m_immediateOutputQueued = true;
     286              :         }
     287              :         else
     288              :         {
     289            2 :             lock.unlock();
     290            2 :             if (!client->setImmediateOutput(m_sourceId, immediateOutput))
     291              :             {
     292            1 :                 GST_ERROR_OBJECT(m_sink, "Could not set immediate-output");
     293              :             }
     294              :         }
     295            4 :         break;
     296              :     }
     297            4 :     case Property::SyncmodeStreaming:
     298              :     {
     299            4 :         bool syncmodeStreaming = (g_value_get_boolean(value) != FALSE);
     300            4 :         std::unique_lock lock{m_propertyMutex};
     301            4 :         m_syncmodeStreaming = syncmodeStreaming;
     302            4 :         if (!client)
     303              :         {
     304            2 :             GST_DEBUG_OBJECT(m_sink, "Syncmode streaming setting enqueued");
     305            2 :             m_syncmodeStreamingQueued = true;
     306              :         }
     307              :         else
     308              :         {
     309            2 :             lock.unlock();
     310            2 :             if (!client->setStreamSyncMode(m_sourceId, syncmodeStreaming))
     311              :             {
     312            1 :                 GST_ERROR_OBJECT(m_sink, "Could not set syncmode-streaming");
     313              :             }
     314              :         }
     315            4 :         break;
     316              :     }
     317            2 :     case Property::ShowVideoWindow:
     318              :     {
     319            2 :         bool videoMute = (g_value_get_boolean(value) == FALSE);
     320            2 :         std::unique_lock lock{m_propertyMutex};
     321            2 :         m_videoMute = videoMute;
     322            2 :         if (!client || !m_sourceAttached)
     323              :         {
     324            1 :             GST_DEBUG_OBJECT(m_sink, "Show video window setting enqueued");
     325            1 :             m_videoMuteQueued = true;
     326              :         }
     327              :         else
     328              :         {
     329            1 :             lock.unlock();
     330            1 :             client->setMute(videoMute, m_sourceId);
     331              :         }
     332            2 :         break;
     333              :     }
     334           60 :     default:
     335              :     {
     336           60 :         PullModePlaybackDelegate::setProperty(type, value);
     337           60 :         break;
     338              :     }
     339              :     }
     340           83 : }
     341              : 
     342            1 : void PullModeVideoPlaybackDelegate::handleQos(uint64_t processed, uint64_t dropped) const
     343              : {
     344            1 :     GstBus *bus = gst_element_get_bus(m_sink);
     345              :     /* Hardcode isLive to FALSE and set invalid timestamps */
     346            1 :     GstMessage *message = gst_message_new_qos(GST_OBJECT(m_sink), FALSE, GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE,
     347              :                                               GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE);
     348              : 
     349            1 :     gst_message_set_qos_stats(message, GST_FORMAT_BUFFERS, processed, dropped);
     350            1 :     gst_bus_post(bus, message);
     351            1 :     gst_object_unref(bus);
     352              : }
     353              : 
     354              : std::unique_ptr<firebolt::rialto::IMediaPipeline::MediaSource>
     355           29 : PullModeVideoPlaybackDelegate::createMediaSource(GstCaps *caps) const
     356              : {
     357           29 :     GstStructure *structure = gst_caps_get_structure(caps, 0);
     358           29 :     const gchar *strct_name = gst_structure_get_name(structure);
     359              : 
     360           29 :     firebolt::rialto::SegmentAlignment alignment = get_segment_alignment(structure);
     361           29 :     std::shared_ptr<firebolt::rialto::CodecData> codecData = get_codec_data(structure);
     362           29 :     firebolt::rialto::StreamFormat format = get_stream_format(structure);
     363              : 
     364           29 :     gint width{0};
     365           29 :     gint height{0};
     366           29 :     gst_structure_get_int(structure, "width", &width);
     367           29 :     gst_structure_get_int(structure, "height", &height);
     368              : 
     369           29 :     if (strct_name)
     370              :     {
     371           29 :         std::string mimeType{};
     372           29 :         if (g_str_has_prefix(strct_name, "video/x-h264"))
     373              :         {
     374           24 :             mimeType = "video/h264";
     375              :         }
     376            5 :         else if (g_str_has_prefix(strct_name, "video/x-h265"))
     377              :         {
     378            4 :             mimeType = "video/h265";
     379              : 
     380            4 :             uint32_t dolbyVisionProfile{0};
     381            4 :             if (get_dv_profile(structure, dolbyVisionProfile))
     382              :             {
     383            1 :                 return std::make_unique<firebolt::rialto::IMediaPipeline::MediaSourceVideoDolbyVision>(mimeType,
     384              :                                                                                                        dolbyVisionProfile,
     385            1 :                                                                                                        m_hasDrm, width,
     386              :                                                                                                        height, alignment,
     387            1 :                                                                                                        format, codecData);
     388              :             }
     389              :         }
     390              :         else
     391              :         {
     392            1 :             mimeType = strct_name;
     393              :         }
     394              : 
     395           28 :         GST_INFO_OBJECT(m_sink, "%s video media source created", mimeType.c_str());
     396           56 :         return std::make_unique<firebolt::rialto::IMediaPipeline::MediaSourceVideo>(mimeType, m_hasDrm, width, height,
     397           28 :                                                                                     alignment, format, codecData);
     398           29 :     }
     399              :     else
     400              :     {
     401            0 :         GST_ERROR_OBJECT(m_sink,
     402              :                          "Empty caps' structure name! Failed to set mime type when constructing video media source");
     403              :     }
     404              : 
     405            0 :     return nullptr;
     406           29 : }
        

Generated by: LCOV version 2.0-1