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
4#include "qffmpegmediaformatinfo_p.h"
5#include "qffmpegcodecstorage_p.h"
6#include "qaudioformat.h"
7#include "qimagewriter.h"
8
9#include <qloggingcategory.h>
10
11QT_BEGIN_NAMESPACE
12
13static Q_LOGGING_CATEGORY(qLcMediaFormatInfo, "qt.multimedia.ffmpeg.mediaformatinfo")
14
15static constexpr struct {
16 AVCodecID id;
17 QMediaFormat::VideoCodec codec;
18} s_videoCodecMap [] = {
19 { .id: AV_CODEC_ID_MPEG1VIDEO, .codec: QMediaFormat::VideoCodec::MPEG1 },
20 { .id: AV_CODEC_ID_MPEG2VIDEO, .codec: QMediaFormat::VideoCodec::MPEG2 },
21 { .id: AV_CODEC_ID_MPEG4, .codec: QMediaFormat::VideoCodec::MPEG4 },
22 { .id: AV_CODEC_ID_H264, .codec: QMediaFormat::VideoCodec::H264 },
23 { .id: AV_CODEC_ID_HEVC, .codec: QMediaFormat::VideoCodec::H265 },
24 { .id: AV_CODEC_ID_VP8, .codec: QMediaFormat::VideoCodec::VP8 },
25 { .id: AV_CODEC_ID_VP9, .codec: QMediaFormat::VideoCodec::VP9 },
26 { .id: AV_CODEC_ID_AV1, .codec: QMediaFormat::VideoCodec::AV1 },
27 { .id: AV_CODEC_ID_THEORA, .codec: QMediaFormat::VideoCodec::Theora },
28 { .id: AV_CODEC_ID_WMV3, .codec: QMediaFormat::VideoCodec::WMV },
29 { .id: AV_CODEC_ID_MJPEG, .codec: QMediaFormat::VideoCodec::MotionJPEG }
30};
31
32static AVCodecID codecId(QMediaFormat::VideoCodec codec)
33{
34 for (const auto &c : s_videoCodecMap) {
35 if (c.codec == codec)
36 return c.id;
37 }
38 return AV_CODEC_ID_NONE;
39}
40
41static constexpr struct {
42 AVCodecID id;
43 QMediaFormat::AudioCodec codec;
44} s_audioCodecMap [] = {
45 { .id: AV_CODEC_ID_MP3, .codec: QMediaFormat::AudioCodec::MP3 },
46 { .id: AV_CODEC_ID_AAC, .codec: QMediaFormat::AudioCodec::AAC },
47 { .id: AV_CODEC_ID_AC3, .codec: QMediaFormat::AudioCodec::AC3 },
48 { .id: AV_CODEC_ID_EAC3, .codec: QMediaFormat::AudioCodec::EAC3 },
49 { .id: AV_CODEC_ID_FLAC, .codec: QMediaFormat::AudioCodec::FLAC },
50 { .id: AV_CODEC_ID_TRUEHD, .codec: QMediaFormat::AudioCodec::DolbyTrueHD },
51 { .id: AV_CODEC_ID_OPUS, .codec: QMediaFormat::AudioCodec::Opus },
52 { .id: AV_CODEC_ID_VORBIS, .codec: QMediaFormat::AudioCodec::Vorbis },
53 { .id: AV_CODEC_ID_PCM_S16LE, .codec: QMediaFormat::AudioCodec::Wave },
54 { .id: AV_CODEC_ID_WMAPRO, .codec: QMediaFormat::AudioCodec::WMA },
55 { .id: AV_CODEC_ID_ALAC, .codec: QMediaFormat::AudioCodec::ALAC }
56};
57
58static AVCodecID codecId(QMediaFormat::AudioCodec codec)
59{
60 for (const auto &c : s_audioCodecMap) {
61 if (c.codec == codec)
62 return c.id;
63 }
64 return AV_CODEC_ID_NONE;
65}
66
67// mimetypes are mostly copied from qmediaformat.cpp. Unfortunately, FFmpeg uses
68// in some cases slightly different mimetypes
69static constexpr struct
70{
71 QMediaFormat::FileFormat fileFormat;
72 const char *mimeType;
73 const char *name; // disambiguate if we have several muxers/demuxers
74} s_mimeMap[] = {
75 { .fileFormat: QMediaFormat::WMV, .mimeType: "video/x-ms-asf", .name: "asf" },
76 { .fileFormat: QMediaFormat::AVI, .mimeType: "video/x-msvideo", .name: nullptr },
77 { .fileFormat: QMediaFormat::Matroska, .mimeType: "video/x-matroska", .name: nullptr },
78 { .fileFormat: QMediaFormat::MPEG4, .mimeType: "video/mp4", .name: "mp4" },
79 { .fileFormat: QMediaFormat::Ogg, .mimeType: "video/ogg", .name: nullptr },
80 // QuickTime is the same as MP4
81 { .fileFormat: QMediaFormat::WebM, .mimeType: "video/webm", .name: "webm" },
82 // Audio Formats
83 // Mpeg4Audio is the same as MP4 without the video codecs
84 { .fileFormat: QMediaFormat::AAC, .mimeType: "audio/aac", .name: nullptr },
85 // WMA is the same as WMV
86 { .fileFormat: QMediaFormat::FLAC, .mimeType: "audio/x-flac", .name: nullptr },
87 { .fileFormat: QMediaFormat::MP3, .mimeType: "audio/mpeg", .name: "mp3" },
88 { .fileFormat: QMediaFormat::Wave, .mimeType: "audio/x-wav", .name: nullptr }
89};
90
91template <typename AVFormat>
92static QMediaFormat::FileFormat formatForAVFormat(AVFormat *format)
93{
94 if (!format->mime_type || !*format->mime_type)
95 return QMediaFormat::UnspecifiedFormat;
96
97 for (const auto &m : s_mimeMap) {
98 if (m.mimeType && !strcmp(m.mimeType, format->mime_type)) {
99 // check if the name matches. This is used to disambiguate where FFmpeg provides
100 // multiple muxers or demuxers
101 if (!m.name || !strcmp(m.name, format->name))
102 return m.fileFormat;
103 }
104 }
105 return QMediaFormat::UnspecifiedFormat;
106}
107
108static const AVOutputFormat *avFormatForFormat(QMediaFormat::FileFormat format)
109{
110 if (format == QMediaFormat::QuickTime || format == QMediaFormat::Mpeg4Audio)
111 format = QMediaFormat::MPEG4;
112 if (format == QMediaFormat::WMA)
113 format = QMediaFormat::WMV;
114
115 for (const auto &m : s_mimeMap) {
116 if (m.fileFormat == format)
117 return av_guess_format(short_name: m.name, filename: nullptr, mime_type: m.mimeType);
118 }
119
120 return nullptr;
121}
122
123QFFmpegMediaFormatInfo::QFFmpegMediaFormatInfo()
124{
125 qCDebug(qLcMediaFormatInfo) << ">>>> listing codecs";
126
127 QList<QMediaFormat::AudioCodec> audioEncoders;
128 QList<QMediaFormat::AudioCodec> extraAudioDecoders;
129 QList<QMediaFormat::VideoCodec> videoEncoders;
130 QList<QMediaFormat::VideoCodec> extraVideoDecoders;
131
132 const AVCodecDescriptor *descriptor = nullptr;
133 while ((descriptor = avcodec_descriptor_next(prev: descriptor))) {
134 const bool canEncode = QFFmpeg::findAVEncoder(codecId: descriptor->id) != nullptr;
135 const bool canDecode = QFFmpeg::findAVDecoder(codecId: descriptor->id) != nullptr;
136 auto videoCodec = videoCodecForAVCodecId(id: descriptor->id);
137 auto audioCodec = audioCodecForAVCodecId(id: descriptor->id);
138 if (descriptor->type == AVMEDIA_TYPE_VIDEO && videoCodec != QMediaFormat::VideoCodec::Unspecified) {
139 if (canEncode) {
140 if (!videoEncoders.contains(t: videoCodec))
141 videoEncoders.append(t: videoCodec);
142 } else if (canDecode) {
143 if (!extraVideoDecoders.contains(t: videoCodec))
144 extraVideoDecoders.append(t: videoCodec);
145 }
146 } else if (descriptor->type == AVMEDIA_TYPE_AUDIO
147 && audioCodec != QMediaFormat::AudioCodec::Unspecified) {
148 if (canEncode) {
149 if (!audioEncoders.contains(t: audioCodec))
150 audioEncoders.append(t: audioCodec);
151 } else if (canDecode) {
152 if (!extraAudioDecoders.contains(t: audioCodec))
153 extraAudioDecoders.append(t: audioCodec);
154 }
155 }
156 }
157
158 // get demuxers
159 void *opaque = nullptr;
160 const AVOutputFormat *outputFormat = nullptr;
161 while ((outputFormat = av_muxer_iterate(opaque: &opaque))) {
162 auto mediaFormat = formatForAVFormat(format: outputFormat);
163 if (mediaFormat == QMediaFormat::UnspecifiedFormat)
164 continue;
165
166 CodecMap encoder;
167 encoder.format = mediaFormat;
168
169 for (auto codec : audioEncoders) {
170 auto id = codecId(codec);
171 // only add the codec if it can be used with this container
172 int result = avformat_query_codec(ofmt: outputFormat, codec_id: id, FF_COMPLIANCE_NORMAL);
173 if (result == 1 || (result < 0 && id == outputFormat->audio_codec)) {
174 // add codec for container
175 encoder.audio.append(t: codec);
176 }
177 }
178 for (auto codec : videoEncoders) {
179 auto id = codecId(codec);
180 // only add the codec if it can be used with this container
181 int result = avformat_query_codec(ofmt: outputFormat, codec_id: id, FF_COMPLIANCE_NORMAL);
182 if (result == 1 || (result < 0 && id == outputFormat->video_codec)) {
183 // add codec for container
184 encoder.video.append(t: codec);
185 }
186 }
187
188 // sanity checks and handling special cases
189 if (encoder.audio.isEmpty() && encoder.video.isEmpty())
190 continue;
191 switch (encoder.format) {
192 case QMediaFormat::WMV:
193 // add WMA
194 encoders.append(t: { .format: QMediaFormat::WMA, .audio: encoder.audio, .video: {} });
195 break;
196 case QMediaFormat::MPEG4:
197 // add Mpeg4Audio and QuickTime
198 encoders.append(t: { .format: QMediaFormat::QuickTime, .audio: encoder.audio, .video: encoder.video });
199 encoders.append(t: { .format: QMediaFormat::Mpeg4Audio, .audio: encoder.audio, .video: {} });
200 break;
201 case QMediaFormat::Wave:
202 // FFmpeg allows other encoded formats in WAV containers, but we do not want that
203 if (!encoder.audio.contains(t: QMediaFormat::AudioCodec::Wave))
204 continue;
205 encoder.audio = { QMediaFormat::AudioCodec::Wave };
206 break;
207 default:
208 break;
209 }
210 encoders.append(t: encoder);
211 }
212
213 // FFmpeg doesn't allow querying supported codecs for decoders
214 // we take a simple approximation stating that we can decode what we
215 // can encode. That's a safe subset.
216 decoders = encoders;
217
218#ifdef Q_OS_WINDOWS
219 // MediaFoundation HVEC encoder fails when processing frames
220 for (auto &encoder : encoders) {
221 auto h265index = encoder.video.indexOf(QMediaFormat::VideoCodec::H265);
222 if (h265index >= 0)
223 encoder.video.removeAt(h265index);
224 }
225#endif
226
227 // FFmpeg can currently only decode WMA and WMV, not encode
228 if (extraAudioDecoders.contains(t: QMediaFormat::AudioCodec::WMA)) {
229 decoders[QMediaFormat::WMA].audio.append(t: QMediaFormat::AudioCodec::WMA);
230 decoders[QMediaFormat::WMV].audio.append(t: QMediaFormat::AudioCodec::WMA);
231 }
232 if (extraVideoDecoders.contains(t: QMediaFormat::VideoCodec::WMV)) {
233 decoders[QMediaFormat::WMV].video.append(t: QMediaFormat::VideoCodec::WMV);
234 }
235
236 // Add image formats we support. We currently simply use Qt's built-in image write
237 // to save images. That doesn't give us HDR support or support for larger bit depths,
238 // but most cameras can currently not generate those anyway.
239 const auto imgFormats = QImageWriter::supportedImageFormats();
240 for (const auto &f : imgFormats) {
241 if (f == "png")
242 imageFormats.append(t: QImageCapture::PNG);
243 else if (f == "jpeg")
244 imageFormats.append(t: QImageCapture::JPEG);
245 else if (f == "tiff")
246 imageFormats.append(t: QImageCapture::Tiff);
247 else if (f == "webp")
248 imageFormats.append(t: QImageCapture::WebP);
249 }
250
251}
252
253QFFmpegMediaFormatInfo::~QFFmpegMediaFormatInfo() = default;
254
255QMediaFormat::AudioCodec QFFmpegMediaFormatInfo::audioCodecForAVCodecId(AVCodecID id)
256{
257 for (const auto &c : s_audioCodecMap) {
258 if (c.id == id)
259 return c.codec;
260 }
261 return QMediaFormat::AudioCodec::Unspecified;
262}
263
264QMediaFormat::VideoCodec QFFmpegMediaFormatInfo::videoCodecForAVCodecId(AVCodecID id)
265{
266 for (const auto &c : s_videoCodecMap) {
267 if (c.id == id)
268 return c.codec;
269 }
270 return QMediaFormat::VideoCodec::Unspecified;
271}
272
273QMediaFormat::FileFormat
274QFFmpegMediaFormatInfo::fileFormatForAVInputFormat(const AVInputFormat *format)
275{
276 // Seems like FFmpeg uses different names for muxers and demuxers of the same format.
277 // that makes it somewhat cumbersome to detect things correctly.
278 // The input formats have a comma separated list of short names. We check the first one of those
279 // as the docs specify that you only append to the list
280 static const struct
281 {
282 QMediaFormat::FileFormat fileFormat;
283 const char *name;
284 } map[QMediaFormat::LastFileFormat + 1] = {
285 { .fileFormat: QMediaFormat::WMV, .name: "asf" },
286 { .fileFormat: QMediaFormat::AVI, .name: "avi" },
287 { .fileFormat: QMediaFormat::Matroska, .name: "matroska" },
288 { .fileFormat: QMediaFormat::MPEG4, .name: "mov" },
289 { .fileFormat: QMediaFormat::Ogg, .name: "ogg" },
290 { .fileFormat: QMediaFormat::WebM, .name: "webm" },
291 // Audio Formats
292 // Mpeg4Audio is the same as MP4 without the video codecs
293 { .fileFormat: QMediaFormat::AAC, .name: "aac"},
294 // WMA is the same as WMV
295 { .fileFormat: QMediaFormat::FLAC, .name: "flac" },
296 { .fileFormat: QMediaFormat::MP3, .name: "mp3" },
297 { .fileFormat: QMediaFormat::Wave, .name: "wav" },
298 { .fileFormat: QMediaFormat::UnspecifiedFormat, .name: nullptr }
299 };
300
301 if (!format->name)
302 return QMediaFormat::UnspecifiedFormat;
303
304 auto *m = map;
305 while (m->fileFormat != QMediaFormat::UnspecifiedFormat) {
306 if (!strncmp(s1: m->name, s2: format->name, n: strlen(s: m->name)))
307 return m->fileFormat;
308 ++m;
309 }
310
311 return QMediaFormat::UnspecifiedFormat;
312}
313
314const AVOutputFormat *
315QFFmpegMediaFormatInfo::outputFormatForFileFormat(QMediaFormat::FileFormat format)
316{
317 return avFormatForFormat(format);
318}
319
320AVCodecID QFFmpegMediaFormatInfo::codecIdForVideoCodec(QMediaFormat::VideoCodec codec)
321{
322 return codecId(codec);
323}
324
325AVCodecID QFFmpegMediaFormatInfo::codecIdForAudioCodec(QMediaFormat::AudioCodec codec)
326{
327 return codecId(codec);
328}
329
330QAudioFormat::SampleFormat QFFmpegMediaFormatInfo::sampleFormat(AVSampleFormat format)
331{
332 switch (format) {
333 case AV_SAMPLE_FMT_NONE:
334 default:
335 return QAudioFormat::Unknown;
336 case AV_SAMPLE_FMT_U8: ///< unsigned 8 bits
337 case AV_SAMPLE_FMT_U8P: ///< unsigned 8 bits: planar
338 return QAudioFormat::UInt8;
339 case AV_SAMPLE_FMT_S16: ///< signed 16 bits
340 case AV_SAMPLE_FMT_S16P: ///< signed 16 bits: planar
341 return QAudioFormat::Int16;
342 case AV_SAMPLE_FMT_S32: ///< signed 32 bits
343 case AV_SAMPLE_FMT_S32P: ///< signed 32 bits: planar
344 return QAudioFormat::Int32;
345 case AV_SAMPLE_FMT_FLT: ///< float
346 case AV_SAMPLE_FMT_FLTP: ///< float: planar
347 return QAudioFormat::Float;
348 case AV_SAMPLE_FMT_DBL: ///< double
349 case AV_SAMPLE_FMT_DBLP: ///< double: planar
350 case AV_SAMPLE_FMT_S64: ///< signed 64 bits
351 case AV_SAMPLE_FMT_S64P: ///< signed 64 bits, planar
352 // let's use float
353 return QAudioFormat::Float;
354 }
355}
356
357AVSampleFormat QFFmpegMediaFormatInfo::avSampleFormat(QAudioFormat::SampleFormat format)
358{
359 switch (format) {
360 case QAudioFormat::UInt8:
361 return AV_SAMPLE_FMT_U8;
362 case QAudioFormat::Int16:
363 return AV_SAMPLE_FMT_S16;
364 case QAudioFormat::Int32:
365 return AV_SAMPLE_FMT_S32;
366 case QAudioFormat::Float:
367 return AV_SAMPLE_FMT_FLT;
368 default:
369 return AV_SAMPLE_FMT_NONE;
370 }
371}
372
373int64_t QFFmpegMediaFormatInfo::avChannelLayout(QAudioFormat::ChannelConfig channelConfig)
374{
375 int64_t avChannelLayout = 0;
376 if (channelConfig & (1 << QAudioFormat::FrontLeft))
377 avChannelLayout |= AV_CH_FRONT_LEFT;
378 if (channelConfig & (1 << QAudioFormat::FrontRight))
379 avChannelLayout |= AV_CH_FRONT_RIGHT;
380 if (channelConfig & (1 << QAudioFormat::FrontCenter))
381 avChannelLayout |= AV_CH_FRONT_CENTER;
382 if (channelConfig & (1 << QAudioFormat::LFE))
383 avChannelLayout |= AV_CH_LOW_FREQUENCY;
384 if (channelConfig & (1 << QAudioFormat::BackLeft))
385 avChannelLayout |= AV_CH_BACK_LEFT;
386 if (channelConfig & (1 << QAudioFormat::BackRight))
387 avChannelLayout |= AV_CH_BACK_RIGHT;
388 if (channelConfig & (1 << QAudioFormat::FrontLeftOfCenter))
389 avChannelLayout |= AV_CH_FRONT_LEFT_OF_CENTER;
390 if (channelConfig & (1 << QAudioFormat::FrontRightOfCenter))
391 avChannelLayout |= AV_CH_FRONT_RIGHT_OF_CENTER;
392 if (channelConfig & (1 << QAudioFormat::BackCenter))
393 avChannelLayout |= AV_CH_BACK_CENTER;
394 if (channelConfig & (1 << QAudioFormat::LFE2))
395 avChannelLayout |= AV_CH_LOW_FREQUENCY_2;
396 if (channelConfig & (1 << QAudioFormat::SideLeft))
397 avChannelLayout |= AV_CH_SIDE_LEFT;
398 if (channelConfig & (1 << QAudioFormat::SideRight))
399 avChannelLayout |= AV_CH_SIDE_RIGHT;
400 if (channelConfig & (1 << QAudioFormat::TopFrontLeft))
401 avChannelLayout |= AV_CH_TOP_FRONT_LEFT;
402 if (channelConfig & (1 << QAudioFormat::TopFrontRight))
403 avChannelLayout |= AV_CH_TOP_FRONT_RIGHT;
404 if (channelConfig & (1 << QAudioFormat::TopFrontCenter))
405 avChannelLayout |= AV_CH_TOP_FRONT_CENTER;
406 if (channelConfig & (1 << QAudioFormat::TopCenter))
407 avChannelLayout |= AV_CH_TOP_CENTER;
408 if (channelConfig & (1 << QAudioFormat::TopBackLeft))
409 avChannelLayout |= AV_CH_TOP_BACK_LEFT;
410 if (channelConfig & (1 << QAudioFormat::TopBackRight))
411 avChannelLayout |= AV_CH_TOP_BACK_RIGHT;
412 if (channelConfig & (1 << QAudioFormat::TopBackCenter))
413 avChannelLayout |= AV_CH_TOP_BACK_CENTER;
414 // The defines used below got added together for FFmpeg 4.4
415#ifdef AV_CH_TOP_SIDE_LEFT
416 if (channelConfig & (1 << QAudioFormat::TopSideLeft))
417 avChannelLayout |= AV_CH_TOP_SIDE_LEFT;
418 if (channelConfig & (1 << QAudioFormat::TopSideRight))
419 avChannelLayout |= AV_CH_TOP_SIDE_RIGHT;
420 if (channelConfig & (1 << QAudioFormat::BottomFrontCenter))
421 avChannelLayout |= AV_CH_BOTTOM_FRONT_CENTER;
422 if (channelConfig & (1 << QAudioFormat::BottomFrontLeft))
423 avChannelLayout |= AV_CH_BOTTOM_FRONT_LEFT;
424 if (channelConfig & (1 << QAudioFormat::BottomFrontRight))
425 avChannelLayout |= AV_CH_BOTTOM_FRONT_RIGHT;
426#endif
427 return avChannelLayout;
428}
429
430QAudioFormat::ChannelConfig QFFmpegMediaFormatInfo::channelConfigForAVLayout(int64_t avChannelLayout)
431{
432 quint32 channelConfig = 0;
433 if (avChannelLayout & AV_CH_FRONT_LEFT)
434 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::FrontLeft);
435 if (avChannelLayout & AV_CH_FRONT_RIGHT)
436 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::FrontRight);
437 if (avChannelLayout & AV_CH_FRONT_CENTER)
438 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::FrontCenter);
439 if (avChannelLayout & AV_CH_LOW_FREQUENCY)
440 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::LFE);
441 if (avChannelLayout & AV_CH_BACK_LEFT)
442 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::BackLeft);
443 if (avChannelLayout & AV_CH_BACK_RIGHT)
444 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::BackRight);
445 if (avChannelLayout & AV_CH_FRONT_LEFT_OF_CENTER)
446 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::FrontLeftOfCenter);
447 if (avChannelLayout & AV_CH_FRONT_RIGHT_OF_CENTER)
448 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::FrontRightOfCenter);
449 if (avChannelLayout & AV_CH_BACK_CENTER)
450 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::BackCenter);
451 if (avChannelLayout & AV_CH_LOW_FREQUENCY_2)
452 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::LFE2);
453 if (avChannelLayout & AV_CH_SIDE_LEFT)
454 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::SideLeft);
455 if (avChannelLayout & AV_CH_SIDE_RIGHT)
456 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::SideRight);
457 if (avChannelLayout & AV_CH_TOP_FRONT_LEFT)
458 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::TopFrontLeft);
459 if (avChannelLayout & AV_CH_TOP_FRONT_RIGHT)
460 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::TopFrontRight);
461 if (avChannelLayout & AV_CH_TOP_FRONT_CENTER)
462 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::TopFrontCenter);
463 if (avChannelLayout & AV_CH_TOP_CENTER)
464 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::TopCenter);
465 if (avChannelLayout & AV_CH_TOP_BACK_LEFT)
466 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::TopBackLeft);
467 if (avChannelLayout & AV_CH_TOP_BACK_RIGHT)
468 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::TopBackRight);
469 if (avChannelLayout & AV_CH_TOP_BACK_CENTER)
470 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::TopBackCenter);
471 // The defines used below got added together for FFmpeg 4.4
472#ifdef AV_CH_TOP_SIDE_LEFT
473 if (avChannelLayout & AV_CH_TOP_SIDE_LEFT)
474 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::TopSideLeft);
475 if (avChannelLayout & AV_CH_TOP_SIDE_RIGHT)
476 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::TopSideRight);
477 if (avChannelLayout & AV_CH_BOTTOM_FRONT_CENTER)
478 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::BottomFrontCenter);
479 if (avChannelLayout & AV_CH_BOTTOM_FRONT_LEFT)
480 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::BottomFrontLeft);
481 if (avChannelLayout & AV_CH_BOTTOM_FRONT_RIGHT)
482 channelConfig |= QAudioFormat::channelConfig(channels: QAudioFormat::BottomFrontRight);
483#endif
484 return QAudioFormat::ChannelConfig(channelConfig);
485}
486
487QAudioFormat QFFmpegMediaFormatInfo::audioFormatFromCodecParameters(AVCodecParameters *codecpar)
488{
489 QAudioFormat format;
490 format.setSampleFormat(sampleFormat(format: AVSampleFormat(codecpar->format)));
491 format.setSampleRate(codecpar->sample_rate);
492#if QT_FFMPEG_HAS_AV_CHANNEL_LAYOUT
493 uint64_t channelLayout = 0;
494 if (codecpar->ch_layout.order == AV_CHANNEL_ORDER_NATIVE)
495 channelLayout = codecpar->ch_layout.u.mask;
496 else
497 channelLayout = avChannelLayout(QAudioFormat::defaultChannelConfigForChannelCount(codecpar->ch_layout.nb_channels));
498#else
499 uint64_t channelLayout = codecpar->channel_layout;
500 if (!channelLayout)
501 channelLayout = avChannelLayout(channelConfig: QAudioFormat::defaultChannelConfigForChannelCount(channelCount: codecpar->channels));
502#endif
503 format.setChannelConfig(channelConfigForAVLayout(avChannelLayout: channelLayout));
504 return format;
505}
506
507QT_END_NAMESPACE
508

source code of qtmultimedia/src/plugins/multimedia/ffmpeg/qffmpegmediaformatinfo.cpp