LCOV - code coverage report
Current view: top level - media/server/gstplayer/source - GstCapabilities.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 74.0 % 219 162
Test Date: 2025-03-21 11:02:39 Functions: 100.0 % 19 19

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

Generated by: LCOV version 2.0-1