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
|