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