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
qffmpeg.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
4#include "qffmpeg_p.h"
5
6#include <QtCore/qdebug.h>
7#include <QtCore/qloggingcategory.h>
8#include <QtCore/qscopeguard.h>
9
10extern "C" {
11#include <libavutil/pixdesc.h>
12#include <libavutil/samplefmt.h>
13#include <libavutil/error.h>
14
15#ifdef Q_OS_DARWIN
16#include <libavutil/hwcontext_videotoolbox.h>
17#endif
18}
19
20QT_BEGIN_NAMESPACE
21namespace ranges = QtMultimediaPrivate::ranges;
22
23Q_STATIC_LOGGING_CATEGORY(qLcFFmpegUtils, "qt.multimedia.ffmpeg.utils");
24
25namespace QFFmpeg {
26
27bool isAVFormatSupported(const Codec &codec, PixelOrSampleFormat format)
28{
29 if (codec.type() == AVMEDIA_TYPE_VIDEO) {
30 auto checkFormat = [format](AVPixelFormat f) { return f == format; };
31 return findAVPixelFormat(codec, checkFormat).has_value();
32 }
33
34 if (codec.type() == AVMEDIA_TYPE_AUDIO) {
35 const auto sampleFormats = codec.sampleFormats();
36 return hasValue(sampleFormats, AVSampleFormat(format));
37 }
38
39 return false;
40}
41
42bool isHwPixelFormat(AVPixelFormat format)
43{
44 const auto desc = av_pix_fmt_desc_get(format);
45 return desc && (desc->flags & AV_PIX_FMT_FLAG_HWACCEL) != 0;
46}
47
48void applyExperimentalCodecOptions(const Codec &codec, AVDictionary **opts)
49{
50 if (codec.isExperimental()) {
51 qCWarning(qLcFFmpegUtils) << "Applying the option 'strict -2' for the experimental codec"
52 << codec.name() << ". it's unlikely to work properly";
53 av_dict_set(opts, "strict", "-2", 0);
54 }
55}
56
57AVPixelFormat pixelFormatForHwDevice(AVHWDeviceType deviceType)
58{
59 switch (deviceType) {
60 case AV_HWDEVICE_TYPE_VIDEOTOOLBOX:
61 return AV_PIX_FMT_VIDEOTOOLBOX;
62 case AV_HWDEVICE_TYPE_VAAPI:
63 return AV_PIX_FMT_VAAPI;
64 case AV_HWDEVICE_TYPE_MEDIACODEC:
65 return AV_PIX_FMT_MEDIACODEC;
66 case AV_HWDEVICE_TYPE_CUDA:
67 return AV_PIX_FMT_CUDA;
68 case AV_HWDEVICE_TYPE_VDPAU:
69 return AV_PIX_FMT_VDPAU;
70 case AV_HWDEVICE_TYPE_OPENCL:
71 return AV_PIX_FMT_OPENCL;
72 case AV_HWDEVICE_TYPE_QSV:
73 return AV_PIX_FMT_QSV;
74 case AV_HWDEVICE_TYPE_D3D11VA:
75 return AV_PIX_FMT_D3D11;
76#if QT_FFMPEG_HAS_D3D12VA
77 case AV_HWDEVICE_TYPE_D3D12VA:
78 return AV_PIX_FMT_D3D12;
79#endif
80 case AV_HWDEVICE_TYPE_DXVA2:
81 return AV_PIX_FMT_DXVA2_VLD;
82 case AV_HWDEVICE_TYPE_DRM:
83 return AV_PIX_FMT_DRM_PRIME;
84#if QT_FFMPEG_HAS_VULKAN
85 case AV_HWDEVICE_TYPE_VULKAN:
86 return AV_PIX_FMT_VULKAN;
87#endif
88 default:
89 return AV_PIX_FMT_NONE;
90 }
91}
92
93AVPacketSideData *addStreamSideData(AVStream *stream, AVPacketSideData sideData)
94{
95 QScopeGuard freeData([&sideData]() { av_free(sideData.data); });
96#if QT_FFMPEG_STREAM_SIDE_DATA_DEPRECATED
97 AVPacketSideData *result = av_packet_side_data_add(
98 &stream->codecpar->coded_side_data,
99 &stream->codecpar->nb_coded_side_data,
100 sideData.type,
101 sideData.data,
102 sideData.size,
103 0);
104 if (result) {
105 // If the result is not null, the ownership is taken by AVStream,
106 // otherwise the data must be deleted.
107 freeData.dismiss();
108 return result;
109 }
110#else
111 Q_UNUSED(stream);
112 // TODO: implement for older FFmpeg versions
113 qWarning() << "Adding stream side data is not supported for FFmpeg < 6.1";
114#endif
115
116 return nullptr;
117}
118
119const AVPacketSideData *streamSideData(const AVStream *stream, AVPacketSideDataType type)
120{
121 Q_ASSERT(stream);
122
123#if QT_FFMPEG_STREAM_SIDE_DATA_DEPRECATED
124 return av_packet_side_data_get(stream->codecpar->coded_side_data,
125 stream->codecpar->nb_coded_side_data, type);
126#else
127 const auto end = stream->side_data + stream->nb_side_data;
128 const auto found = std::find_if(stream->side_data, end, [&](const auto &item) {
129 return item.type == type;
130 });
131 return found == end ? nullptr : found;
132#endif
133}
134
135SwrContextUPtr createResampleContext(const AVAudioFormat &inputFormat,
136 const AVAudioFormat &outputFormat)
137{
138 SwrContext *resampler = nullptr;
139#if QT_FFMPEG_HAS_AV_CHANNEL_LAYOUT
140
141#if QT_FFMPEG_SWR_CONST_CH_LAYOUT
142 using AVChannelLayoutPrm = const AVChannelLayout*;
143#else
144 using AVChannelLayoutPrm = AVChannelLayout*;
145#endif
146
147 swr_alloc_set_opts2(&resampler,
148 const_cast<AVChannelLayoutPrm>(&outputFormat.channelLayout),
149 outputFormat.sampleFormat,
150 outputFormat.sampleRate,
151 const_cast<AVChannelLayoutPrm>(&inputFormat.channelLayout),
152 inputFormat.sampleFormat,
153 inputFormat.sampleRate,
154 0,
155 nullptr);
156
157#else
158
159 resampler = swr_alloc_set_opts(nullptr,
160 outputFormat.channelLayoutMask,
161 outputFormat.sampleFormat,
162 outputFormat.sampleRate,
163 inputFormat.channelLayoutMask,
164 inputFormat.sampleFormat,
165 inputFormat.sampleRate,
166 0,
167 nullptr);
168
169#endif
170
171 auto error = QFFmpeg::AVError{
172 swr_init(resampler),
173 };
174 if (error != QFFmpeg::AVError::Success) {
175 qCWarning(qLcFFmpegUtils) << "Failed to initialize audio resampler:" << error;
176 return nullptr;
177 }
178 return SwrContextUPtr(resampler);
179}
180
181QVideoFrameFormat::ColorTransfer fromAvColorTransfer(AVColorTransferCharacteristic colorTrc) {
182 switch (colorTrc) {
183 case AVCOL_TRC_BT709:
184 // The following three cases have transfer characteristics identical to BT709
185 case AVCOL_TRC_BT1361_ECG:
186 case AVCOL_TRC_BT2020_10:
187 case AVCOL_TRC_BT2020_12:
188 case AVCOL_TRC_SMPTE240M: // almost identical to bt709
189 return QVideoFrameFormat::ColorTransfer_BT709;
190 case AVCOL_TRC_GAMMA22:
191 case AVCOL_TRC_SMPTE428: // No idea, let's hope for the best...
192 case AVCOL_TRC_IEC61966_2_1: // sRGB, close enough to 2.2...
193 case AVCOL_TRC_IEC61966_2_4: // not quite, but probably close enough
194 return QVideoFrameFormat::ColorTransfer_Gamma22;
195 case AVCOL_TRC_GAMMA28:
196 return QVideoFrameFormat::ColorTransfer_Gamma28;
197 case AVCOL_TRC_SMPTE170M:
198 return QVideoFrameFormat::ColorTransfer_BT601;
199 case AVCOL_TRC_LINEAR:
200 return QVideoFrameFormat::ColorTransfer_Linear;
201 case AVCOL_TRC_SMPTE2084:
202 return QVideoFrameFormat::ColorTransfer_ST2084;
203 case AVCOL_TRC_ARIB_STD_B67:
204 return QVideoFrameFormat::ColorTransfer_STD_B67;
205 default:
206 break;
207 }
208 return QVideoFrameFormat::ColorTransfer_Unknown;
209}
210
211AVColorTransferCharacteristic toAvColorTransfer(QVideoFrameFormat::ColorTransfer colorTrc)
212{
213 switch (colorTrc) {
214 case QVideoFrameFormat::ColorTransfer_BT709:
215 return AVCOL_TRC_BT709;
216 case QVideoFrameFormat::ColorTransfer_BT601:
217 return AVCOL_TRC_BT709; // which one is the best?
218 case QVideoFrameFormat::ColorTransfer_Linear:
219 return AVCOL_TRC_SMPTE2084;
220 case QVideoFrameFormat::ColorTransfer_Gamma22:
221 return AVCOL_TRC_GAMMA22;
222 case QVideoFrameFormat::ColorTransfer_Gamma28:
223 return AVCOL_TRC_GAMMA28;
224 case QVideoFrameFormat::ColorTransfer_ST2084:
225 return AVCOL_TRC_SMPTE2084;
226 case QVideoFrameFormat::ColorTransfer_STD_B67:
227 return AVCOL_TRC_ARIB_STD_B67;
228 default:
229 return AVCOL_TRC_UNSPECIFIED;
230 }
231}
232
234{
235 switch (colorSpace) {
236 default:
237 case AVCOL_SPC_UNSPECIFIED:
238 case AVCOL_SPC_RESERVED:
239 case AVCOL_SPC_FCC:
240 case AVCOL_SPC_SMPTE240M:
241 case AVCOL_SPC_YCGCO:
242 case AVCOL_SPC_SMPTE2085:
243 case AVCOL_SPC_CHROMA_DERIVED_NCL:
244 case AVCOL_SPC_CHROMA_DERIVED_CL:
245 case AVCOL_SPC_ICTCP: // BT.2100 ICtCp
246 return QVideoFrameFormat::ColorSpace_Undefined;
247 case AVCOL_SPC_RGB:
248 return QVideoFrameFormat::ColorSpace_AdobeRgb;
249 case AVCOL_SPC_BT709:
250 return QVideoFrameFormat::ColorSpace_BT709;
251 case AVCOL_SPC_BT470BG: // BT601
252 case AVCOL_SPC_SMPTE170M: // Also BT601
253 return QVideoFrameFormat::ColorSpace_BT601;
254 case AVCOL_SPC_BT2020_NCL: // Non constant luminence
255 case AVCOL_SPC_BT2020_CL: // Constant luminence
256 return QVideoFrameFormat::ColorSpace_BT2020;
257 }
258}
259
260AVColorSpace toAvColorSpace(QVideoFrameFormat::ColorSpace colorSpace)
261{
262 switch (colorSpace) {
263 case QVideoFrameFormat::ColorSpace_BT601:
264 return AVCOL_SPC_BT470BG;
265 case QVideoFrameFormat::ColorSpace_BT709:
266 return AVCOL_SPC_BT709;
267 case QVideoFrameFormat::ColorSpace_AdobeRgb:
268 return AVCOL_SPC_RGB;
269 case QVideoFrameFormat::ColorSpace_BT2020:
270 return AVCOL_SPC_BT2020_CL;
271 default:
272 return AVCOL_SPC_UNSPECIFIED;
273 }
274}
275
277{
278 switch (colorRange) {
279 case AVCOL_RANGE_MPEG:
280 return QVideoFrameFormat::ColorRange_Video;
281 case AVCOL_RANGE_JPEG:
282 return QVideoFrameFormat::ColorRange_Full;
283 default:
284 return QVideoFrameFormat::ColorRange_Unknown;
285 }
286}
287
288AVColorRange toAvColorRange(QVideoFrameFormat::ColorRange colorRange)
289{
290 switch (colorRange) {
291 case QVideoFrameFormat::ColorRange_Video:
292 return AVCOL_RANGE_MPEG;
293 case QVideoFrameFormat::ColorRange_Full:
294 return AVCOL_RANGE_JPEG;
295 default:
296 return AVCOL_RANGE_UNSPECIFIED;
297 }
298}
299
301 if (!frame)
302 return {};
303 if (!frame->hw_frames_ctx)
304 return {};
305
306 const auto *frameCtx = reinterpret_cast<AVHWFramesContext *>(frame->hw_frames_ctx->data);
307 if (!frameCtx)
308 return {};
309
310 return frameCtx->device_ctx;
311}
312
313SwsContextUPtr createSwsContext(const QSize &srcSize, AVPixelFormat srcPixFmt, const QSize &dstSize,
314 AVPixelFormat dstPixFmt, SwsFlags conversionType)
315{
316
317 SwsContext *result =
318 sws_getContext(srcSize.width(), srcSize.height(), srcPixFmt, dstSize.width(),
319 dstSize.height(), dstPixFmt, conversionType, nullptr, nullptr, nullptr);
320
321 if (!result)
322 qCWarning(qLcFFmpegUtils) << "Cannot create sws context for:\n"
323 << "srcSize:" << srcSize
324 << "srcPixFmt:" << srcPixFmt
325 << "dstSize:" << dstSize
326 << "dstPixFmt:" << dstPixFmt
327 << "conversionType:" << conversionType;
328
329 return SwsContextUPtr(result);
330}
331
332#ifdef Q_OS_DARWIN
334{
336}
337
339{
340 auto formatDescIt = std::make_reverse_iterator(reinterpret_cast<const char *>(&cvFormat));
341 return std::string(formatDescIt - 4, formatDescIt);
342}
343
344#endif
345
346} // namespace QFFmpeg
347
348QDebug operator<<(QDebug dbg, const AVRational &value)
349{
350 dbg << value.num << "/" << value.den;
351 return dbg;
352}
353
354QDebug operator<<(QDebug dbg, const AVDictionary &dict)
355{
356 char *buffer = 0;
357 auto freeBuffer = QScopeGuard([&] {
358 av_free(buffer);
359 });
360
361 int status = av_dict_get_string(&dict, &buffer, '=', ',');
362 if (status < 0 || !buffer)
363 return dbg << "Failed to print AVDictionary";
364
365 dbg << buffer;
366 return dbg;
367}
368
369QDebug operator<<(QDebug dbg, const QFFmpeg::AVDictionaryHolder &dict)
370{
371 const AVDictionary *rawDict = dict.opts;
372 if (rawDict)
373 return dbg << *rawDict;
374 else
375 return dbg << "Empty AVDictionaryHolder";
376}
377
378QDebug operator<<(QDebug dbg, QFFmpeg::AVError error)
379{
380 if (error == QFFmpeg::AVError::Success) {
381 dbg << "Success";
382 return dbg;
383 }
384
385 char errBuf[AV_ERROR_MAX_STRING_SIZE];
386 dbg << av_make_error_string(errBuf, AV_ERROR_MAX_STRING_SIZE, qToUnderlying(error));
387 return dbg;
388}
389
390#if QT_FFMPEG_HAS_AV_CHANNEL_LAYOUT
391QDebug operator<<(QDebug dbg, const AVChannelLayout &layout)
392{
393 dbg << '[';
394 dbg << "nb_channels:" << layout.nb_channels;
395 dbg << ", order:" << layout.order;
396
397 if (layout.order == AV_CHANNEL_ORDER_NATIVE || layout.order == AV_CHANNEL_ORDER_AMBISONIC)
398 dbg << ", mask:" << Qt::bin << layout.u.mask << Qt::dec;
399 else if (layout.order == AV_CHANNEL_ORDER_CUSTOM && layout.u.map)
400 dbg << ", id: " << layout.u.map->id;
401
402 dbg << ']';
403
404 return dbg;
405}
406#endif
407
408#if QT_FFMPEG_HAS_AVCODEC_GET_SUPPORTED_CONFIG
409QDebug operator<<(QDebug dbg, const AVCodecConfig value)
410{
411 switch (value) {
412 case AV_CODEC_CONFIG_CHANNEL_LAYOUT:
413 dbg << "AV_CODEC_CONFIG_CHANNEL_LAYOUT";
414 break;
415 case AV_CODEC_CONFIG_COLOR_RANGE:
416 dbg << "AV_CODEC_CONFIG_COLOR_RANGE";
417 break;
418 case AV_CODEC_CONFIG_COLOR_SPACE:
419 dbg << "AV_CODEC_CONFIG_COLOR_SPACE";
420 break;
421 case AV_CODEC_CONFIG_FRAME_RATE:
422 dbg << "AV_CODEC_CONFIG_FRAME_RATE";
423 break;
424 case AV_CODEC_CONFIG_PIX_FORMAT:
425 dbg << "AV_CODEC_CONFIG_PIX_FORMAT";
426 break;
427 case AV_CODEC_CONFIG_SAMPLE_FORMAT:
428 dbg << "AV_CODEC_CONFIG_SAMPLE_FORMAT";
429 break;
430 case AV_CODEC_CONFIG_SAMPLE_RATE:
431 dbg << "AV_CODEC_CONFIG_SAMPLE_RATE";
432 break;
433 default:
434 dbg << "<UNKNOWN_CODEC_CONFIG>";
435 break;
436 }
437
438 return dbg;
439}
440#endif
441
442QT_END_NAMESPACE
AVColorSpace toAvColorSpace(QVideoFrameFormat::ColorSpace colorSpace)
Definition qffmpeg.cpp:260
SwsContextUPtr createSwsContext(const QSize &srcSize, AVPixelFormat srcPixFmt, const QSize &dstSize, AVPixelFormat dstPixFmt, SwsFlags conversionType)
Definition qffmpeg.cpp:313
void applyExperimentalCodecOptions(const Codec &codec, AVDictionary **opts)
Definition qffmpeg.cpp:48
bool isHwPixelFormat(AVPixelFormat format)
Definition qffmpeg.cpp:42
AVPacketSideData * addStreamSideData(AVStream *stream, AVPacketSideData sideData)
Definition qffmpeg.cpp:93
AVHWDeviceContext * avFrameDeviceContext(const AVFrame *frame)
Definition qffmpeg.cpp:300
AVColorTransferCharacteristic toAvColorTransfer(QVideoFrameFormat::ColorTransfer colorTrc)
Definition qffmpeg.cpp:211
std::conditional_t< QT_FFMPEG_AVIO_WRITE_CONST, const uint8_t *, uint8_t * > AvioWriteBufferType
QVideoFrameFormat::ColorSpace fromAvColorSpace(AVColorSpace colorSpace)
Definition qffmpeg.cpp:233
bool isAVFormatSupported(const Codec &codec, PixelOrSampleFormat format)
Definition qffmpeg.cpp:27
QVideoFrameFormat::ColorTransfer fromAvColorTransfer(AVColorTransferCharacteristic colorTrc)
Definition qffmpeg.cpp:181
QVideoFrameFormat::ColorRange fromAvColorRange(AVColorRange colorRange)
Definition qffmpeg.cpp:276
SwrContextUPtr createResampleContext(const AVAudioFormat &inputFormat, const AVAudioFormat &outputFormat)
Definition qffmpeg.cpp:135
AVPixelFormat pixelFormatForHwDevice(AVHWDeviceType deviceType)
Definition qffmpeg.cpp:57
const AVPacketSideData * streamSideData(const AVStream *stream, AVPacketSideDataType type)
Definition qffmpeg.cpp:119
AVColorRange toAvColorRange(QVideoFrameFormat::ColorRange colorRange)
Definition qffmpeg.cpp:288
QDebug operator<<(QDebug dbg, const QFFmpeg::AVDictionaryHolder &dict)
Definition qffmpeg.cpp:369
QDebug operator<<(QDebug dbg, QFFmpeg::AVError error)
Definition qffmpeg.cpp:378
QDebug operator<<(QDebug dbg, const QFileInfo &fi)
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)