LCOV - code coverage report
Current view: top level - source - PullModeSubtitlePlaybackDelegate.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 90.3 % 144 130
Test Date: 2025-10-17 10:59:19 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 "PullModeSubtitlePlaybackDelegate.h"
      20              : #include "GstreamerCatLog.h"
      21              : 
      22              : #define GST_CAT_DEFAULT rialtoGStreamerCat
      23              : 
      24           20 : PullModeSubtitlePlaybackDelegate::PullModeSubtitlePlaybackDelegate(GstElement *sink) : PullModePlaybackDelegate(sink)
      25              : {
      26           20 :     m_mediaSourceType = firebolt::rialto::MediaSourceType::SUBTITLE;
      27           20 :     m_isAsync = false;
      28              : }
      29              : 
      30           62 : GstStateChangeReturn PullModeSubtitlePlaybackDelegate::changeState(GstStateChange transition)
      31              : {
      32           62 :     switch (transition)
      33              :     {
      34           11 :     case GST_STATE_CHANGE_READY_TO_PAUSED:
      35              :     {
      36           11 :         if (!attachToMediaClientAndSetStreamsNumber())
      37              :         {
      38            1 :             return GST_STATE_CHANGE_FAILURE;
      39              :         }
      40              :     }
      41              :     default:
      42           61 :         break;
      43              :     }
      44           61 :     return PullModePlaybackDelegate::changeState(transition);
      45              : }
      46              : 
      47           16 : gboolean PullModeSubtitlePlaybackDelegate::handleEvent(GstPad *pad, GstObject *parent, GstEvent *event)
      48              : {
      49           16 :     switch (GST_EVENT_TYPE(event))
      50              :     {
      51           12 :     case GST_EVENT_CAPS:
      52              :     {
      53              :         GstCaps *caps;
      54           12 :         gst_event_parse_caps(event, &caps);
      55           12 :         if (m_sourceAttached)
      56              :         {
      57            1 :             GST_INFO_OBJECT(m_sink, "Source already attached. Skip calling attachSource");
      58            1 :             break;
      59              :         }
      60              : 
      61           11 :         GST_INFO_OBJECT(m_sink, "Attaching SUBTITLE source with caps %" GST_PTR_FORMAT, caps);
      62              : 
      63           11 :         std::unique_ptr<firebolt::rialto::IMediaPipeline::MediaSource> subtitleSource{createMediaSource(caps)};
      64           11 :         if (subtitleSource)
      65              :         {
      66           11 :             std::shared_ptr<GStreamerMSEMediaPlayerClient> client = m_mediaPlayerManager.getMediaPlayerClient();
      67           11 :             if ((!client) || (!client->attachSource(subtitleSource, RIALTO_MSE_BASE_SINK(m_sink), shared_from_this())))
      68              :             {
      69            1 :                 GST_ERROR_OBJECT(m_sink, "Failed to attach SUBTITLE source");
      70              :             }
      71              :             else
      72              :             {
      73           10 :                 m_sourceAttached = true;
      74           10 :                 if (m_isMuteQueued)
      75              :                 {
      76            1 :                     client->setMute(m_isMuted, m_sourceId);
      77            1 :                     m_isMuteQueued = false;
      78              :                 }
      79              : 
      80              :                 {
      81           10 :                     std::unique_lock lock{m_mutex};
      82           10 :                     if (m_isTextTrackIdentifierQueued)
      83              :                     {
      84            1 :                         client->setTextTrackIdentifier(m_textTrackIdentifier);
      85            1 :                         m_isTextTrackIdentifierQueued = false;
      86              :                     }
      87              : 
      88           10 :                     if (m_queuedOffset)
      89              :                     {
      90            0 :                         GST_DEBUG_OBJECT(m_sink, "Setting subtitle offset to: %" GST_TIME_FORMAT,
      91              :                                          GST_TIME_ARGS(m_queuedOffset.value()));
      92            0 :                         client->setSubtitleOffset(m_sourceId, m_queuedOffset.value());
      93            0 :                         m_queuedOffset.reset();
      94              :                     }
      95           10 :                 }
      96              : 
      97              :                 // check if READY -> PAUSED was requested before source was attached
      98           10 :                 if (GST_STATE_NEXT(m_sink) == GST_STATE_PAUSED)
      99              :                 {
     100           10 :                     client->pause(m_sourceId);
     101              :                 }
     102              :             }
     103           11 :         }
     104              :         else
     105              :         {
     106            0 :             GST_ERROR_OBJECT(m_sink, "Failed to create SUBTITLE source");
     107              :         }
     108              : 
     109           11 :         break;
     110              :     }
     111            2 :     case GST_EVENT_CUSTOM_DOWNSTREAM:
     112              :     case GST_EVENT_CUSTOM_DOWNSTREAM_OOB:
     113              :     {
     114            2 :         if (gst_event_has_name(event, "set-pts-offset"))
     115              :         {
     116            2 :             GST_DEBUG_OBJECT(m_sink, "Set pts offset event received");
     117            2 :             const GstStructure *structure{gst_event_get_structure(event)};
     118            2 :             guint64 ptsOffset{GST_CLOCK_TIME_NONE};
     119            2 :             if (gst_structure_get_uint64(structure, "pts-offset", &ptsOffset) == TRUE)
     120              :             {
     121            1 :                 std::unique_lock lock{m_sinkMutex};
     122              : 
     123            1 :                 std::shared_ptr<GStreamerMSEMediaPlayerClient> client = m_mediaPlayerManager.getMediaPlayerClient();
     124            1 :                 if (!client || !m_sourceAttached)
     125              :                 {
     126            0 :                     GST_DEBUG_OBJECT(m_sink, "offset setting enqueued");
     127            0 :                     m_queuedOffset = static_cast<int64_t>(ptsOffset);
     128              :                 }
     129              :                 else
     130              :                 {
     131            1 :                     GST_DEBUG_OBJECT(m_sink, "Setting subtitle offset to: %" GST_TIME_FORMAT, GST_TIME_ARGS(ptsOffset));
     132            1 :                     client->setSubtitleOffset(m_sourceId, ptsOffset);
     133              :                 }
     134              :             }
     135              :             else
     136              :             {
     137            1 :                 GST_WARNING_OBJECT(m_sink, "Unable to set pts offset. Value not present");
     138              :             }
     139              :         }
     140            2 :         break;
     141              :     }
     142            2 :     default:
     143            2 :         break;
     144              :     }
     145           16 :     return PullModePlaybackDelegate::handleEvent(pad, parent, event);
     146              : }
     147              : 
     148            6 : void PullModeSubtitlePlaybackDelegate::getProperty(const Property &type, GValue *value)
     149              : {
     150            6 :     std::shared_ptr<GStreamerMSEMediaPlayerClient> client{m_mediaPlayerManager.getMediaPlayerClient()};
     151              : 
     152            6 :     switch (type)
     153              :     {
     154            2 :     case Property::Mute:
     155              :     {
     156            2 :         if (!client)
     157              :         {
     158            1 :             g_value_set_boolean(value, m_isMuted);
     159            1 :             return;
     160              :         }
     161            1 :         g_value_set_boolean(value, client->getMute(m_sourceId));
     162            1 :         break;
     163              :     }
     164            2 :     case Property::TextTrackIdentifier:
     165              :     {
     166              :         {
     167            2 :             std::unique_lock lock{m_mutex};
     168            2 :             if (!client)
     169              :             {
     170            1 :                 g_value_set_string(value, m_textTrackIdentifier.c_str());
     171            1 :                 return;
     172              :             }
     173            2 :         }
     174            1 :         g_value_set_string(value, client->getTextTrackIdentifier().c_str());
     175              : 
     176            1 :         break;
     177              :     }
     178            1 :     case Property::WindowId:
     179              :     {
     180            1 :         g_value_set_uint(value, m_videoId);
     181            1 :         break;
     182              :     }
     183            1 :     case Property::Async:
     184              :     {
     185            1 :         g_value_set_boolean(value, m_isAsync);
     186            1 :         break;
     187              :     }
     188            0 :     default:
     189              :     {
     190            0 :         PullModePlaybackDelegate::getProperty(type, value);
     191            0 :         break;
     192              :     }
     193              :     }
     194            6 : }
     195              : 
     196           35 : void PullModeSubtitlePlaybackDelegate::setProperty(const Property &type, const GValue *value)
     197              : {
     198           35 :     std::shared_ptr<GStreamerMSEMediaPlayerClient> client = m_mediaPlayerManager.getMediaPlayerClient();
     199              : 
     200           35 :     switch (type)
     201              :     {
     202            3 :     case Property::Mute:
     203            3 :         m_isMuted = g_value_get_boolean(value);
     204            3 :         if (!client || !m_sourceAttached)
     205              :         {
     206            2 :             m_isMuteQueued = true;
     207            2 :             return;
     208              :         }
     209              : 
     210            1 :         client->setMute(m_isMuted, m_sourceId);
     211              : 
     212            1 :         break;
     213            4 :     case Property::TextTrackIdentifier:
     214              :     {
     215            4 :         const gchar *textTrackIdentifier = g_value_get_string(value);
     216            4 :         if (!textTrackIdentifier)
     217              :         {
     218            1 :             GST_WARNING_OBJECT(m_sink, "TextTrackIdentifier string not valid");
     219            1 :             break;
     220              :         }
     221              : 
     222            3 :         std::unique_lock lock{m_mutex};
     223            3 :         m_textTrackIdentifier = std::string(textTrackIdentifier);
     224            3 :         if (!client || !m_sourceAttached)
     225              :         {
     226            2 :             GST_DEBUG_OBJECT(m_sink, "Text track identifier setting enqueued");
     227            2 :             m_isTextTrackIdentifierQueued = true;
     228              :         }
     229              :         else
     230              :         {
     231            1 :             client->setTextTrackIdentifier(m_textTrackIdentifier);
     232              :         }
     233              : 
     234            3 :         break;
     235              :     }
     236            1 :     case Property::WindowId:
     237              :     {
     238            1 :         m_videoId = g_value_get_uint(value);
     239            1 :         break;
     240              :     }
     241            1 :     case Property::Async:
     242              :     {
     243            1 :         m_isAsync = g_value_get_boolean(value);
     244            1 :         break;
     245              :     }
     246           26 :     default:
     247              :     {
     248           26 :         PullModePlaybackDelegate::setProperty(type, value);
     249           26 :         break;
     250              :     }
     251              :     }
     252           35 : }
     253              : 
     254            1 : void PullModeSubtitlePlaybackDelegate::handleQos(uint64_t processed, uint64_t dropped) const
     255              : {
     256            1 :     GstBus *bus = gst_element_get_bus(m_sink);
     257              :     /* Hardcode isLive to FALSE and set invalid timestamps */
     258            1 :     GstMessage *message = gst_message_new_qos(GST_OBJECT(m_sink), FALSE, GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE,
     259              :                                               GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE);
     260              : 
     261            1 :     gst_message_set_qos_stats(message, GST_FORMAT_BUFFERS, processed, dropped);
     262            1 :     gst_bus_post(bus, message);
     263            1 :     gst_object_unref(bus);
     264              : }
     265              : 
     266              : std::unique_ptr<firebolt::rialto::IMediaPipeline::MediaSource>
     267           11 : PullModeSubtitlePlaybackDelegate::createMediaSource(GstCaps *caps) const
     268              : {
     269           11 :     GstStructure *structure = gst_caps_get_structure(caps, 0);
     270           11 :     const gchar *mimeName = gst_structure_get_name(structure);
     271           11 :     if (mimeName)
     272              :     {
     273           11 :         std::string mimeType{};
     274           11 :         if (g_str_has_prefix(mimeName, "text/vtt") || g_str_has_prefix(mimeName, "application/x-subtitle-vtt"))
     275              :         {
     276            1 :             mimeType = "text/vtt";
     277              :         }
     278           10 :         else if (g_str_has_prefix(mimeName, "application/ttml+xml"))
     279              :         {
     280            9 :             mimeType = "text/ttml";
     281              :         }
     282            1 :         else if (g_str_has_prefix(mimeName, "closedcaption/x-cea-608") ||
     283            1 :                  g_str_has_prefix(mimeName, "closedcaption/x-cea-708") ||
     284            0 :                  g_str_has_prefix(mimeName, "application/x-cea-608") ||
     285            2 :                  g_str_has_prefix(mimeName, "application/x-cea-708") ||
     286            0 :                  g_str_has_prefix(mimeName, "application/x-subtitle-cc"))
     287              :         {
     288            1 :             mimeType = "text/cc";
     289              :         }
     290              :         else
     291              :         {
     292            0 :             mimeType = mimeName;
     293              :         }
     294              : 
     295           11 :         GST_INFO_OBJECT(m_sink, "%s subtitle media source created", mimeType.c_str());
     296           11 :         return std::make_unique<firebolt::rialto::IMediaPipeline::MediaSourceSubtitle>(mimeType, m_textTrackIdentifier);
     297              :     }
     298              :     else
     299              :     {
     300            0 :         GST_ERROR_OBJECT(m_sink,
     301              :                          "Empty caps' structure name! Failed to set mime type when constructing subtitle media source");
     302              :     }
     303              : 
     304            0 :     return nullptr;
     305              : }
        

Generated by: LCOV version 2.0-1