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 bool hasAnySrcCaps = false;
266
267 for (GstStaticPadTemplate *padTemplate :
268 QGstUtils::GListConstRangeAdaptor<GstStaticPadTemplate *>(
269 gst_element_factory_get_static_pad_templates(factory))) {
270
271 // check the other side for supported inputs/outputs
272 if (padTemplate->direction != dataPadDirection) {
273 auto caps = QGstCaps(gst_static_caps_get(&padTemplate->static_caps), QGstCaps::HasRef);
274
275 // Demuxers like matroskademux/qtdemux/asfdemux advertise ANY on their SRC pad
276 // templates because the actual output caps depend on the stream.
277 if (gst_caps_is_any(caps.get())) {
278 hasAnySrcCaps = true;
279 continue;
280 }
281
282 bool acceptsRawAudio = false;
283 for (int i = 0; i < caps.size(); i++) {
284 QGstStructureView structure = caps.at(i);
285 if (structure.name() == "audio/x-raw")
286 acceptsRawAudio = true;
287 auto audio = audioCodecForCaps(structure);
288 if (audio != QMediaFormat::AudioCodec::Unspecified && supportedAudioCodecs.contains(audio))
289 audioCodecs.append(audio);
290 auto video = videoCodecForCaps(structure);
291 if (video != QMediaFormat::VideoCodec::Unspecified && supportedVideoCodecs.contains(video))
292 videoCodecs.append(video);
293 }
294 if (acceptsRawAudio && fileFormats.size() == 1) {
295 switch (fileFormats.at(0)) {
296 case QMediaFormat::Mpeg4Audio:
297 default:
298 break;
299 case QMediaFormat::MP3:
300 audioCodecs.append(QMediaFormat::AudioCodec::MP3);
301 break;
302 case QMediaFormat::FLAC:
303 audioCodecs.append(QMediaFormat::AudioCodec::FLAC);
304 break;
305 case QMediaFormat::Wave:
306 audioCodecs.append(QMediaFormat::AudioCodec::Wave);
307 break;
308 }
309 }
310 }
311 }
312
313 // SRC pads reported ANY: the demuxer supports whatever codecs the installed decoders
314 // handle. We claim to support all discovered audio/video codecs in that case, and rely on fixupCodecMaps() to
315 // remove unsupported combinations.
316 if (hasAnySrcCaps) {
317 if (audioCodecs.isEmpty())
318 audioCodecs = supportedAudioCodecs;
319 if (videoCodecs.isEmpty())
320 videoCodecs = supportedVideoCodecs;
321 }
322 if (!audioCodecs.isEmpty() || !videoCodecs.isEmpty()) {
323 for (auto f : std::as_const(fileFormats)) {
324 // QTBUG-131726: encodebin rejects muxers for x-ms-asf, so we should ignore WMV/WMA
325 // for encoding
326 if ((f == QMediaFormat::WMV || f == QMediaFormat::WMA)
327 && conversionMode == QMediaFormat::Encode)
328 continue;
329
330 maps.append({f, audioCodecs, videoCodecs});
331 if (f == QMediaFormat::MPEG4 && !fileFormats.contains(QMediaFormat::Mpeg4Audio)) {
332 maps.append({QMediaFormat::Mpeg4Audio, audioCodecs, {}});
333 if (audioCodecs.contains(QMediaFormat::AudioCodec::AAC))
334 maps.append({QMediaFormat::AAC, { QMediaFormat::AudioCodec::AAC }, {}});
335 } else if (f == QMediaFormat::WMV && !fileFormats.contains(QMediaFormat::WMA)) {
336 maps.append({QMediaFormat::WMA, audioCodecs, {}});
337 }
338 }
339 }
340 }
341 gst_plugin_feature_list_free(elementList);
342 return maps;
343}
344
346{
347 QSet<QImageCapture::FileFormat> formats;
348
349 GList *elementList = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_ENCODER,
350 GST_RANK_MARGINAL);
351
352 for (GstElementFactory *factory :
353 QGstUtils::GListRangeAdaptor<GstElementFactory *>(elementList)) {
354
355 for (GstStaticPadTemplate *padTemplate :
356 QGstUtils::GListConstRangeAdaptor<GstStaticPadTemplate *>(
357 gst_element_factory_get_static_pad_templates(factory))) {
358 if (padTemplate->direction == GST_PAD_SRC) {
359 QGstCaps caps = QGstCaps(gst_static_caps_get(&padTemplate->static_caps), QGstCaps::HasRef);
360
361 for (int i = 0; i < caps.size(); i++) {
362 QGstStructureView structure = caps.at(i);
363 auto f = QGstreamerFormatInfo::imageFormatForCaps(structure);
364 if (f != QImageCapture::UnspecifiedFormat) {
365// qDebug() << structure.toString() << f;
366 formats.insert(f);
367 }
368 }
369 }
370 }
371 }
372 gst_plugin_feature_list_free(elementList);
373 return formats.values();
374}
375
376#if 0
377static void dumpAudioCodecs(const QList<QMediaFormat::AudioCodec> &codecList)
378{
379 qDebug() << "Audio codecs:";
380 for (const auto &c : codecList)
381 qDebug() << " " << QMediaFormat::audioCodecName(c);
382}
383
384static void dumpVideoCodecs(const QList<QMediaFormat::VideoCodec> &codecList)
385{
386 qDebug() << "Video codecs:";
387 for (const auto &c : codecList)
388 qDebug() << " " << QMediaFormat::videoCodecName(c);
389}
390
391static void dumpMuxers(const QList<QPlatformMediaFormatInfo::CodecMap> &muxerList)
392{
393 for (const auto &m : muxerList) {
394 qDebug() << " " << QMediaFormat::fileFormatName(m.format);
395 qDebug() << " Audio";
396 for (const auto &a : m.audio)
397 qDebug() << " " << QMediaFormat::audioCodecName(a);
398 qDebug() << " Video";
399 for (const auto &v : m.video)
400 qDebug() << " " << QMediaFormat::videoCodecName(v);
401 }
402
403}
404#endif
405
407{
408 auto codecs = getCodecsList(/*decode = */ true);
409 decoders = getCodecMaps(QMediaFormat::Decode, codecs.first, codecs.second);
410
411 codecs = getCodecsList(/*decode = */ false);
412 encoders = getCodecMaps(QMediaFormat::Encode, codecs.first, codecs.second);
413// dumpAudioCodecs(codecs.first);
414// dumpVideoCodecs(codecs.second);
415// dumpMuxers(encoders);
416
417 imageFormats = getImageFormatList();
418
419 fixupCodecMaps();
420}
421
423
424QGstCaps QGstreamerFormatInfo::formatCaps(const QMediaFormat &f) const
425{
426 auto format = f.fileFormat();
427 Q_ASSERT(format != QMediaFormat::UnspecifiedFormat);
428
429 const char *capsForFormat[QMediaFormat::LastFileFormat + 1] = {
430 "video/x-ms-asf", // WMV
431 "video/x-msvideo", // AVI
432 "video/x-matroska", // Matroska
433 "video/quicktime, variant=(string)iso", // MPEG4
434 "video/ogg", // Ogg
435 "video/quicktime", // QuickTime
436 "video/webm", // WebM
437 "video/quicktime, variant=(string)iso", // Mpeg4Audio is the same is mp4...
438 "video/quicktime, variant=(string)iso", // AAC is also an MP4 container
439 "video/x-ms-asf", // WMA, same as WMV
440 "audio/mpeg, mpegversion=(int)1, layer=(int)3", // MP3
441 "audio/x-flac", // FLAC
442 "audio/x-wav" // Wave
443 };
444 return QGstCaps(gst_caps_from_string(capsForFormat[format]), QGstCaps::HasRef);
445}
446
447QGstCaps QGstreamerFormatInfo::audioCaps(const QMediaFormat &f) const
448{
449 auto codec = f.audioCodec();
450 if (codec == QMediaFormat::AudioCodec::Unspecified)
451 return {};
452
453 const char *capsForCodec[(int)QMediaFormat::AudioCodec::LastAudioCodec + 1] = {
454 "audio/mpeg, mpegversion=(int)1, layer=(int)3", // MP3
455 "audio/mpeg, mpegversion=(int)4", // AAC
456 "audio/x-ac3", // AC3
457 "audio/x-eac3", // EAC3
458 "audio/x-flac", // FLAC
459 "audio/x-true-hd", // DolbyTrueHD
460 "audio/x-opus", // Opus
461 "audio/x-vorbis", // Vorbis
462 "audio/x-raw", // WAVE
463 "audio/x-wma", // WMA
464 "audio/x-alac", // ALAC
465 };
466 return QGstCaps(gst_caps_from_string(capsForCodec[(int)codec]), QGstCaps::HasRef);
467}
468
469QGstCaps QGstreamerFormatInfo::videoCaps(const QMediaFormat &f) const
470{
471 auto codec = f.videoCodec();
472 if (codec == QMediaFormat::VideoCodec::Unspecified)
473 return {};
474
475 const char *capsForCodec[(int)QMediaFormat::VideoCodec::LastVideoCodec + 1] = {
476 "video/mpeg, mpegversion=(int)1", // MPEG1,
477 "video/mpeg, mpegversion=(int)2", // MPEG2,
478 "video/mpeg, mpegversion=(int)4", // MPEG4,
479 "video/x-h264", // H264,
480 "video/x-h265", // H265,
481 "video/x-vp8", // VP8,
482 "video/x-vp9", // VP9,
483 "video/x-av1", // AV1,
484 "video/x-theora", // Theora,
485 "audio/x-wmv", // WMV
486 "video/x-jpeg", // MotionJPEG,
487 };
488 return QGstCaps(gst_caps_from_string(capsForCodec[(int)codec]), QGstCaps::HasRef);
489}
490
491QT_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()