LCOV - code coverage report
Current view: top level - source - PullModeVideoPlaybackDelegate.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 95.5 % 200 191
Test Date: 2025-08-04 11:40:41 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          112 : PullModeVideoPlaybackDelegate::PullModeVideoPlaybackDelegate(GstElement *sink) : PullModePlaybackDelegate(sink)
      27              : {
      28           56 :     m_mediaSourceType = firebolt::rialto::MediaSourceType::VIDEO;
      29           56 :     m_isAsync = true;
      30              : }
      31              : 
      32          170 : GstStateChangeReturn PullModeVideoPlaybackDelegate::changeState(GstStateChange transition)
      33              : {
      34          170 :     switch (transition)
      35              :     {
      36           29 :     case GST_STATE_CHANGE_READY_TO_PAUSED:
      37              :     {
      38           29 :         if (!attachToMediaClientAndSetStreamsNumber(m_maxWidth, m_maxHeight))
      39              :         {
      40            1 :             return GST_STATE_CHANGE_FAILURE;
      41              :         }
      42              : 
      43           28 :         std::shared_ptr<GStreamerMSEMediaPlayerClient> client = m_mediaPlayerManager.getMediaPlayerClient();
      44           28 :         if (!client)
      45              :         {
      46            0 :             GST_ERROR_OBJECT(m_sink, "MediaPlayerClient is nullptr");
      47            0 :             return GST_STATE_CHANGE_FAILURE;
      48              :         }
      49              : 
      50           28 :         std::unique_lock lock{m_propertyMutex};
      51           28 :         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           28 :         break;
      58           56 :     }
      59          141 :     default:
      60          141 :         break;
      61              :     }
      62          169 :     return PullModePlaybackDelegate::changeState(transition);
      63              : }
      64              : 
      65           30 : gboolean PullModeVideoPlaybackDelegate::handleEvent(GstEvent *event)
      66              : {
      67           30 :     switch (GST_EVENT_TYPE(event))
      68              :     {
      69           29 :     case GST_EVENT_CAPS:
      70              :     {
      71           29 :         GstCaps *caps{nullptr};
      72           29 :         gst_event_parse_caps(event, &caps);
      73           29 :         if (m_sourceAttached)
      74              :         {
      75            1 :             GST_INFO_OBJECT(m_sink, "Source already attached. Skip calling attachSource");
      76            1 :             break;
      77              :         }
      78              : 
      79           28 :         GST_INFO_OBJECT(m_sink, "Attaching VIDEO source with caps %" GST_PTR_FORMAT, caps);
      80              : 
      81           28 :         std::unique_ptr<firebolt::rialto::IMediaPipeline::MediaSource> vsource = createMediaSource(caps);
      82           28 :         if (vsource)
      83              :         {
      84           28 :             std::shared_ptr<GStreamerMSEMediaPlayerClient> client = m_mediaPlayerManager.getMediaPlayerClient();
      85           28 :             if ((!client) || (!client->attachSource(vsource, RIALTO_MSE_BASE_SINK(m_sink))))
      86              :             {
      87            1 :                 GST_ERROR_OBJECT(m_sink, "Failed to attach VIDEO source");
      88              :             }
      89              :             else
      90              :             {
      91           27 :                 m_sourceAttached = true;
      92              : 
      93              :                 // check if READY -> PAUSED was requested before source was attached
      94           27 :                 if (GST_STATE_NEXT(m_sink) == GST_STATE_PAUSED)
      95              :                 {
      96           27 :                     client->pause(m_sourceId);
      97              :                 }
      98           27 :                 std::unique_lock lock{m_propertyMutex};
      99           27 :                 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           27 :                 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           27 :                 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           27 :             }
     124           28 :         }
     125              :         else
     126              :         {
     127            0 :             GST_ERROR_OBJECT(m_sink, "Failed to create VIDEO source");
     128              :         }
     129              : 
     130           28 :         break;
     131              :     }
     132            1 :     default:
     133            1 :         break;
     134              :     }
     135           30 :     return PullModePlaybackDelegate::handleEvent(event);
     136              : }
     137              : 
     138           13 : void PullModeVideoPlaybackDelegate::getProperty(const Property &type, GValue *value)
     139              : {
     140           13 :     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            0 :     default:
     205              :     {
     206            0 :         PullModePlaybackDelegate::getProperty(type, value);
     207            0 :         break;
     208              :     }
     209              :     }
     210           13 : }
     211              : 
     212           81 : void PullModeVideoPlaybackDelegate::setProperty(const Property &type, const GValue *value)
     213              : {
     214           81 :     std::shared_ptr<GStreamerMSEMediaPlayerClient> client = m_mediaPlayerManager.getMediaPlayerClient();
     215           81 :     switch (type)
     216              :     {
     217            4 :     case Property::WindowSet:
     218              :     {
     219            4 :         const gchar *rectangle = g_value_get_string(value);
     220            4 :         if (!rectangle)
     221              :         {
     222            1 :             GST_WARNING_OBJECT(m_sink, "Rectangle string not valid");
     223            1 :             break;
     224              :         }
     225            6 :         std::string videoRectangle{rectangle};
     226            3 :         std::unique_lock lock{m_propertyMutex};
     227            3 :         m_videoRectangle = videoRectangle;
     228            3 :         if (!client)
     229              :         {
     230            2 :             GST_DEBUG_OBJECT(m_sink, "Rectangle setting enqueued");
     231            2 :             m_rectangleSettingQueued = true;
     232              :         }
     233              :         else
     234              :         {
     235            1 :             lock.unlock();
     236            1 :             client->setVideoRectangle(videoRectangle);
     237              :         }
     238            3 :         break;
     239              :     }
     240            2 :     case Property::MaxVideoWidth:
     241            2 :         m_maxWidth = g_value_get_uint(value);
     242            2 :         break;
     243            2 :     case Property::MaxVideoHeight:
     244            2 :         m_maxHeight = g_value_get_uint(value);
     245            2 :         break;
     246            5 :     case Property::FrameStepOnPreroll:
     247              :     {
     248            5 :         bool stepOnPrerollEnabled = g_value_get_boolean(value);
     249            5 :         if (client && stepOnPrerollEnabled && !m_stepOnPrerollEnabled)
     250              :         {
     251            2 :             GST_INFO_OBJECT(m_sink, "Frame stepping on preroll");
     252            2 :             client->renderFrame(RIALTO_MSE_BASE_SINK(m_sink));
     253              :         }
     254            5 :         m_stepOnPrerollEnabled = stepOnPrerollEnabled;
     255            5 :         break;
     256              :     }
     257            4 :     case Property::ImmediateOutput:
     258              :     {
     259            4 :         bool immediateOutput = (g_value_get_boolean(value) != FALSE);
     260            4 :         std::unique_lock lock{m_propertyMutex};
     261            4 :         m_immediateOutput = immediateOutput;
     262            4 :         if (!client)
     263              :         {
     264            2 :             GST_DEBUG_OBJECT(m_sink, "Immediate output setting enqueued");
     265            2 :             m_immediateOutputQueued = true;
     266              :         }
     267              :         else
     268              :         {
     269            2 :             lock.unlock();
     270            2 :             if (!client->setImmediateOutput(m_sourceId, immediateOutput))
     271              :             {
     272            1 :                 GST_ERROR_OBJECT(m_sink, "Could not set immediate-output");
     273              :             }
     274              :         }
     275            4 :         break;
     276              :     }
     277            4 :     case Property::SyncmodeStreaming:
     278              :     {
     279            4 :         bool syncmodeStreaming = (g_value_get_boolean(value) != FALSE);
     280            4 :         std::unique_lock lock{m_propertyMutex};
     281            4 :         m_syncmodeStreaming = syncmodeStreaming;
     282            4 :         if (!client)
     283              :         {
     284            2 :             GST_DEBUG_OBJECT(m_sink, "Syncmode streaming setting enqueued");
     285            2 :             m_syncmodeStreamingQueued = true;
     286              :         }
     287              :         else
     288              :         {
     289            2 :             lock.unlock();
     290            2 :             if (!client->setStreamSyncMode(m_sourceId, syncmodeStreaming))
     291              :             {
     292            1 :                 GST_ERROR_OBJECT(m_sink, "Could not set syncmode-streaming");
     293              :             }
     294              :         }
     295            4 :         break;
     296              :     }
     297            2 :     case Property::ShowVideoWindow:
     298              :     {
     299            2 :         bool videoMute = (g_value_get_boolean(value) == FALSE);
     300            2 :         std::unique_lock lock{m_propertyMutex};
     301            2 :         m_videoMute = videoMute;
     302            2 :         if (!client || !m_sourceAttached)
     303              :         {
     304            1 :             GST_DEBUG_OBJECT(m_sink, "Show video window setting enqueued");
     305            1 :             m_videoMuteQueued = true;
     306              :         }
     307              :         else
     308              :         {
     309            1 :             lock.unlock();
     310            1 :             client->setMute(videoMute, m_sourceId);
     311              :         }
     312            2 :         break;
     313              :     }
     314           58 :     default:
     315              :     {
     316           58 :         PullModePlaybackDelegate::setProperty(type, value);
     317           58 :         break;
     318              :     }
     319              :     }
     320           81 : }
     321              : 
     322            1 : void PullModeVideoPlaybackDelegate::handleQos(uint64_t processed, uint64_t dropped) const
     323              : {
     324            1 :     GstBus *bus = gst_element_get_bus(m_sink);
     325              :     /* Hardcode isLive to FALSE and set invalid timestamps */
     326            1 :     GstMessage *message = gst_message_new_qos(GST_OBJECT(m_sink), FALSE, GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE,
     327              :                                               GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE);
     328              : 
     329            1 :     gst_message_set_qos_stats(message, GST_FORMAT_BUFFERS, processed, dropped);
     330            1 :     gst_bus_post(bus, message);
     331            1 :     gst_object_unref(bus);
     332              : }
     333              : 
     334              : std::unique_ptr<firebolt::rialto::IMediaPipeline::MediaSource>
     335           28 : PullModeVideoPlaybackDelegate::createMediaSource(GstCaps *caps) const
     336              : {
     337           28 :     GstStructure *structure = gst_caps_get_structure(caps, 0);
     338           28 :     const gchar *strct_name = gst_structure_get_name(structure);
     339              : 
     340           28 :     firebolt::rialto::SegmentAlignment alignment = get_segment_alignment(structure);
     341           28 :     std::shared_ptr<firebolt::rialto::CodecData> codecData = get_codec_data(structure);
     342           28 :     firebolt::rialto::StreamFormat format = get_stream_format(structure);
     343              : 
     344           28 :     gint width{0};
     345           28 :     gint height{0};
     346           28 :     gst_structure_get_int(structure, "width", &width);
     347           28 :     gst_structure_get_int(structure, "height", &height);
     348              : 
     349           28 :     if (strct_name)
     350              :     {
     351           28 :         std::string mimeType{};
     352           28 :         if (g_str_has_prefix(strct_name, "video/x-h264"))
     353              :         {
     354           23 :             mimeType = "video/h264";
     355              :         }
     356            5 :         else if (g_str_has_prefix(strct_name, "video/x-h265"))
     357              :         {
     358            4 :             mimeType = "video/h265";
     359              : 
     360            4 :             uint32_t dolbyVisionProfile{0};
     361            4 :             if (get_dv_profile(structure, dolbyVisionProfile))
     362              :             {
     363            1 :                 return std::make_unique<firebolt::rialto::IMediaPipeline::MediaSourceVideoDolbyVision>(mimeType,
     364              :                                                                                                        dolbyVisionProfile,
     365            1 :                                                                                                        m_hasDrm, width,
     366              :                                                                                                        height, alignment,
     367            1 :                                                                                                        format, codecData);
     368              :             }
     369              :         }
     370              :         else
     371              :         {
     372            1 :             mimeType = strct_name;
     373              :         }
     374              : 
     375           27 :         GST_INFO_OBJECT(m_sink, "%s video media source created", mimeType.c_str());
     376           54 :         return std::make_unique<firebolt::rialto::IMediaPipeline::MediaSourceVideo>(mimeType, m_hasDrm, width, height,
     377           27 :                                                                                     alignment, format, codecData);
     378           28 :     }
     379              :     else
     380              :     {
     381            0 :         GST_ERROR_OBJECT(m_sink,
     382              :                          "Empty caps' structure name! Failed to set mime type when constructing video media source");
     383              :     }
     384              : 
     385            0 :     return nullptr;
     386           28 : }
        

Generated by: LCOV version 2.0-1