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
qffmpegcodec.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 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 "qffmpeg_p.h"
6
7#include <QtCore/qloggingcategory.h>
8
10
11namespace QFFmpeg {
12namespace {
13
14template <typename T>
15inline constexpr auto InvalidAvValue = T{};
16
17template <>
18inline constexpr auto InvalidAvValue<AVSampleFormat> = AV_SAMPLE_FMT_NONE;
19
20template <>
21inline constexpr auto InvalidAvValue<AVPixelFormat> = AV_PIX_FMT_NONE;
22
23template <typename T>
24QSpan<const T> makeSpan(const T *values)
25{
26 if (!values)
27 return {};
28
29 qsizetype size = 0;
30 while (values[size] != InvalidAvValue<T>)
31 ++size;
32
33 return QSpan<const T>{ values, size };
34}
35
36#if QT_FFMPEG_HAS_AVCODEC_GET_SUPPORTED_CONFIG
37
38Q_STATIC_LOGGING_CATEGORY(qLcFFmpegUtils, "qt.multimedia.ffmpeg.utils");
39
40void logGetCodecConfigError(const AVCodec *codec, AVCodecConfig config, int error)
41{
42 qCWarning(qLcFFmpegUtils) << "Failed to retrieve config" << config << "for codec" << codec->name
43 << "with error" << error << AVError(error);
44}
45
46template <typename T>
47QSpan<const T> getCodecConfig(const AVCodec *codec, AVCodecConfig config)
48{
49 const T *result = nullptr;
50 int size = 0;
51 const auto error = avcodec_get_supported_config(
52 nullptr, codec, config, 0u, reinterpret_cast<const void **>(&result), &size);
53 if (error != 0) {
54 logGetCodecConfigError(codec, config, error);
55 return {};
56 }
57
58 // Sanity check of FFmpeg's array layout. If it is not nullptr, it should end with a terminator,
59 // and be non-empty. A non-null but empty config would mean that no values are accepted by the
60 // codec, which does not make sense.
61 Q_ASSERT(!result || (size > 0 && result[size] == InvalidAvValue<T>));
62
63 // Returns empty span if 'result' is nullptr. This can be misleading, as it may
64 // mean that 'any' value is allowed, or that the result is 'unknown'.
65 return QSpan<const T>{ result, size };
66}
67#endif
68
69QSpan<const AVPixelFormat> getCodecPixelFormats(const AVCodec *codec)
70{
71#if QT_FFMPEG_HAS_AVCODEC_GET_SUPPORTED_CONFIG
72 return getCodecConfig<AVPixelFormat>(codec, AV_CODEC_CONFIG_PIX_FORMAT);
73#else
74 return makeSpan(codec->pix_fmts);
75#endif
76}
77
78QSpan<const AVSampleFormat> getCodecSampleFormats(const AVCodec *codec)
79{
80#if QT_FFMPEG_HAS_AVCODEC_GET_SUPPORTED_CONFIG
81 return getCodecConfig<AVSampleFormat>(codec, AV_CODEC_CONFIG_SAMPLE_FORMAT);
82#else
83 return makeSpan(codec->sample_fmts);
84#endif
85}
86
87QSpan<const int> getCodecSampleRates(const AVCodec *codec)
88{
89#if QT_FFMPEG_HAS_AVCODEC_GET_SUPPORTED_CONFIG
90 return getCodecConfig<int>(codec, AV_CODEC_CONFIG_SAMPLE_RATE);
91#else
92 return makeSpan(codec->supported_samplerates);
93#endif
94}
95
96#ifdef Q_OS_WINDOWS
97
98auto stereoLayout() // unused function on non-Windows platforms
99{
100 constexpr uint64_t mask = AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT;
101#if QT_FFMPEG_HAS_AV_CHANNEL_LAYOUT
102 AVChannelLayout channelLayout{};
103 av_channel_layout_from_mask(&channelLayout, mask);
104 return channelLayout;
105#else
106 return mask;
107#endif
108};
109
110#endif
111
112QSpan<const ChannelLayoutT> getCodecChannelLayouts(const AVCodec *codec)
113{
114 QSpan<const ChannelLayoutT> layout;
115#if QT_FFMPEG_HAS_AVCODEC_GET_SUPPORTED_CONFIG
116 layout = getCodecConfig<AVChannelLayout>(codec, AV_CODEC_CONFIG_CHANNEL_LAYOUT);
117#elif QT_FFMPEG_HAS_AV_CHANNEL_LAYOUT
118 layout = makeSpan(codec->ch_layouts);
119#else
120 layout = makeSpan(codec->channel_layouts);
121#endif
122
123#ifdef Q_OS_WINDOWS
124 // The mp3_mf codec does not report any layout restrictions, but does not
125 // handle more than 2 channels. We therefore make this explicit here.
126 if (layout.empty() && QLatin1StringView(codec->name) == QLatin1StringView("mp3_mf")) {
127 static const ChannelLayoutT defaultLayout[] = { stereoLayout() };
128 layout = defaultLayout;
129 }
130#endif
131 return layout;
132}
133
134QSpan<const AVRational> getCodecFrameRates(const AVCodec *codec)
135{
136#if QT_FFMPEG_HAS_AVCODEC_GET_SUPPORTED_CONFIG
137 return getCodecConfig<AVRational>(codec, AV_CODEC_CONFIG_FRAME_RATE);
138#else
139 return makeSpan(codec->supported_framerates);
140#endif
141}
142} // namespace
143
144Codec::Codec(const AVCodec *codec) : m_codec{ codec }
145{
146 Q_ASSERT(m_codec);
147}
148
149const AVCodec* Codec::get() const noexcept
150{
151 Q_ASSERT(m_codec);
152 return m_codec;
153}
154
155AVCodecID Codec::id() const noexcept
156{
157 Q_ASSERT(m_codec);
158
159 return m_codec->id;
160}
161
162QLatin1StringView Codec::name() const noexcept
163{
164 Q_ASSERT(m_codec);
165
166 return QLatin1StringView{ m_codec->name };
167}
168
169AVMediaType Codec::type() const noexcept
170{
171 Q_ASSERT(m_codec);
172
173 return m_codec->type;
174}
175
176// See AV_CODEC_CAP_*
177int Codec::capabilities() const noexcept
178{
179 Q_ASSERT(m_codec);
180
181 return m_codec->capabilities;
182}
183
184bool Codec::isEncoder() const noexcept
185{
186 Q_ASSERT(m_codec);
187
188 return av_codec_is_encoder(m_codec) != 0;
189}
190
191bool Codec::isDecoder() const noexcept
192{
193 Q_ASSERT(m_codec);
194
195 return av_codec_is_decoder(m_codec) != 0;
196}
197
198bool Codec::isExperimental() const noexcept
199{
200 Q_ASSERT(m_codec);
201
202 return (m_codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) != 0;
203}
204
205QSpan<const AVPixelFormat> Codec::pixelFormats() const noexcept
206{
207 Q_ASSERT(m_codec);
208
209 if (m_codec->type != AVMEDIA_TYPE_VIDEO)
210 return {};
211
212 return getCodecPixelFormats(m_codec);
213}
214
215QSpan<const AVSampleFormat> Codec::sampleFormats() const noexcept
216{
217 Q_ASSERT(m_codec);
218
219 if (m_codec->type != AVMEDIA_TYPE_AUDIO)
220 return {};
221
222 return getCodecSampleFormats(m_codec);
223}
224
225QSpan<const int> Codec::sampleRates() const noexcept
226{
227 Q_ASSERT(m_codec);
228
229 if (m_codec->type != AVMEDIA_TYPE_AUDIO)
230 return {};
231
232 return getCodecSampleRates(m_codec);
233}
234
235QSpan<const ChannelLayoutT> Codec::channelLayouts() const noexcept
236{
237 Q_ASSERT(m_codec);
238
239 if (m_codec->type != AVMEDIA_TYPE_AUDIO)
240 return {};
241
242 return getCodecChannelLayouts(m_codec);
243}
244
245QSpan<const AVRational> Codec::frameRates() const noexcept
246{
247 Q_ASSERT(m_codec);
248
249 if (m_codec->type != AVMEDIA_TYPE_VIDEO)
250 return {};
251
252 return getCodecFrameRates(m_codec);
253}
254
255std::vector<const AVCodecHWConfig *> Codec::hwConfigs() const noexcept
256{
257 Q_ASSERT(m_codec);
258
259 // For most codecs, hwConfig is empty so we optimize for
260 // the hot path and do not preallocate/reserve any memory.
261 std::vector<const AVCodecHWConfig *> configs;
262
263 for (int index = 0; auto config = avcodec_get_hw_config(m_codec, index); ++index)
264 configs.push_back(config);
265
266 return configs;
267}
268
270{
271 CodecIterator iterator;
272 iterator.m_codec = av_codec_iterate(&iterator.m_state);
273 return iterator;
274}
275
277{
278 return { };
279}
280
282{
283 Q_ASSERT(m_codec);
284 m_codec = av_codec_iterate(&m_state);
285 return *this;
286}
287
288Codec CodecIterator::operator*() const noexcept
289{
290 Q_ASSERT(m_codec); // Avoid dereferencing end() iterator
291 return Codec{ m_codec };
292}
293
294bool CodecIterator::operator!=(const CodecIterator &other) const noexcept
295{
296 return m_codec != other.m_codec;
297}
298
299QSpan<const AVPixelFormat> makeSpan(const AVPixelFormat *values)
300{
301 return makeSpan<AVPixelFormat>(values);
302}
303
304
305} // namespace QFFmpeg
306
307QT_END_NAMESPACE
static CodecIterator end()
CodecIterator & operator++() noexcept
bool operator!=(const CodecIterator &other) const noexcept
static CodecIterator begin()
Codec operator*() const noexcept
QSpan< const int > sampleRates() const noexcept
const AVCodec * get() const noexcept
AVCodecID id() const noexcept
QSpan< const ChannelLayoutT > channelLayouts() const noexcept
bool isExperimental() const noexcept
QSpan< const AVPixelFormat > pixelFormats() const noexcept
QLatin1StringView name() const noexcept
std::vector< const AVCodecHWConfig * > hwConfigs() const noexcept
bool isEncoder() const noexcept
Codec(const AVCodec *codec)
int capabilities() const noexcept
QSpan< const AVRational > frameRates() const noexcept
AVMediaType type() const noexcept
bool isDecoder() const noexcept
QSpan< const AVSampleFormat > sampleFormats() const noexcept
std::conditional_t< QT_FFMPEG_AVIO_WRITE_CONST, const uint8_t *, uint8_t * > AvioWriteBufferType
QSpan< const AVPixelFormat > makeSpan(const AVPixelFormat *values)