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

Generated by: LCOV version 2.0-1