1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qgstcodecsinfo_p.h"
41#include "qgstutils_p.h"
42#include <QtCore/qset.h>
43
44#include <gst/pbutils/pbutils.h>
45
46static QSet<QString> streamTypes(GstElementFactory *factory, GstPadDirection direction)
47{
48 QSet<QString> types;
49 const GList *pads = gst_element_factory_get_static_pad_templates(factory);
50 for (const GList *pad = pads; pad; pad = g_list_next(pad)) {
51 GstStaticPadTemplate *templ = reinterpret_cast<GstStaticPadTemplate *>(pad->data);
52 if (templ->direction == direction) {
53 GstCaps *caps = gst_static_caps_get(static_caps: &templ->static_caps);
54 for (uint i = 0; i < gst_caps_get_size(caps); ++i) {
55 GstStructure *structure = gst_caps_get_structure(caps, index: i);
56 types.insert(value: QString::fromUtf8(str: gst_structure_get_name(structure)));
57 }
58 gst_caps_unref(caps);
59 }
60 }
61
62 return types;
63}
64
65QGstCodecsInfo::QGstCodecsInfo(QGstCodecsInfo::ElementType elementType)
66{
67 updateCodecs(elementType);
68 for (auto &codec : supportedCodecs()) {
69 GstElementFactory *factory = gst_element_factory_find(name: codecElement(codec).constData());
70 if (factory) {
71 GstPadDirection direction = elementType == Muxer ? GST_PAD_SINK : GST_PAD_SRC;
72 m_streamTypes.insert(akey: codec, avalue: streamTypes(factory, direction));
73 gst_object_unref(GST_OBJECT(factory));
74 }
75 }
76}
77
78QStringList QGstCodecsInfo::supportedCodecs() const
79{
80 return m_codecs;
81}
82
83QString QGstCodecsInfo::codecDescription(const QString &codec) const
84{
85 return m_codecInfo.value(akey: codec).description;
86}
87
88QByteArray QGstCodecsInfo::codecElement(const QString &codec) const
89
90{
91 return m_codecInfo.value(akey: codec).elementName;
92}
93
94QStringList QGstCodecsInfo::codecOptions(const QString &codec) const
95{
96 QStringList options;
97
98 QByteArray elementName = m_codecInfo.value(akey: codec).elementName;
99 if (elementName.isEmpty())
100 return options;
101
102 GstElement *element = gst_element_factory_make(factoryname: elementName, name: nullptr);
103 if (element) {
104 guint numProperties;
105 GParamSpec **properties = g_object_class_list_properties(G_OBJECT_GET_CLASS(element),
106 n_properties: &numProperties);
107 for (guint j = 0; j < numProperties; ++j) {
108 GParamSpec *property = properties[j];
109 // ignore some properties
110 if (strcmp(s1: property->name, s2: "name") == 0 || strcmp(s1: property->name, s2: "parent") == 0)
111 continue;
112
113 options.append(t: QLatin1String(property->name));
114 }
115 g_free(mem: properties);
116 gst_object_unref(object: element);
117 }
118
119 return options;
120}
121
122void QGstCodecsInfo::updateCodecs(ElementType elementType)
123{
124 m_codecs.clear();
125 m_codecInfo.clear();
126
127 GList *elements = elementFactories(elementType);
128
129 QSet<QByteArray> fakeEncoderMimeTypes;
130 fakeEncoderMimeTypes << "unknown/unknown"
131 << "audio/x-raw-int" << "audio/x-raw-float"
132 << "video/x-raw-yuv" << "video/x-raw-rgb";
133
134 QSet<QByteArray> fieldsToAdd;
135 fieldsToAdd << "mpegversion" << "layer" << "layout" << "raversion"
136 << "wmaversion" << "wmvversion" << "variant" << "systemstream";
137
138 GList *element = elements;
139 while (element) {
140 GstElementFactory *factory = (GstElementFactory *)element->data;
141 element = element->next;
142
143 const GList *padTemplates = gst_element_factory_get_static_pad_templates(factory);
144 while (padTemplates) {
145 GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *)padTemplates->data;
146 padTemplates = padTemplates->next;
147
148 if (padTemplate->direction == GST_PAD_SRC) {
149 GstCaps *caps = gst_static_caps_get(static_caps: &padTemplate->static_caps);
150 for (uint i=0; i<gst_caps_get_size(caps); i++) {
151 const GstStructure *structure = gst_caps_get_structure(caps, index: i);
152
153 //skip "fake" encoders
154 if (fakeEncoderMimeTypes.contains(value: gst_structure_get_name(structure)))
155 continue;
156
157 GstStructure *newStructure = qt_gst_structure_new_empty(name: gst_structure_get_name(structure));
158
159 //add structure fields to distinguish between formats with similar mime types,
160 //like audio/mpeg
161 for (int j=0; j<gst_structure_n_fields(structure); j++) {
162 const gchar* fieldName = gst_structure_nth_field_name(structure, index: j);
163 if (fieldsToAdd.contains(value: fieldName)) {
164 const GValue *value = gst_structure_get_value(structure, fieldname: fieldName);
165 GType valueType = G_VALUE_TYPE(value);
166
167 //don't add values of range type,
168 //gst_pb_utils_get_codec_description complains about not fixed caps
169
170 if (valueType != GST_TYPE_INT_RANGE && valueType != GST_TYPE_DOUBLE_RANGE &&
171 valueType != GST_TYPE_FRACTION_RANGE && valueType != GST_TYPE_LIST &&
172 valueType != GST_TYPE_ARRAY)
173 gst_structure_set_value(structure: newStructure, fieldname: fieldName, value);
174 }
175 }
176
177 GstCaps *newCaps = gst_caps_new_full(struct1: newStructure, nullptr);
178
179 gchar *capsString = gst_caps_to_string(caps: newCaps);
180 QString codec = QLatin1String(capsString);
181 if (capsString)
182 g_free(mem: capsString);
183 GstRank rank = GstRank(gst_plugin_feature_get_rank(GST_PLUGIN_FEATURE(factory)));
184
185 // If two elements provide the same codec, use the highest ranked one
186 QMap<QString, CodecInfo>::const_iterator it = m_codecInfo.constFind(akey: codec);
187 if (it == m_codecInfo.constEnd() || it->rank < rank) {
188 if (it == m_codecInfo.constEnd())
189 m_codecs.append(t: codec);
190
191 CodecInfo info;
192 info.elementName = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory));
193
194 gchar *description = gst_pb_utils_get_codec_description(caps: newCaps);
195 info.description = QString::fromUtf8(str: description);
196 if (description)
197 g_free(mem: description);
198
199 info.rank = rank;
200
201 m_codecInfo.insert(akey: codec, avalue: info);
202 }
203
204 gst_caps_unref(caps: newCaps);
205 }
206 gst_caps_unref(caps);
207 }
208 }
209 }
210
211 gst_plugin_feature_list_free(list: elements);
212}
213
214#if !GST_CHECK_VERSION(0, 10, 31)
215static gboolean element_filter(GstPluginFeature *feature, gpointer user_data)
216{
217 if (Q_UNLIKELY(!GST_IS_ELEMENT_FACTORY(feature)))
218 return FALSE;
219
220 const QGstCodecsInfo::ElementType type = *reinterpret_cast<QGstCodecsInfo::ElementType *>(user_data);
221
222 const gchar *klass = gst_element_factory_get_klass(GST_ELEMENT_FACTORY(feature));
223 if (type == QGstCodecsInfo::AudioEncoder && !(strstr(klass, "Encoder") && strstr(klass, "Audio")))
224 return FALSE;
225 if (type == QGstCodecsInfo::VideoEncoder && !(strstr(klass, "Encoder") && strstr(klass, "Video")))
226 return FALSE;
227 if (type == QGstCodecsInfo::Muxer && !strstr(klass, "Muxer"))
228 return FALSE;
229
230 guint rank = gst_plugin_feature_get_rank(feature);
231 if (rank < GST_RANK_MARGINAL)
232 return FALSE;
233
234 return TRUE;
235}
236
237static gint compare_plugin_func(const void *item1, const void *item2)
238{
239 GstPluginFeature *f1 = reinterpret_cast<GstPluginFeature *>(const_cast<void *>(item1));
240 GstPluginFeature *f2 = reinterpret_cast<GstPluginFeature *>(const_cast<void *>(item2));
241
242 gint diff = gst_plugin_feature_get_rank(f2) - gst_plugin_feature_get_rank(f1);
243 if (diff != 0)
244 return diff;
245
246 return strcmp(gst_plugin_feature_get_name(f1), gst_plugin_feature_get_name (f2));
247}
248#endif
249
250GList *QGstCodecsInfo::elementFactories(ElementType elementType) const
251{
252#if GST_CHECK_VERSION(0,10,31)
253 GstElementFactoryListType gstElementType = 0;
254 switch (elementType) {
255 case AudioEncoder:
256 gstElementType = GST_ELEMENT_FACTORY_TYPE_AUDIO_ENCODER;
257 break;
258 case VideoEncoder:
259 gstElementType = GST_ELEMENT_FACTORY_TYPE_VIDEO_ENCODER;
260 break;
261 case Muxer:
262 gstElementType = GST_ELEMENT_FACTORY_TYPE_MUXER;
263 break;
264 }
265
266 GList *list = gst_element_factory_list_get_elements(type: gstElementType, minrank: GST_RANK_MARGINAL);
267 if (elementType == AudioEncoder) {
268 // Manually add "audioconvert" to the list
269 // to allow linking with various containers.
270 auto factory = gst_element_factory_find(name: "audioconvert");
271 if (factory)
272 list = g_list_prepend(list, data: factory);
273 }
274
275 return list;
276#else
277 GList *result = gst_registry_feature_filter(gst_registry_get_default(),
278 element_filter,
279 FALSE, &elementType);
280 result = g_list_sort(result, compare_plugin_func);
281 return result;
282#endif
283}
284
285QSet<QString> QGstCodecsInfo::supportedStreamTypes(const QString &codec) const
286{
287 return m_streamTypes.value(akey: codec);
288}
289
290QStringList QGstCodecsInfo::supportedCodecs(const QSet<QString> &types) const
291{
292 QStringList result;
293 for (auto &candidate : supportedCodecs()) {
294 auto candidateTypes = supportedStreamTypes(codec: candidate);
295 if (candidateTypes.intersects(other: types))
296 result << candidate;
297 }
298
299 return result;
300}
301

source code of qtmultimedia/src/gsttools/qgstcodecsinfo.cpp