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 : {
167 19 : gstInitialiser.waitForInitialisation();
168 19 : fillSupportedMimeTypes();
169 : }
170 :
171 13 : std::vector<std::string> GstCapabilities::getSupportedMimeTypes(MediaSourceType sourceType)
172 : {
173 13 : std::vector<std::string> supportedMimeTypesSource;
174 13 : std::string type;
175 13 : if (sourceType == MediaSourceType::VIDEO)
176 : {
177 6 : type = "video/";
178 : }
179 7 : else if (sourceType == MediaSourceType::AUDIO)
180 : {
181 6 : type = "audio/";
182 : }
183 1 : else if (sourceType == MediaSourceType::SUBTITLE)
184 : {
185 3 : return {"text/vtt", "text/ttml"};
186 : }
187 : else
188 : {
189 0 : RIALTO_SERVER_LOG_WARN("Unsupported media type");
190 0 : return {};
191 : }
192 :
193 12 : std::copy_if(m_supportedMimeTypes.begin(), m_supportedMimeTypes.end(), std::back_inserter(supportedMimeTypesSource),
194 21 : [&type](const std::string &supportedMimeType) { return supportedMimeType.find(type) == 0; });
195 :
196 12 : return supportedMimeTypesSource;
197 13 : }
198 :
199 39 : bool GstCapabilities::isMimeTypeSupported(const std::string &mimeType)
200 : {
201 39 : return m_supportedMimeTypes.find(mimeType) != m_supportedMimeTypes.end();
202 : }
203 :
204 4 : std::vector<std::string> GstCapabilities::getSupportedProperties(MediaSourceType mediaType,
205 : const std::vector<std::string> &propertyNames)
206 : {
207 : // Get gstreamer element factories. The following flag settings will fetch both SINK and DECODER types
208 : // of gstreamer classes...
209 4 : GstElementFactoryListType factoryListType{GST_ELEMENT_FACTORY_TYPE_SINK | GST_ELEMENT_FACTORY_TYPE_DECODER |
210 : GST_ELEMENT_FACTORY_TYPE_PARSER};
211 : {
212 : // If MediaSourceType::AUDIO is specified then adjust the flag so that we
213 : // restrict the list to gstreamer AUDIO element types (and likewise for video and subtitle)...
214 : static const std::unordered_map<MediaSourceType, GstElementFactoryListType>
215 : kLookupExtraConditions{{MediaSourceType::AUDIO, GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO},
216 : {MediaSourceType::VIDEO, GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO},
217 4 : {MediaSourceType::SUBTITLE, GST_ELEMENT_FACTORY_TYPE_MEDIA_SUBTITLE}};
218 4 : auto i = kLookupExtraConditions.find(mediaType);
219 4 : if (i != kLookupExtraConditions.end())
220 4 : factoryListType |= i->second;
221 : }
222 :
223 4 : GList *factories{m_gstWrapper->gstElementFactoryListGetElements(factoryListType, GST_RANK_NONE)};
224 :
225 : // Scan all returned elements for the specified properties...
226 4 : std::unordered_set<std::string> propertiesToLookFor{propertyNames.begin(), propertyNames.end()};
227 4 : std::vector<std::string> propertiesFound;
228 8 : for (GList *iter = factories; iter != nullptr && !propertiesToLookFor.empty(); iter = iter->next)
229 : {
230 4 : GstElementFactory *factory = GST_ELEMENT_FACTORY(iter->data);
231 :
232 : // WORKAROUND: initialising element "rtkv1sink" causes that another playback's video goes black
233 : // we don't need to scan this element, so ignore it
234 4 : if (std::string{GST_OBJECT_NAME(GST_OBJECT(factory))} == "rtkv1sink")
235 : {
236 1 : RIALTO_SERVER_LOG_DEBUG("Ignoring rtkv1sink element");
237 1 : continue;
238 : }
239 :
240 3 : GstElement *elementObj{nullptr};
241 :
242 : // We instantiate an object because fetching the class, even after gstPluginFeatureLoad,
243 : // was found to sometimes return a class with no properties. A code branch is
244 : // kept with this feature "supportedPropertiesViaClass"
245 3 : elementObj = m_gstWrapper->gstElementFactoryCreate(factory, nullptr);
246 3 : if (elementObj)
247 : {
248 : GParamSpec **props;
249 : guint nProps;
250 3 : props = m_glibWrapper->gObjectClassListProperties(G_OBJECT_GET_CLASS(elementObj), &nProps);
251 3 : if (props)
252 : {
253 9 : for (guint j = 0; j < nProps && !propertiesToLookFor.empty(); ++j)
254 : {
255 6 : const std::string kPropName{props[j]->name};
256 6 : auto it = propertiesToLookFor.find(kPropName);
257 6 : if (it != propertiesToLookFor.end())
258 : {
259 4 : RIALTO_SERVER_LOG_DEBUG("Found property '%s'", kPropName.c_str());
260 4 : propertiesFound.push_back(kPropName);
261 4 : propertiesToLookFor.erase(it);
262 : }
263 6 : }
264 3 : m_glibWrapper->gFree(props);
265 : }
266 3 : m_gstWrapper->gstObjectUnref(elementObj);
267 : }
268 : }
269 :
270 : // Some sinks do not specifically support the "audio-fade" property, but the mechanism is supported through the use
271 : // 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.
272 4 : if (propertiesToLookFor.find("audio-fade") != propertiesToLookFor.end())
273 : {
274 2 : bool socAudioFadeSupported = m_rdkGstreamerUtilsWrapper->isSocAudioFadeSupported();
275 2 : if (socAudioFadeSupported)
276 : {
277 1 : RIALTO_SERVER_LOG_DEBUG("Audio fade property is supported by the SoC");
278 1 : propertiesFound.push_back("audio-fade"); // Add "audio-fade" if supported by SoC
279 : }
280 : }
281 : // Cleanup
282 4 : m_gstWrapper->gstPluginFeatureListFree(factories);
283 8 : return propertiesFound;
284 4 : }
285 :
286 19 : void GstCapabilities::fillSupportedMimeTypes()
287 : {
288 19 : std::vector<GstCaps *> supportedCaps;
289 19 : appendSupportedCapsFromFactoryType(GST_ELEMENT_FACTORY_TYPE_DECODER, supportedCaps);
290 :
291 : // Only append caps from decoder parser if they can link with the decoder
292 19 : appendLinkableCapsFromParserDecoderChains(supportedCaps);
293 :
294 : // Sink caps do not require decoder support
295 19 : appendSupportedCapsFromFactoryType(GST_ELEMENT_FACTORY_TYPE_SINK, supportedCaps);
296 :
297 19 : if (supportedCaps.empty())
298 : {
299 9 : RIALTO_SERVER_LOG_WARN("There are no supported caps");
300 9 : return;
301 : }
302 :
303 10 : m_supportedMimeTypes = firebolt::rialto::server::convertFromCapsVectorToMimeSet(supportedCaps, m_gstWrapper);
304 :
305 29 : for (GstCaps *caps : supportedCaps)
306 : {
307 19 : m_gstWrapper->gstCapsUnref(caps);
308 : }
309 : }
310 :
311 19 : void GstCapabilities::appendLinkableCapsFromParserDecoderChains(std::vector<GstCaps *> &supportedCaps)
312 : {
313 19 : if (supportedCaps.empty())
314 : {
315 12 : return;
316 : }
317 :
318 9 : std::vector<GstCaps *> decoderSupportedCaps = supportedCaps;
319 :
320 : GList *parserFactories =
321 9 : m_gstWrapper->gstElementFactoryListGetElements(GST_ELEMENT_FACTORY_TYPE_PARSER, GST_RANK_MARGINAL);
322 9 : if (!parserFactories)
323 : {
324 2 : RIALTO_SERVER_LOG_WARN("Could not find any parser");
325 2 : return;
326 : }
327 :
328 15 : for (GstCaps *decoderCaps : decoderSupportedCaps)
329 : {
330 17 : for (GList *factoriesIter = parserFactories; factoriesIter; factoriesIter = factoriesIter->next)
331 : {
332 9 : GstElementFactory *factory = static_cast<GstElementFactory *>(factoriesIter->data);
333 9 : const GList *kParserPadTemplates = m_gstWrapper->gstElementFactoryGetStaticPadTemplates(factory);
334 :
335 9 : if (canCreateParserDecoderChain(decoderCaps, kParserPadTemplates))
336 : {
337 7 : addAllUniqueSinkPadsCapsToVector(supportedCaps, kParserPadTemplates);
338 : }
339 : }
340 : }
341 :
342 7 : m_gstWrapper->gstPluginFeatureListFree(parserFactories);
343 9 : }
344 :
345 38 : void GstCapabilities::appendSupportedCapsFromFactoryType(const GstElementFactoryListType &type,
346 : std::vector<GstCaps *> &supportedCaps)
347 : {
348 38 : GList *factories = m_gstWrapper->gstElementFactoryListGetElements(type, GST_RANK_MARGINAL);
349 38 : if (!factories)
350 : {
351 26 : RIALTO_SERVER_LOG_WARN("Could not find any %s", toString(type));
352 26 : return;
353 : }
354 :
355 25 : for (GList *factoriesIter = factories; factoriesIter; factoriesIter = factoriesIter->next)
356 : {
357 13 : GstElementFactory *factory = static_cast<GstElementFactory *>(factoriesIter->data);
358 13 : const GList *kPadTemplates = m_gstWrapper->gstElementFactoryGetStaticPadTemplates(factory);
359 :
360 13 : addAllUniqueSinkPadsCapsToVector(supportedCaps, kPadTemplates);
361 : }
362 :
363 12 : m_gstWrapper->gstPluginFeatureListFree(factories);
364 : }
365 :
366 9 : bool GstCapabilities::canCreateParserDecoderChain(GstCaps *decoderCaps, const GList *kParserPadTemplates)
367 : {
368 21 : for (const GList *padTemplateIter = kParserPadTemplates; padTemplateIter; padTemplateIter = padTemplateIter->next)
369 : {
370 19 : GstStaticPadTemplate *padTemplate = static_cast<GstStaticPadTemplate *>(padTemplateIter->data);
371 19 : if (padTemplate->direction == GST_PAD_SRC)
372 : {
373 10 : GstCaps *padTemplateCaps = m_gstWrapper->gstStaticCapsGet(&padTemplate->static_caps);
374 :
375 : // check if parser's src pad can be connected to decoder's sink pad
376 10 : bool canIntersect = m_gstWrapper->gstCapsCanIntersect(decoderCaps, padTemplateCaps);
377 10 : m_gstWrapper->gstCapsUnref(padTemplateCaps);
378 10 : if (canIntersect)
379 : {
380 7 : return true;
381 : }
382 : }
383 : }
384 :
385 2 : return false;
386 : }
387 :
388 20 : void GstCapabilities::addAllUniqueSinkPadsCapsToVector(std::vector<GstCaps *> &capsVector, const GList *padTemplates)
389 : {
390 52 : for (const GList *padTemplateIter = padTemplates; padTemplateIter; padTemplateIter = padTemplateIter->next)
391 : {
392 32 : GstStaticPadTemplate *padTemplate = static_cast<GstStaticPadTemplate *>(padTemplateIter->data);
393 32 : if (padTemplate->direction == GST_PAD_SINK)
394 : {
395 21 : GstCaps *padTemplateCaps = m_gstWrapper->gstStaticCapsGet(&padTemplate->static_caps);
396 21 : if (!isCapsInVector(capsVector, padTemplateCaps))
397 : {
398 19 : capsVector.push_back(padTemplateCaps);
399 : }
400 : else
401 : {
402 2 : m_gstWrapper->gstCapsUnref(padTemplateCaps);
403 : }
404 : }
405 : }
406 20 : }
407 :
408 21 : bool GstCapabilities::isCapsInVector(const std::vector<GstCaps *> &capsVector, GstCaps *caps) const
409 : {
410 21 : return std::find_if(capsVector.begin(), capsVector.end(),
411 14 : [&](const GstCaps *comparedCaps)
412 56 : { return m_gstWrapper->gstCapsIsStrictlyEqual(caps, comparedCaps); }) != capsVector.end();
413 : }
414 :
415 : } // namespace firebolt::rialto::server
416 :
417 : // namespace firebolt::rialto::server
|