Qt 6.x
The Qt SDK
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_x264(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
61{
63 codec->bit_rate = settings.videoBitRate();
64 } else {
65 const char *scales[] = {
66 "29", "26", "23", "21", "19"
67 };
68 av_dict_set(opts, "crf", scales[settings.quality()], 0);
69 }
70}
71
72static void apply_x265(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
73{
75 codec->bit_rate = settings.videoBitRate();
76 } else {
77 const char *scales[QMediaRecorder::VeryHighQuality+1] = {
78 "40", "34", "28", "26", "24",
79 };
80 av_dict_set(opts, "crf", scales[settings.quality()], 0);
81 }
82}
83
84static void apply_libvpx(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
85{
87 codec->bit_rate = settings.videoBitRate();
88 } else {
89 const char *scales[QMediaRecorder::VeryHighQuality+1] = {
90 "38", "34", "31", "28", "25",
91 };
92 av_dict_set(opts, "crf", scales[settings.quality()], 0);
93 av_dict_set(opts, "b", 0, 0);
94 }
95 av_dict_set(opts, "row-mt", "1", 0); // better multithreading
96}
97
98#ifdef Q_OS_DARWIN
99static void apply_videotoolbox(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
100{
102 codec->bit_rate = settings.videoBitRate();
103 } else {
104 // only use quality on macOS/ARM, as FFmpeg doesn't support it on the other platforms and would throw
105 // an error when initializing the codec
106#if defined(Q_OS_MACOS) && defined(Q_PROCESSOR_ARM_64)
107 // Videotoolbox describes quality as a number from 0 to 1, with low == 0.25, normal 0.5, high 0.75 and lossless = 1
108 // ffmpeg uses a different scale going from 0 to 11800.
109 // Values here are adjusted to agree approximately with the target bit rates listed above
110 const int scales[] = {
111 3000, 4800, 5900, 6900, 7700,
112 };
113 codec->global_quality = scales[settings.quality()];
114 codec->flags |= AV_CODEC_FLAG_QSCALE;
115#else
116 codec->bit_rate = bitrateForSettings(settings);
117#endif
118 }
119
120 // Videotooldox hw acceleration fails of some hardwares,
121 // allow_sw makes sw encoding available if hw encoding failed.
122 // Under the hood, ffmpeg sets
123 // kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder instead of
124 // kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder
125 av_dict_set(opts, "allow_sw", "1", 0);
126}
127#endif
128
129#if QT_CONFIG(vaapi)
130static void apply_vaapi(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **/*opts*/)
131{
132 // See also vaapi_encode_init_rate_control() in libavcodec
134 codec->bit_rate = settings.videoBitRate();
135 codec->rc_max_rate = settings.videoBitRate();
136 } else if (settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding) {
137 codec->bit_rate = settings.videoBitRate();
138 } else {
139 const int *quality = nullptr;
140 // unfortunately, all VA codecs use different quality scales :/
141 switch (settings.videoCodec()) {
143 static const int q[] = { 20, 15, 10, 8, 6 };
144 quality = q;
145 break;
146 }
149 static const int q[] = { 29, 26, 23, 21, 19 };
150 quality = q;
151 break;
152 }
154 static const int q[] = { 40, 34, 28, 26, 24 };
155 quality = q;
156 break;
157 }
159 static const int q[] = { 56, 48, 40, 34, 28 };
160 quality = q;
161 break;
162 }
164 static const int q[] = { 124, 112, 100, 88, 76 };
165 quality = q;
166 break;
167 }
169 static const int q[] = { 40, 60, 80, 90, 95 };
170 quality = q;
171 break;
172 }
176 default:
177 break;
178 }
179
180 if (quality)
181 codec->global_quality = quality[settings.quality()];
182 }
183}
184#endif
185
186static void apply_nvenc(const QMediaEncoderSettings &settings, AVCodecContext *codec,
187 AVDictionary ** /*opts*/)
188{
191 codec->bit_rate = settings.videoBitRate();
192 } else {
193 static const int q[] = { 51, 48, 35, 15, 1 };
194 codec->global_quality = q[settings.quality()];
195 }
196}
197
198#ifdef Q_OS_WINDOWS
199static void apply_mf(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
200{
202 codec->bit_rate = settings.videoBitRate();
203 av_dict_set(opts, "rate_control", "cbr", 0);
204 } else {
205 av_dict_set(opts, "rate_control", "quality", 0);
206 const char *scales[] = {
207 "25", "50", "75", "90", "100"
208 };
209 av_dict_set(opts, "quality", scales[settings.quality()], 0);
210 }
211}
212#endif
213
214#ifdef Q_OS_ANDROID
215static void apply_mediacodec(const QMediaEncoderSettings &settings, AVCodecContext *codec,
216 AVDictionary **opts)
217{
218 codec->bit_rate = settings.videoBitRate();
219
220 const int quality[] = { 25, 50, 75, 90, 100 };
221 codec->global_quality = quality[settings.quality()];
222
223 switch (settings.encodingMode()) {
225 av_dict_set(opts, "bitrate_mode", "vbr", 1);
226 break;
228 av_dict_set(opts, "bitrate_mode", "cbr", 1);
229 break;
231 // av_dict_set(opts, "bitrate_mode", "cq", 1);
232 av_dict_set(opts, "bitrate_mode", "cbr", 1);
233 break;
234 default:
235 break;
236 }
237
238 switch (settings.videoCodec()) {
240 const char *levels[] = { "2.2", "3.2", "4.2", "5.2", "6.2" };
241 av_dict_set(opts, "level", levels[settings.quality()], 1);
242 codec->profile = FF_PROFILE_H264_HIGH;
243 break;
244 }
246 const char *levels[] = { "h2.1", "h3.1", "h4.1", "h5.1", "h6.1" };
247 av_dict_set(opts, "level", levels[settings.quality()], 1);
248 codec->profile = FF_PROFILE_HEVC_MAIN;
249 break;
250 }
251 default:
252 break;
253 }
254}
255#endif
256
257namespace QFFmpeg {
258
259using ApplyOptions = void (*)(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts);
260
261const struct {
262 const char *name;
264} videoCodecOptionTable[] = { { "libx264", apply_x264 },
265 { "libx265xx", apply_x265 },
266 { "libvpx", apply_libvpx },
267 { "libvpx_vp9", apply_libvpx },
268 { "h264_nvenc", apply_nvenc },
269 { "hevc_nvenc", apply_nvenc },
270#ifdef Q_OS_DARWIN
271 { "h264_videotoolbox", apply_videotoolbox },
272 { "hevc_videotoolbox", apply_videotoolbox },
273 { "prores_videotoolbox", apply_videotoolbox },
274 { "vp9_videotoolbox", apply_videotoolbox },
275#endif
276#if QT_CONFIG(vaapi)
277 { "mpeg2_vaapi", apply_vaapi },
278 { "mjpeg_vaapi", apply_vaapi },
279 { "h264_vaapi", apply_vaapi },
280 { "hevc_vaapi", apply_vaapi },
281 { "vp8_vaapi", apply_vaapi },
282 { "vp9_vaapi", apply_vaapi },
283#endif
284#ifdef Q_OS_WINDOWS
285 { "hevc_mf", apply_mf },
286 { "h264_mf", apply_mf },
287#endif
288#ifdef Q_OS_ANDROID
289 { "hevc_mediacodec", apply_mediacodec},
290 { "h264_mediacodec", apply_mediacodec },
291#endif
292 { nullptr, nullptr } };
293
294const struct {
295 const char *name;
298 { nullptr, nullptr }
300
301void applyVideoEncoderOptions(const QMediaEncoderSettings &settings, const QByteArray &codecName, AVCodecContext *codec, AVDictionary **opts)
302{
303 av_dict_set(opts, "threads", "auto", 0); // we always want automatic threading
304
306 while (table->name) {
307 if (codecName == table->name) {
308 table->apply(settings, codec, opts);
309 return;
310 }
311
312 ++table;
313 }
314}
315
316void applyAudioEncoderOptions(const QMediaEncoderSettings &settings, const QByteArray &codecName, AVCodecContext *codec, AVDictionary **opts)
317{
318 codec->thread_count = -1; // we always want automatic threading
320 codec->bit_rate = settings.audioBitRate();
321
323 while (table->name) {
324 if (codecName == table->name) {
325 table->apply(settings, codec, opts);
326 return;
327 }
328
329 ++table;
330 }
331
332}
333
334}
335
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore
Definition qsize.h:25
const struct QFFmpeg::@723 videoCodecOptionTable[]
const char * name
void applyVideoEncoderOptions(const QMediaEncoderSettings &settings, const QByteArray &codecName, AVCodecContext *codec, AVDictionary **opts)
const struct QFFmpeg::@724 audioCodecOptionTable[]
ApplyOptions apply
void(*)(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts) ApplyOptions
void applyAudioEncoderOptions(const QMediaEncoderSettings &settings, const QByteArray &codecName, AVCodecContext *codec, AVDictionary **opts)
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 **)
static void apply_x264(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
QMediaFormat::AudioCodec codec
GLsizei levels
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLdouble s
[6]
Definition qopenglext.h:235
GLenum GLenum GLsizei void * table
constexpr QPixelLayout::BPP bitsPerPixel()
static int log2(uint i)
QSettings settings("MySoft", "Star Runner")
[0]