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 const QList<QMediaFormat::AudioCodec> &supportedAudioCodecs,
222 const 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 // QTBUG-131726: encodebin rejects muxers for x-ms-asf, so we should ignore WMV/WMA
307 // for encoding
308 if ((f == QMediaFormat::WMV || f == QMediaFormat::WMA)
309 && conversionMode == QMediaFormat::Encode)
310 continue;
311
312 maps.append({f, audioCodecs, videoCodecs});
313 if (f == QMediaFormat::MPEG4 && !fileFormats.contains(QMediaFormat::Mpeg4Audio)) {
314 maps.append({QMediaFormat::Mpeg4Audio, audioCodecs, {}});
315 if (audioCodecs.contains(QMediaFormat::AudioCodec::AAC))
316 maps.append({QMediaFormat::AAC, { QMediaFormat::AudioCodec::AAC }, {}});
317 } else if (f == QMediaFormat::WMV && !fileFormats.contains(QMediaFormat::WMA)) {
318 maps.append({QMediaFormat::WMA, audioCodecs, {}});
319 }
320 }
321 }
322 }
323 gst_plugin_feature_list_free(elementList);
324 return maps;
325}
326
328{
329 QSet<QImageCapture::FileFormat> formats;
330
331 GList *elementList = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_ENCODER,
332 GST_RANK_MARGINAL);
333
334 for (GstElementFactory *factory :
335 QGstUtils::GListRangeAdaptor<GstElementFactory *>(elementList)) {
336
337 for (GstStaticPadTemplate *padTemplate :
338 QGstUtils::GListConstRangeAdaptor<GstStaticPadTemplate *>(
339 gst_element_factory_get_static_pad_templates(factory))) {
340 if (padTemplate->direction == GST_PAD_SRC) {
341 QGstCaps caps = QGstCaps(gst_static_caps_get(&padTemplate->static_caps), QGstCaps::HasRef);
342
343 for (int i = 0; i < caps.size(); i++) {
344 QGstStructureView structure = caps.at(i);
345 auto f = QGstreamerFormatInfo::imageFormatForCaps(structure);
346 if (f != QImageCapture::UnspecifiedFormat) {
347// qDebug() << structure.toString() << f;
348 formats.insert(f);
349 }
350 }
351 }
352 }
353 }
354 gst_plugin_feature_list_free(elementList);
355 return formats.values();
356}
357
358#if 0
359static void dumpAudioCodecs(const QList<QMediaFormat::AudioCodec> &codecList)
360{
361 qDebug() << "Audio codecs:";
362 for (const auto &c : codecList)
363 qDebug() << " " << QMediaFormat::audioCodecName(c);
364}
365
366static void dumpVideoCodecs(const QList<QMediaFormat::VideoCodec> &codecList)
367{
368 qDebug() << "Video codecs:";
369 for (const auto &c : codecList)
370 qDebug() << " " << QMediaFormat::videoCodecName(c);
371}
372
373static void dumpMuxers(const QList<QPlatformMediaFormatInfo::CodecMap> &muxerList)
374{
375 for (const auto &m : muxerList) {
376 qDebug() << " " << QMediaFormat::fileFormatName(m.format);
377 qDebug() << " Audio";
378 for (const auto &a : m.audio)
379 qDebug() << " " << QMediaFormat::audioCodecName(a);
380 qDebug() << " Video";
381 for (const auto &v : m.video)
382 qDebug() << " " << QMediaFormat::videoCodecName(v);
383 }
384
385}
386#endif
387
389{
390 auto codecs = getCodecsList(/*decode = */ true);
391 decoders = getCodecMaps(QMediaFormat::Decode, codecs.first, codecs.second);
392
393 codecs = getCodecsList(/*decode = */ false);
394 encoders = getCodecMaps(QMediaFormat::Encode, codecs.first, codecs.second);
395// dumpAudioCodecs(codecs.first);
396// dumpVideoCodecs(codecs.second);
397// dumpMuxers(encoders);
398
399 imageFormats = getImageFormatList();
400}
401
403
404QGstCaps QGstreamerFormatInfo::formatCaps(const QMediaFormat &f) const
405{
406 auto format = f.fileFormat();
407 Q_ASSERT(format != QMediaFormat::UnspecifiedFormat);
408
409 const char *capsForFormat[QMediaFormat::LastFileFormat + 1] = {
410 "video/x-ms-asf", // WMV
411 "video/x-msvideo", // AVI
412 "video/x-matroska", // Matroska
413 "video/quicktime, variant=(string)iso", // MPEG4
414 "video/ogg", // Ogg
415 "video/quicktime", // QuickTime
416 "video/webm", // WebM
417 "video/quicktime, variant=(string)iso", // Mpeg4Audio is the same is mp4...
418 "video/quicktime, variant=(string)iso", // AAC is also an MP4 container
419 "video/x-ms-asf", // WMA, same as WMV
420 "audio/mpeg, mpegversion=(int)1, layer=(int)3", // MP3
421 "audio/x-flac", // FLAC
422 "audio/x-wav" // Wave
423 };
424 return QGstCaps(gst_caps_from_string(capsForFormat[format]), QGstCaps::HasRef);
425}
426
427QGstCaps QGstreamerFormatInfo::audioCaps(const QMediaFormat &f) const
428{
429 auto codec = f.audioCodec();
430 if (codec == QMediaFormat::AudioCodec::Unspecified)
431 return {};
432
433 const char *capsForCodec[(int)QMediaFormat::AudioCodec::LastAudioCodec + 1] = {
434 "audio/mpeg, mpegversion=(int)1, layer=(int)3", // MP3
435 "audio/mpeg, mpegversion=(int)4", // AAC
436 "audio/x-ac3", // AC3
437 "audio/x-eac3", // EAC3
438 "audio/x-flac", // FLAC
439 "audio/x-true-hd", // DolbyTrueHD
440 "audio/x-opus", // Opus
441 "audio/x-vorbis", // Vorbis
442 "audio/x-raw", // WAVE
443 "audio/x-wma", // WMA
444 "audio/x-alac", // ALAC
445 };
446 return QGstCaps(gst_caps_from_string(capsForCodec[(int)codec]), QGstCaps::HasRef);
447}
448
449QGstCaps QGstreamerFormatInfo::videoCaps(const QMediaFormat &f) const
450{
451 auto codec = f.videoCodec();
452 if (codec == QMediaFormat::VideoCodec::Unspecified)
453 return {};
454
455 const char *capsForCodec[(int)QMediaFormat::VideoCodec::LastVideoCodec + 1] = {
456 "video/mpeg, mpegversion=(int)1", // MPEG1,
457 "video/mpeg, mpegversion=(int)2", // MPEG2,
458 "video/mpeg, mpegversion=(int)4", // MPEG4,
459 "video/x-h264", // H264,
460 "video/x-h265", // H265,
461 "video/x-vp8", // VP8,
462 "video/x-vp9", // VP9,
463 "video/x-av1", // AV1,
464 "video/x-theora", // Theora,
465 "audio/x-wmv", // WMV
466 "video/x-jpeg", // MotionJPEG,
467 };
468 return QGstCaps(gst_caps_from_string(capsForCodec[(int)codec]), QGstCaps::HasRef);
469}
470
471QT_END_NAMESPACE
const char * toString() const
Definition qgst.cpp:152
QGValue operator[](const char *fieldname) const
Definition qgst.cpp:234
QGstCaps formatCaps(const QMediaFormat &f) const
QGstCaps videoCaps(const QMediaFormat &f) const
~QGstreamerFormatInfo() override
QGstCaps audioCaps(const QMediaFormat &f) const
Combined button and popup list for selecting options.
static QList< QImageCapture::FileFormat > getImageFormatList()