From 8465fa6ac8d3363d59a0fa943dc128b5d6b3cf8d Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 27 Oct 2024 20:32:05 -0500 Subject: [PATCH] feat(vaapi): add option to enable strict enforcement of frame size --- docs/configuration.md | 27 ++++++++++++++++++ src/config.cpp | 6 ++++ src/config.h | 4 +++ src/platform/common.h | 2 +- src/platform/linux/vaapi.cpp | 21 +++++++++++--- src/video.cpp | 2 +- src_assets/common/assets/web/config.html | 11 ++++++-- .../web/configs/tabs/ContainerEncoders.vue | 8 ++++++ .../configs/tabs/encoders/VAAPIEncoder.vue | 28 +++++++++++++++++++ .../assets/web/public/assets/locale/en.json | 2 ++ 10 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 src_assets/common/assets/web/configs/tabs/encoders/VAAPIEncoder.vue diff --git a/docs/configuration.md b/docs/configuration.md index 2b282d1c7a1..d4e3354045b 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -2303,6 +2303,33 @@ editing the `conf` file in a text editor. Use the examples as reference. +## VA-API Encoder + +### vaapi_strict_rc_buffer + + + + + + + + + + + + + + +
Description + Enabling this option can avoid dropped frames over the network during scene changes, but video quality may + be reduced during motion. + @note{This option only applies for H.264 and HEVC when using VA-API [encoder](#encoder) on AMD GPUs.} +
Default@code{} + disabled + @endcode
Example@code{} + vaapi_strict_rc_buffer = enabled + @endcode
+ ## Software Encoder ### sw_preset diff --git a/src/config.cpp b/src/config.cpp index 8475a5e3370..a61b69b96f1 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -377,6 +377,10 @@ namespace config { -1, }, // vt + { + false, // strict_rc_buffer + }, // vaapi + {}, // capture {}, // encoder {}, // adapter_name @@ -1014,6 +1018,8 @@ 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_strict_rc_buffer", video.vaapi.strict_rc_buffer); + string_f(vars, "capture", video.capture); string_f(vars, "encoder", video.encoder); string_f(vars, "adapter_name", video.adapter_name); diff --git a/src/config.h b/src/config.h index e599afae454..891a4079772 100644 --- a/src/config.h +++ b/src/config.h @@ -71,6 +71,10 @@ namespace config { int vt_coder; } vt; + struct { + bool strict_rc_buffer; + } vaapi; + std::string capture; std::string encoder; std::string adapter_name; diff --git a/src/platform/common.h b/src/platform/common.h index 368bc733b19..ee48ed3909a 100644 --- a/src/platform/common.h +++ b/src/platform/common.h @@ -418,7 +418,7 @@ namespace platf { * @note Implementations may set or modify codec options prior to codec initialization. */ virtual void - init_codec_options(AVCodecContext *ctx, AVDictionary *options) {}; + init_codec_options(AVCodecContext *ctx, AVDictionary **options) {}; /** * @brief Prepare to derive a context. diff --git a/src/platform/linux/vaapi.cpp b/src/platform/linux/vaapi.cpp index ada6370034a..1e491fc39f7 100644 --- a/src/platform/linux/vaapi.cpp +++ b/src/platform/linux/vaapi.cpp @@ -130,11 +130,24 @@ namespace va { } void - init_codec_options(AVCodecContext *ctx, AVDictionary *options) override { - // Don't set the RC buffer size when using H.264 on Intel GPUs. It causes - // major encoding quality degradation. + init_codec_options(AVCodecContext *ctx, AVDictionary **options) override { auto vendor = vaQueryVendorString(va_display); - if (ctx->codec_id != AV_CODEC_ID_H264 || (vendor && !strstr(vendor, "Intel"))) { + + // Use VBR with a single frame VBV when the user forces it and for known good cases: + // - Intel GPUs + // - AV1 + // + // VBR ensures the bitstream isn't full of filler data for bitrate undershoots and + // single frame VBV ensures that we don't have large bitrate overshoots (at least + // as much as they can be avoided without pre-analysis). + // + // 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. + if (config::video.vaapi.strict_rc_buffer || + (vendor && strstr(vendor, "Intel")) || + ctx->codec_id == AV_CODEC_ID_AV1) { + av_dict_set(options, "rc_mode", "VBR", 0); ctx->rc_buffer_size = ctx->bit_rate * ctx->framerate.den / ctx->framerate.num; } } diff --git a/src/video.cpp b/src/video.cpp index ecd92e2504a..b630a63b443 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -1696,7 +1696,7 @@ namespace video { } // Allow the encoding device a final opportunity to set/unset or override any options - encode_device->init_codec_options(ctx.get(), options); + encode_device->init_codec_options(ctx.get(), &options); if (auto status = avcodec_open2(ctx.get(), codec, &options)) { char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index c9c0a6ddf71..a7681fdde02 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -257,6 +257,13 @@

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

"vt_realtime": "enabled", }, }, + { + id: "vaapi", + name: "VA-API Encoder", + options: { + "vaapi_strict_rc_buffer": "disabled", + }, + }, { id: "sw", name: "Software Encoder", @@ -283,7 +290,7 @@

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

var app = document.getElementById("app"); if (this.platform === "windows") { this.tabs = this.tabs.filter((el) => { - return el.id !== "vt"; + return el.id !== "vt" && el.id !== "vaapi"; }); } if (this.platform === "linux") { @@ -293,7 +300,7 @@

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

} if (this.platform === "macos") { this.tabs = this.tabs.filter((el) => { - return el.id !== "amd" && el.id !== "nv" && el.id !== "qsv"; + return el.id !== "amd" && el.id !== "nv" && el.id !== "qsv" && el.id !== "vaapi"; }); } diff --git a/src_assets/common/assets/web/configs/tabs/ContainerEncoders.vue b/src_assets/common/assets/web/configs/tabs/ContainerEncoders.vue index 084c3001f57..1dce1403adf 100644 --- a/src_assets/common/assets/web/configs/tabs/ContainerEncoders.vue +++ b/src_assets/common/assets/web/configs/tabs/ContainerEncoders.vue @@ -5,6 +5,7 @@ import IntelQuickSyncEncoder from './encoders/IntelQuickSyncEncoder.vue' import AmdAmfEncoder from './encoders/AmdAmfEncoder.vue' import VideotoolboxEncoder from './encoders/VideotoolboxEncoder.vue' import SoftwareEncoder from './encoders/SoftwareEncoder.vue' +import VAAPIEncoder from './encoders/VAAPIEncoder.vue' const props = defineProps([ 'platform', @@ -45,6 +46,13 @@ const config = ref(props.config) :config="config" /> + + + +import { ref } from 'vue' + +const props = defineProps([ + 'platform', + 'config', +]) + +const config = ref(props.config) + + + + + diff --git a/src_assets/common/assets/web/public/assets/locale/en.json b/src_assets/common/assets/web/public/assets/locale/en.json index 12844fcf874..a41617fe682 100644 --- a/src_assets/common/assets/web/public/assets/locale/en.json +++ b/src_assets/common/assets/web/public/assets/locale/en.json @@ -310,6 +310,8 @@ "touchpad_as_ds4_desc": "If disabled, touchpad presence will not be taken into account during gamepad type selection.", "upnp": "UPnP", "upnp_desc": "Automatically configure port forwarding for streaming over the Internet", + "vaapi_strict_rc_buffer": "Strictly enforce frame bitrate limits for H.264/HEVC on AMD GPUs", + "vaapi_strict_rc_buffer_desc": "Enabling this option can avoid dropped frames over the network during scene changes, but video quality may be reduced during motion.", "virtual_sink": "Virtual Sink", "virtual_sink_desc": "Manually specify a virtual audio device to use. If unset, the device is chosen automatically. We strongly recommend leaving this field blank to use automatic device selection!", "virtual_sink_placeholder": "Steam Streaming Speakers",