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
|