LCOV - code coverage report
Current view: top level - source - PullModeVideoPlaybackDelegate.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 96.1 % 229 220
Test Date: 2026-06-10 12:08:30 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          120 : PullModeVideoPlaybackDelegate::PullModeVideoPlaybackDelegate(GstElement *sink) : PullModePlaybackDelegate(sink)
      27              : {
      28           60 :     m_mediaSourceType = firebolt::rialto::MediaSourceType::VIDEO;
      29           60 :     m_isAsync = true;
      30              : }
      31              : 
      32          180 : GstStateChangeReturn PullModeVideoPlaybackDelegate::changeState(GstStateChange transition)
      33              : {
      34          180 :     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          150 :     default:
      60          150 :         break;
      61              :     }
      62          179 :     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           57 :             if ((!client) || (!client->attachSource(vsource, RIALTO_MSE_BASE_SINK(m_sink),
      86           57 :                                                     PullModePlaybackDelegate::shared_from_this())))
      87              :             {
      88            1 :                 GST_ERROR_OBJECT(m_sink, "Failed to attach VIDEO source");
      89              :             }
      90              :             else
      91              :             {
      92           28 :                 m_sourceAttached = true;
      93              : 
      94              :                 // check if READY -> PAUSED was requested before source was attached
      95           28 :                 if (GST_STATE_NEXT(m_sink) == GST_STATE_PAUSED)
      96              :                 {
      97           28 :                     client->pause(m_sourceId);
      98              :                 }
      99           28 :                 std::unique_lock lock{m_propertyMutex};
     100           28 :                 if (m_immediateOutputQueued)
     101              :                 {
     102            1 :                     GST_DEBUG_OBJECT(m_sink, "Set queued immediate-output");
     103            1 :                     m_immediateOutputQueued = false;
     104            1 :                     if (!client->setImmediateOutput(m_sourceId, m_immediateOutput))
     105              :                     {
     106            0 :                         GST_ERROR_OBJECT(m_sink, "Could not set immediate-output");
     107              :                     }
     108              :                 }
     109           28 :                 if (m_syncmodeStreamingQueued)
     110              :                 {
     111            2 :                     GST_DEBUG_OBJECT(m_sink, "Set queued syncmode-streaming");
     112            2 :                     m_syncmodeStreamingQueued = false;
     113            2 :                     if (!client->setStreamSyncMode(m_sourceId, m_syncmodeStreaming))
     114              :                     {
     115            1 :                         GST_ERROR_OBJECT(m_sink, "Could not set syncmode-streaming");
     116              :                     }
     117              :                 }
     118           28 :                 if (m_videoMuteQueued)
     119              :                 {
     120            1 :                     GST_DEBUG_OBJECT(m_sink, "Set queued show-video-window");
     121            1 :                     m_videoMuteQueued = false;
     122            1 :                     client->setMute(m_videoMute, m_sourceId);
     123              :                 }
     124           28 :             }
     125           29 :         }
     126              :         else
     127              :         {
     128            0 :             GST_ERROR_OBJECT(m_sink, "Failed to create VIDEO source");
     129              :         }
     130              : 
     131           29 :         break;
     132              :     }
     133            1 :     default:
     134            1 :         break;
     135              :     }
     136           31 :     return PullModePlaybackDelegate::handleEvent(pad, parent, event);
     137              : }
     138              : 
     139           15 : void PullModeVideoPlaybackDelegate::getProperty(const Property &type, GValue *value)
     140              : {
     141           15 :     switch (type)
     142              :     {
     143            5 :     case Property::WindowSet:
     144              :     {
     145            5 :         std::string rectangleValue;
     146            5 :         auto client = m_mediaPlayerManager.getMediaPlayerClient();
     147              : 
     148              :         {
     149            5 :             std::unique_lock lock{m_propertyMutex};
     150            5 :             if (!client)
     151              :             {
     152              :                 // Return the default value and
     153              :                 // queue a setting event (for the default value) so that it will become true when
     154              :                 // the client connects...
     155            2 :                 GST_DEBUG_OBJECT(m_sink, "Return default rectangle setting, and queue an event to set the default upon "
     156              :                                          "client connect");
     157            2 :                 m_rectangleSettingQueued = true;
     158            2 :                 rectangleValue = m_videoRectangle;
     159              :             }
     160            5 :         } // lock released here
     161              : 
     162            5 :         if (client)
     163              :         {
     164            3 :             rectangleValue = client->getVideoRectangle();
     165              :         }
     166            5 :         g_value_set_string(value, rectangleValue.c_str());
     167            5 :         break;
     168              :     }
     169            2 :     case Property::MaxVideoWidth:
     170              :     {
     171            2 :         g_value_set_uint(value, m_maxWidth);
     172            2 :         break;
     173              :     }
     174            2 :     case Property::MaxVideoHeight:
     175              :     {
     176            2 :         g_value_set_uint(value, m_maxHeight);
     177            2 :         break;
     178              :     }
     179            1 :     case Property::FrameStepOnPreroll:
     180              :     {
     181            1 :         g_value_set_boolean(value, m_stepOnPrerollEnabled);
     182            1 :         break;
     183              :     }
     184            3 :     case Property::ImmediateOutput:
     185              :     {
     186            3 :         bool immediateOutputValue = false;
     187            3 :         auto client = m_mediaPlayerManager.getMediaPlayerClient();
     188              : 
     189              :         {
     190            3 :             std::unique_lock lock{m_propertyMutex};
     191            3 :             if (!client)
     192              :             {
     193              :                 // Return the default value and
     194              :                 // queue a setting event (for the default value) so that it will become true when
     195              :                 // the client connects...
     196            1 :                 GST_DEBUG_OBJECT(m_sink,
     197              :                                  "Return default immediate-output setting, and queue an event to set the default "
     198              :                                  "upon client connect");
     199            1 :                 m_immediateOutputQueued = true;
     200            1 :                 immediateOutputValue = m_immediateOutput;
     201              :             }
     202              :             else
     203              :             {
     204            2 :                 immediateOutputValue = m_immediateOutput;
     205              :             }
     206            3 :         } // lock released here
     207              : 
     208            3 :         if (client)
     209              :         {
     210            2 :             if (!client->getImmediateOutput(m_sourceId, immediateOutputValue))
     211              :             {
     212            1 :                 GST_ERROR_OBJECT(m_sink, "Could not get immediate-output");
     213              :             }
     214              :         }
     215            3 :         g_value_set_boolean(value, immediateOutputValue);
     216            3 :         break;
     217              :     }
     218            2 :     case Property::VideoPts:
     219              :     {
     220            2 :         int64_t videoPts = 0;
     221              : 
     222            2 :         std::unique_lock lock{m_propertyMutex};
     223            2 :         auto client = m_mediaPlayerManager.getMediaPlayerClient();
     224            2 :         if (!client)
     225              :         {
     226            1 :             GST_DEBUG_OBJECT(m_sink, "Getting video PTS: no client, returning 0");
     227              :         }
     228              :         else
     229              :         {
     230            1 :             lock.unlock();
     231            1 :             gint64 position = client->getPosition(m_sourceId);
     232            1 :             videoPts = ((position / 100000) * 9LL); // 90Khz PTS
     233              :         }
     234              : 
     235            2 :         g_value_set_int64(value, videoPts);
     236            2 :         break;
     237              :     }
     238            0 :     default:
     239              :     {
     240            0 :         PullModePlaybackDelegate::getProperty(type, value);
     241            0 :         break;
     242              :     }
     243              :     }
     244           15 : }
     245              : 
     246           83 : void PullModeVideoPlaybackDelegate::setProperty(const Property &type, const GValue *value)
     247              : {
     248           83 :     std::shared_ptr<GStreamerMSEMediaPlayerClient> client = m_mediaPlayerManager.getMediaPlayerClient();
     249           83 :     switch (type)
     250              :     {
     251            4 :     case Property::WindowSet:
     252              :     {
     253            4 :         const gchar *rectangle = g_value_get_string(value);
     254            4 :         if (!rectangle)
     255              :         {
     256            1 :             GST_WARNING_OBJECT(m_sink, "Rectangle string not valid");
     257            1 :             break;
     258              :         }
     259            6 :         std::string videoRectangle{rectangle};
     260            3 :         bool shouldSetRectangle = false;
     261              :         {
     262            3 :             std::unique_lock lock{m_propertyMutex};
     263            3 :             m_videoRectangle = videoRectangle;
     264            3 :             if (!client)
     265              :             {
     266            2 :                 GST_DEBUG_OBJECT(m_sink, "Rectangle setting enqueued");
     267            2 :                 m_rectangleSettingQueued = true;
     268              :             }
     269              :             else
     270              :             {
     271            1 :                 shouldSetRectangle = true;
     272              :             }
     273            3 :         } // lock released here
     274              : 
     275            3 :         if (shouldSetRectangle)
     276              :         {
     277            1 :             client->setVideoRectangle(videoRectangle);
     278              :         }
     279            3 :         break;
     280              :     }
     281            2 :     case Property::MaxVideoWidth:
     282            2 :         m_maxWidth = g_value_get_uint(value);
     283            2 :         break;
     284            2 :     case Property::MaxVideoHeight:
     285            2 :         m_maxHeight = g_value_get_uint(value);
     286            2 :         break;
     287            5 :     case Property::FrameStepOnPreroll:
     288              :     {
     289            5 :         bool stepOnPrerollEnabled = g_value_get_boolean(value);
     290            5 :         if (client && stepOnPrerollEnabled && !m_stepOnPrerollEnabled)
     291              :         {
     292            2 :             GST_INFO_OBJECT(m_sink, "Frame stepping on preroll");
     293            2 :             client->renderFrame(m_sourceId);
     294              :         }
     295            5 :         m_stepOnPrerollEnabled = stepOnPrerollEnabled;
     296            5 :         break;
     297              :     }
     298            4 :     case Property::ImmediateOutput:
     299              :     {
     300            4 :         bool immediateOutput = (g_value_get_boolean(value) != FALSE);
     301            4 :         bool shouldSetImmediateOutput = false;
     302              :         {
     303            4 :             std::unique_lock lock{m_propertyMutex};
     304            4 :             m_immediateOutput = immediateOutput;
     305            4 :             if (!client)
     306              :             {
     307            2 :                 GST_DEBUG_OBJECT(m_sink, "Immediate output setting enqueued");
     308            2 :                 m_immediateOutputQueued = true;
     309              :             }
     310              :             else
     311              :             {
     312            2 :                 shouldSetImmediateOutput = true;
     313              :             }
     314            4 :         } // lock released here
     315              : 
     316            4 :         if (shouldSetImmediateOutput)
     317              :         {
     318            2 :             if (!client->setImmediateOutput(m_sourceId, immediateOutput))
     319              :             {
     320            1 :                 GST_ERROR_OBJECT(m_sink, "Could not set immediate-output");
     321              :             }
     322              :         }
     323            4 :         break;
     324              :     }
     325            4 :     case Property::SyncmodeStreaming:
     326              :     {
     327            4 :         bool syncmodeStreaming = (g_value_get_boolean(value) != FALSE);
     328            4 :         bool shouldSetSyncMode = false;
     329              :         {
     330            4 :             std::unique_lock lock{m_propertyMutex};
     331            4 :             m_syncmodeStreaming = syncmodeStreaming;
     332            4 :             if (!client)
     333              :             {
     334            2 :                 GST_DEBUG_OBJECT(m_sink, "Syncmode streaming setting enqueued");
     335            2 :                 m_syncmodeStreamingQueued = true;
     336              :             }
     337              :             else
     338              :             {
     339            2 :                 shouldSetSyncMode = true;
     340              :             }
     341            4 :         } // lock released here
     342              : 
     343            4 :         if (shouldSetSyncMode)
     344              :         {
     345            2 :             if (!client->setStreamSyncMode(m_sourceId, syncmodeStreaming))
     346              :             {
     347            1 :                 GST_ERROR_OBJECT(m_sink, "Could not set syncmode-streaming");
     348              :             }
     349              :         }
     350            4 :         break;
     351              :     }
     352            2 :     case Property::ShowVideoWindow:
     353              :     {
     354            2 :         bool videoMute = (g_value_get_boolean(value) == FALSE);
     355            2 :         bool shouldSetMute = false;
     356              :         {
     357            2 :             std::unique_lock lock{m_propertyMutex};
     358            2 :             m_videoMute = videoMute;
     359            2 :             if (!client || !m_sourceAttached)
     360              :             {
     361            1 :                 GST_DEBUG_OBJECT(m_sink, "Show video window setting enqueued");
     362            1 :                 m_videoMuteQueued = true;
     363              :             }
     364              :             else
     365              :             {
     366            1 :                 shouldSetMute = true;
     367              :             }
     368            2 :         } // lock released here
     369              : 
     370            2 :         if (shouldSetMute)
     371              :         {
     372            1 :             client->setMute(videoMute, m_sourceId);
     373              :         }
     374            2 :         break;
     375              :     }
     376           60 :     default:
     377              :     {
     378           60 :         PullModePlaybackDelegate::setProperty(type, value);
     379           60 :         break;
     380              :     }
     381              :     }
     382           83 : }
     383              : 
     384            1 : void PullModeVideoPlaybackDelegate::handleQos(uint64_t processed, uint64_t dropped) const
     385              : {
     386            1 :     GstBus *bus = gst_element_get_bus(m_sink);
     387              :     /* Hardcode isLive to FALSE and set invalid timestamps */
     388            1 :     GstMessage *message = gst_message_new_qos(GST_OBJECT(m_sink), FALSE, GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE,
     389              :                                               GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE);
     390              : 
     391            1 :     gst_message_set_qos_stats(message, GST_FORMAT_BUFFERS, processed, dropped);
     392            1 :     gst_bus_post(bus, message);
     393            1 :     gst_object_unref(bus);
     394              : }
     395              : 
     396              : std::unique_ptr<firebolt::rialto::IMediaPipeline::MediaSource>
     397           29 : PullModeVideoPlaybackDelegate::createMediaSource(GstCaps *caps) const
     398              : {
     399           29 :     GstStructure *structure = gst_caps_get_structure(caps, 0);
     400           29 :     const gchar *strct_name = gst_structure_get_name(structure);
     401              : 
     402           29 :     firebolt::rialto::SegmentAlignment alignment = get_segment_alignment(structure);
     403           29 :     std::shared_ptr<firebolt::rialto::CodecData> codecData = get_codec_data(structure);
     404           29 :     firebolt::rialto::StreamFormat format = get_stream_format(structure);
     405              : 
     406           29 :     gint width{0};
     407           29 :     gint height{0};
     408           29 :     gst_structure_get_int(structure, "width", &width);
     409           29 :     gst_structure_get_int(structure, "height", &height);
     410              : 
     411           29 :     if (strct_name)
     412              :     {
     413           29 :         std::string mimeType{};
     414           29 :         if (g_str_has_prefix(strct_name, "video/x-h264"))
     415              :         {
     416           24 :             mimeType = "video/h264";
     417              :         }
     418            5 :         else if (g_str_has_prefix(strct_name, "video/x-h265"))
     419              :         {
     420            4 :             mimeType = "video/h265";
     421              : 
     422            4 :             uint32_t dolbyVisionProfile{0};
     423            4 :             if (get_dv_profile(structure, dolbyVisionProfile))
     424              :             {
     425            1 :                 return std::make_unique<firebolt::rialto::IMediaPipeline::MediaSourceVideoDolbyVision>(mimeType,
     426              :                                                                                                        dolbyVisionProfile,
     427            1 :                                                                                                        m_hasDrm, width,
     428              :                                                                                                        height, alignment,
     429            1 :                                                                                                        format, codecData);
     430              :             }
     431              :         }
     432              :         else
     433              :         {
     434            1 :             mimeType = strct_name;
     435              :         }
     436              : 
     437           28 :         GST_INFO_OBJECT(m_sink, "%s video media source created", mimeType.c_str());
     438           56 :         return std::make_unique<firebolt::rialto::IMediaPipeline::MediaSourceVideo>(mimeType, m_hasDrm, width, height,
     439           28 :                                                                                     alignment, format, codecData);
     440           29 :     }
     441              :     else
     442              :     {
     443            0 :         GST_ERROR_OBJECT(m_sink,
     444              :                          "Empty caps' structure name! Failed to set mime type when constructing video media source");
     445              :     }
     446              : 
     447            0 :     return nullptr;
     448           29 : }
        

Generated by: LCOV version 2.0-1