LCOV - code coverage report
Current view: top level - media/server/gstplayer/source - GstCapabilities.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 75.6 % 234 177
Test Date: 2026-07-07 06:27:16 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           34 : const char *toString(const GstElementFactoryListType &listType)
      33              : {
      34           34 :     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           13 :     case GST_ELEMENT_FACTORY_TYPE_DECODER:
      45           13 :         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           21 :     case GST_ELEMENT_FACTORY_TYPE_SINK:
      81           21 :         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           23 : 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           23 :     const IGstInitialiser &gstInitialiser)
     166           23 :     : m_gstWrapper{gstWrapper}, m_glibWrapper{glibWrapper}, m_rdkGstreamerUtilsWrapper{rdkGstreamerUtilsWrapper},
     167           46 :       m_gstInitialiser{gstInitialiser}
     168              : {
     169           23 :     m_initialisationThread = std::thread(
     170           23 :         [this]()
     171              :         {
     172           23 :             std::unique_lock lock{m_initialisationMutex};
     173              : 
     174           23 :             m_gstInitialiser.waitForInitialisation();
     175           23 :             fillSupportedMimeTypes();
     176           23 :             m_isInitialised = true;
     177           23 :             m_initialisationCv.notify_all();
     178           46 :         });
     179           23 : }
     180              : 
     181           46 : GstCapabilities::~GstCapabilities()
     182              : {
     183           23 :     if (m_initialisationThread.joinable())
     184              :     {
     185           23 :         m_initialisationThread.join();
     186              :     }
     187           46 : }
     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            5 : std::vector<std::string> GstCapabilities::getSupportedProperties(MediaSourceType mediaType,
     226              :                                                                  const std::vector<std::string> &propertyNames)
     227              : {
     228            5 :     waitForInitialisation();
     229              : 
     230              :     // Get gstreamer element factories. The following flag settings will fetch both SINK and DECODER types
     231              :     // of gstreamer classes...
     232            5 :     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            7 :                                    {MediaSourceType::SUBTITLE, GST_ELEMENT_FACTORY_TYPE_MEDIA_SUBTITLE}};
     241            5 :         auto i = kLookupExtraConditions.find(mediaType);
     242            5 :         if (i != kLookupExtraConditions.end())
     243            5 :             factoryListType |= i->second;
     244              :     }
     245              : 
     246            5 :     GList *factories{m_gstWrapper->gstElementFactoryListGetElements(factoryListType, GST_RANK_NONE)};
     247              : 
     248              :     // Scan all returned elements for the specified properties...
     249           10 :     std::unordered_set<std::string> propertiesToLookFor{propertyNames.begin(), propertyNames.end()};
     250            5 :     std::vector<std::string> propertiesFound;
     251           10 :     for (GList *iter = factories; iter != nullptr && !propertiesToLookFor.empty(); iter = iter->next)
     252              :     {
     253            5 :         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           15 :         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            4 :         GType elementType = m_gstWrapper->gstElementFactoryGetElementType(factory);
     264            4 :         if (elementType == G_TYPE_INVALID)
     265            1 :             continue;
     266            3 :         gpointer elementClass = m_glibWrapper->gTypeClassRef(elementType);
     267            3 :         if (elementClass)
     268              :         {
     269              :             GParamSpec **props;
     270              :             guint nProps;
     271            3 :             props = m_glibWrapper->gObjectClassListProperties(G_OBJECT_CLASS(elementClass), &nProps);
     272            3 :             if (props)
     273              :             {
     274            9 :                 for (guint j = 0; j < nProps && !propertiesToLookFor.empty(); ++j)
     275              :                 {
     276           12 :                     std::string propName{props[j]->name};
     277            6 :                     auto it = propertiesToLookFor.find(propName);
     278            6 :                     if (it != propertiesToLookFor.end())
     279              :                     {
     280            4 :                         RIALTO_SERVER_LOG_DEBUG("Found property '%s'", propName.c_str());
     281            4 :                         propertiesFound.push_back(std::move(propName));
     282            4 :                         propertiesToLookFor.erase(it);
     283              :                     }
     284            6 :                 }
     285            3 :                 m_glibWrapper->gFree(props);
     286              :             }
     287            3 :             m_glibWrapper->gTypeClassUnref(elementClass);
     288              :         }
     289              :     }
     290              : 
     291              :     // Some sinks do not specifically support the "audio-fade" property, but the mechanism is supported through the use
     292              :     // 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.
     293           15 :     if (propertiesToLookFor.find("audio-fade") != propertiesToLookFor.end())
     294              :     {
     295            3 :         bool socAudioFadeSupported = m_rdkGstreamerUtilsWrapper->isSocAudioFadeSupported();
     296            3 :         if (socAudioFadeSupported)
     297              :         {
     298            1 :             RIALTO_SERVER_LOG_DEBUG("Audio fade property is supported by the SoC");
     299            2 :             propertiesFound.push_back("audio-fade"); // Add "audio-fade" if supported by SoC
     300              :         }
     301              :     }
     302              :     // Cleanup
     303            5 :     m_gstWrapper->gstPluginFeatureListFree(factories);
     304           10 :     return propertiesFound;
     305            5 : }
     306              : 
     307           23 : void GstCapabilities::fillSupportedMimeTypes()
     308              : {
     309           23 :     std::vector<GstCaps *> supportedCaps;
     310           23 :     appendSupportedCapsFromFactoryType(GST_ELEMENT_FACTORY_TYPE_DECODER, supportedCaps);
     311              : 
     312              :     // Only append caps from decoder parser if they can link with the decoder
     313           23 :     appendLinkableCapsFromParserDecoderChains(supportedCaps);
     314              : 
     315              :     // Sink caps do not require decoder support
     316           23 :     appendSupportedCapsFromFactoryType(GST_ELEMENT_FACTORY_TYPE_SINK, supportedCaps);
     317              : 
     318           23 :     if (supportedCaps.empty())
     319              :     {
     320           13 :         RIALTO_SERVER_LOG_WARN("There are no supported caps");
     321           13 :         return;
     322              :     }
     323              : 
     324           10 :     m_supportedMimeTypes = firebolt::rialto::server::convertFromCapsVectorToMimeSet(supportedCaps, m_gstWrapper);
     325              : 
     326           29 :     for (GstCaps *caps : supportedCaps)
     327              :     {
     328           19 :         m_gstWrapper->gstCapsUnref(caps);
     329              :     }
     330           23 : }
     331              : 
     332           23 : void GstCapabilities::appendLinkableCapsFromParserDecoderChains(std::vector<GstCaps *> &supportedCaps)
     333              : {
     334           23 :     if (supportedCaps.empty())
     335              :     {
     336           16 :         return;
     337              :     }
     338              : 
     339            9 :     std::vector<GstCaps *> decoderSupportedCaps = supportedCaps;
     340              : 
     341              :     GList *parserFactories =
     342            9 :         m_gstWrapper->gstElementFactoryListGetElements(GST_ELEMENT_FACTORY_TYPE_PARSER, GST_RANK_MARGINAL);
     343            9 :     if (!parserFactories)
     344              :     {
     345            2 :         RIALTO_SERVER_LOG_WARN("Could not find any parser");
     346            2 :         return;
     347              :     }
     348              : 
     349           15 :     for (GstCaps *decoderCaps : decoderSupportedCaps)
     350              :     {
     351           17 :         for (GList *factoriesIter = parserFactories; factoriesIter; factoriesIter = factoriesIter->next)
     352              :         {
     353            9 :             GstElementFactory *factory = static_cast<GstElementFactory *>(factoriesIter->data);
     354            9 :             const GList *kParserPadTemplates = m_gstWrapper->gstElementFactoryGetStaticPadTemplates(factory);
     355              : 
     356            9 :             if (canCreateParserDecoderChain(decoderCaps, kParserPadTemplates))
     357              :             {
     358            7 :                 addAllUniqueSinkPadsCapsToVector(supportedCaps, kParserPadTemplates);
     359              :             }
     360              :         }
     361              :     }
     362              : 
     363            7 :     m_gstWrapper->gstPluginFeatureListFree(parserFactories);
     364            9 : }
     365              : 
     366           46 : void GstCapabilities::appendSupportedCapsFromFactoryType(const GstElementFactoryListType &type,
     367              :                                                          std::vector<GstCaps *> &supportedCaps)
     368              : {
     369           46 :     GList *factories = m_gstWrapper->gstElementFactoryListGetElements(type, GST_RANK_MARGINAL);
     370           46 :     if (!factories)
     371              :     {
     372           34 :         RIALTO_SERVER_LOG_WARN("Could not find any %s", toString(type));
     373           34 :         return;
     374              :     }
     375              : 
     376           25 :     for (GList *factoriesIter = factories; factoriesIter; factoriesIter = factoriesIter->next)
     377              :     {
     378           13 :         GstElementFactory *factory = static_cast<GstElementFactory *>(factoriesIter->data);
     379           13 :         const GList *kPadTemplates = m_gstWrapper->gstElementFactoryGetStaticPadTemplates(factory);
     380              : 
     381           13 :         addAllUniqueSinkPadsCapsToVector(supportedCaps, kPadTemplates);
     382              :     }
     383              : 
     384           12 :     m_gstWrapper->gstPluginFeatureListFree(factories);
     385              : }
     386              : 
     387            9 : bool GstCapabilities::canCreateParserDecoderChain(GstCaps *decoderCaps, const GList *kParserPadTemplates)
     388              : {
     389           21 :     for (const GList *padTemplateIter = kParserPadTemplates; padTemplateIter; padTemplateIter = padTemplateIter->next)
     390              :     {
     391           19 :         GstStaticPadTemplate *padTemplate = static_cast<GstStaticPadTemplate *>(padTemplateIter->data);
     392           19 :         if (padTemplate->direction == GST_PAD_SRC)
     393              :         {
     394           10 :             GstCaps *padTemplateCaps = m_gstWrapper->gstStaticCapsGet(&padTemplate->static_caps);
     395              : 
     396              :             // check if parser's src pad can be connected to decoder's sink pad
     397           10 :             bool canIntersect = m_gstWrapper->gstCapsCanIntersect(decoderCaps, padTemplateCaps);
     398           10 :             m_gstWrapper->gstCapsUnref(padTemplateCaps);
     399           10 :             if (canIntersect)
     400              :             {
     401            7 :                 return true;
     402              :             }
     403              :         }
     404              :     }
     405              : 
     406            2 :     return false;
     407              : }
     408              : 
     409           20 : void GstCapabilities::addAllUniqueSinkPadsCapsToVector(std::vector<GstCaps *> &capsVector, const GList *padTemplates)
     410              : {
     411           52 :     for (const GList *padTemplateIter = padTemplates; padTemplateIter; padTemplateIter = padTemplateIter->next)
     412              :     {
     413           32 :         GstStaticPadTemplate *padTemplate = static_cast<GstStaticPadTemplate *>(padTemplateIter->data);
     414           32 :         if (padTemplate->direction == GST_PAD_SINK)
     415              :         {
     416           21 :             GstCaps *padTemplateCaps = m_gstWrapper->gstStaticCapsGet(&padTemplate->static_caps);
     417           21 :             if (!isCapsInVector(capsVector, padTemplateCaps))
     418              :             {
     419           19 :                 capsVector.push_back(padTemplateCaps);
     420              :             }
     421              :             else
     422              :             {
     423            2 :                 m_gstWrapper->gstCapsUnref(padTemplateCaps);
     424              :             }
     425              :         }
     426              :     }
     427           20 : }
     428              : 
     429           21 : bool GstCapabilities::isCapsInVector(const std::vector<GstCaps *> &capsVector, GstCaps *caps) const
     430              : {
     431           21 :     return std::find_if(capsVector.begin(), capsVector.end(), [&](const GstCaps *comparedCaps)
     432           56 :                         { return m_gstWrapper->gstCapsIsStrictlyEqual(caps, comparedCaps); }) != capsVector.end();
     433              : }
     434              : 
     435           61 : void GstCapabilities::waitForInitialisation()
     436              : {
     437           61 :     std::unique_lock lock{m_initialisationMutex};
     438          134 :     m_initialisationCv.wait(lock, [this]() { return m_isInitialised; });
     439           61 : }
     440              : 
     441            3 : bool GstCapabilities::isVideoMaster(bool &isVideoMaster)
     442              : {
     443            3 :     waitForInitialisation();
     444              : 
     445            3 :     GstRegistry *reg = m_gstWrapper->gstRegistryGet();
     446            3 :     if (!reg)
     447              :     {
     448            1 :         RIALTO_SERVER_LOG_ERROR("Failed get the gst registry");
     449            1 :         return false;
     450              :     }
     451            2 :     GstPluginFeature *feature{nullptr};
     452            2 :     isVideoMaster = true;
     453            2 :     if (nullptr != (feature = m_gstWrapper->gstRegistryLookupFeature(reg, "amlhalasink")))
     454              :     {
     455            1 :         isVideoMaster = false;
     456            1 :         m_gstWrapper->gstObjectUnref(feature);
     457              :     }
     458            2 :     return true;
     459              : }
     460              : 
     461              : } // namespace firebolt::rialto::server
     462              : 
     463              : // namespace firebolt::rialto::server
        

Generated by: LCOV version 2.0-1