Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qffmpegencoderoptions.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 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
4
5#if QT_CONFIG(vaapi)
6#include <va/va.h>
7#endif
8
10
11// unfortunately there is no common way to specify options for the encoders. The code here tries to map our settings sensibly
12// to options available in different encoders
13
14// For constant quality options, we're trying to map things to approx those bit rates for 1080p@30fps (in Mbps):
15// VeryLow Low Normal High VeryHigh
16// H264: 0.8M 1.5M 3.5M 6M 10M
17// H265: 0.5M 1.0M 2.5M 4M 7M
18
19[[maybe_unused]]
20static int bitrateForSettings(const QMediaEncoderSettings &settings, bool hdr = false)
21{
22 // calculate an acceptable bitrate depending on video codec, resolution, framerate and requested quality
23 // The calculations are rather heuristic here, trying to take into account how well codecs compress using
24 // the tables above.
25
26 // The table here is for 30FPS
28 { 1.2, 2.25, 5, 9, 15 }, // MPEG1,
29 { 0.8, 1.5, 3.5, 6, 10 }, // MPEG2
30 { 0.4, 0.75, 1.75, 3, 5 }, // MPEG4
31 { 0.4, 0.75, 1.75, 3, 5 }, // H264
32 { 0.3, 0.5, 0.2, 2, 3 }, // H265
33 { 0.4, 0.75, 1.75, 3, 5 }, // VP8
34 { 0.3, 0.5, 0.2, 2, 3 }, // VP9
35 { 0.2, 0.4, 0.9, 1.5, 2.5 }, // AV1
36 { 0.4, 0.75, 1.75, 3, 5 }, // Theora
37 { 0.8, 1.5, 3.5, 6, 10 }, // WMV
38 { 16, 24, 32, 40, 48 }, // MotionJPEG
39 };
40
41 QSize s = settings.videoResolution();
42 double bitrate = bitsPerPixel[int(settings.videoCodec())][settings.quality()]*s.width()*s.height();
43
45 // We assume that doubling the framerate requires 1.5 times the amount of data (not twice, as intraframe
46 // differences will be smaller). 4 times the frame rate uses thus 2.25 times the data, etc.
47 float rateMultiplier = log2(settings.videoFrameRate()/30.);
48 bitrate *= pow(1.5, rateMultiplier);
49 } else {
50 // MotionJPEG doesn't optimize between frames, so we have a linear dependency on framerate
51 bitrate *= settings.videoFrameRate()/30.;
52 }
53
54 // HDR requires 10bits per pixel instead of 8, so apply a factor of 1.25.
55 if (hdr)
56 bitrate *= 1.25;
57 return bitrate;
58}
59
60static void apply_openh264(const QMediaEncoderSettings &settings, AVCodecContext *codec,
61 AVDictionary **opts)
62{
65 codec->bit_rate = settings.videoBitRate();
66 av_dict_set(opts, "rc_mode", "bitrate", 0);
67 } else {
68 av_dict_set(opts, "rc_mode", "quality", 0);
69 static const int q[] = { 51, 48, 38, 25, 5 };
70 codec->qmax = codec->qmin = q[settings.quality()];
71 }
72}
73
74static void apply_x264(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
75{
77 codec->bit_rate = settings.videoBitRate();
78 } else {
79 const char *scales[] = {
80 "29", "26", "23", "21", "19"
81 };
82 av_dict_set(opts, "crf", scales[settings.quality()], 0);
83 }
84}
85
86static void apply_x265(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
87{
89 codec->bit_rate = settings.videoBitRate();
90 } else {
91 const char *scales[QMediaRecorder::VeryHighQuality+1] = {
92 "40", "34", "28", "26", "24",
93 };
94 av_dict_set(opts, "crf", scales[settings.quality()], 0);
95 }
96}
97
98static void apply_libvpx(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
99{
101 codec->bit_rate = settings.videoBitRate();
102 } else {
103 const char *scales[QMediaRecorder::VeryHighQuality+1] = {
104 "38", "34", "31", "28", "25",
105 };
106 av_dict_set(opts, "crf", scales[settings.quality()], 0);
107 av_dict_set(opts, "b", 0, 0);
108 }
109 av_dict_set(opts, "row-mt", "1", 0); // better multithreading
110}
111
112#ifdef Q_OS_DARWIN
113static void apply_videotoolbox(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
114{
116 codec->bit_rate = settings.videoBitRate();
117 } else {
118 // only use quality on macOS/ARM, as FFmpeg doesn't support it on the other platforms and would throw
119 // an error when initializing the codec
120#if defined(Q_OS_MACOS) && defined(Q_PROCESSOR_ARM_64)
121 // Videotoolbox describes quality as a number from 0 to 1, with low == 0.25, normal 0.5, high 0.75 and lossless = 1
122 // ffmpeg uses a different scale going from 0 to 11800.
123 // Values here are adjusted to agree approximately with the target bit rates listed above
124 const int scales[] = {
125 3000, 4800, 5900, 6900, 7700,
126 };
127 codec->global_quality = scales[settings.quality()];
128 codec->flags |= AV_CODEC_FLAG_QSCALE;
129#else
130 codec->bit_rate = bitrateForSettings(settings);
131#endif
132 }
133
134 // Videotooldox hw acceleration fails of some hardwares,
135 // allow_sw makes sw encoding available if hw encoding failed.
136 // Under the hood, ffmpeg sets
137 // kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder instead of
138 // kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder
139 av_dict_set(opts, "allow_sw", "1", 0);
140}
141#endif
142
143#if QT_CONFIG(vaapi)
144static void apply_vaapi(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **/*opts*/)
145{
146 // See also vaapi_encode_init_rate_control() in libavcodec
148 codec->bit_rate = settings.videoBitRate();
149 codec->rc_max_rate = settings.videoBitRate();
150 } else if (settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding) {
151 codec->bit_rate = settings.videoBitRate();
152 } else {
153 const int *quality = nullptr;
154 // unfortunately, all VA codecs use different quality scales :/
155 switch (settings.videoCodec()) {
157 static const int q[] = { 20, 15, 10, 8, 6 };
158 quality = q;
159 break;
160 }
163 static const int q[] = { 29, 26, 23, 21, 19 };
164 quality = q;
165 break;
166 }
168 static const int q[] = { 40, 34, 28, 26, 24 };
169 quality = q;
170 break;
171 }
173 static const int q[] = { 56, 48, 40, 34, 28 };
174 quality = q;
175 break;
176 }
178 static const int q[] = { 124, 112, 100, 88, 76 };
179 quality = q;
180 break;
181 }
183 static const int q[] = { 40, 60, 80, 90, 95 };
184 quality = q;
185 break;
186 }
190 default:
191 break;
192 }
193
194 if (quality)
195 codec->global_quality = quality[settings.quality()];
196 }
197}
198#endif
199
200static void apply_nvenc(const QMediaEncoderSettings &settings, AVCodecContext *codec,
201 AVDictionary **opts)
202{
203 switch (settings.encodingMode()) {
205 av_dict_set(opts, "vbr", "1", 0);
206 codec->bit_rate = settings.videoBitRate();
207 break;
209 av_dict_set(opts, "cbr", "1", 0);
210 codec->bit_rate = settings.videoBitRate();
211 codec->rc_max_rate = codec->rc_min_rate = codec->bit_rate;
212 break;
214 static const char *q[] = { "51", "48", "35", "15", "1" };
215 av_dict_set(opts, "cq", q[settings.quality()], 0);
216 } break;
217 default:
218 break;
219 }
220}
221
222#ifdef Q_OS_WINDOWS
223static void apply_mf(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
224{
226 codec->bit_rate = settings.videoBitRate();
227 av_dict_set(opts, "rate_control", "cbr", 0);
228 } else {
229 av_dict_set(opts, "rate_control", "quality", 0);
230 const char *scales[] = {
231 "25", "50", "75", "90", "100"
232 };
233 av_dict_set(opts, "quality", scales[settings.quality()], 0);
234 }
235}
236#endif
237
238#ifdef Q_OS_ANDROID
239static void apply_mediacodec(const QMediaEncoderSettings &settings, AVCodecContext *codec,
240 AVDictionary **opts)
241{
242 codec->bit_rate = settings.videoBitRate();
243
244 const int quality[] = { 25, 50, 75, 90, 100 };
245 codec->global_quality = quality[settings.quality()];
246
247 switch (settings.encodingMode()) {
249 av_dict_set(opts, "bitrate_mode", "vbr", 1);
250 break;
252 av_dict_set(opts, "bitrate_mode", "cbr", 1);
253 break;
255 // av_dict_set(opts, "bitrate_mode", "cq", 1);
256 av_dict_set(opts, "bitrate_mode", "cbr", 1);
257 break;
258 default:
259 break;
260 }
261
262 switch (settings.videoCodec()) {
264 const char *levels[] = { "2.2", "3.2", "4.2", "5.2", "6.2" };
265 av_dict_set(opts, "level", levels[settings.quality()], 1);
266 codec->profile = FF_PROFILE_H264_HIGH;
267 break;
268 }
270 const char *levels[] = { "h2.1", "h3.1", "h4.1", "h5.1", "h6.1" };
271 av_dict_set(opts, "level", levels[settings.quality()], 1);
272 codec->profile = FF_PROFILE_HEVC_MAIN;
273 break;
274 }
275 default:
276 break;
277 }
278}
279#endif
280
281namespace QFFmpeg {
282
283using ApplyOptions = void (*)(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts);
284
285const struct {
286 const char *name;
288} videoCodecOptionTable[] = { { "libx264", apply_x264 },
289 { "libx265xx", apply_x265 },
290 { "libvpx", apply_libvpx },
291 { "libvpx_vp9", apply_libvpx },
292 { "libopenh264", apply_openh264 },
293 { "h264_nvenc", apply_nvenc },
294 { "hevc_nvenc", apply_nvenc },
295 { "av1_nvenc", apply_nvenc },
296#ifdef Q_OS_DARWIN
297 { "h264_videotoolbox", apply_videotoolbox },
298 { "hevc_videotoolbox", apply_videotoolbox },
299 { "prores_videotoolbox", apply_videotoolbox },
300 { "vp9_videotoolbox", apply_videotoolbox },
301#endif
302#if QT_CONFIG(vaapi)
303 { "mpeg2_vaapi", apply_vaapi },
304 { "mjpeg_vaapi", apply_vaapi },
305 { "h264_vaapi", apply_vaapi },
306 { "hevc_vaapi", apply_vaapi },
307 { "vp8_vaapi", apply_vaapi },
308 { "vp9_vaapi", apply_vaapi },
309#endif
310#ifdef Q_OS_WINDOWS
311 { "hevc_mf", apply_mf },
312 { "h264_mf", apply_mf },
313#endif
314#ifdef Q_OS_ANDROID
315 { "hevc_mediacodec", apply_mediacodec },
316 { "h264_mediacodec", apply_mediacodec },
317#endif
318 { nullptr, nullptr } };
319
320const struct {
321 const char *name;
324 { nullptr, nullptr }
326
327void applyVideoEncoderOptions(const QMediaEncoderSettings &settings, const QByteArray &codecName, AVCodecContext *codec, AVDictionary **opts)
328{
329 av_dict_set(opts, "threads", "auto", 0); // we always want automatic threading
330
332 while (table->name) {
333 if (codecName == table->name) {
334 table->apply(settings, codec, opts);
335 return;
336 }
337
338 ++table;
339 }
340}
341
342void applyAudioEncoderOptions(const QMediaEncoderSettings &settings, const QByteArray &codecName, AVCodecContext *codec, AVDictionary **opts)
343{
344 codec->thread_count = -1; // we always want automatic threading
346 codec->bit_rate = settings.audioBitRate();
347
349 while (table->name) {
350 if (codecName == table->name) {
351 table->apply(settings, codec, opts);
352 return;
353 }
354
355 ++table;
356 }
357
358}
359
360}
361
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore
Definition qsize.h:25
const struct QFFmpeg::@732 videoCodecOptionTable[]
const char * name
void applyVideoEncoderOptions(const QMediaEncoderSettings &settings, const QByteArray &codecName, AVCodecContext *codec, AVDictionary **opts)
ApplyOptions apply
const struct QFFmpeg::@733 audioCodecOptionTable[]
void applyAudioEncoderOptions(const QMediaEncoderSettings &settings, const QByteArray &codecName, AVCodecContext *codec, AVDictionary **opts)
void(*)(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts) ApplyOptions
Combined button and popup list for selecting options.
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
static void apply_libvpx(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
static QT_BEGIN_NAMESPACE int bitrateForSettings(const QMediaEncoderSettings &settings, bool hdr=false)
static void apply_x265(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
static void apply_nvenc(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
static void apply_x264(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
static void apply_openh264(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
QMediaFormat::AudioCodec codec
GLsizei levels
GLdouble s
[6]
Definition qopenglext.h:235
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLenum GLenum GLsizei void * table
constexpr QPixelLayout::BPP bitsPerPixel()
static int log2(uint i)
QSettings settings("MySoft", "Star Runner")
[0]