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

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