LCOV - code coverage report
Current view: top level - media/server/gstplayer/source - GstCapabilities.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 75.4 % 232 175
Test Date: 2026-03-31 12:03:04 Functions: 100.0 % 20 20

            Line data    Source code
       1              : /*
       2              :  * If not stated otherwise in this file or this component's LICENSE file the
       3              :  * following copyright and licenses apply:
       4              :  *
       5              :  * Copyright 2022 Sky UK
       6              :  *
       7              :  * Licensed under the Apache License, Version 2.0 (the "License");
       8              :  * you may not use this file except in compliance with the License.
       9              :  * You may obtain a copy of the License at
      10              :  *
      11              :  * http://www.apache.org/licenses/LICENSE-2.0
      12              :  *
      13              :  * Unless required by applicable law or agreed to in writing, software
      14              :  * distributed under the License is distributed on an "AS IS" BASIS,
      15              :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      16              :  * See the License for the specific language governing permissions and
      17              :  * limitations under the License.
      18              :  */
      19              : 
      20              : #include <algorithm>
      21              : #include <stdexcept>
      22              : #include <unordered_map>
      23              : #include <unordered_set>
      24              : #include <utility>
      25              : 
      26              : #include "GstCapabilities.h"
      27              : #include "GstMimeMapping.h"
      28              : #include "RialtoServerLogging.h"
      29              : 
      30              : namespace
      31              : {
      32           32 : const char *toString(const GstElementFactoryListType &listType)
      33              : {
      34           32 :     switch (listType)
      35              :     {
      36            0 :     case GST_ELEMENT_FACTORY_TYPE_ANY:
      37            0 :         return "Any";
      38            0 :     case GST_ELEMENT_FACTORY_TYPE_AUDIOVIDEO_SINKS:
      39            0 :         return "AudioVideo Sinks";
      40            0 :     case GST_ELEMENT_FACTORY_TYPE_AUDIO_ENCODER:
      41            0 :         return "Audio Encoder";
      42            0 :     case GST_ELEMENT_FACTORY_TYPE_DECODABLE:
      43            0 :         return "Decodable";
      44           12 :     case GST_ELEMENT_FACTORY_TYPE_DECODER:
      45           12 :         return "Decoder";
      46            0 :     case GST_ELEMENT_FACTORY_TYPE_DECRYPTOR:
      47            0 :         return "Decryptor";
      48            0 :     case GST_ELEMENT_FACTORY_TYPE_DEMUXER:
      49            0 :         return "Demuxer";
      50            0 :     case GST_ELEMENT_FACTORY_TYPE_DEPAYLOADER:
      51            0 :         return "Depayloader";
      52            0 :     case GST_ELEMENT_FACTORY_TYPE_ENCODER:
      53            0 :         return "Encoder";
      54            0 :     case GST_ELEMENT_FACTORY_TYPE_ENCRYPTOR:
      55            0 :         return "Encryptor";
      56            0 :     case GST_ELEMENT_FACTORY_TYPE_FORMATTER:
      57            0 :         return "Formatter";
      58            0 :     case GST_ELEMENT_FACTORY_TYPE_HARDWARE:
      59            0 :         return "Hardware";
      60            0 :     case GST_ELEMENT_FACTORY_TYPE_MAX_ELEMENTS:
      61            0 :         return "Max Elements";
      62            0 :     case GST_ELEMENT_FACTORY_TYPE_MEDIA_ANY:
      63            0 :         return "Media Any";
      64            0 :     case GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO:
      65            0 :         return "Media Audio";
      66            0 :     case GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE:
      67            0 :         return "Media Image";
      68            0 :     case GST_ELEMENT_FACTORY_TYPE_MEDIA_METADATA:
      69            0 :         return "Media Metadata";
      70            0 :     case GST_ELEMENT_FACTORY_TYPE_MEDIA_SUBTITLE:
      71            0 :         return "Media Subtitle";
      72            0 :     case GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO:
      73            0 :         return "Media Video";
      74            0 :     case GST_ELEMENT_FACTORY_TYPE_MUXER:
      75            0 :         return "Muxer";
      76            0 :     case GST_ELEMENT_FACTORY_TYPE_PARSER:
      77            0 :         return "Parser";
      78            0 :     case GST_ELEMENT_FACTORY_TYPE_PAYLOADER:
      79            0 :         return "Payloader";
      80           20 :     case GST_ELEMENT_FACTORY_TYPE_SINK:
      81           20 :         return "Sink";
      82            0 :     case GST_ELEMENT_FACTORY_TYPE_SRC:
      83            0 :         return "Source";
      84            0 :     case GST_ELEMENT_FACTORY_TYPE_VIDEO_ENCODER:
      85            0 :         return "Video Encoder";
      86            0 :     default:
      87            0 :         return "Unknown";
      88              :     }
      89              : }
      90              : } // namespace
      91              : 
      92              : namespace firebolt::rialto::server
      93              : {
      94              : std::weak_ptr<IGstCapabilitiesFactory> GstCapabilitiesFactory::m_factory;
      95              : 
      96            2 : std::shared_ptr<IGstCapabilitiesFactory> IGstCapabilitiesFactory::getFactory()
      97              : {
      98            2 :     std::shared_ptr<IGstCapabilitiesFactory> factory = GstCapabilitiesFactory::m_factory.lock();
      99              : 
     100            2 :     if (!factory)
     101              :     {
     102              :         try
     103              :         {
     104            2 :             factory = std::make_shared<GstCapabilitiesFactory>();
     105              :         }
     106            0 :         catch (const std::exception &e)
     107              :         {
     108            0 :             RIALTO_SERVER_LOG_ERROR("Failed to create the gstreamer capabilities factory, reason: %s", e.what());
     109              :         }
     110              : 
     111            2 :         GstCapabilitiesFactory::m_factory = factory;
     112              :     }
     113              : 
     114            2 :     return factory;
     115              : }
     116              : 
     117            2 : std::unique_ptr<IGstCapabilities> GstCapabilitiesFactory::createGstCapabilities()
     118              : {
     119            2 :     std::unique_ptr<IGstCapabilities> gstCapabilities;
     120              :     try
     121              :     {
     122              :         std::shared_ptr<firebolt::rialto::wrappers::IGstWrapperFactory> gstWrapperFactory =
     123            2 :             firebolt::rialto::wrappers::IGstWrapperFactory::getFactory();
     124            2 :         std::shared_ptr<firebolt::rialto::wrappers::IGstWrapper> gstWrapper;
     125              : 
     126            2 :         if ((!gstWrapperFactory) || (!(gstWrapper = gstWrapperFactory->getGstWrapper())))
     127              :         {
     128            0 :             throw std::runtime_error("Cannot create GstWrapper");
     129              :         }
     130              : 
     131              :         std::shared_ptr<firebolt::rialto::wrappers::IGlibWrapperFactory> glibWrapperFactory =
     132            2 :             firebolt::rialto::wrappers::IGlibWrapperFactory::getFactory();
     133            2 :         std::shared_ptr<firebolt::rialto::wrappers::IGlibWrapper> glibWrapper;
     134              : 
     135            2 :         if ((!glibWrapperFactory) || (!(glibWrapper = glibWrapperFactory->getGlibWrapper())))
     136              :         {
     137            0 :             throw std::runtime_error("Cannot create GlibWrapper");
     138              :         }
     139              : 
     140              :         std::shared_ptr<firebolt::rialto::wrappers::IRdkGstreamerUtilsWrapperFactory> rdkGstreamerUtilsWrapperFactory =
     141            2 :             firebolt::rialto::wrappers::IRdkGstreamerUtilsWrapperFactory::getFactory();
     142            2 :         std::shared_ptr<firebolt::rialto::wrappers::IRdkGstreamerUtilsWrapper> rdkGstreamerUtilsWrapper;
     143              : 
     144            4 :         if ((!rdkGstreamerUtilsWrapperFactory) ||
     145            4 :             (!(rdkGstreamerUtilsWrapper = rdkGstreamerUtilsWrapperFactory->createRdkGstreamerUtilsWrapper())))
     146              :         {
     147            0 :             throw std::runtime_error("Cannot create RdkGstreamerUtilsWrapper");
     148              :         }
     149              : 
     150            4 :         gstCapabilities = std::make_unique<GstCapabilities>(gstWrapper, glibWrapper, rdkGstreamerUtilsWrapper,
     151            2 :                                                             IGstInitialiser::instance());
     152              :     }
     153            0 :     catch (const std::exception &e)
     154              :     {
     155            0 :         RIALTO_SERVER_LOG_ERROR("Failed to create the gstreamer capabilities, reason: %s", e.what());
     156              :     }
     157              : 
     158            2 :     return gstCapabilities;
     159              : }
     160              : 
     161           22 : GstCapabilities::GstCapabilities(
     162              :     const std::shared_ptr<firebolt::rialto::wrappers::IGstWrapper> &gstWrapper,
     163              :     const std::shared_ptr<firebolt::rialto::wrappers::IGlibWrapper> &glibWrapper,
     164              :     const std::shared_ptr<firebolt::rialto::wrappers::IRdkGstreamerUtilsWrapper> &rdkGstreamerUtilsWrapper,
     165           22 :     const IGstInitialiser &gstInitialiser)
     166           22 :     : m_gstWrapper{gstWrapper}, m_glibWrapper{glibWrapper}, m_rdkGstreamerUtilsWrapper{rdkGstreamerUtilsWrapper},
     167           44 :       m_gstInitialiser{gstInitialiser}
     168              : {
     169           22 :     m_initialisationThread = std::thread(
     170           22 :         [this]()
     171              :         {
     172           22 :             std::unique_lock lock{m_initialisationMutex};
     173              : 
     174           22 :             m_gstInitialiser.waitForInitialisation();
     175           22 :             fillSupportedMimeTypes();
     176           22 :             m_isInitialised = true;
     177           22 :             m_initialisationCv.notify_all();
     178           44 :         });
     179           22 : }
     180              : 
     181           44 : GstCapabilities::~GstCapabilities()
     182              : {
     183           22 :     if (m_initialisationThread.joinable())
     184              :     {
     185           22 :         m_initialisationThread.join();
     186              :     }
     187           44 : }
     188              : 
     189           13 : std::vector<std::string> GstCapabilities::getSupportedMimeTypes(MediaSourceType sourceType)
     190              : {
     191           13 :     waitForInitialisation();
     192              : 
     193           13 :     std::vector<std::string> supportedMimeTypesSource;
     194           13 :     std::string type;
     195           13 :     if (sourceType == MediaSourceType::VIDEO)
     196              :     {
     197            6 :         type = "video/";
     198              :     }
     199            7 :     else if (sourceType == MediaSourceType::AUDIO)
     200              :     {
     201            6 :         type = "audio/";
     202              :     }
     203            1 :     else if (sourceType == MediaSourceType::SUBTITLE)
     204              :     {
     205            3 :         return {"text/vtt", "text/ttml", "text/cc"};
     206              :     }
     207              :     else
     208              :     {
     209            0 :         RIALTO_SERVER_LOG_WARN("Unsupported media type");
     210            0 :         return {};
     211              :     }
     212              : 
     213           12 :     std::copy_if(m_supportedMimeTypes.begin(), m_supportedMimeTypes.end(), std::back_inserter(supportedMimeTypesSource),
     214           22 :                  [&type](const std::string &supportedMimeType) { return supportedMimeType.find(type) == 0; });
     215              : 
     216           12 :     return supportedMimeTypesSource;
     217           13 : }
     218              : 
     219           40 : bool GstCapabilities::isMimeTypeSupported(const std::string &mimeType)
     220              : {
     221           40 :     waitForInitialisation();
     222           40 :     return m_supportedMimeTypes.find(mimeType) != m_supportedMimeTypes.end();
     223              : }
     224              : 
     225            4 : std::vector<std::string> GstCapabilities::getSupportedProperties(MediaSourceType mediaType,
     226              :                                                                  const std::vector<std::string> &propertyNames)
     227              : {
     228            4 :     waitForInitialisation();
     229              : 
     230              :     // Get gstreamer element factories. The following flag settings will fetch both SINK and DECODER types
     231              :     // of gstreamer classes...
     232            4 :     GstElementFactoryListType factoryListType{GST_ELEMENT_FACTORY_TYPE_SINK | GST_ELEMENT_FACTORY_TYPE_DECODER |
     233              :                                               GST_ELEMENT_FACTORY_TYPE_PARSER};
     234              :     {
     235              :         // If MediaSourceType::AUDIO is specified then adjust the flag so that we
     236              :         // restrict the list to gstreamer AUDIO element types (and likewise for video and subtitle)...
     237              :         static const std::unordered_map<MediaSourceType, GstElementFactoryListType>
     238              :             kLookupExtraConditions{{MediaSourceType::AUDIO, GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO},
     239              :                                    {MediaSourceType::VIDEO, GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO},
     240            6 :                                    {MediaSourceType::SUBTITLE, GST_ELEMENT_FACTORY_TYPE_MEDIA_SUBTITLE}};
     241            4 :         auto i = kLookupExtraConditions.find(mediaType);
     242            4 :         if (i != kLookupExtraConditions.end())
     243            4 :             factoryListType |= i->second;
     244              :     }
     245              : 
     246            4 :     GList *factories{m_gstWrapper->gstElementFactoryListGetElements(factoryListType, GST_RANK_NONE)};
     247              : 
     248              :     // Scan all returned elements for the specified properties...
     249            8 :     std::unordered_set<std::string> propertiesToLookFor{propertyNames.begin(), propertyNames.end()};
     250            4 :     std::vector<std::string> propertiesFound;
     251            8 :     for (GList *iter = factories; iter != nullptr && !propertiesToLookFor.empty(); iter = iter->next)
     252              :     {
     253            4 :         GstElementFactory *factory = GST_ELEMENT_FACTORY(iter->data);
     254              : 
     255              :         // WORKAROUND: initialising element "rtkv1sink" causes that another playback's video goes black
     256              :         // we don't need to scan this element, so ignore it
     257           12 :         if (std::string{GST_OBJECT_NAME(GST_OBJECT(factory))} == "rtkv1sink")
     258              :         {
     259            1 :             RIALTO_SERVER_LOG_DEBUG("Ignoring rtkv1sink element");
     260            1 :             continue;
     261              :         }
     262              : 
     263            3 :         GstElement *elementObj{nullptr};
     264              : 
     265              :         // We instantiate an object because fetching the class, even after gstPluginFeatureLoad,
     266              :         // was found to sometimes return a class with no properties. A code branch is
     267              :         // kept with this feature "supportedPropertiesViaClass"
     268            3 :         elementObj = m_gstWrapper->gstElementFactoryCreate(factory, nullptr);
     269            3 :         if (elementObj)
     270              :         {
     271              :             GParamSpec **props;
     272              :             guint nProps;
     273            3 :             props = m_glibWrapper->gObjectClassListProperties(G_OBJECT_GET_CLASS(elementObj), &nProps);
     274            3 :             if (props)
     275              :             {
     276            9 :                 for (guint j = 0; j < nProps && !propertiesToLookFor.empty(); ++j)
     277              :                 {
     278           12 :                     std::string propName{props[j]->name};
     279            6 :                     auto it = propertiesToLookFor.find(propName);
     280            6 :                     if (it != propertiesToLookFor.end())
     281              :                     {
     282            4 :                         RIALTO_SERVER_LOG_DEBUG("Found property '%s'", propName.c_str());
     283            4 :                         propertiesFound.push_back(std::move(propName));
     284            4 :                         propertiesToLookFor.erase(it);
     285              :                     }
     286            6 :                 }
     287            3 :                 m_glibWrapper->gFree(props);
     288              :             }
     289            3 :             m_gstWrapper->gstObjectUnref(elementObj);
     290              :         }
     291              :     }
     292              : 
     293              :     // Some sinks do not specifically support the "audio-fade" property, but the mechanism is supported through the use
     294              :     // of the rdk_gstreamer_utils library. Check for audio fade support if the property is required and we haven't found it in the sinks.
     295           12 :     if (propertiesToLookFor.find("audio-fade") != propertiesToLookFor.end())
     296              :     {
     297            2 :         bool socAudioFadeSupported = m_rdkGstreamerUtilsWrapper->isSocAudioFadeSupported();
     298            2 :         if (socAudioFadeSupported)
     299              :         {
     300            1 :             RIALTO_SERVER_LOG_DEBUG("Audio fade property is supported by the SoC");
     301            2 :             propertiesFound.push_back("audio-fade"); // Add "audio-fade" if supported by SoC
     302              :         }
     303              :     }
     304              :     // Cleanup
     305            4 :     m_gstWrapper->gstPluginFeatureListFree(factories);
     306            8 :     return propertiesFound;
     307            4 : }
     308              : 
     309           22 : void GstCapabilities::fillSupportedMimeTypes()
     310              : {
     311           22 :     std::vector<GstCaps *> supportedCaps;
     312           22 :     appendSupportedCapsFromFactoryType(GST_ELEMENT_FACTORY_TYPE_DECODER, supportedCaps);
     313              : 
     314              :     // Only append caps from decoder parser if they can link with the decoder
     315           22 :     appendLinkableCapsFromParserDecoderChains(supportedCaps);
     316              : 
     317              :     // Sink caps do not require decoder support
     318           22 :     appendSupportedCapsFromFactoryType(GST_ELEMENT_FACTORY_TYPE_SINK, supportedCaps);
     319              : 
     320           22 :     if (supportedCaps.empty())
     321              :     {
     322           12 :         RIALTO_SERVER_LOG_WARN("There are no supported caps");
     323           12 :         return;
     324              :     }
     325              : 
     326           10 :     m_supportedMimeTypes = firebolt::rialto::server::convertFromCapsVectorToMimeSet(supportedCaps, m_gstWrapper);
     327              : 
     328           29 :     for (GstCaps *caps : supportedCaps)
     329              :     {
     330           19 :         m_gstWrapper->gstCapsUnref(caps);
     331              :     }
     332           22 : }
     333              : 
     334           22 : void GstCapabilities::appendLinkableCapsFromParserDecoderChains(std::vector<GstCaps *> &supportedCaps)
     335              : {
     336           22 :     if (supportedCaps.empty())
     337              :     {
     338           15 :         return;
     339              :     }
     340              : 
     341            9 :     std::vector<GstCaps *> decoderSupportedCaps = supportedCaps;
     342              : 
     343              :     GList *parserFactories =
     344            9 :         m_gstWrapper->gstElementFactoryListGetElements(GST_ELEMENT_FACTORY_TYPE_PARSER, GST_RANK_MARGINAL);
     345            9 :     if (!parserFactories)
     346              :     {
     347            2 :         RIALTO_SERVER_LOG_WARN("Could not find any parser");
     348            2 :         return;
     349              :     }
     350              : 
     351           15 :     for (GstCaps *decoderCaps : decoderSupportedCaps)
     352              :     {
     353           17 :         for (GList *factoriesIter = parserFactories; factoriesIter; factoriesIter = factoriesIter->next)
     354              :         {
     355            9 :             GstElementFactory *factory = static_cast<GstElementFactory *>(factoriesIter->data);
     356            9 :             const GList *kParserPadTemplates = m_gstWrapper->gstElementFactoryGetStaticPadTemplates(factory);
     357              : 
     358            9 :             if (canCreateParserDecoderChain(decoderCaps, kParserPadTemplates))
     359              :             {
     360            7 :                 addAllUniqueSinkPadsCapsToVector(supportedCaps, kParserPadTemplates);
     361              :             }
     362              :         }
     363              :     }
     364              : 
     365            7 :     m_gstWrapper->gstPluginFeatureListFree(parserFactories);
     366            9 : }
     367              : 
     368           44 : void GstCapabilities::appendSupportedCapsFromFactoryType(const GstElementFactoryListType &type,
     369              :                                                          std::vector<GstCaps *> &supportedCaps)
     370              : {
     371           44 :     GList *factories = m_gstWrapper->gstElementFactoryListGetElements(type, GST_RANK_MARGINAL);
     372           44 :     if (!factories)
     373              :     {
     374           32 :         RIALTO_SERVER_LOG_WARN("Could not find any %s", toString(type));
     375           32 :         return;
     376              :     }
     377              : 
     378           25 :     for (GList *factoriesIter = factories; factoriesIter; factoriesIter = factoriesIter->next)
     379              :     {
     380           13 :         GstElementFactory *factory = static_cast<GstElementFactory *>(factoriesIter->data);
     381           13 :         const GList *kPadTemplates = m_gstWrapper->gstElementFactoryGetStaticPadTemplates(factory);
     382              : 
     383           13 :         addAllUniqueSinkPadsCapsToVector(supportedCaps, kPadTemplates);
     384              :     }
     385              : 
     386           12 :     m_gstWrapper->gstPluginFeatureListFree(factories);
     387              : }
     388              : 
     389            9 : bool GstCapabilities::canCreateParserDecoderChain(GstCaps *decoderCaps, const GList *kParserPadTemplates)
     390              : {
     391           21 :     for (const GList *padTemplateIter = kParserPadTemplates; padTemplateIter; padTemplateIter = padTemplateIter->next)
     392              :     {
     393           19 :         GstStaticPadTemplate *padTemplate = static_cast<GstStaticPadTemplate *>(padTemplateIter->data);
     394           19 :         if (padTemplate->direction == GST_PAD_SRC)
     395              :         {
     396           10 :             GstCaps *padTemplateCaps = m_gstWrapper->gstStaticCapsGet(&padTemplate->static_caps);
     397              : 
     398              :             // check if parser's src pad can be connected to decoder's sink pad
     399           10 :             bool canIntersect = m_gstWrapper->gstCapsCanIntersect(decoderCaps, padTemplateCaps);
     400           10 :             m_gstWrapper->gstCapsUnref(padTemplateCaps);
     401           10 :             if (canIntersect)
     402              :             {
     403            7 :                 return true;
     404              :             }
     405              :         }
     406              :     }
     407              : 
     408            2 :     return false;
     409              : }
     410              : 
     411           20 : void GstCapabilities::addAllUniqueSinkPadsCapsToVector(std::vector<GstCaps *> &capsVector, const GList *padTemplates)
     412              : {
     413           52 :     for (const GList *padTemplateIter = padTemplates; padTemplateIter; padTemplateIter = padTemplateIter->next)
     414              :     {
     415           32 :         GstStaticPadTemplate *padTemplate = static_cast<GstStaticPadTemplate *>(padTemplateIter->data);
     416           32 :         if (padTemplate->direction == GST_PAD_SINK)
     417              :         {
     418           21 :             GstCaps *padTemplateCaps = m_gstWrapper->gstStaticCapsGet(&padTemplate->static_caps);
     419           21 :             if (!isCapsInVector(capsVector, padTemplateCaps))
     420              :             {
     421           19 :                 capsVector.push_back(padTemplateCaps);
     422              :             }
     423              :             else
     424              :             {
     425            2 :                 m_gstWrapper->gstCapsUnref(padTemplateCaps);
     426              :             }
     427              :         }
     428              :     }
     429           20 : }
     430              : 
     431           21 : bool GstCapabilities::isCapsInVector(const std::vector<GstCaps *> &capsVector, GstCaps *caps) const
     432              : {
     433           21 :     return std::find_if(capsVector.begin(), capsVector.end(), [&](const GstCaps *comparedCaps)
     434           56 :                         { return m_gstWrapper->gstCapsIsStrictlyEqual(caps, comparedCaps); }) != capsVector.end();
     435              : }
     436              : 
     437           60 : void GstCapabilities::waitForInitialisation()
     438              : {
     439           60 :     std::unique_lock lock{m_initialisationMutex};
     440          132 :     m_initialisationCv.wait(lock, [this]() { return m_isInitialised; });
     441           60 : }
     442              : 
     443            3 : bool GstCapabilities::isVideoMaster(bool &isVideoMaster)
     444              : {
     445            3 :     waitForInitialisation();
     446              : 
     447            3 :     GstRegistry *reg = m_gstWrapper->gstRegistryGet();
     448            3 :     if (!reg)
     449              :     {
     450            1 :         RIALTO_SERVER_LOG_ERROR("Failed get the gst registry");
     451            1 :         return false;
     452              :     }
     453            2 :     GstPluginFeature *feature{nullptr};
     454            2 :     isVideoMaster = true;
     455            2 :     if (nullptr != (feature = m_gstWrapper->gstRegistryLookupFeature(reg, "amlhalasink")))
     456              :     {
     457            1 :         isVideoMaster = false;
     458            1 :         m_gstWrapper->gstObjectUnref(feature);
     459              :     }
     460            2 :     return true;
     461              : }
     462              : 
     463              : } // namespace firebolt::rialto::server
     464              : 
     465              : // namespace firebolt::rialto::server
        

Generated by: LCOV version 2.0-1