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

Generated by: LCOV version 2.0-1