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
|