Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qgstreamerformatinfo.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <common/qglist_helper_p.h>
7
8#include <QtCore/qset.h>
9
10#include <gst/gst.h>
11
13
14QMediaFormat::AudioCodec QGstreamerFormatInfo::audioCodecForCaps(QGstStructureView structure)
15{
16 using namespace std::string_view_literals;
17 const char *name = structure.name().data();
18
19 if (!name || (strncmp(name, "audio/", 6) != 0))
20 return QMediaFormat::AudioCodec::Unspecified;
21 name += 6;
22 if (name == "mpeg"sv) {
23 auto version = structure["mpegversion"].toInt();
24 if (version == 1) {
25 auto layer = structure["layer"];
26 if (!layer.isNull())
27 return QMediaFormat::AudioCodec::MP3;
28 }
29 if (version == 4)
30 return QMediaFormat::AudioCodec::AAC;
31 return QMediaFormat::AudioCodec::Unspecified;
32 }
33 if (name == "x-ac3"sv)
34 return QMediaFormat::AudioCodec::AC3;
35
36 if (name == "x-eac3"sv)
37 return QMediaFormat::AudioCodec::EAC3;
38
39 if (name == "x-flac"sv)
40 return QMediaFormat::AudioCodec::FLAC;
41
42 if (name == "x-alac"sv)
43 return QMediaFormat::AudioCodec::ALAC;
44
45 if (name == "x-true-hd"sv)
46 return QMediaFormat::AudioCodec::DolbyTrueHD;
47
48 if (name == "x-vorbis"sv)
49 return QMediaFormat::AudioCodec::Vorbis;
50
51 if (name == "x-opus"sv)
52 return QMediaFormat::AudioCodec::Opus;
53
54 if (name == "x-wav"sv)
55 return QMediaFormat::AudioCodec::Wave;
56
57 if (name == "x-wma"sv)
58 return QMediaFormat::AudioCodec::WMA;
59
60 return QMediaFormat::AudioCodec::Unspecified;
61}
62
63QMediaFormat::VideoCodec QGstreamerFormatInfo::videoCodecForCaps(QGstStructureView structure)
64{
65 using namespace std::string_view_literals;
66 const char *name = structure.name().data();
67
68 if (!name || (strncmp(name, "video/", 6) != 0))
69 return QMediaFormat::VideoCodec::Unspecified;
70 name += 6;
71
72 if (name == "mpeg"sv) {
73 auto version = structure["mpegversion"].toInt();
74 if (version == 1)
75 return QMediaFormat::VideoCodec::MPEG1;
76 if (version == 2)
77 return QMediaFormat::VideoCodec::MPEG2;
78 if (version == 4)
79 return QMediaFormat::VideoCodec::MPEG4;
80 return QMediaFormat::VideoCodec::Unspecified;
81 }
82 if (name == "x-h264"sv)
83 return QMediaFormat::VideoCodec::H264;
84
85 if (name == "x-h265"sv)
86 return QMediaFormat::VideoCodec::H265;
87
88 if (name == "x-vp8"sv)
89 return QMediaFormat::VideoCodec::VP8;
90
91 if (name == "x-vp9"sv)
92 return QMediaFormat::VideoCodec::VP9;
93
94 if (name == "x-av1"sv)
95 return QMediaFormat::VideoCodec::AV1;
96
97 if (name == "x-theora"sv)
98 return QMediaFormat::VideoCodec::Theora;
99
100 if (name == "x-jpeg"sv)
101 return QMediaFormat::VideoCodec::MotionJPEG;
102
103 if (name == "x-wmv"sv)
104 return QMediaFormat::VideoCodec::WMV;
105
106 return QMediaFormat::VideoCodec::Unspecified;
107}
108
109QMediaFormat::FileFormat QGstreamerFormatInfo::fileFormatForCaps(QGstStructureView structure)
110{
111 using namespace std::string_view_literals;
112 const char *name = structure.name().data();
113
114 if (name == "video/x-ms-asf"sv)
115 return QMediaFormat::FileFormat::WMV;
116
117 if (name == "video/x-msvideo"sv)
118 return QMediaFormat::FileFormat::AVI;
119
120 if (name == "video/x-matroska"sv)
121 return QMediaFormat::FileFormat::Matroska;
122
123 if (name == "video/quicktime"sv) {
124 const char *variant = structure["variant"].toString();
125 if (!variant)
126 return QMediaFormat::FileFormat::QuickTime;
127 if (variant == "iso"sv)
128 return QMediaFormat::FileFormat::MPEG4;
129 }
130 if (name == "video/ogg"sv)
131 return QMediaFormat::FileFormat::Ogg;
132
133 if (name == "video/webm"sv)
134 return QMediaFormat::FileFormat::WebM;
135
136 if (name == "audio/x-m4a"sv)
137 return QMediaFormat::FileFormat::Mpeg4Audio;
138
139 if (name == "audio/x-wav"sv)
140 return QMediaFormat::FileFormat::Wave;
141
142 if (name == "audio/mpeg"sv) {
143 auto mpegversion = structure["mpegversion"].toInt();
144 if (mpegversion == 1) {
145 auto layer = structure["layer"];
146 if (!layer.isNull())
147 return QMediaFormat::FileFormat::MP3;
148 }
149 }
150
151 if (name == "audio/aac"sv)
152 return QMediaFormat::FileFormat::AAC;
153
154 if (name == "audio/x-ms-wma"sv)
155 return QMediaFormat::FileFormat::WMA;
156
157 if (name == "audio/x-flac"sv)
158 return QMediaFormat::FileFormat::FLAC;
159
160 return QMediaFormat::UnspecifiedFormat;
161}
162
163
164QImageCapture::FileFormat QGstreamerFormatInfo::imageFormatForCaps(QGstStructureView structure)
165{
166 using namespace std::string_view_literals;
167 const char *name = structure.name().data();
168
169 if (name == "image/jpeg"sv)
170 return QImageCapture::JPEG;
171
172 if (name == "image/png"sv)
173 return QImageCapture::PNG;
174
175 if (name == "image/webp"sv)
176 return QImageCapture::WebP;
177
178 if (name == "image/tiff"sv)
179 return QImageCapture::Tiff;
180
181 return QImageCapture::UnspecifiedFormat;
182}
183
184static std::pair<QList<QMediaFormat::AudioCodec>, QList<QMediaFormat::VideoCodec>>
185getCodecsList(bool decode)
186{
187 QList<QMediaFormat::AudioCodec> audio;
188 QList<QMediaFormat::VideoCodec> video;
189
190 GstPadDirection padDirection = decode ? GST_PAD_SINK : GST_PAD_SRC;
191
192 GList *elementList = gst_element_factory_list_get_elements(decode ? GST_ELEMENT_FACTORY_TYPE_DECODER : GST_ELEMENT_FACTORY_TYPE_ENCODER,
193 GST_RANK_MARGINAL);
194
195 for (GstElementFactory *factory :
196 QGstUtils::GListRangeAdaptor<GstElementFactory *>(elementList)) {
197 for (GstStaticPadTemplate *padTemplate :
198 QGstUtils::GListConstRangeAdaptor<GstStaticPadTemplate *>(
199 gst_element_factory_get_static_pad_templates(factory))) {
200 if (padTemplate->direction == padDirection) {
201 auto caps = QGstCaps(gst_static_caps_get(&padTemplate->static_caps), QGstCaps::HasRef);
202
203 for (int i = 0; i < caps.size(); i++) {
204 QGstStructureView structure = caps.at(i);
205 auto a = QGstreamerFormatInfo::audioCodecForCaps(structure);
206 if (a != QMediaFormat::AudioCodec::Unspecified && !audio.contains(a))
207 audio.append(a);
208 auto v = QGstreamerFormatInfo::videoCodecForCaps(structure);
209 if (v != QMediaFormat::VideoCodec::Unspecified && !video.contains(v))
210 video.append(v);
211 }
212 }
213 }
214 }
215 gst_plugin_feature_list_free(elementList);
216 return {audio, video};
217}
218
219QList<QGstreamerFormatInfo::CodecMap>
220QGstreamerFormatInfo::getCodecMaps(QMediaFormat::ConversionMode conversionMode,
221 QList<QMediaFormat::AudioCodec> supportedAudioCodecs,
222 QList<QMediaFormat::VideoCodec> supportedVideoCodecs)
223{
224 QList<QGstreamerFormatInfo::CodecMap> maps;
225
226 GstPadDirection dataPadDirection =
227 (conversionMode == QMediaFormat::Decode) ? GST_PAD_SINK : GST_PAD_SRC;
228
229 auto encodableFactoryTypes =
230 (GST_ELEMENT_FACTORY_TYPE_MUXER | GST_ELEMENT_FACTORY_TYPE_PAYLOADER
231 | GST_ELEMENT_FACTORY_TYPE_ENCRYPTOR | GST_ELEMENT_FACTORY_TYPE_ENCODER);
232 GList *elementList = gst_element_factory_list_get_elements(
233 (conversionMode == QMediaFormat::Decode) ? GST_ELEMENT_FACTORY_TYPE_DECODABLE
234 : encodableFactoryTypes,
235 GST_RANK_MARGINAL);
236
237 for (GstElementFactory *factory :
238 QGstUtils::GListRangeAdaptor<GstElementFactory *>(elementList)) {
239 QList<QMediaFormat::FileFormat> fileFormats;
240
241 for (GstStaticPadTemplate *padTemplate :
242 QGstUtils::GListConstRangeAdaptor<GstStaticPadTemplate *>(
243 gst_element_factory_get_static_pad_templates(factory))) {
244
245 // Check pads on data side for file formats, except for parsers check source side
246 if (padTemplate->direction == dataPadDirection
247 || (gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_PARSER)
248 && padTemplate->direction == GST_PAD_SRC)) {
249 auto caps = QGstCaps(gst_static_caps_get(&padTemplate->static_caps), QGstCaps::HasRef);
250
251 for (int i = 0; i < caps.size(); i++) {
252 QGstStructureView structure = caps.at(i);
253 auto fmt = fileFormatForCaps(structure);
254 if (fmt != QMediaFormat::UnspecifiedFormat)
255 fileFormats.append(fmt);
256 }
257 }
258 }
259
260 if (fileFormats.isEmpty())
261 continue;
262
263 QList<QMediaFormat::AudioCodec> audioCodecs;
264 QList<QMediaFormat::VideoCodec> videoCodecs;
265
266 for (GstStaticPadTemplate *padTemplate :
267 QGstUtils::GListConstRangeAdaptor<GstStaticPadTemplate *>(
268 gst_element_factory_get_static_pad_templates(factory))) {
269
270 // check the other side for supported inputs/outputs
271 if (padTemplate->direction != dataPadDirection) {
272 auto caps = QGstCaps(gst_static_caps_get(&padTemplate->static_caps), QGstCaps::HasRef);
273
274 bool acceptsRawAudio = false;
275 for (int i = 0; i < caps.size(); i++) {
276 QGstStructureView structure = caps.at(i);
277 if (structure.name() == "audio/x-raw")
278 acceptsRawAudio = true;
279 auto audio = audioCodecForCaps(structure);
280 if (audio != QMediaFormat::AudioCodec::Unspecified && supportedAudioCodecs.contains(audio))
281 audioCodecs.append(audio);
282 auto video = videoCodecForCaps(structure);
283 if (video != QMediaFormat::VideoCodec::Unspecified && supportedVideoCodecs.contains(video))
284 videoCodecs.append(video);
285 }
286 if (acceptsRawAudio && fileFormats.size() == 1) {
287 switch (fileFormats.at(0)) {
288 case QMediaFormat::Mpeg4Audio:
289 default:
290 break;
291 case QMediaFormat::MP3:
292 audioCodecs.append(QMediaFormat::AudioCodec::MP3);
293 break;
294 case QMediaFormat::FLAC:
295 audioCodecs.append(QMediaFormat::AudioCodec::FLAC);
296 break;
297 case QMediaFormat::Wave:
298 audioCodecs.append(QMediaFormat::AudioCodec::Wave);
299 break;
300 }
301 }
302 }
303 }
304 if (!audioCodecs.isEmpty() || !videoCodecs.isEmpty()) {
305 for (auto f : std::as_const(fileFormats)) {
306 maps.append({f, audioCodecs, videoCodecs});
307 if (f == QMediaFormat::MPEG4 && !fileFormats.contains(QMediaFormat::Mpeg4Audio)) {
308 maps.append({QMediaFormat::Mpeg4Audio, audioCodecs, {}});
309 if (audioCodecs.contains(QMediaFormat::AudioCodec::AAC))
310 maps.append({QMediaFormat::AAC, { QMediaFormat::AudioCodec::AAC }, {}});
311 } else if (f == QMediaFormat::WMV && !fileFormats.contains(QMediaFormat::WMA)) {
312 maps.append({QMediaFormat::WMA, audioCodecs, {}});
313 }
314 }
315 }
316 }
317 gst_plugin_feature_list_free(elementList);
318 return maps;
319}
320
322{
323 QSet<QImageCapture::FileFormat> formats;
324
325 GList *elementList = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_ENCODER,
326 GST_RANK_MARGINAL);
327
328 for (GstElementFactory *factory :
329 QGstUtils::GListRangeAdaptor<GstElementFactory *>(elementList)) {
330
331 for (GstStaticPadTemplate *padTemplate :
332 QGstUtils::GListConstRangeAdaptor<GstStaticPadTemplate *>(
333 gst_element_factory_get_static_pad_templates(factory))) {
334 if (padTemplate->direction == GST_PAD_SRC) {
335 QGstCaps caps = QGstCaps(gst_static_caps_get(&padTemplate->static_caps), QGstCaps::HasRef);
336
337 for (int i = 0; i < caps.size(); i++) {
338 QGstStructureView structure = caps.at(i);
339 auto f = QGstreamerFormatInfo::imageFormatForCaps(structure);
340 if (f != QImageCapture::UnspecifiedFormat) {
341// qDebug() << structure.toString() << f;
342 formats.insert(f);
343 }
344 }
345 }
346 }
347 }
348 gst_plugin_feature_list_free(elementList);
349 return formats.values();
350}
351
352#if 0
353static void dumpAudioCodecs(const QList<QMediaFormat::AudioCodec> &codecList)
354{
355 qDebug() << "Audio codecs:";
356 for (const auto &c : codecList)
357 qDebug() << " " << QMediaFormat::audioCodecName(c);
358}
359
360static void dumpVideoCodecs(const QList<QMediaFormat::VideoCodec> &codecList)
361{
362 qDebug() << "Video codecs:";
363 for (const auto &c : codecList)
364 qDebug() << " " << QMediaFormat::videoCodecName(c);
365}
366
367static void dumpMuxers(const QList<QPlatformMediaFormatInfo::CodecMap> &muxerList)
368{
369 for (const auto &m : muxerList) {
370 qDebug() << " " << QMediaFormat::fileFormatName(m.format);
371 qDebug() << " Audio";
372 for (const auto &a : m.audio)
373 qDebug() << " " << QMediaFormat::audioCodecName(a);
374 qDebug() << " Video";
375 for (const auto &v : m.video)
376 qDebug() << " " << QMediaFormat::videoCodecName(v);
377 }
378
379}
380#endif
381
383{
384 auto codecs = getCodecsList(/*decode = */ true);
385 decoders = getCodecMaps(QMediaFormat::Decode, codecs.first, codecs.second);
386
387 codecs = getCodecsList(/*decode = */ false);
388 encoders = getCodecMaps(QMediaFormat::Encode, codecs.first, codecs.second);
389// dumpAudioCodecs(codecs.first);
390// dumpVideoCodecs(codecs.second);
391// dumpMuxers(encoders);
392
393 imageFormats = getImageFormatList();
394}
395
397
398QGstCaps QGstreamerFormatInfo::formatCaps(const QMediaFormat &f) const
399{
400 auto format = f.fileFormat();
401 Q_ASSERT(format != QMediaFormat::UnspecifiedFormat);
402
403 const char *capsForFormat[QMediaFormat::LastFileFormat + 1] = {
404 "video/x-ms-asf", // WMV
405 "video/x-msvideo", // AVI
406 "video/x-matroska", // Matroska
407 "video/quicktime, variant=(string)iso", // MPEG4
408 "video/ogg", // Ogg
409 "video/quicktime", // QuickTime
410 "video/webm", // WebM
411 "video/quicktime, variant=(string)iso", // Mpeg4Audio is the same is mp4...
412 "video/quicktime, variant=(string)iso", // AAC is also an MP4 container
413 "video/x-ms-asf", // WMA, same as WMV
414 "audio/mpeg, mpegversion=(int)1, layer=(int)3", // MP3
415 "audio/x-flac", // FLAC
416 "audio/x-wav" // Wave
417 };
418 return QGstCaps(gst_caps_from_string(capsForFormat[format]), QGstCaps::HasRef);
419}
420
421QGstCaps QGstreamerFormatInfo::audioCaps(const QMediaFormat &f) const
422{
423 auto codec = f.audioCodec();
424 if (codec == QMediaFormat::AudioCodec::Unspecified)
425 return {};
426
427 const char *capsForCodec[(int)QMediaFormat::AudioCodec::LastAudioCodec + 1] = {
428 "audio/mpeg, mpegversion=(int)1, layer=(int)3", // MP3
429 "audio/mpeg, mpegversion=(int)4", // AAC
430 "audio/x-ac3", // AC3
431 "audio/x-eac3", // EAC3
432 "audio/x-flac", // FLAC
433 "audio/x-true-hd", // DolbyTrueHD
434 "audio/x-opus", // Opus
435 "audio/x-vorbis", // Vorbis
436 "audio/x-raw", // WAVE
437 "audio/x-wma", // WMA
438 "audio/x-alac", // ALAC
439 };
440 return QGstCaps(gst_caps_from_string(capsForCodec[(int)codec]), QGstCaps::HasRef);
441}
442
443QGstCaps QGstreamerFormatInfo::videoCaps(const QMediaFormat &f) const
444{
445 auto codec = f.videoCodec();
446 if (codec == QMediaFormat::VideoCodec::Unspecified)
447 return {};
448
449 const char *capsForCodec[(int)QMediaFormat::VideoCodec::LastVideoCodec + 1] = {
450 "video/mpeg, mpegversion=(int)1", // MPEG1,
451 "video/mpeg, mpegversion=(int)2", // MPEG2,
452 "video/mpeg, mpegversion=(int)4", // MPEG4,
453 "video/x-h264", // H264,
454 "video/x-h265", // H265,
455 "video/x-vp8", // VP8,
456 "video/x-vp9", // VP9,
457 "video/x-av1", // AV1,
458 "video/x-theora", // Theora,
459 "audio/x-wmv", // WMV
460 "video/x-jpeg", // MotionJPEG,
461 };
462 return QGstCaps(gst_caps_from_string(capsForCodec[(int)codec]), QGstCaps::HasRef);
463}
464
465QT_END_NAMESPACE
const char * toString() const
Definition qgst.cpp:102
QGValue operator[](const char *fieldname) const
Definition qgst.cpp:184
QGstCaps formatCaps(const QMediaFormat &f) const
QGstCaps videoCaps(const QMediaFormat &f) const
~QGstreamerFormatInfo() override
QGstCaps audioCaps(const QMediaFormat &f) const
static QList< QImageCapture::FileFormat > getImageFormatList()