Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qffmpegmediaformatinfo.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#include "qaudioformat.h"
6#include "qimagewriter.h"
7
8#include <qloggingcategory.h>
9
11
12static Q_LOGGING_CATEGORY(qLcMediaFormatInfo, "qt.multimedia.ffmpeg.mediaformatinfo")
13
14static struct {
15 AVCodecID id;
18 { AV_CODEC_ID_MPEG1VIDEO, QMediaFormat::VideoCodec::MPEG1 },
19 { AV_CODEC_ID_MPEG2VIDEO, QMediaFormat::VideoCodec::MPEG2 },
20 { AV_CODEC_ID_MPEG4, QMediaFormat::VideoCodec::MPEG4 },
21 { AV_CODEC_ID_H264, QMediaFormat::VideoCodec::H264 },
22 { AV_CODEC_ID_HEVC, QMediaFormat::VideoCodec::H265 },
23 { AV_CODEC_ID_VP8, QMediaFormat::VideoCodec::VP8 },
24 { AV_CODEC_ID_VP9, QMediaFormat::VideoCodec::VP9 },
25 { AV_CODEC_ID_AV1, QMediaFormat::VideoCodec::AV1 },
26 { AV_CODEC_ID_THEORA, QMediaFormat::VideoCodec::Theora },
27 { AV_CODEC_ID_WMV3, QMediaFormat::VideoCodec::WMV },
28 { AV_CODEC_ID_MJPEG, QMediaFormat::VideoCodec::MotionJPEG }
29};
30
32{
33 for (const auto &c : videoCodecMap) {
34 if (c.codec == codec)
35 return c.id;
36 }
37 return AV_CODEC_ID_NONE;
38}
39
40static struct {
41 AVCodecID id;
43} audioCodecMap [] = {
44 { AV_CODEC_ID_MP3, QMediaFormat::AudioCodec::MP3 },
45 { AV_CODEC_ID_AAC, QMediaFormat::AudioCodec::AAC },
46 { AV_CODEC_ID_AC3, QMediaFormat::AudioCodec::AC3 },
47 { AV_CODEC_ID_EAC3, QMediaFormat::AudioCodec::EAC3 },
48 { AV_CODEC_ID_FLAC, QMediaFormat::AudioCodec::FLAC },
49 { AV_CODEC_ID_TRUEHD, QMediaFormat::AudioCodec::DolbyTrueHD },
50 { AV_CODEC_ID_OPUS, QMediaFormat::AudioCodec::Opus },
51 { AV_CODEC_ID_VORBIS, QMediaFormat::AudioCodec::Vorbis },
52 { AV_CODEC_ID_PCM_S16LE, QMediaFormat::AudioCodec::Wave },
53 { AV_CODEC_ID_WMAPRO, QMediaFormat::AudioCodec::WMA },
54 { AV_CODEC_ID_ALAC, QMediaFormat::AudioCodec::ALAC }
55};
56
58{
59 for (const auto &c : audioCodecMap) {
60 if (c.codec == codec)
61 return c.id;
62 }
63 return AV_CODEC_ID_NONE;
64}
65
66// mimetypes are mostly copied from qmediaformat.cpp. Unfortunately, FFmpeg uses
67// in some cases slightly different mimetypes
68static const struct
69{
71 const char *mimeType;
72 const char *name; // disambiguate if we have several muxers/demuxers
74 { QMediaFormat::WMV, "video/x-ms-asf", "asf" },
75 { QMediaFormat::AVI, "video/x-msvideo", nullptr },
76 { QMediaFormat::Matroska, "video/x-matroska", nullptr },
77 { QMediaFormat::MPEG4, "video/mp4", "mp4" },
78 { QMediaFormat::Ogg, "video/ogg", nullptr },
79 // QuickTime is the same as MP4
80 { QMediaFormat::WebM, "video/webm", "webm" },
81 // Audio Formats
82 // Mpeg4Audio is the same as MP4 without the video codecs
83 { QMediaFormat::AAC, "audio/aac", nullptr },
84 // WMA is the same as WMV
85 { QMediaFormat::FLAC, "audio/x-flac", nullptr },
86 { QMediaFormat::MP3, "audio/mpeg", "mp3" },
87 { QMediaFormat::Wave, "audio/x-wav", nullptr },
88 { QMediaFormat::UnspecifiedFormat, nullptr, nullptr }
89};
90
91template <typename AVFormat>
93{
94
95 if (!format->mime_type || !*format->mime_type)
97
98 auto *m = map;
99 while (m->fileFormat != QMediaFormat::UnspecifiedFormat) {
100 if (m->mimeType && !strcmp(m->mimeType, format->mime_type)) {
101 // check if the name matches. This is used to disambiguate where FFmpeg provides
102 // multiple muxers or demuxers
103 if (!m->name || !strcmp(m->name, format->name))
104 return m->fileFormat;
105 }
106 ++m;
107 }
108
110}
111
113{
118
119 auto *m = map;
120 while (m->fileFormat != QMediaFormat::UnspecifiedFormat) {
121 if (m->fileFormat == format)
122 return av_guess_format(m->name, nullptr, m->mimeType);
123 ++m;
124 }
125
126 return nullptr;
127}
128
129
131{
132 qCDebug(qLcMediaFormatInfo) << ">>>> listing codecs";
133
134 QList<QMediaFormat::AudioCodec> audioEncoders;
135 QList<QMediaFormat::AudioCodec> extraAudioDecoders;
136 QList<QMediaFormat::VideoCodec> videoEncoders;
137 QList<QMediaFormat::VideoCodec> extraVideoDecoders;
138
139 const AVCodecDescriptor *descriptor = nullptr;
140 while ((descriptor = avcodec_descriptor_next(descriptor))) {
141 const bool canEncode = QFFmpeg::findAVEncoder(descriptor->id) != nullptr;
142 const bool canDecode = QFFmpeg::findAVDecoder(descriptor->id) != nullptr;
143 auto videoCodec = videoCodecForAVCodecId(descriptor->id);
144 auto audioCodec = audioCodecForAVCodecId(descriptor->id);
145 if (descriptor->type == AVMEDIA_TYPE_VIDEO && videoCodec != QMediaFormat::VideoCodec::Unspecified) {
146 if (canEncode) {
147 if (!videoEncoders.contains(videoCodec))
148 videoEncoders.append(videoCodec);
149 } else if (canDecode) {
150 if (!extraVideoDecoders.contains(videoCodec))
151 extraVideoDecoders.append(videoCodec);
152 }
153 } else if (descriptor->type == AVMEDIA_TYPE_AUDIO
154 && audioCodec != QMediaFormat::AudioCodec::Unspecified) {
155 if (canEncode) {
156 if (!audioEncoders.contains(audioCodec))
157 audioEncoders.append(audioCodec);
158 } else if (canDecode) {
159 if (!extraAudioDecoders.contains(audioCodec))
160 extraAudioDecoders.append(audioCodec);
161 }
162 }
163 }
164
165 // get demuxers
166// qCDebug(qLcMediaFormatInfo) << ">>>> Muxers";
167 void *opaque = nullptr;
168 const AVOutputFormat *outputFormat = nullptr;
169 while ((outputFormat = av_muxer_iterate(&opaque))) {
170 auto mediaFormat = formatForAVFormat(outputFormat);
171 if (mediaFormat == QMediaFormat::UnspecifiedFormat)
172 continue;
173// qCDebug(qLcMediaFormatInfo) << " mux:" << outputFormat->name << outputFormat->long_name << outputFormat->mime_type << outputFormat->extensions << mediaFormat;
174
175 CodecMap encoder;
176 encoder.format = mediaFormat;
177
178 for (auto codec : audioEncoders) {
179 auto id = codecId(codec);
180 // only add the codec if it can be used with this container
181 if (avformat_query_codec(outputFormat, id, FF_COMPLIANCE_NORMAL) == 1) {
182 // add codec for container
183// qCDebug(qLcMediaFormatInfo) << " " << codec << Qt::hex << av_codec_get_tag(outputFormat->codec_tag, id);
184 encoder.audio.append(codec);
185 }
186 }
187 for (auto codec : videoEncoders) {
188 auto id = codecId(codec);
189 // only add the codec if it can be used with this container
190 if (avformat_query_codec(outputFormat, id, FF_COMPLIANCE_NORMAL) == 1) {
191 // add codec for container
192// qCDebug(qLcMediaFormatInfo) << " " << codec << Qt::hex << av_codec_get_tag(outputFormat->codec_tag, id);
193 encoder.video.append(codec);
194 }
195 }
196
197 // sanity checks and handling special cases
198 if (encoder.audio.isEmpty() && encoder.video.isEmpty())
199 continue;
200 switch (encoder.format) {
202 // add WMA
203 encoders.append({ QMediaFormat::WMA, encoder.audio, {} });
204 break;
206 // add Mpeg4Audio and QuickTime
207 encoders.append({ QMediaFormat::QuickTime, encoder.audio, encoder.video });
208 encoders.append({ QMediaFormat::Mpeg4Audio, encoder.audio, {} });
209 break;
211 // FFmpeg allows other encoded formats in WAV containers, but we do not want that
213 continue;
215 break;
216 default:
217 break;
218 }
219 encoders.append(encoder);
220 }
221
222 // FFmpeg doesn't allow querying supported codecs for decoders
223 // we take a simple approximation stating that we can decode what we
224 // can encode. That's a safe subset.
226
227#ifdef Q_OS_WINDOWS
228 // MediaFoundation HVEC encoder fails when processing frames
229 for (auto &encoder : encoders) {
230 auto h265index = encoder.video.indexOf(QMediaFormat::VideoCodec::H265);
231 if (h265index >= 0)
232 encoder.video.removeAt(h265index);
233 }
234#endif
235
236// qCDebug(qLcMediaFormatInfo) << "extraDecoders:" << extraAudioDecoders << extraVideoDecoders;
237 // FFmpeg can currently only decode WMA and WMV, not encode
238 if (extraAudioDecoders.contains(QMediaFormat::AudioCodec::WMA)) {
241 }
242 if (extraVideoDecoders.contains(QMediaFormat::VideoCodec::WMV)) {
244 }
245
246 // Add image formats we support. We currently simply use Qt's built-in image write
247 // to save images. That doesn't give us HDR support or support for larger bit depths,
248 // but most cameras can currently not generate those anyway.
249 const auto imgFormats = QImageWriter::supportedImageFormats();
250 for (const auto &f : imgFormats) {
251 if (f == "png")
253 else if (f == "jpeg")
255 else if (f == "tiff")
257 else if (f == "webp")
259 }
260
261}
262
264
266{
267 for (const auto &c : audioCodecMap) {
268 if (c.id == id)
269 return c.codec;
270 }
272}
273
275{
276 for (const auto &c : videoCodecMap) {
277 if (c.id == id)
278 return c.codec;
279 }
281}
282
285{
286 // Seems like FFmpeg uses different names for muxers and demuxers of the same format.
287 // that makes it somewhat cumbersome to detect things correctly.
288 // The input formats have a comma separated list of short names. We check the first one of those
289 // as the docs specify that you only append to the list
290 static const struct
291 {
293 const char *name;
295 { QMediaFormat::WMV, "asf" },
296 { QMediaFormat::AVI, "avi" },
297 { QMediaFormat::Matroska, "matroska" },
298 { QMediaFormat::MPEG4, "mov" },
299 { QMediaFormat::Ogg, "ogg" },
300 { QMediaFormat::WebM, "webm" },
301 // Audio Formats
302 // Mpeg4Audio is the same as MP4 without the video codecs
303 { QMediaFormat::AAC, "aac"},
304 // WMA is the same as WMV
305 { QMediaFormat::FLAC, "flac" },
306 { QMediaFormat::MP3, "mp3" },
307 { QMediaFormat::Wave, "wav" },
309 };
310
311 if (!format->name)
313
314 auto *m = map;
315 while (m->fileFormat != QMediaFormat::UnspecifiedFormat) {
316 if (!strncmp(m->name, format->name, strlen(m->name)))
317 return m->fileFormat;
318 ++m;
319 }
320
322}
323
324const AVOutputFormat *
329
334
339
341{
342 switch (format) {
343 case AV_SAMPLE_FMT_NONE:
344 default:
346 case AV_SAMPLE_FMT_U8:
347 case AV_SAMPLE_FMT_U8P:
348 return QAudioFormat::UInt8;
349 case AV_SAMPLE_FMT_S16:
350 case AV_SAMPLE_FMT_S16P:
351 return QAudioFormat::Int16;
352 case AV_SAMPLE_FMT_S32:
353 case AV_SAMPLE_FMT_S32P:
354 return QAudioFormat::Int32;
355 case AV_SAMPLE_FMT_FLT:
356 case AV_SAMPLE_FMT_FLTP:
357 return QAudioFormat::Float;
358 case AV_SAMPLE_FMT_DBL:
359 case AV_SAMPLE_FMT_DBLP:
360 case AV_SAMPLE_FMT_S64:
361 case AV_SAMPLE_FMT_S64P:
362 // let's use float
363 return QAudioFormat::Float;
364 }
365}
366
368{
369 switch (format) {
371 return AV_SAMPLE_FMT_U8;
373 return AV_SAMPLE_FMT_S16;
375 return AV_SAMPLE_FMT_S32;
377 return AV_SAMPLE_FMT_FLT;
378 default:
379 return AV_SAMPLE_FMT_NONE;
380 }
381}
382
384{
385 int64_t avChannelLayout = 0;
387 avChannelLayout |= AV_CH_FRONT_LEFT;
389 avChannelLayout |= AV_CH_FRONT_RIGHT;
391 avChannelLayout |= AV_CH_FRONT_CENTER;
392 if (channelConfig & (1 << QAudioFormat::LFE))
393 avChannelLayout |= AV_CH_LOW_FREQUENCY;
395 avChannelLayout |= AV_CH_BACK_LEFT;
397 avChannelLayout |= AV_CH_BACK_RIGHT;
399 avChannelLayout |= AV_CH_FRONT_LEFT_OF_CENTER;
401 avChannelLayout |= AV_CH_FRONT_RIGHT_OF_CENTER;
403 avChannelLayout |= AV_CH_BACK_CENTER;
405 avChannelLayout |= AV_CH_LOW_FREQUENCY_2;
407 avChannelLayout |= AV_CH_SIDE_LEFT;
409 avChannelLayout |= AV_CH_SIDE_RIGHT;
411 avChannelLayout |= AV_CH_TOP_FRONT_LEFT;
413 avChannelLayout |= AV_CH_TOP_FRONT_RIGHT;
415 avChannelLayout |= AV_CH_TOP_FRONT_CENTER;
417 avChannelLayout |= AV_CH_TOP_CENTER;
419 avChannelLayout |= AV_CH_TOP_BACK_LEFT;
421 avChannelLayout |= AV_CH_TOP_BACK_RIGHT;
423 avChannelLayout |= AV_CH_TOP_BACK_CENTER;
424 // The defines used below got added together for FFmpeg 4.4
425#ifdef AV_CH_TOP_SIDE_LEFT
427 avChannelLayout |= AV_CH_TOP_SIDE_LEFT;
429 avChannelLayout |= AV_CH_TOP_SIDE_RIGHT;
431 avChannelLayout |= AV_CH_BOTTOM_FRONT_CENTER;
433 avChannelLayout |= AV_CH_BOTTOM_FRONT_LEFT;
435 avChannelLayout |= AV_CH_BOTTOM_FRONT_RIGHT;
436#endif
437 return avChannelLayout;
438}
439
441{
443 if (avChannelLayout & AV_CH_FRONT_LEFT)
445 if (avChannelLayout & AV_CH_FRONT_RIGHT)
447 if (avChannelLayout & AV_CH_FRONT_CENTER)
449 if (avChannelLayout & AV_CH_LOW_FREQUENCY)
451 if (avChannelLayout & AV_CH_BACK_LEFT)
453 if (avChannelLayout & AV_CH_BACK_RIGHT)
455 if (avChannelLayout & AV_CH_FRONT_LEFT_OF_CENTER)
457 if (avChannelLayout & AV_CH_FRONT_RIGHT_OF_CENTER)
459 if (avChannelLayout & AV_CH_BACK_CENTER)
461 if (avChannelLayout & AV_CH_LOW_FREQUENCY_2)
463 if (avChannelLayout & AV_CH_SIDE_LEFT)
465 if (avChannelLayout & AV_CH_SIDE_RIGHT)
467 if (avChannelLayout & AV_CH_TOP_FRONT_LEFT)
469 if (avChannelLayout & AV_CH_TOP_FRONT_RIGHT)
471 if (avChannelLayout & AV_CH_TOP_FRONT_CENTER)
473 if (avChannelLayout & AV_CH_TOP_CENTER)
475 if (avChannelLayout & AV_CH_TOP_BACK_LEFT)
477 if (avChannelLayout & AV_CH_TOP_BACK_RIGHT)
479 if (avChannelLayout & AV_CH_TOP_BACK_CENTER)
481 // The defines used below got added together for FFmpeg 4.4
482#ifdef AV_CH_TOP_SIDE_LEFT
483 if (avChannelLayout & AV_CH_TOP_SIDE_LEFT)
485 if (avChannelLayout & AV_CH_TOP_SIDE_RIGHT)
487 if (avChannelLayout & AV_CH_BOTTOM_FRONT_CENTER)
489 if (avChannelLayout & AV_CH_BOTTOM_FRONT_LEFT)
491 if (avChannelLayout & AV_CH_BOTTOM_FRONT_RIGHT)
493#endif
495}
496
498{
500 format.setSampleFormat(sampleFormat(AVSampleFormat(codecpar->format)));
501 format.setSampleRate(codecpar->sample_rate);
502#if QT_FFMPEG_OLD_CHANNEL_LAYOUT
503 uint64_t channelLayout = codecpar->channel_layout;
504 if (!channelLayout)
505 channelLayout = avChannelLayout(QAudioFormat::defaultChannelConfigForChannelCount(codecpar->channels));
506#else
507 uint64_t channelLayout = 0;
508 if (codecpar->ch_layout.order == AV_CHANNEL_ORDER_NATIVE)
509 channelLayout = codecpar->ch_layout.u.mask;
510 else
511 channelLayout = avChannelLayout(QAudioFormat::defaultChannelConfigForChannelCount(codecpar->ch_layout.nb_channels));
512#endif
513 format.setChannelConfig(channelConfigForAVLayout(channelLayout));
514 return format;
515}
516
The QAudioFormat class stores audio stream parameter information.
static Q_MULTIMEDIA_EXPORT ChannelConfig defaultChannelConfigForChannelCount(int channelCount)
Returns a default channel configuration for channelCount.
SampleFormat
Qt will always expect and use samples in the endianness of the host platform.
constexpr ChannelConfig channelConfig() const noexcept
Returns the current channel configuration.
constexpr void setSampleFormat(SampleFormat f) noexcept
Sets the sample format to format.
ChannelConfig
\variable QAudioFormat::NChannelPositions
static QAudioFormat audioFormatFromCodecParameters(AVCodecParameters *codecPar)
static AVCodecID codecIdForAudioCodec(QMediaFormat::AudioCodec codec)
static QMediaFormat::VideoCodec videoCodecForAVCodecId(AVCodecID id)
static QMediaFormat::FileFormat fileFormatForAVInputFormat(const AVInputFormat *format)
static QAudioFormat::SampleFormat sampleFormat(AVSampleFormat format)
static const AVOutputFormat * outputFormatForFileFormat(QMediaFormat::FileFormat format)
static AVCodecID codecIdForVideoCodec(QMediaFormat::VideoCodec codec)
static QMediaFormat::AudioCodec audioCodecForAVCodecId(AVCodecID id)
static AVSampleFormat avSampleFormat(QAudioFormat::SampleFormat format)
static QAudioFormat::ChannelConfig channelConfigForAVLayout(int64_t avChannelLayout)
static int64_t avChannelLayout(QAudioFormat::ChannelConfig channelConfig)
static QList< QByteArray > supportedImageFormats()
Returns the list of image formats supported by QImageWriter.
bool isEmpty() const noexcept
Definition qlist.h:401
void append(parameter_type t)
Definition qlist.h:458
AudioCodec
\qmlproperty enumeration QtMultimedia::mediaFormat::fileFormat
FileFormat
Describes the container format used in a multimedia file or stream.
VideoCodec
\qmlproperty enumeration QtMultimedia::mediaFormat::audioCodec
QList< QImageCapture::FileFormat > imageFormats
QMap< QString, QString > map
[6]
const AVCodec * findAVEncoder(AVCodecID codecId, const std::optional< AVHWDeviceType > &deviceType, const std::optional< PixelOrSampleFormat > &format)
Definition qffmpeg.cpp:433
const AVCodec * findAVDecoder(AVCodecID codecId, const std::optional< AVHWDeviceType > &deviceType, const std::optional< PixelOrSampleFormat > &format)
Definition qffmpeg.cpp:427
Combined button and popup list for selecting options.
QAudioFormat::ChannelConfig channelConfig
static struct @727 videoCodecMap[]
static struct @728 audioCodecMap[]
static QMediaFormat::FileFormat formatForAVFormat(AVFormat *format)
AVCodecID id
QMediaFormat::AudioCodec codec
const char * mimeType
static AVCodecID codecId(QMediaFormat::VideoCodec codec)
static const struct @730 map[QMediaFormat::LastFileFormat+1]
const char * name
static const AVOutputFormat * avFormatForFormat(QMediaFormat::FileFormat format)
static struct @729 audioCodecMap[]
QMediaFormat::FileFormat fileFormat
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
const GLfloat * m
GLfloat GLfloat f
GLuint name
GLint GLsizei GLsizei GLenum format
const GLubyte * c
unsigned int quint32
Definition qtypes.h:50
bool contains(const AT &t) const noexcept
Definition qlist.h:45
QList< QMediaFormat::VideoCodec > video
QList< QMediaFormat::AudioCodec > audio