7#include <QtMultimedia/qaudioformat.h>
15#include <libavcodec/avcodec.h>
19#include <libavutil/channel_layout.h>
39 const double bitsPerPixel[
int(QMediaFormat::VideoCodec::LastVideoCodec)+1][QMediaRecorder::VeryHighQuality+1] = {
40 { 1.2, 2.25, 5, 9, 15 },
41 { 0.8, 1.5, 3.5, 6, 10 },
42 { 0.4, 0.75, 1.75, 3, 5 },
43 { 0.4, 0.75, 1.75, 3, 5 },
44 { 0.3, 0.5, 0.2, 2, 3 },
45 { 0.4, 0.75, 1.75, 3, 5 },
46 { 0.3, 0.5, 0.2, 2, 3 },
47 { 0.2, 0.4, 0.9, 1.5, 2.5 },
48 { 0.4, 0.75, 1.75, 3, 5 },
49 { 0.8, 1.5, 3.5, 6, 10 },
50 { 16, 24, 32, 40, 48 },
53 QSize s = settings.videoResolution();
54 double bitrate = bitsPerPixel[
int(settings.videoCodec())][settings.quality()]*s.width()*s.height();
56 if (settings.videoCodec() != QMediaFormat::VideoCodec::MotionJPEG) {
59 float rateMultiplier = log2(settings.videoFrameRate()/30.);
60 bitrate *= pow(1.5, rateMultiplier);
63 bitrate *= settings.videoFrameRate()/30.;
72static void apply_openh264(
const QMediaEncoderSettings &settings, AVCodecContext *codec,
75 if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding
76 || settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding) {
77 codec->bit_rate = settings.videoBitRate();
78 av_dict_set(opts,
"rc_mode",
"bitrate", 0);
80 av_dict_set(opts,
"rc_mode",
"quality", 0);
81 static const int q[] = { 51, 48, 38, 25, 5 };
82 codec->qmax = codec->qmin = q[settings.quality()];
86static void apply_x264(
const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
88 if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding || settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding) {
89 codec->bit_rate = settings.videoBitRate();
91 const char *scales[] = {
92 "29",
"26",
"23",
"21",
"19"
94 av_dict_set(opts,
"crf", scales[settings.quality()], 0);
98static void apply_x265(
const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
100 if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding || settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding) {
101 codec->bit_rate = settings.videoBitRate();
103 const char *scales[QMediaRecorder::VeryHighQuality+1] = {
104 "40",
"34",
"28",
"26",
"24",
106 av_dict_set(opts,
"crf", scales[settings.quality()], 0);
110static void apply_libvpx(
const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
112 if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding || settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding) {
113 codec->bit_rate = settings.videoBitRate();
115 const char *scales[QMediaRecorder::VeryHighQuality+1] = {
116 "38",
"34",
"31",
"28",
"25",
118 av_dict_set(opts,
"crf", scales[settings.quality()], 0);
119 av_dict_set(opts,
"b",
nullptr, 0);
121 av_dict_set(opts,
"row-mt",
"1", 0);
124static void apply_mpeg4(
const QMediaEncoderSettings &settings, AVCodecContext *codec,
129 QMediaRecorder::EncodingMode encodingMode = settings.encodingMode();
130 switch (encodingMode) {
131 case QMediaRecorder::ConstantBitRateEncoding:
132 case QMediaRecorder::QMediaRecorder::AverageBitRateEncoding: {
133 codec->bit_rate = settings.videoBitRate();
134 if (encodingMode == QMediaRecorder::ConstantBitRateEncoding)
135 codec->rc_min_rate = codec->rc_max_rate = settings.videoBitRate();
139 case QMediaRecorder::ConstantQualityEncoding: {
140 constexpr auto scales = std::array{
147 av_dict_set_int(opts,
"qscale", scales[settings.quality()], 0);
150 case QMediaRecorder::TwoPassEncoding: {
151 qWarning(
"Two pass encoding is not supported for MPEG4");
155 Q_UNREACHABLE_RETURN();
161static void apply_videotoolbox(
const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
163 if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding || settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding) {
164 codec->bit_rate = settings.videoBitRate();
168#if defined(Q_OS_MACOS) && defined(Q_PROCESSOR_ARM_64)
172 const int scales[] = {
173 3000, 4800, 5900, 6900, 7700,
175 codec->global_quality = scales[settings.quality()];
176 codec->flags |= AV_CODEC_FLAG_QSCALE;
178 codec->bit_rate = bitrateForSettings(settings);
187 av_dict_set(opts,
"allow_sw",
"1", 0);
192static void apply_vaapi(
const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **)
195 if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding) {
196 codec->bit_rate = settings.videoBitRate();
197 codec->rc_max_rate = settings.videoBitRate();
198 }
else if (settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding) {
199 codec->bit_rate = settings.videoBitRate();
201 const int *quality =
nullptr;
203 switch (settings.videoCodec()) {
204 case QMediaFormat::VideoCodec::MPEG2: {
205 static const int q[] = { 20, 15, 10, 8, 6 };
209 case QMediaFormat::VideoCodec::MPEG4:
210 case QMediaFormat::VideoCodec::H264: {
211 static const int q[] = { 29, 26, 23, 21, 19 };
215 case QMediaFormat::VideoCodec::H265: {
216 static const int q[] = { 40, 34, 28, 26, 24 };
220 case QMediaFormat::VideoCodec::VP8: {
221 static const int q[] = { 56, 48, 40, 34, 28 };
225 case QMediaFormat::VideoCodec::VP9: {
226 static const int q[] = { 124, 112, 100, 88, 76 };
230 case QMediaFormat::VideoCodec::MotionJPEG: {
231 static const int q[] = { 40, 60, 80, 90, 95 };
235 case QMediaFormat::VideoCodec::AV1:
236 case QMediaFormat::VideoCodec::Theora:
237 case QMediaFormat::VideoCodec::WMV:
243 codec->global_quality = quality[settings.quality()];
248static void apply_nvenc(
const QMediaEncoderSettings &settings, AVCodecContext *codec,
251 switch (settings.encodingMode()) {
252 case QMediaRecorder::EncodingMode::AverageBitRateEncoding:
253 av_dict_set(opts,
"vbr",
"1", 0);
254 codec->bit_rate = settings.videoBitRate();
256 case QMediaRecorder::EncodingMode::ConstantBitRateEncoding:
257 av_dict_set(opts,
"cbr",
"1", 0);
258 codec->bit_rate = settings.videoBitRate();
259 codec->rc_max_rate = codec->rc_min_rate = codec->bit_rate;
261 case QMediaRecorder::EncodingMode::ConstantQualityEncoding: {
262 static const char *q[] = {
"51",
"48",
"35",
"15",
"1" };
263 av_dict_set(opts,
"cq", q[settings.quality()], 0);
271static void apply_mf(
const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
273 if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding || settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding) {
274 codec->bit_rate = settings.videoBitRate();
275 av_dict_set(opts,
"rate_control",
"cbr", 0);
277 av_dict_set(opts,
"rate_control",
"quality", 0);
278 const char *scales[] = {
279 "25",
"50",
"75",
"90",
"100"
281 av_dict_set(opts,
"quality", scales[settings.quality()], 0);
287static void apply_mediacodec(
const QMediaEncoderSettings &settings, AVCodecContext *codec,
290 if (settings.videoBitRate() != -1)
291 codec->bit_rate = settings.videoBitRate();
293 codec->bit_rate = bitrateForSettings(settings);
295 const int quality[] = { 25, 50, 75, 90, 100 };
296 codec->global_quality = quality[settings.quality()];
298 switch (settings.encodingMode()) {
299 case QMediaRecorder::EncodingMode::AverageBitRateEncoding:
300 av_dict_set(opts,
"bitrate_mode",
"vbr", 1);
302 case QMediaRecorder::EncodingMode::ConstantBitRateEncoding:
303 av_dict_set(opts,
"bitrate_mode",
"cbr", 1);
305 case QMediaRecorder::EncodingMode::ConstantQualityEncoding:
307 av_dict_set(opts,
"bitrate_mode",
"cbr", 1);
313 switch (settings.videoCodec()) {
314 case QMediaFormat::VideoCodec::H264: {
315 const char *levels[] = {
"2.2",
"3.2",
"4.2",
"5.2",
"6.2" };
316 av_dict_set(opts,
"level", levels[settings.quality()], 1);
317 codec->profile = FF_PROFILE_H264_HIGH;
320 case QMediaFormat::VideoCodec::H265: {
324 if (avcodec_version() < 4000612 || avcodec_version() > 4002660) {
325 const char *levels[] = {
"h2.1",
"h3.1",
"h4.1",
"h5.1",
"h6.1" };
326 av_dict_set(opts,
"level", levels[settings.quality()], 1);
329 codec->profile = FF_PROFILE_HEVC_MAIN;
340using ApplyOptions =
void (*)(
const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts);
345} videoCodecOptionTable[] = { {
"libx264", apply_x264 },
346 {
"libx265xx", apply_x265 },
347 {
"libvpx", apply_libvpx },
348 {
"libvpx_vp9", apply_libvpx },
349 {
"libopenh264", apply_openh264 },
350 {
"h264_nvenc", apply_nvenc },
351 {
"hevc_nvenc", apply_nvenc },
352 {
"av1_nvenc", apply_nvenc },
353 {
"mpeg4", apply_mpeg4 },
355 {
"h264_videotoolbox", apply_videotoolbox },
356 {
"hevc_videotoolbox", apply_videotoolbox },
357 {
"prores_videotoolbox", apply_videotoolbox },
358 {
"vp9_videotoolbox", apply_videotoolbox },
361 {
"mpeg2_vaapi", apply_vaapi },
362 {
"mjpeg_vaapi", apply_vaapi },
363 {
"h264_vaapi", apply_vaapi },
364 {
"hevc_vaapi", apply_vaapi },
365 {
"vp8_vaapi", apply_vaapi },
366 {
"vp9_vaapi", apply_vaapi },
369 {
"hevc_mf", apply_mf },
370 {
"h264_mf", apply_mf },
373 {
"hevc_mediacodec", apply_mediacodec },
374 {
"h264_mediacodec", apply_mediacodec },
376 {
nullptr,
nullptr } };
381} audioCodecOptionTable[] = {
385void applyVideoEncoderOptions(
const QMediaEncoderSettings &settings,
const QByteArray &codecName, AVCodecContext *codec, AVDictionary **opts)
387 av_dict_set(opts,
"threads",
"auto", 0);
389 auto *table = videoCodecOptionTable;
390 while (table->name) {
391 if (codecName == table->name) {
392 table->apply(settings, codec, opts);
400void applyAudioEncoderOptions(
const QMediaEncoderSettings &settings,
const QByteArray &codecName, AVCodecContext *codec, AVDictionary **opts)
402 codec->thread_count = -1;
403 if (settings.encodingMode() == QMediaRecorder::ConstantBitRateEncoding || settings.encodingMode() == QMediaRecorder::AverageBitRateEncoding)
404 codec->bit_rate = settings.audioBitRate();
406 if (settings.audioSampleRate() != -1)
407 codec->sample_rate = settings.audioSampleRate();
409 if (settings.audioChannelCount() != -1) {
410 auto mask = QFFmpegMediaFormatInfo::avChannelLayout(
411 QAudioFormat::defaultChannelConfigForChannelCount(settings.audioChannelCount()));
413#if QT_FFMPEG_HAS_AV_CHANNEL_LAYOUT
414 av_channel_layout_from_mask(&codec->ch_layout, mask);
416 codec->channel_layout = mask;
417 codec->channels = qPopulationCount(codec->channel_layout);
421 auto *table = audioCodecOptionTable;
422 while (table->name) {
423 if (codecName == table->name) {
424 table->apply(settings, codec, opts);
void(*)(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts) ApplyOptions
std::conditional_t< QT_FFMPEG_AVIO_WRITE_CONST, const uint8_t *, uint8_t * > AvioWriteBufferType
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_mpeg4(const QMediaEncoderSettings &settings, AVCodecContext *codec, AVDictionary **opts)
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)