From a7a1838eda791580050404108ff853139f674f3d Mon Sep 17 00:00:00 2001 From: Conn O'Griofa Date: Tue, 7 Jul 2026 08:16:41 +0100 Subject: [PATCH 01/17] feat(linux/vaapi): VAAPI encoder improvements * Split rate control selection logic out from VBV bitrate enforcement. When VBV bitrate limits are enabled, the VBV constraint is now also possible to apply to CBR rate control. * Add opt-in option to allow VBR rate control. If selected, the existing RC logic detection will try to select VBR, then CBR, then fall back to CQP. If unselected, only CBR followed by CQP will be attempted. * Add encoder quality profile selection. As VAAPI doesn't mandate specific quality levels, we must probe the maximum level during initialization. If VAConfigAttribEncQualityRange returns a value, the maximum value will represent the fastest/lowest quality and value 1 sets highest quality. This value is then mapped onto the compression_level AVOption which sets the VAEncMiscParameterTypeQualityLevel via ffmpeg's vaapi_encode.c. * Add Block Level Bitrate Control (BLBRC) option that can be combined with VBR or CBR rate control on supported encoders. --- src/config.cpp | 6 +++ src/config.h | 3 ++ src/platform/linux/vaapi.cpp | 54 ++++++++++++++----- src/video.cpp | 3 ++ src_assets/common/assets/web/config.html | 3 ++ .../configs/tabs/encoders/VAAPIEncoder.vue | 25 +++++++++ .../assets/web/public/assets/locale/en.json | 9 ++++ 7 files changed, 89 insertions(+), 14 deletions(-) diff --git a/src/config.cpp b/src/config.cpp index e1dab3516fb..8b58bbad3be 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -669,6 +669,9 @@ namespace config { }, // vt { + false, // allow vbr + 0, // blbrc + 0, // quality false, // strict_rc_buffer }, // vaapi @@ -1551,6 +1554,9 @@ namespace config { int_f(vars, "vt_software", video.vt.vt_require_sw, vt::force_software_from_view); int_f(vars, "vt_realtime", video.vt.vt_realtime, vt::rt_from_view); + bool_f(vars, "vaapi_allow_vbr", video.vaapi.allow_vbr); + int_f(vars, "vaapi_blbrc", video.vaapi.blbrc); + int_f(vars, "vaapi_quality", video.vaapi.quality); bool_f(vars, "vaapi_strict_rc_buffer", video.vaapi.strict_rc_buffer); int_f(vars, "vk_tune", video.vk.tune); diff --git a/src/config.h b/src/config.h index cbe0978ede4..b8fa3f0fea8 100644 --- a/src/config.h +++ b/src/config.h @@ -105,6 +105,9 @@ namespace config { } vt; ///< VideoToolbox encoder options. struct { + bool allow_vbr; + int blbrc; + int quality; bool strict_rc_buffer; } vaapi; ///< VA-API encoder options. diff --git a/src/platform/linux/vaapi.cpp b/src/platform/linux/vaapi.cpp index 22843fe2007..d884b466f10 100644 --- a/src/platform/linux/vaapi.cpp +++ b/src/platform/linux/vaapi.cpp @@ -308,8 +308,30 @@ namespace va { BOOST_LOG(info) << "Using normal encoding mode"sv; } + // When the compression_level AVOption is set, vaapi_encode.c assigns the value to VAEncMiscParameterBufferQualityLevel + VAConfigAttrib quality_attr = {VAConfigAttribEncQualityRange}; + auto status = vaGetConfigAttributes(va_display, va_profile, va_entrypoint, &quality_attr, 1); + if (status != VA_STATUS_SUCCESS) { + quality_attr.value = 0; + } + switch (config::video.vaapi.quality) { + case 0: // auto or probe failed + default: + break; + case 1: // low quality (highest value in range) + case 2: // med quality (middle value in range) + ctx->compression_level = quality_attr.value / config::video.vaapi.quality; + break; + case 3: // high quality (1) + ctx->compression_level = 1; + break; + } + if (ctx->compression_level > 0) { + BOOST_LOG(info) << "[VAAPI] Quality level set to "sv << ctx->compression_level << " (fastest level: "sv << quality_attr.value << ")"sv; + } + VAConfigAttrib rc_attr = {VAConfigAttribRateControl}; - auto status = vaGetConfigAttributes(va_display, va_profile, va_entrypoint, &rc_attr, 1); + status = vaGetConfigAttributes(va_display, va_profile, va_entrypoint, &rc_attr, 1); if (status != VA_STATUS_SUCCESS) { // Stick to the default rate control (CQP) rc_attr.value = 0; @@ -337,26 +359,30 @@ namespace va { // When we have to resort to the default 1 second VBV for encoding quality reasons, // we stick to CBR in order to avoid encoding huge frames after bitrate undershoots // leave headroom available in the RC window. + auto rc_msg = "with standard VBV size"; + auto rc_mode = "CQP"; + if (config::video.vaapi.strict_rc_buffer || (vendor && strstr(vendor, "Intel")) || ctx->codec_id == AV_CODEC_ID_AV1) { ctx->rc_buffer_size = ctx->bit_rate * ctx->framerate.den / ctx->framerate.num; + rc_msg = "with single frame VBV size"; + } - if (rc_attr.value & VA_RC_VBR) { - BOOST_LOG(info) << "Using VBR with single frame VBV size"sv; - av_dict_set(options, "rc_mode", "VBR", 0); - } else if (rc_attr.value & VA_RC_CBR) { - BOOST_LOG(info) << "Using CBR with single frame VBV size"sv; - av_dict_set(options, "rc_mode", "CBR", 0); - } else { - BOOST_LOG(warning) << "Using CQP with single frame VBV size"sv; - av_dict_set_int(options, "qp", config::video.qp, 0); - } - } else if (!(rc_attr.value & (VA_RC_CBR | VA_RC_VBR))) { - BOOST_LOG(warning) << "Using CQP rate control"sv; + if (rc_attr.value & VA_RC_VBR && config::video.vaapi.allow_vbr) { + rc_mode = "VBR"; + } else if (rc_attr.value & VA_RC_CBR) { + rc_mode = "CBR"; + } else { + rc_mode = "CQP"; + } + + av_dict_set(options, "rc_mode", rc_mode, 0); + if (std::string_view(rc_mode) == "CQP"sv) { + BOOST_LOG(warning) << "[VAAPI] Using CQP rate control (QP value: "sv << config::video.qp << ")"sv; av_dict_set_int(options, "qp", config::video.qp, 0); } else { - BOOST_LOG(info) << "Using default rate control"sv; + BOOST_LOG(info) << "[VAAPI] Using "sv << rc_mode << " rate control "sv << rc_msg; } } diff --git a/src/video.cpp b/src/video.cpp index c34751aa4f0..2abc1eb4504 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -1191,6 +1191,7 @@ namespace video { // Common options { {"async_depth"s, 1}, + {"blbrc"s, &config::video.vaapi.blbrc}, {"idr_interval"s, std::numeric_limits::max()}, }, {}, // SDR-specific options @@ -1204,6 +1205,7 @@ namespace video { // Common options { {"async_depth"s, 1}, + {"blbrc"s, &config::video.vaapi.blbrc}, {"sei"s, 0}, {"idr_interval"s, std::numeric_limits::max()}, }, @@ -1218,6 +1220,7 @@ namespace video { // Common options { {"async_depth"s, 1}, + {"blbrc"s, &config::video.vaapi.blbrc}, {"sei"s, 0}, {"idr_interval"s, std::numeric_limits::max()}, }, diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index a9476ea02bf..339224290ff 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -356,6 +356,9 @@

{{ $t('config.configuration') }}

id: "vaapi", name: "VA-API Encoder", options: { + "vaapi_allow_vbr": "disabled", + "vaapi_blbrc": "disabled", + "vaapi_quality": 0, "vaapi_strict_rc_buffer": "disabled", }, }, diff --git a/src_assets/common/assets/web/configs/tabs/encoders/VAAPIEncoder.vue b/src_assets/common/assets/web/configs/tabs/encoders/VAAPIEncoder.vue index 063bba45b50..f635a95cf13 100644 --- a/src_assets/common/assets/web/configs/tabs/encoders/VAAPIEncoder.vue +++ b/src_assets/common/assets/web/configs/tabs/encoders/VAAPIEncoder.vue @@ -12,6 +12,31 @@ const config = ref(props.config)