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 | #ifndef QFFMPEG_P_H |
4 | #define QFFMPEG_P_H |
5 | |
6 | // |
7 | // W A R N I N G |
8 | // ------------- |
9 | // |
10 | // This file is not part of the Qt API. It exists purely as an |
11 | // implementation detail. This header file may change from version to |
12 | // version without notice, or even be removed. |
13 | // |
14 | // We mean it. |
15 | // |
16 | |
17 | #include "qffmpegdefs_p.h" |
18 | #include "qffmpegavaudioformat_p.h" |
19 | #include <QtMultimedia/qvideoframeformat.h> |
20 | |
21 | #include <qstring.h> |
22 | #include <optional> |
23 | |
24 | inline bool operator==(const AVRational &lhs, const AVRational &rhs) |
25 | { |
26 | return lhs.num == rhs.num && lhs.den == rhs.den; |
27 | } |
28 | |
29 | inline bool operator!=(const AVRational &lhs, const AVRational &rhs) |
30 | { |
31 | return !(lhs == rhs); |
32 | } |
33 | |
34 | QT_BEGIN_NAMESPACE |
35 | |
36 | namespace QFFmpeg |
37 | { |
38 | |
39 | inline std::optional<qint64> mul(qint64 a, AVRational b) |
40 | { |
41 | return b.den != 0 ? (a * b.num + b.den / 2) / b.den : std::optional<qint64>{}; |
42 | } |
43 | |
44 | inline std::optional<qreal> mul(qreal a, AVRational b) |
45 | { |
46 | return b.den != 0 ? a * qreal(b.num) / qreal(b.den) : std::optional<qreal>{}; |
47 | } |
48 | |
49 | inline std::optional<qint64> timeStampMs(qint64 ts, AVRational base) |
50 | { |
51 | return mul(a: 1'000 * ts, b: base); |
52 | } |
53 | |
54 | inline std::optional<qint64> timeStampUs(qint64 ts, AVRational base) |
55 | { |
56 | return mul(a: 1'000'000 * ts, b: base); |
57 | } |
58 | |
59 | inline std::optional<float> toFloat(AVRational r) |
60 | { |
61 | return r.den != 0 ? float(r.num) / float(r.den) : std::optional<float>{}; |
62 | } |
63 | |
64 | inline QString err2str(int errnum) |
65 | { |
66 | char buffer[AV_ERROR_MAX_STRING_SIZE + 1] = {}; |
67 | av_make_error_string(errbuf: buffer, AV_ERROR_MAX_STRING_SIZE, errnum); |
68 | return QString::fromLocal8Bit(ba: buffer); |
69 | } |
70 | |
71 | inline void setAVFrameTime(AVFrame &frame, int64_t pts, const AVRational &timeBase) |
72 | { |
73 | frame.pts = pts; |
74 | #if QT_FFMPEG_HAS_FRAME_TIME_BASE |
75 | frame.time_base = timeBase; |
76 | #else |
77 | Q_UNUSED(timeBase); |
78 | #endif |
79 | } |
80 | |
81 | inline void getAVFrameTime(const AVFrame &frame, int64_t &pts, AVRational &timeBase) |
82 | { |
83 | pts = frame.pts; |
84 | #if QT_FFMPEG_HAS_FRAME_TIME_BASE |
85 | timeBase = frame.time_base; |
86 | #else |
87 | timeBase = { .num: 0, .den: 1 }; |
88 | #endif |
89 | } |
90 | |
91 | inline int64_t getAVFrameDuration(const AVFrame &frame) |
92 | { |
93 | #if QT_FFMPEG_HAS_FRAME_DURATION |
94 | return frame.duration; |
95 | #else |
96 | Q_UNUSED(frame); |
97 | return 0; |
98 | #endif |
99 | } |
100 | |
101 | #if QT_FFMPEG_HAS_AVCODEC_GET_SUPPORTED_CONFIG |
102 | void logGetCodecConfigError(const AVCodec *codec, AVCodecConfig config, int error); |
103 | |
104 | template <typename T> |
105 | inline const T *getCodecConfig(const AVCodec *codec, AVCodecConfig config) |
106 | { |
107 | const T *result = nullptr; |
108 | const auto error = avcodec_get_supported_config( |
109 | nullptr, codec, config, 0u, reinterpret_cast<const void **>(&result), nullptr); |
110 | if (error != 0) { |
111 | logGetCodecConfigError(codec, config, error); |
112 | return nullptr; |
113 | } |
114 | return result; |
115 | } |
116 | #endif |
117 | |
118 | inline const AVPixelFormat *getCodecPixelFormats(const AVCodec *codec) |
119 | { |
120 | #if QT_FFMPEG_HAS_AVCODEC_GET_SUPPORTED_CONFIG |
121 | return getCodecConfig<AVPixelFormat>(codec, AV_CODEC_CONFIG_PIX_FORMAT); |
122 | #else |
123 | return codec->pix_fmts; |
124 | #endif |
125 | } |
126 | |
127 | inline const AVSampleFormat *getCodecSampleFormats(const AVCodec *codec) |
128 | { |
129 | #if QT_FFMPEG_HAS_AVCODEC_GET_SUPPORTED_CONFIG |
130 | return getCodecConfig<AVSampleFormat>(codec, AV_CODEC_CONFIG_SAMPLE_FORMAT); |
131 | #else |
132 | return codec->sample_fmts; |
133 | #endif |
134 | } |
135 | |
136 | inline const int *getCodecSampleRates(const AVCodec *codec) |
137 | { |
138 | #if QT_FFMPEG_HAS_AVCODEC_GET_SUPPORTED_CONFIG |
139 | return getCodecConfig<int>(codec, AV_CODEC_CONFIG_SAMPLE_RATE); |
140 | #else |
141 | return codec->supported_samplerates; |
142 | #endif |
143 | } |
144 | |
145 | #if QT_FFMPEG_HAS_AV_CHANNEL_LAYOUT |
146 | |
147 | inline const AVChannelLayout *getCodecChannelLayouts(const AVCodec *codec) |
148 | { |
149 | #if QT_FFMPEG_HAS_AVCODEC_GET_SUPPORTED_CONFIG |
150 | return getCodecConfig<AVChannelLayout>(codec, AV_CODEC_CONFIG_CHANNEL_LAYOUT); |
151 | #else |
152 | return codec->ch_layouts; |
153 | #endif |
154 | } |
155 | |
156 | #else |
157 | |
158 | inline const uint64_t *getCodecChannelLayouts(const AVCodec *codec) |
159 | { |
160 | return codec->channel_layouts; |
161 | } |
162 | |
163 | #endif |
164 | |
165 | inline const AVRational *getCodecFrameRates(const AVCodec *codec) |
166 | { |
167 | #if QT_FFMPEG_HAS_AVCODEC_GET_SUPPORTED_CONFIG |
168 | return getCodecConfig<AVRational>(codec, AV_CODEC_CONFIG_FRAME_RATE); |
169 | #else |
170 | return codec->supported_framerates; |
171 | #endif |
172 | } |
173 | |
174 | struct AVDictionaryHolder |
175 | { |
176 | AVDictionary *opts = nullptr; |
177 | |
178 | operator AVDictionary **() { return &opts; } |
179 | |
180 | AVDictionaryHolder() = default; |
181 | |
182 | Q_DISABLE_COPY(AVDictionaryHolder) |
183 | |
184 | AVDictionaryHolder(AVDictionaryHolder &&other) noexcept |
185 | : opts(std::exchange(obj&: other.opts, new_val: nullptr)) |
186 | { |
187 | } |
188 | |
189 | ~AVDictionaryHolder() |
190 | { |
191 | if (opts) |
192 | av_dict_free(m: &opts); |
193 | } |
194 | }; |
195 | |
196 | template<typename FunctionType, FunctionType F> |
197 | struct AVDeleter |
198 | { |
199 | template <typename T, std::invoke_result_t<FunctionType, T **> * = nullptr> |
200 | void operator()(T *object) const |
201 | { |
202 | if (object) |
203 | F(&object); |
204 | } |
205 | |
206 | template <typename T, std::invoke_result_t<FunctionType, T *> * = nullptr> |
207 | void operator()(T *object) const |
208 | { |
209 | F(object); |
210 | } |
211 | }; |
212 | |
213 | using AVFrameUPtr = std::unique_ptr<AVFrame, AVDeleter<decltype(&av_frame_free), &av_frame_free>>; |
214 | |
215 | inline AVFrameUPtr makeAVFrame() |
216 | { |
217 | return AVFrameUPtr(av_frame_alloc()); |
218 | } |
219 | |
220 | using AVPacketUPtr = |
221 | std::unique_ptr<AVPacket, AVDeleter<decltype(&av_packet_free), &av_packet_free>>; |
222 | |
223 | using AVCodecContextUPtr = |
224 | std::unique_ptr<AVCodecContext, |
225 | AVDeleter<decltype(&avcodec_free_context), &avcodec_free_context>>; |
226 | |
227 | using AVBufferUPtr = |
228 | std::unique_ptr<AVBufferRef, AVDeleter<decltype(&av_buffer_unref), &av_buffer_unref>>; |
229 | |
230 | using AVHWFramesConstraintsUPtr = std::unique_ptr< |
231 | AVHWFramesConstraints, |
232 | AVDeleter<decltype(&av_hwframe_constraints_free), &av_hwframe_constraints_free>>; |
233 | |
234 | using SwrContextUPtr = std::unique_ptr<SwrContext, AVDeleter<decltype(&swr_free), &swr_free>>; |
235 | |
236 | using SwsContextUPtr = |
237 | std::unique_ptr<SwsContext, AVDeleter<decltype(&sws_freeContext), &sws_freeContext>>; |
238 | |
239 | template <typename T> |
240 | inline constexpr auto InvalidAvValue = T{}; |
241 | |
242 | template<> |
243 | inline constexpr auto InvalidAvValue<AVSampleFormat> = AV_SAMPLE_FMT_NONE; |
244 | |
245 | template<> |
246 | inline constexpr auto InvalidAvValue<AVPixelFormat> = AV_PIX_FMT_NONE; |
247 | |
248 | bool isAVFormatSupported(const AVCodec *codec, PixelOrSampleFormat format); |
249 | |
250 | template <typename Format> |
251 | bool hasAVValue(const Format *fmts, Format format) |
252 | { |
253 | return findAVValue(fmts, [format](Format f) { return f == format; }) != InvalidAvValue<Format>; |
254 | } |
255 | |
256 | template <typename AVValue, typename Predicate> |
257 | AVValue findAVValue(const AVValue *fmts, const Predicate &predicate) |
258 | { |
259 | auto scoresGetter = [&predicate](AVValue value) { |
260 | return predicate(value) ? BestAVScore : NotSuitableAVScore; |
261 | }; |
262 | return findBestAVValue(fmts, scoresGetter).first; |
263 | } |
264 | |
265 | template <typename Predicate> |
266 | const AVCodecHWConfig *findHwConfig(const AVCodec *codec, const Predicate &predicate) |
267 | { |
268 | for (int i = 0; const auto hwConfig = avcodec_get_hw_config(codec, index: i); ++i) { |
269 | if (predicate(hwConfig)) |
270 | return hwConfig; |
271 | } |
272 | |
273 | return nullptr; |
274 | } |
275 | |
276 | template <typename Predicate> |
277 | AVPixelFormat findAVPixelFormat(const AVCodec *codec, const Predicate &predicate) |
278 | { |
279 | const auto pixelFormats = getCodecPixelFormats(codec); |
280 | const AVPixelFormat format = findAVValue(pixelFormats, predicate); |
281 | if (format != AV_PIX_FMT_NONE) |
282 | return format; |
283 | |
284 | auto checkHwConfig = [&predicate](const AVCodecHWConfig *config) { |
285 | return config->pix_fmt != AV_PIX_FMT_NONE && predicate(config->pix_fmt); |
286 | }; |
287 | |
288 | if (auto hwConfig = findHwConfig(codec, checkHwConfig)) |
289 | return hwConfig->pix_fmt; |
290 | |
291 | return AV_PIX_FMT_NONE; |
292 | } |
293 | |
294 | template <typename Value, typename CalculateScore> |
295 | auto findBestAVValue(const Value *values, const CalculateScore &calculateScore) |
296 | { |
297 | using Limits = std::numeric_limits<decltype(calculateScore(*values))>; |
298 | |
299 | const Value invalidValue = InvalidAvValue<Value>; |
300 | std::pair result(invalidValue, Limits::min()); |
301 | if (values) { |
302 | |
303 | for (; *values != invalidValue && result.second != Limits::max(); ++values) { |
304 | const auto score = calculateScore(*values); |
305 | if (score > result.second) |
306 | result = { *values, score }; |
307 | } |
308 | } |
309 | |
310 | return result; |
311 | } |
312 | |
313 | bool isHwPixelFormat(AVPixelFormat format); |
314 | |
315 | inline bool isSwPixelFormat(AVPixelFormat format) |
316 | { |
317 | return !isHwPixelFormat(format); |
318 | } |
319 | |
320 | bool isAVCodecExperimental(const AVCodec *codec); |
321 | |
322 | void applyExperimentalCodecOptions(const AVCodec *codec, AVDictionary** opts); |
323 | |
324 | AVPixelFormat pixelFormatForHwDevice(AVHWDeviceType deviceType); |
325 | |
326 | AVPacketSideData *addStreamSideData(AVStream *stream, AVPacketSideData sideData); |
327 | |
328 | const AVPacketSideData *streamSideData(const AVStream *stream, AVPacketSideDataType type); |
329 | |
330 | SwrContextUPtr createResampleContext(const AVAudioFormat &inputFormat, |
331 | const AVAudioFormat &outputFormat); |
332 | |
333 | QVideoFrameFormat::ColorTransfer fromAvColorTransfer(AVColorTransferCharacteristic colorTrc); |
334 | |
335 | AVColorTransferCharacteristic toAvColorTransfer(QVideoFrameFormat::ColorTransfer colorTrc); |
336 | |
337 | QVideoFrameFormat::ColorSpace fromAvColorSpace(AVColorSpace colorSpace); |
338 | |
339 | AVColorSpace toAvColorSpace(QVideoFrameFormat::ColorSpace colorSpace); |
340 | |
341 | QVideoFrameFormat::ColorRange fromAvColorRange(AVColorRange colorRange); |
342 | |
343 | AVColorRange toAvColorRange(QVideoFrameFormat::ColorRange colorRange); |
344 | |
345 | AVHWDeviceContext *avFrameDeviceContext(const AVFrame *frame); |
346 | |
347 | SwsContextUPtr createSwsContext(const QSize &srcSize, AVPixelFormat srcPixFmt, const QSize &dstSize, |
348 | AVPixelFormat dstPixFmt, int conversionType = SWS_BICUBIC); |
349 | |
350 | #ifdef Q_OS_DARWIN |
351 | bool isCVFormatSupported(uint32_t format); |
352 | |
353 | std::string cvFormatToString(uint32_t format); |
354 | |
355 | #endif |
356 | } |
357 | |
358 | QDebug operator<<(QDebug, const AVRational &); |
359 | |
360 | #if QT_FFMPEG_HAS_AV_CHANNEL_LAYOUT |
361 | QDebug operator<<(QDebug, const AVChannelLayout &); |
362 | #endif |
363 | |
364 | QT_END_NAMESPACE |
365 | |
366 | #endif |
367 |
Definitions
- operator==
- operator!=
- mul
- mul
- timeStampMs
- timeStampUs
- toFloat
- err2str
- setAVFrameTime
- getAVFrameTime
- getAVFrameDuration
- getCodecPixelFormats
- getCodecSampleFormats
- getCodecSampleRates
- getCodecChannelLayouts
- getCodecFrameRates
- AVDictionaryHolder
- operator AVDictionary **
- AVDictionaryHolder
- AVDictionaryHolder
- AVDictionaryHolder
- ~AVDictionaryHolder
- AVDeleter
- operator()
- operator()
- makeAVFrame
- InvalidAvValue
- InvalidAvValue
- InvalidAvValue
- hasAVValue
- findAVValue
- findHwConfig
- findAVPixelFormat
- findBestAVValue
Learn Advanced QML with KDAB
Find out more