aboutsummaryrefslogtreecommitdiffhomepage
path: root/subprojects
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects')
-rw-r--r--subprojects/ffmpeg.wrap7
-rw-r--r--subprojects/nv-codec-headers.wrap6
-rw-r--r--subprojects/openssl.wrap6
-rw-r--r--subprojects/opus.wrap6
-rw-r--r--subprojects/packagefiles/ffmpeg-nvenc-runtime-api-version.patch837
-rw-r--r--subprojects/packagefiles/ffmpeg/meson.build35
-rw-r--r--subprojects/packagefiles/nv-codec-headers/meson.build3
-rw-r--r--subprojects/packagefiles/openssl/meson.build3
-rw-r--r--subprojects/packagefiles/opus/meson.build3
-rw-r--r--subprojects/packagefiles/srt/meson.build3
-rw-r--r--subprojects/packagefiles/x264/meson.build3
-rw-r--r--subprojects/srt.wrap6
-rw-r--r--subprojects/x264.wrap6
13 files changed, 924 insertions, 0 deletions
diff --git a/subprojects/ffmpeg.wrap b/subprojects/ffmpeg.wrap
new file mode 100644
index 0000000..56e8e65
--- /dev/null
+++ b/subprojects/ffmpeg.wrap
@@ -0,0 +1,7 @@
+[wrap-file]
+directory = ffmpeg-8.1
+source_url = https://ffmpeg.org/releases/ffmpeg-8.1.tar.xz
+source_filename = ffmpeg-8.1.tar.xz
+source_hash = b072aed6871998cce9b36e7774033105ca29e33632be5b6347f3206898e0756a
+patch_directory = ffmpeg
+diff_files = ffmpeg-nvenc-runtime-api-version.patch
diff --git a/subprojects/nv-codec-headers.wrap b/subprojects/nv-codec-headers.wrap
new file mode 100644
index 0000000..e3b36de
--- /dev/null
+++ b/subprojects/nv-codec-headers.wrap
@@ -0,0 +1,6 @@
+[wrap-file]
+directory = nv-codec-headers-n13.0.19.0
+source_url = https://github.com/FFmpeg/nv-codec-headers/archive/refs/tags/n13.0.19.0.tar.gz
+source_filename = nv-codec-headers-13.0.19.0.tar.gz
+source_hash = 86d15d1a7c0ac73a0eafdfc57bebfeba7da8264595bf531cf4d8db1c22940116
+patch_directory = nv-codec-headers
diff --git a/subprojects/openssl.wrap b/subprojects/openssl.wrap
new file mode 100644
index 0000000..fe386af
--- /dev/null
+++ b/subprojects/openssl.wrap
@@ -0,0 +1,6 @@
+[wrap-file]
+directory = openssl-3.6.3
+source_url = https://github.com/openssl/openssl/releases/download/openssl-3.6.3/openssl-3.6.3.tar.gz
+source_filename = openssl-3.6.3.tar.gz
+source_hash = 243a86649cf6f23eeb6a2ff2456e09e5d77dd9018a54d3d96b0c6bdd6ba6c7f1
+patch_directory = openssl
diff --git a/subprojects/opus.wrap b/subprojects/opus.wrap
new file mode 100644
index 0000000..af08d8b
--- /dev/null
+++ b/subprojects/opus.wrap
@@ -0,0 +1,6 @@
+[wrap-file]
+directory = opus-1.6.1
+source_url = https://downloads.xiph.org/releases/opus/opus-1.6.1.tar.gz
+source_filename = opus-1.6.1.tar.gz
+source_hash = 6ffcb593207be92584df15b32466ed64bbec99109f007c82205f0194572411a1
+patch_directory = opus
diff --git a/subprojects/packagefiles/ffmpeg-nvenc-runtime-api-version.patch b/subprojects/packagefiles/ffmpeg-nvenc-runtime-api-version.patch
new file mode 100644
index 0000000..53c8419
--- /dev/null
+++ b/subprojects/packagefiles/ffmpeg-nvenc-runtime-api-version.patch
@@ -0,0 +1,837 @@
+diff --git a/libavcodec/nvenc.c b/libavcodec/nvenc.c
+index 392230526d..ba26580acb 100644
+--- a/libavcodec/nvenc.c
++++ b/libavcodec/nvenc.c
+@@ -263,7 +263,15 @@ static void nvenc_map_preset(NvencContext *ctx)
+
+ static void nvenc_print_driver_requirement(AVCodecContext *avctx, int level)
+ {
+-#if NVENCAPI_CHECK_VERSION(13, 1)
++/* With runtime API version negotiation the build-time header version no
++ * longer dictates the required driver; the runtime floor is API 11.1. */
++#if 1
++# if defined(_WIN32) || defined(__CYGWIN__)
++ const char *minver = "471.41";
++# else
++ const char *minver = "470.57.02";
++# endif
++#elif NVENCAPI_CHECK_VERSION(13, 1)
+ const char *minver = "(unknown)";
+ #elif NVENCAPI_CHECK_VERSION(13, 0)
+ const char *minver = "570.0";
+@@ -347,6 +355,193 @@ static void nvenc_print_driver_requirement(AVCodecContext *avctx, int level)
+ #define to_nv_color_trc(n) (uint32_t)(n)
+ #endif
+
++/*
++ * Runtime NVENC API version negotiation, see nvenc.h.
++ *
++ * Each row holds the exact NV_ENC_*_VER constants of one SDK's nvEncodeAPI.h.
++ * A struct version word is composed as
++ * major | (minor << 24) | (struct_revision << 16) | (0x7 << 28)
++ * optionally OR'd with (1u << 31); the driver validates the whole word, so
++ * presenting a row of these values is indistinguishable from an FFmpeg that
++ * was actually compiled against that SDK's headers.
++ */
++#define NVENC_API_WORD(major, minor) \
++ ((uint32_t)(major) | ((uint32_t)(minor) << 24))
++#define NVENC_STRUCT_VER(major, minor, rev) \
++ (NVENC_API_WORD(major, minor) | ((uint32_t)(rev) << 16) | (0x7u << 28))
++#define NVENC_STRUCT_VER_RES(major, minor, rev) \
++ (NVENC_STRUCT_VER(major, minor, rev) | (1u << 31))
++
++// Newest first; negotiation picks the first row the driver supports.
++static const NvencApiVersion nvenc_api_versions[] = {
++ { // SDK 13.0, driver >= 570.0 (Linux) / 570.0 (Windows)
++ .packed = NVENC_API_PACKED(13, 0),
++ .api = NVENC_API_WORD(13, 0),
++ .function_list = NVENC_STRUCT_VER(13, 0, 2),
++ .open_session_params = NVENC_STRUCT_VER(13, 0, 1),
++ .caps_param = NVENC_STRUCT_VER(13, 0, 1),
++ .initialize_params = NVENC_STRUCT_VER_RES(13, 0, 7),
++ .config = NVENC_STRUCT_VER_RES(13, 0, 9),
++ .preset_config = NVENC_STRUCT_VER_RES(13, 0, 5),
++ .pic_params = NVENC_STRUCT_VER_RES(13, 0, 7),
++ .lock_bitstream = NVENC_STRUCT_VER_RES(13, 0, 2),
++ .lock_input_buffer = NVENC_STRUCT_VER(13, 0, 1),
++ .map_input_resource = NVENC_STRUCT_VER(13, 0, 4),
++ .register_resource = NVENC_STRUCT_VER(13, 0, 5),
++ .create_input_buffer = NVENC_STRUCT_VER(13, 0, 2),
++ .create_bitstream_buffer = NVENC_STRUCT_VER(13, 0, 1),
++ .reconfigure_params = NVENC_STRUCT_VER_RES(13, 0, 2),
++ .sequence_param_payload = NVENC_STRUCT_VER(13, 0, 1),
++ },
++ { // SDK 12.2, driver >= 550.54.14 (Linux) / 551.76 (Windows)
++ .packed = NVENC_API_PACKED(12, 2),
++ .api = NVENC_API_WORD(12, 2),
++ .function_list = NVENC_STRUCT_VER(12, 2, 2),
++ .open_session_params = NVENC_STRUCT_VER(12, 2, 1),
++ .caps_param = NVENC_STRUCT_VER(12, 2, 1),
++ .initialize_params = NVENC_STRUCT_VER_RES(12, 2, 7),
++ .config = NVENC_STRUCT_VER_RES(12, 2, 9),
++ .preset_config = NVENC_STRUCT_VER_RES(12, 2, 5),
++ .pic_params = NVENC_STRUCT_VER_RES(12, 2, 7),
++ .lock_bitstream = NVENC_STRUCT_VER_RES(12, 2, 2),
++ .lock_input_buffer = NVENC_STRUCT_VER(12, 2, 1),
++ .map_input_resource = NVENC_STRUCT_VER(12, 2, 4),
++ .register_resource = NVENC_STRUCT_VER(12, 2, 5),
++ .create_input_buffer = NVENC_STRUCT_VER(12, 2, 2),
++ .create_bitstream_buffer = NVENC_STRUCT_VER(12, 2, 1),
++ .reconfigure_params = NVENC_STRUCT_VER_RES(12, 2, 2),
++ .sequence_param_payload = NVENC_STRUCT_VER(12, 2, 1),
++ },
++ { // SDK 12.1, driver >= 530.41.03 (Linux) / 531.61 (Windows)
++ .packed = NVENC_API_PACKED(12, 1),
++ .api = NVENC_API_WORD(12, 1),
++ .function_list = NVENC_STRUCT_VER(12, 1, 2),
++ .open_session_params = NVENC_STRUCT_VER(12, 1, 1),
++ .caps_param = NVENC_STRUCT_VER(12, 1, 1),
++ .initialize_params = NVENC_STRUCT_VER_RES(12, 1, 6),
++ .config = NVENC_STRUCT_VER_RES(12, 1, 8),
++ .preset_config = NVENC_STRUCT_VER_RES(12, 1, 4),
++ .pic_params = NVENC_STRUCT_VER_RES(12, 1, 6),
++ .lock_bitstream = NVENC_STRUCT_VER_RES(12, 1, 1),
++ .lock_input_buffer = NVENC_STRUCT_VER(12, 1, 1),
++ .map_input_resource = NVENC_STRUCT_VER(12, 1, 4),
++ .register_resource = NVENC_STRUCT_VER(12, 1, 4),
++ .create_input_buffer = NVENC_STRUCT_VER(12, 1, 1),
++ .create_bitstream_buffer = NVENC_STRUCT_VER(12, 1, 1),
++ .reconfigure_params = NVENC_STRUCT_VER_RES(12, 1, 1),
++ .sequence_param_payload = NVENC_STRUCT_VER(12, 1, 1),
++ },
++ { // SDK 12.0, driver >= 520.56.06 (Linux) / 522.25 (Windows)
++ .packed = NVENC_API_PACKED(12, 0),
++ .api = NVENC_API_WORD(12, 0),
++ .function_list = NVENC_STRUCT_VER(12, 0, 2),
++ .open_session_params = NVENC_STRUCT_VER(12, 0, 1),
++ .caps_param = NVENC_STRUCT_VER(12, 0, 1),
++ .initialize_params = NVENC_STRUCT_VER_RES(12, 0, 5),
++ .config = NVENC_STRUCT_VER_RES(12, 0, 8),
++ .preset_config = NVENC_STRUCT_VER_RES(12, 0, 4),
++ .pic_params = NVENC_STRUCT_VER_RES(12, 0, 6),
++ .lock_bitstream = NVENC_STRUCT_VER(12, 0, 2),
++ .lock_input_buffer = NVENC_STRUCT_VER(12, 0, 1),
++ .map_input_resource = NVENC_STRUCT_VER(12, 0, 4),
++ .register_resource = NVENC_STRUCT_VER(12, 0, 4),
++ .create_input_buffer = NVENC_STRUCT_VER(12, 0, 1),
++ .create_bitstream_buffer = NVENC_STRUCT_VER(12, 0, 1),
++ .reconfigure_params = NVENC_STRUCT_VER_RES(12, 0, 1),
++ .sequence_param_payload = NVENC_STRUCT_VER(12, 0, 1),
++ },
++ { // SDK 11.1, driver >= 470.57.02 (Linux) / 471.41 (Windows) - Kepler's last branch
++ .packed = NVENC_API_PACKED(11, 1),
++ .api = NVENC_API_WORD(11, 1),
++ .function_list = NVENC_STRUCT_VER(11, 1, 2),
++ .open_session_params = NVENC_STRUCT_VER(11, 1, 1),
++ .caps_param = NVENC_STRUCT_VER(11, 1, 1),
++ .initialize_params = NVENC_STRUCT_VER_RES(11, 1, 5),
++ .config = NVENC_STRUCT_VER_RES(11, 1, 7),
++ .preset_config = NVENC_STRUCT_VER_RES(11, 1, 4),
++ .pic_params = NVENC_STRUCT_VER_RES(11, 1, 4),
++ .lock_bitstream = NVENC_STRUCT_VER(11, 1, 1),
++ .lock_input_buffer = NVENC_STRUCT_VER(11, 1, 1),
++ .map_input_resource = NVENC_STRUCT_VER(11, 1, 4),
++ .register_resource = NVENC_STRUCT_VER(11, 1, 3),
++ .create_input_buffer = NVENC_STRUCT_VER(11, 1, 1),
++ .create_bitstream_buffer = NVENC_STRUCT_VER(11, 1, 1),
++ .reconfigure_params = NVENC_STRUCT_VER_RES(11, 1, 1),
++ .sequence_param_payload = NVENC_STRUCT_VER(11, 1, 1),
++ },
++};
++
++/* The newest row must be exactly what this build would present anyway; if any
++ * of these fire, the headers changed and the whole table needs re-validation. */
++_Static_assert(NVENC_API_WORD(13, 0) == NVENCAPI_VERSION, "nvenc api version table outdated");
++_Static_assert(NVENC_STRUCT_VER(13, 0, 2) == NV_ENCODE_API_FUNCTION_LIST_VER, "nvenc api version table outdated");
++_Static_assert(NVENC_STRUCT_VER(13, 0, 1) == NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER, "nvenc api version table outdated");
++_Static_assert(NVENC_STRUCT_VER(13, 0, 1) == NV_ENC_CAPS_PARAM_VER, "nvenc api version table outdated");
++_Static_assert(NVENC_STRUCT_VER_RES(13, 0, 7) == NV_ENC_INITIALIZE_PARAMS_VER, "nvenc api version table outdated");
++_Static_assert(NVENC_STRUCT_VER_RES(13, 0, 9) == NV_ENC_CONFIG_VER, "nvenc api version table outdated");
++_Static_assert(NVENC_STRUCT_VER_RES(13, 0, 5) == NV_ENC_PRESET_CONFIG_VER, "nvenc api version table outdated");
++_Static_assert(NVENC_STRUCT_VER_RES(13, 0, 7) == NV_ENC_PIC_PARAMS_VER, "nvenc api version table outdated");
++_Static_assert(NVENC_STRUCT_VER_RES(13, 0, 2) == NV_ENC_LOCK_BITSTREAM_VER, "nvenc api version table outdated");
++_Static_assert(NVENC_STRUCT_VER(13, 0, 1) == NV_ENC_LOCK_INPUT_BUFFER_VER, "nvenc api version table outdated");
++_Static_assert(NVENC_STRUCT_VER(13, 0, 4) == NV_ENC_MAP_INPUT_RESOURCE_VER, "nvenc api version table outdated");
++_Static_assert(NVENC_STRUCT_VER(13, 0, 5) == NV_ENC_REGISTER_RESOURCE_VER, "nvenc api version table outdated");
++_Static_assert(NVENC_STRUCT_VER(13, 0, 2) == NV_ENC_CREATE_INPUT_BUFFER_VER, "nvenc api version table outdated");
++_Static_assert(NVENC_STRUCT_VER(13, 0, 1) == NV_ENC_CREATE_BITSTREAM_BUFFER_VER, "nvenc api version table outdated");
++_Static_assert(NVENC_STRUCT_VER_RES(13, 0, 2) == NV_ENC_RECONFIGURE_PARAMS_VER, "nvenc api version table outdated");
++_Static_assert(NVENC_STRUCT_VER(13, 0, 1) == NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER, "nvenc api version table outdated");
++
++/* Layout guards for the compatibility padding after NV_ENC_INITIALIZE_PARAMS /
++ * NV_ENC_RECONFIGURE_PARAMS / NV_ENC_LOCK_BITSTREAM (their pre-12.2 layouts
++ * are up to 8 bytes larger: 1808, 1824 and 1552 bytes respectively) and for
++ * the legacy bit-depth pokes below. */
++_Static_assert(sizeof(NV_ENC_INITIALIZE_PARAMS) == 1800, "unexpected NV_ENC_INITIALIZE_PARAMS layout");
++_Static_assert(sizeof(NV_ENC_RECONFIGURE_PARAMS) == 1816, "unexpected NV_ENC_RECONFIGURE_PARAMS layout");
++_Static_assert(sizeof(NV_ENC_LOCK_BITSTREAM) == 1544, "unexpected NV_ENC_LOCK_BITSTREAM layout");
++_Static_assert(offsetof(NV_ENC_CONFIG_HEVC, tier) == 4 && offsetof(NV_ENC_CONFIG_HEVC, idrPeriod) == 20,
++ "unexpected NV_ENC_CONFIG_HEVC layout");
++_Static_assert(offsetof(NV_ENC_CONFIG_AV1, tier) == 4 && offsetof(NV_ENC_CONFIG_AV1, idrPeriod) == 20,
++ "unexpected NV_ENC_CONFIG_AV1 layout");
++
++/*
++ * SDK 12.2 removed the NV_ENC_CONFIG_HEVC.pixelBitDepthMinus8 and
++ * NV_ENC_CONFIG_AV1.inputPixelBitDepthMinus8/pixelBitDepthMinus8 bitfields
++ * and replaced them with the inputBitDepth/outputBitDepth enums that live at
++ * different offsets. Drivers negotiated to API < 12.2 still read the old bit
++ * positions (reserved-and-zero in the compiled 12.2+ layout), so 10 bit
++ * encoding on those drivers has to set the old bits by hand.
++ *
++ * Verified against the SDK 11.1/12.0/12.1 headers: both codec configs keep
++ * their flag bitfields in the uint32 word at byte offset 16 (between
++ * maxCUSize/maxPartSize at 12 and idrPeriod at 20, anchored by the asserts
++ * above). Within that word:
++ * NV_ENC_CONFIG_HEVC: pixelBitDepthMinus8 = bits 11-13
++ * NV_ENC_CONFIG_AV1: inputPixelBitDepthMinus8 = bits 12-14,
++ * pixelBitDepthMinus8 = bits 15-17
++ */
++static void nvenc_legacy_bitfield_poke(void *codec_config, size_t word_offset,
++ unsigned bit_offset, unsigned width, uint32_t value)
++{
++ uint32_t word;
++ memcpy(&word, (uint8_t*)codec_config + word_offset, sizeof(word));
++ word &= ~(((1u << width) - 1) << bit_offset);
++ word |= (value & ((1u << width) - 1)) << bit_offset;
++ memcpy((uint8_t*)codec_config + word_offset, &word, sizeof(word));
++}
++
++static void nvenc_set_legacy_hevc_bit_depth(NV_ENC_CONFIG_HEVC *hevc, uint32_t depth_minus8)
++{
++ nvenc_legacy_bitfield_poke(hevc, 16, 11, 3, depth_minus8);
++}
++
++#if CONFIG_AV1_NVENC_ENCODER
++static void nvenc_set_legacy_av1_bit_depth(NV_ENC_CONFIG_AV1 *av1,
++ uint32_t input_depth_minus8, uint32_t output_depth_minus8)
++{
++ nvenc_legacy_bitfield_poke(av1, 16, 12, 3, input_depth_minus8);
++ nvenc_legacy_bitfield_poke(av1, 16, 15, 3, output_depth_minus8);
++}
++#endif
++
+ static av_cold int nvenc_load_libraries(AVCodecContext *avctx)
+ {
+ NvencContext *ctx = avctx->priv_data;
+@@ -371,16 +566,35 @@ static av_cold int nvenc_load_libraries(AVCodecContext *avctx)
+
+ av_log(avctx, AV_LOG_VERBOSE, "Loaded Nvenc version %d.%d\n", nvenc_max_ver >> 4, nvenc_max_ver & 0xf);
+
+- if ((NVENCAPI_MAJOR_VERSION << 4 | NVENCAPI_MINOR_VERSION) > nvenc_max_ver) {
++ /* Negotiate: present the newest API version both the driver and this
++ * build support instead of failing on drivers older than the headers. */
++ ctx->api_vers = NULL;
++ for (int i = 0; i < (int)FF_ARRAY_ELEMS(nvenc_api_versions); i++) {
++ if (nvenc_api_versions[i].packed <= nvenc_max_ver) {
++ ctx->api_vers = &nvenc_api_versions[i];
++ break;
++ }
++ }
++
++ if (!ctx->api_vers) {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support the required nvenc API version. "
+ "Required: %d.%d Found: %d.%d\n",
+- NVENCAPI_MAJOR_VERSION, NVENCAPI_MINOR_VERSION,
++ 11, 1,
+ nvenc_max_ver >> 4, nvenc_max_ver & 0xf);
+ nvenc_print_driver_requirement(avctx, AV_LOG_ERROR);
+ return AVERROR(ENOSYS);
+ }
+
+- dl_fn->nvenc_funcs.version = NV_ENCODE_API_FUNCTION_LIST_VER;
++ ctx->api_version = ctx->api_vers->packed;
++
++ if (ctx->api_version < NVENC_API_PACKED(NVENCAPI_MAJOR_VERSION, NVENCAPI_MINOR_VERSION))
++ av_log(avctx, AV_LOG_VERBOSE, "Driver supports nvenc API up to %d.%d (compiled for %d.%d), "
++ "using API %d.%d; features requiring a newer driver are disabled\n",
++ nvenc_max_ver >> 4, nvenc_max_ver & 0xf,
++ NVENCAPI_MAJOR_VERSION, NVENCAPI_MINOR_VERSION,
++ ctx->api_version >> 4, ctx->api_version & 0xf);
++
++ dl_fn->nvenc_funcs.version = ctx->api_vers->function_list;
+
+ err = dl_fn->nvenc_dl->NvEncodeAPICreateInstance(&dl_fn->nvenc_funcs);
+ if (err != NV_ENC_SUCCESS)
+@@ -421,8 +635,8 @@ static av_cold int nvenc_open_session(AVCodecContext *avctx)
+ NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &ctx->nvenc_dload_funcs.nvenc_funcs;
+ NVENCSTATUS ret;
+
+- params.version = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER;
+- params.apiVersion = NVENCAPI_VERSION;
++ params.version = ctx->api_vers->open_session_params;
++ params.apiVersion = ctx->api_vers->api;
+ if (ctx->d3d11_device) {
+ params.device = ctx->d3d11_device;
+ params.deviceType = NV_ENC_DEVICE_TYPE_DIRECTX;
+@@ -483,7 +697,7 @@ static int nvenc_check_cap(AVCodecContext *avctx, NV_ENC_CAPS cap)
+ NV_ENC_CAPS_PARAM params = { 0 };
+ int ret, val = 0;
+
+- params.version = NV_ENC_CAPS_PARAM_VER;
++ params.version = ctx->api_vers->caps_param;
+ params.capsToQuery = cap;
+
+ ret = p_nvenc->nvEncGetEncodeCaps(ctx->nvencoder, ctx->init_encode_params.encodeGUID, &params, &val);
+@@ -498,6 +712,15 @@ static int nvenc_check_capabilities(AVCodecContext *avctx)
+ NvencContext *ctx = avctx->priv_data;
+ int tmp, ret;
+
++#if CONFIG_AV1_NVENC_ENCODER
++ if (avctx->codec->id == AV_CODEC_ID_AV1 && !NVENC_API_AT_LEAST(ctx, 12, 0)) {
++ av_log(avctx, AV_LOG_ERROR, "AV1 encoding needs nvenc API 12.0, but the driver "
++ "only supports %d.%d. Driver 520.56.06 or newer is required for AV1.\n",
++ ctx->api_version >> 4, ctx->api_version & 0xf);
++ return AVERROR(ENOSYS);
++ }
++#endif
++
+ ret = nvenc_check_codec_support(avctx);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_WARNING, "Codec not supported\n");
+@@ -1219,7 +1442,10 @@ static av_cold int nvenc_setup_rate_control(AVCodecContext *avctx)
+ ctx->encode_config.rcParams.lookaheadDepth, ctx->rc_lookahead);
+
+ #ifdef NVENC_HAVE_LOOKAHEAD_LEVEL
+- if (ctx->lookahead_level >= 0) {
++ if (ctx->lookahead_level >= 0 && !NVENC_API_AT_LEAST(ctx, 12, 2)) {
++ av_log(avctx, AV_LOG_WARNING, "Lookahead level needs nvenc API 12.2 "
++ "(driver 550.54.14+), ignoring.\n");
++ } else if (ctx->lookahead_level >= 0) {
+ switch (ctx->lookahead_level) {
+ case NV_ENC_LOOKAHEAD_LEVEL_0:
+ case NV_ENC_LOOKAHEAD_LEVEL_1:
+@@ -1413,8 +1639,12 @@ static av_cold int nvenc_setup_h264_config(AVCodecContext *avctx)
+ h264->level = ctx->level;
+
+ #ifdef NVENC_HAVE_NEW_BIT_DEPTH_API
+- h264->inputBitDepth = IS_10BIT(ctx->data_pix_fmt) ? NV_ENC_BIT_DEPTH_10 : NV_ENC_BIT_DEPTH_8;
+- h264->outputBitDepth = (IS_10BIT(ctx->data_pix_fmt) || ctx->highbitdepth) ? NV_ENC_BIT_DEPTH_10 : NV_ENC_BIT_DEPTH_8;
++ /* Pre-12.2 drivers read reserved space at these offsets; H264 on such
++ * drivers is always 8 bit (10 bit was rejected by the caps check). */
++ if (NVENC_API_AT_LEAST(ctx, 12, 2)) {
++ h264->inputBitDepth = IS_10BIT(ctx->data_pix_fmt) ? NV_ENC_BIT_DEPTH_10 : NV_ENC_BIT_DEPTH_8;
++ h264->outputBitDepth = (IS_10BIT(ctx->data_pix_fmt) || ctx->highbitdepth) ? NV_ENC_BIT_DEPTH_10 : NV_ENC_BIT_DEPTH_8;
++ }
+ #endif
+
+ if (ctx->coder >= 0)
+@@ -1450,6 +1680,13 @@ static av_cold int nvenc_setup_h264_config(AVCodecContext *avctx)
+ #endif
+
+ #ifdef NVENC_HAVE_TIME_CODE
++ /* On by default; quietly drop it on old drivers like a pre-12.2 build
++ * (this also disables the per-frame timeCode fill). */
++ if (ctx->s12m_tc && !NVENC_API_AT_LEAST(ctx, 12, 2)) {
++ av_log(avctx, AV_LOG_VERBOSE, "S12M timecodes need nvenc API 12.2, disabling.\n");
++ ctx->s12m_tc = 0;
++ }
++
+ if (ctx->s12m_tc)
+ h264->enableTimeCode = 1;
+ #endif
+@@ -1501,7 +1738,8 @@ static av_cold int nvenc_setup_hevc_config(AVCodecContext *avctx)
+ hevc->intraRefreshCnt = cc->gopLength - 1;
+ cc->gopLength = NVENC_INFINITE_GOPLENGTH;
+ #ifdef NVENC_HAVE_HEVC_OUTPUT_RECOVERY_POINT_SEI
+- hevc->outputRecoveryPointSEI = 1;
++ if (NVENC_API_AT_LEAST(ctx, 12, 0))
++ hevc->outputRecoveryPointSEI = 1;
+ #endif
+ #ifdef NVENC_HAVE_SINGLE_SLICE_INTRA_REFRESH
+ hevc->singleSliceIntraRefresh = ctx->single_slice_intra_refresh;
+@@ -1509,12 +1747,14 @@ static av_cold int nvenc_setup_hevc_config(AVCodecContext *avctx)
+ }
+
+ #ifdef NVENC_HAVE_HEVC_AND_AV1_MASTERING_METADATA
+- ctx->mdm = hevc->outputMasteringDisplay = !!av_frame_side_data_get(avctx->decoded_side_data,
+- avctx->nb_decoded_side_data,
+- AV_FRAME_DATA_MASTERING_DISPLAY_METADATA);
+- ctx->cll = hevc->outputMaxCll = !!av_frame_side_data_get(avctx->decoded_side_data,
+- avctx->nb_decoded_side_data,
+- AV_FRAME_DATA_CONTENT_LIGHT_LEVEL);
++ if (NVENC_API_AT_LEAST(ctx, 13, 0)) {
++ ctx->mdm = hevc->outputMasteringDisplay = !!av_frame_side_data_get(avctx->decoded_side_data,
++ avctx->nb_decoded_side_data,
++ AV_FRAME_DATA_MASTERING_DISPLAY_METADATA);
++ ctx->cll = hevc->outputMaxCll = !!av_frame_side_data_get(avctx->decoded_side_data,
++ avctx->nb_decoded_side_data,
++ AV_FRAME_DATA_CONTENT_LIGHT_LEVEL);
++ }
+ #endif
+
+ #ifdef NVENC_HAVE_HEVC_CONSTRAINED_ENCODING
+@@ -1613,8 +1853,18 @@ static av_cold int nvenc_setup_hevc_config(AVCodecContext *avctx)
+ hevc->chromaFormatIDC = IS_YUV444(ctx->data_pix_fmt) ? 3 : IS_YUV422(ctx->data_pix_fmt) ? 2 : 1;
+
+ #ifdef NVENC_HAVE_NEW_BIT_DEPTH_API
+- hevc->inputBitDepth = IS_10BIT(ctx->data_pix_fmt) ? NV_ENC_BIT_DEPTH_10 : NV_ENC_BIT_DEPTH_8;
+- hevc->outputBitDepth = (IS_10BIT(ctx->data_pix_fmt) || ctx->highbitdepth) ? NV_ENC_BIT_DEPTH_10 : NV_ENC_BIT_DEPTH_8;
++ if (NVENC_API_AT_LEAST(ctx, 12, 2)) {
++ hevc->inputBitDepth = IS_10BIT(ctx->data_pix_fmt) ? NV_ENC_BIT_DEPTH_10 : NV_ENC_BIT_DEPTH_8;
++ hevc->outputBitDepth = (IS_10BIT(ctx->data_pix_fmt) || ctx->highbitdepth) ? NV_ENC_BIT_DEPTH_10 : NV_ENC_BIT_DEPTH_8;
++ } else {
++ if (ctx->highbitdepth && !IS_10BIT(ctx->data_pix_fmt)) {
++ av_log(avctx, AV_LOG_ERROR, "8 -> 10 bit HEVC encoding needs nvenc API 12.2 "
++ "(driver 550.54.14+).\n");
++ return AVERROR(ENOSYS);
++ }
++ // The old pixelBitDepthMinus8 bitfield read by pre-12.2 drivers
++ nvenc_set_legacy_hevc_bit_depth(hevc, IS_10BIT(ctx->data_pix_fmt) ? 2 : 0);
++ }
+ #else
+ hevc->pixelBitDepthMinus8 = IS_10BIT(ctx->data_pix_fmt) ? 2 : 0;
+ #endif
+@@ -1722,20 +1972,28 @@ static av_cold int nvenc_setup_av1_config(AVCodecContext *avctx)
+ av1->chromaFormatIDC = IS_YUV444(ctx->data_pix_fmt) ? 3 : 1;
+
+ #ifdef NVENC_HAVE_NEW_BIT_DEPTH_API
+- av1->inputBitDepth = IS_10BIT(ctx->data_pix_fmt) ? NV_ENC_BIT_DEPTH_10 : NV_ENC_BIT_DEPTH_8;
+- av1->outputBitDepth = (IS_10BIT(ctx->data_pix_fmt) || ctx->highbitdepth) ? NV_ENC_BIT_DEPTH_10 : NV_ENC_BIT_DEPTH_8;
++ if (NVENC_API_AT_LEAST(ctx, 12, 2)) {
++ av1->inputBitDepth = IS_10BIT(ctx->data_pix_fmt) ? NV_ENC_BIT_DEPTH_10 : NV_ENC_BIT_DEPTH_8;
++ av1->outputBitDepth = (IS_10BIT(ctx->data_pix_fmt) || ctx->highbitdepth) ? NV_ENC_BIT_DEPTH_10 : NV_ENC_BIT_DEPTH_8;
++ } else {
++ // The old bitfields read by 12.0/12.1 drivers
++ nvenc_set_legacy_av1_bit_depth(av1, IS_10BIT(ctx->data_pix_fmt) ? 2 : 0,
++ (IS_10BIT(ctx->data_pix_fmt) || ctx->highbitdepth) ? 2 : 0);
++ }
+ #else
+ av1->inputPixelBitDepthMinus8 = IS_10BIT(ctx->data_pix_fmt) ? 2 : 0;
+ av1->pixelBitDepthMinus8 = (IS_10BIT(ctx->data_pix_fmt) || ctx->highbitdepth) ? 2 : 0;
+ #endif
+
+ #ifdef NVENC_HAVE_HEVC_AND_AV1_MASTERING_METADATA
+- ctx->mdm = av1->outputMasteringDisplay = !!av_frame_side_data_get(avctx->decoded_side_data,
+- avctx->nb_decoded_side_data,
+- AV_FRAME_DATA_MASTERING_DISPLAY_METADATA);
+- ctx->cll = av1->outputMaxCll = !!av_frame_side_data_get(avctx->decoded_side_data,
+- avctx->nb_decoded_side_data,
+- AV_FRAME_DATA_CONTENT_LIGHT_LEVEL);
++ if (NVENC_API_AT_LEAST(ctx, 13, 0)) {
++ ctx->mdm = av1->outputMasteringDisplay = !!av_frame_side_data_get(avctx->decoded_side_data,
++ avctx->nb_decoded_side_data,
++ AV_FRAME_DATA_MASTERING_DISPLAY_METADATA);
++ ctx->cll = av1->outputMaxCll = !!av_frame_side_data_get(avctx->decoded_side_data,
++ avctx->nb_decoded_side_data,
++ AV_FRAME_DATA_CONTENT_LIGHT_LEVEL);
++ }
+ #endif
+
+ if (ctx->b_ref_mode >= 0)
+@@ -1829,16 +2087,16 @@ static av_cold int nvenc_setup_encoder(AVCodecContext *avctx)
+ int res = 0;
+ int dw, dh;
+
+- ctx->encode_config.version = NV_ENC_CONFIG_VER;
+- ctx->init_encode_params.version = NV_ENC_INITIALIZE_PARAMS_VER;
++ ctx->encode_config.version = ctx->api_vers->config;
++ ctx->init_encode_params.version = ctx->api_vers->initialize_params;
+
+ ctx->init_encode_params.encodeHeight = avctx->height;
+ ctx->init_encode_params.encodeWidth = avctx->width;
+
+ ctx->init_encode_params.encodeConfig = &ctx->encode_config;
+
+- preset_config.version = NV_ENC_PRESET_CONFIG_VER;
+- preset_config.presetCfg.version = NV_ENC_CONFIG_VER;
++ preset_config.version = ctx->api_vers->preset_config;
++ preset_config.presetCfg.version = ctx->api_vers->config;
+
+ #ifdef NVENC_HAVE_NEW_PRESETS
+ ctx->init_encode_params.tuningInfo = ctx->tuning_info;
+@@ -1848,6 +2106,16 @@ static av_cold int nvenc_setup_encoder(AVCodecContext *avctx)
+ else if (ctx->flags & NVENC_LOWLATENCY)
+ ctx->init_encode_params.tuningInfo = NV_ENC_TUNING_INFO_LOW_LATENCY;
+
++#ifdef NVENC_HAVE_UHQ_TUNING
++ if (ctx->init_encode_params.tuningInfo == NV_ENC_TUNING_INFO_ULTRA_HIGH_QUALITY &&
++ (!NVENC_API_AT_LEAST(ctx, 12, 2) ||
++ (avctx->codec->id == AV_CODEC_ID_AV1 && !NVENC_API_AT_LEAST(ctx, 13, 0)))) {
++ av_log(avctx, AV_LOG_ERROR, "UHQ tuning needs nvenc API 12.2 (driver 550.54.14+), "
++ "13.0 (driver 570+) for AV1.\n");
++ return AVERROR(ENOSYS);
++ }
++#endif
++
+ nv_status = p_nvenc->nvEncGetEncodePresetConfigEx(ctx->nvencoder,
+ ctx->init_encode_params.encodeGUID,
+ ctx->init_encode_params.presetGUID,
+@@ -1864,7 +2132,7 @@ static av_cold int nvenc_setup_encoder(AVCodecContext *avctx)
+
+ memcpy(&ctx->encode_config, &preset_config.presetCfg, sizeof(ctx->encode_config));
+
+- ctx->encode_config.version = NV_ENC_CONFIG_VER;
++ ctx->encode_config.version = ctx->api_vers->config;
+
+ compute_dar(avctx, &dw, &dh);
+ ctx->init_encode_params.darHeight = dh;
+@@ -1897,11 +2165,16 @@ static av_cold int nvenc_setup_encoder(AVCodecContext *avctx)
+ ctx->init_encode_params.enableWeightedPrediction = 1;
+
+ #ifdef NVENC_HAVE_SPLIT_FRAME_ENCODING
+- ctx->init_encode_params.splitEncodeMode = ctx->split_encode_mode;
++ if (NVENC_API_AT_LEAST(ctx, 12, 1)) {
++ ctx->init_encode_params.splitEncodeMode = ctx->split_encode_mode;
+
+- if (ctx->split_encode_mode != NV_ENC_SPLIT_DISABLE_MODE) {
+- if (avctx->codec->id == AV_CODEC_ID_HEVC && ctx->weighted_pred == 1)
+- av_log(avctx, AV_LOG_WARNING, "Split encoding not supported with weighted prediction enabled.\n");
++ if (ctx->split_encode_mode != NV_ENC_SPLIT_DISABLE_MODE) {
++ if (avctx->codec->id == AV_CODEC_ID_HEVC && ctx->weighted_pred == 1)
++ av_log(avctx, AV_LOG_WARNING, "Split encoding not supported with weighted prediction enabled.\n");
++ }
++ } else if (ctx->split_encode_mode != NV_ENC_SPLIT_AUTO_MODE) {
++ av_log(avctx, AV_LOG_WARNING, "Split frame encoding needs nvenc API 12.1 "
++ "(driver 530.41.03+), ignoring.\n");
+ }
+ #endif
+
+@@ -2042,7 +2315,7 @@ static av_cold int nvenc_alloc_surface(AVCodecContext *avctx, int idx)
+
+ NVENCSTATUS nv_status;
+ NV_ENC_CREATE_BITSTREAM_BUFFER allocOut = { 0 };
+- allocOut.version = NV_ENC_CREATE_BITSTREAM_BUFFER_VER;
++ allocOut.version = ctx->api_vers->create_bitstream_buffer;
+
+ if (avctx->pix_fmt == AV_PIX_FMT_CUDA || avctx->pix_fmt == AV_PIX_FMT_D3D11) {
+ ctx->surfaces[idx].in_ref = av_frame_alloc();
+@@ -2058,7 +2331,7 @@ static av_cold int nvenc_alloc_surface(AVCodecContext *avctx, int idx)
+ return AVERROR(EINVAL);
+ }
+
+- allocSurf.version = NV_ENC_CREATE_INPUT_BUFFER_VER;
++ allocSurf.version = ctx->api_vers->create_input_buffer;
+ allocSurf.width = avctx->width;
+ allocSurf.height = avctx->height;
+ allocSurf.bufferFmt = ctx->surfaces[idx].format;
+@@ -2146,7 +2419,7 @@ static av_cold int nvenc_setup_extradata(AVCodecContext *avctx)
+ char tmpHeader[NV_MAX_SEQ_HDR_LEN];
+
+ NV_ENC_SEQUENCE_PARAM_PAYLOAD payload = { 0 };
+- payload.version = NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER;
++ payload.version = ctx->api_vers->sequence_param_payload;
+
+ payload.spsppsBuffer = tmpHeader;
+ payload.inBufferSize = sizeof(tmpHeader);
+@@ -2178,7 +2451,7 @@ av_cold int ff_nvenc_encode_close(AVCodecContext *avctx)
+
+ /* the encoder has to be flushed before it can be closed */
+ if (ctx->nvencoder) {
+- NV_ENC_PIC_PARAMS params = { .version = NV_ENC_PIC_PARAMS_VER,
++ NV_ENC_PIC_PARAMS params = { .version = ctx->api_vers->pic_params,
+ .encodePicFlags = NV_ENC_PIC_FLAG_EOS };
+
+ res = nvenc_push_context(avctx);
+@@ -2402,7 +2675,7 @@ static int nvenc_register_frame(AVCodecContext *avctx, const AVFrame *frame)
+ if (idx < 0)
+ return idx;
+
+- reg.version = NV_ENC_REGISTER_RESOURCE_VER;
++ reg.version = ctx->api_vers->register_resource;
+ reg.width = frames_ctx->width;
+ reg.height = frames_ctx->height;
+ reg.pitch = frame->linesize[0];
+@@ -2457,7 +2730,7 @@ static int nvenc_upload_frame(AVCodecContext *avctx, const AVFrame *frame,
+ return res;
+
+ if (!ctx->registered_frames[reg_idx].mapped) {
+- ctx->registered_frames[reg_idx].in_map.version = NV_ENC_MAP_INPUT_RESOURCE_VER;
++ ctx->registered_frames[reg_idx].in_map.version = ctx->api_vers->map_input_resource;
+ ctx->registered_frames[reg_idx].in_map.registeredResource = ctx->registered_frames[reg_idx].regptr;
+ nv_status = p_nvenc->nvEncMapInputResource(ctx->nvencoder, &ctx->registered_frames[reg_idx].in_map);
+ if (nv_status != NV_ENC_SUCCESS) {
+@@ -2477,7 +2750,7 @@ static int nvenc_upload_frame(AVCodecContext *avctx, const AVFrame *frame,
+ } else {
+ NV_ENC_LOCK_INPUT_BUFFER lockBufferParams = { 0 };
+
+- lockBufferParams.version = NV_ENC_LOCK_INPUT_BUFFER_VER;
++ lockBufferParams.version = ctx->api_vers->lock_input_buffer;
+ lockBufferParams.inputBuffer = nvenc_frame->input_surface;
+
+ nv_status = p_nvenc->nvEncLockInputBuffer(ctx->nvencoder, &lockBufferParams);
+@@ -2725,31 +2998,38 @@ static int process_output_surface(AVCodecContext *avctx, AVPacket *pkt, NvencSur
+ NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
+ NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &dl_fn->nvenc_funcs;
+
+- NV_ENC_LOCK_BITSTREAM lock_params = { 0 };
++ /* The SDK 12.1 NV_ENC_LOCK_BITSTREAM layout is 8 bytes larger than the
++ * 12.2+ layout this is compiled against; keep zeroed spare space after it
++ * for drivers negotiated to API <= 12.1. */
++ struct {
++ NV_ENC_LOCK_BITSTREAM params;
++ uint32_t compat_pad[2];
++ } lock_params_padded = { 0 };
++ NV_ENC_LOCK_BITSTREAM *lock_params = &lock_params_padded.params;
+ NVENCSTATUS nv_status;
+ int res = 0;
+
+ enum AVPictureType pict_type;
+
+- lock_params.version = NV_ENC_LOCK_BITSTREAM_VER;
++ lock_params->version = ctx->api_vers->lock_bitstream;
+
+- lock_params.doNotWait = 0;
+- lock_params.outputBitstream = tmpoutsurf->output_surface;
++ lock_params->doNotWait = 0;
++ lock_params->outputBitstream = tmpoutsurf->output_surface;
+
+- nv_status = p_nvenc->nvEncLockBitstream(ctx->nvencoder, &lock_params);
++ nv_status = p_nvenc->nvEncLockBitstream(ctx->nvencoder, lock_params);
+ if (nv_status != NV_ENC_SUCCESS) {
+ res = nvenc_print_error(avctx, nv_status, "Failed locking bitstream buffer");
+ goto error;
+ }
+
+- res = ff_get_encode_buffer(avctx, pkt, lock_params.bitstreamSizeInBytes, 0);
++ res = ff_get_encode_buffer(avctx, pkt, lock_params->bitstreamSizeInBytes, 0);
+
+ if (res < 0) {
+ p_nvenc->nvEncUnlockBitstream(ctx->nvencoder, tmpoutsurf->output_surface);
+ goto error;
+ }
+
+- memcpy(pkt->data, lock_params.bitstreamBufferPtr, lock_params.bitstreamSizeInBytes);
++ memcpy(pkt->data, lock_params->bitstreamBufferPtr, lock_params->bitstreamSizeInBytes);
+
+ nv_status = p_nvenc->nvEncUnlockBitstream(ctx->nvencoder, tmpoutsurf->output_surface);
+ if (nv_status != NV_ENC_SUCCESS) {
+@@ -2776,7 +3056,7 @@ static int process_output_surface(AVCodecContext *avctx, AVPacket *pkt, NvencSur
+ tmpoutsurf->input_surface = NULL;
+ }
+
+- switch (lock_params.pictureType) {
++ switch (lock_params->pictureType) {
+ case NV_ENC_PIC_TYPE_IDR:
+ pkt->flags |= AV_PKT_FLAG_KEY;
+ case NV_ENC_PIC_TYPE_I:
+@@ -2799,13 +3079,13 @@ static int process_output_surface(AVCodecContext *avctx, AVPacket *pkt, NvencSur
+ }
+
+ ff_encode_add_stats_side_data(pkt,
+- (lock_params.frameAvgQP - 1) * FF_QP2LAMBDA, NULL, 0, pict_type);
++ (lock_params->frameAvgQP - 1) * FF_QP2LAMBDA, NULL, 0, pict_type);
+
+- res = nvenc_set_timestamp(avctx, &lock_params, pkt);
++ res = nvenc_set_timestamp(avctx, lock_params, pkt);
+ if (res < 0)
+ goto error2;
+
+- res = nvenc_retrieve_frame_data(avctx, &lock_params, pkt);
++ res = nvenc_retrieve_frame_data(avctx, lock_params, pkt);
+ if (res < 0)
+ goto error2;
+
+@@ -2948,14 +3228,21 @@ static void reconfig_encoder(AVCodecContext *avctx, const AVFrame *frame)
+ NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &ctx->nvenc_dload_funcs.nvenc_funcs;
+ NVENCSTATUS ret;
+
+- NV_ENC_RECONFIGURE_PARAMS params = { 0 };
++ /* The pre-12.2 NV_ENC_RECONFIGURE_PARAMS layout is 8 bytes larger than
++ * the 12.2+ layout this is compiled against; keep zeroed spare space
++ * after it for drivers negotiated to API < 12.2. */
++ struct {
++ NV_ENC_RECONFIGURE_PARAMS params;
++ uint32_t compat_pad[2];
++ } params_padded = { 0 };
++ NV_ENC_RECONFIGURE_PARAMS *params = &params_padded.params;
+ int needs_reconfig = 0;
+ int needs_encode_config = 0;
+ int reconfig_bitrate = 0, reconfig_dar = 0;
+ int dw, dh;
+
+- params.version = NV_ENC_RECONFIGURE_PARAMS_VER;
+- params.reInitEncodeParams = ctx->init_encode_params;
++ params->version = ctx->api_vers->reconfigure_params;
++ params->reInitEncodeParams = ctx->init_encode_params;
+
+ compute_dar(avctx, &dw, &dh);
+ if (dw != ctx->init_encode_params.darWidth || dh != ctx->init_encode_params.darHeight) {
+@@ -2964,47 +3251,59 @@ static void reconfig_encoder(AVCodecContext *avctx, const AVFrame *frame)
+ ctx->init_encode_params.darWidth,
+ ctx->init_encode_params.darHeight, dw, dh);
+
+- params.reInitEncodeParams.darHeight = dh;
+- params.reInitEncodeParams.darWidth = dw;
++ params->reInitEncodeParams.darHeight = dh;
++ params->reInitEncodeParams.darWidth = dw;
+
+ needs_reconfig = 1;
+ reconfig_dar = 1;
+ }
+
+ if (ctx->rc != NV_ENC_PARAMS_RC_CONSTQP && ctx->support_dyn_bitrate) {
+- if (avctx->bit_rate > 0 && params.reInitEncodeParams.encodeConfig->rcParams.averageBitRate != avctx->bit_rate) {
++ if (avctx->bit_rate > 0 && params->reInitEncodeParams.encodeConfig->rcParams.averageBitRate != avctx->bit_rate) {
+ av_log(avctx, AV_LOG_VERBOSE,
+ "avg bitrate change: %d -> %d\n",
+- params.reInitEncodeParams.encodeConfig->rcParams.averageBitRate,
++ params->reInitEncodeParams.encodeConfig->rcParams.averageBitRate,
+ (uint32_t)avctx->bit_rate);
+
+- params.reInitEncodeParams.encodeConfig->rcParams.averageBitRate = avctx->bit_rate;
++ params->reInitEncodeParams.encodeConfig->rcParams.averageBitRate = avctx->bit_rate;
+ reconfig_bitrate = 1;
+ }
+
+ if (avctx->rc_max_rate > 0 && ctx->encode_config.rcParams.maxBitRate != avctx->rc_max_rate) {
+ av_log(avctx, AV_LOG_VERBOSE,
+ "max bitrate change: %d -> %d\n",
+- params.reInitEncodeParams.encodeConfig->rcParams.maxBitRate,
++ params->reInitEncodeParams.encodeConfig->rcParams.maxBitRate,
+ (uint32_t)avctx->rc_max_rate);
+
+- params.reInitEncodeParams.encodeConfig->rcParams.maxBitRate = avctx->rc_max_rate;
++ params->reInitEncodeParams.encodeConfig->rcParams.maxBitRate = avctx->rc_max_rate;
+ reconfig_bitrate = 1;
+ }
+
+ if (avctx->rc_buffer_size > 0 && ctx->encode_config.rcParams.vbvBufferSize != avctx->rc_buffer_size) {
+ av_log(avctx, AV_LOG_VERBOSE,
+ "vbv buffer size change: %d -> %d\n",
+- params.reInitEncodeParams.encodeConfig->rcParams.vbvBufferSize,
++ params->reInitEncodeParams.encodeConfig->rcParams.vbvBufferSize,
+ avctx->rc_buffer_size);
+
+- params.reInitEncodeParams.encodeConfig->rcParams.vbvBufferSize = avctx->rc_buffer_size;
++ params->reInitEncodeParams.encodeConfig->rcParams.vbvBufferSize = avctx->rc_buffer_size;
+ reconfig_bitrate = 1;
+ }
+
+ if (reconfig_bitrate) {
+- params.resetEncoder = 1;
+- params.forceIDR = 1;
++ if (NVENC_API_AT_LEAST(ctx, 12, 2)) {
++ params->resetEncoder = 1;
++ params->forceIDR = 1;
++ } else {
++ /* SDK 12.2 inserted a reserved word at offset 4 of
++ * NV_ENC_RECONFIGURE_PARAMS, shifting the trailing
++ * resetEncoder/forceIDR bits from byte 1816 to 1808. Pre-12.2
++ * drivers read them at 1816, which is compat_pad[0] of the
++ * padded wrapper (verified against SDK 11.1-12.1 headers:
++ * resetEncoder = bit 0, forceIDR = bit 1). The compiled
++ * bitfields must stay 0: byte 1808 lies in reserved space of
++ * the embedded pre-12.2 reInitEncodeParams. */
++ params_padded.compat_pad[0] = 0x1 | 0x2;
++ }
+
+ needs_encode_config = 1;
+ needs_reconfig = 1;
+@@ -3012,10 +3311,10 @@ static void reconfig_encoder(AVCodecContext *avctx, const AVFrame *frame)
+ }
+
+ if (!needs_encode_config)
+- params.reInitEncodeParams.encodeConfig = NULL;
++ params->reInitEncodeParams.encodeConfig = NULL;
+
+ if (needs_reconfig) {
+- ret = p_nvenc->nvEncReconfigureEncoder(ctx->nvencoder, &params);
++ ret = p_nvenc->nvEncReconfigureEncoder(ctx->nvencoder, params);
+ if (ret != NV_ENC_SUCCESS) {
+ nvenc_print_error(avctx, ret, "failed to reconfigure nvenc");
+ } else {
+@@ -3025,9 +3324,9 @@ static void reconfig_encoder(AVCodecContext *avctx, const AVFrame *frame)
+ }
+
+ if (reconfig_bitrate) {
+- ctx->encode_config.rcParams.averageBitRate = params.reInitEncodeParams.encodeConfig->rcParams.averageBitRate;
+- ctx->encode_config.rcParams.maxBitRate = params.reInitEncodeParams.encodeConfig->rcParams.maxBitRate;
+- ctx->encode_config.rcParams.vbvBufferSize = params.reInitEncodeParams.encodeConfig->rcParams.vbvBufferSize;
++ ctx->encode_config.rcParams.averageBitRate = params->reInitEncodeParams.encodeConfig->rcParams.averageBitRate;
++ ctx->encode_config.rcParams.maxBitRate = params->reInitEncodeParams.encodeConfig->rcParams.maxBitRate;
++ ctx->encode_config.rcParams.vbvBufferSize = params->reInitEncodeParams.encodeConfig->rcParams.vbvBufferSize;
+ }
+
+ }
+@@ -3126,7 +3425,7 @@ static int nvenc_send_frame(AVCodecContext *avctx, const AVFrame *frame)
+ NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &dl_fn->nvenc_funcs;
+
+ NV_ENC_PIC_PARAMS pic_params = { 0 };
+- pic_params.version = NV_ENC_PIC_PARAMS_VER;
++ pic_params.version = ctx->api_vers->pic_params;
+
+ if ((!ctx->cu_context && !ctx->d3d11_device) || !ctx->nvencoder)
+ return AVERROR(EINVAL);
+diff --git a/libavcodec/nvenc.h b/libavcodec/nvenc.h
+index 069ba82bc9..4225c612aa 100644
+--- a/libavcodec/nvenc.h
++++ b/libavcodec/nvenc.h
+@@ -111,6 +111,54 @@ typedef void ID3D11Device;
+ #define NVENC_HAVE_MVHEVC
+ #endif
+
++/*
++ * Runtime NVENC API version negotiation.
++ *
++ * The NVENC driver rejects clients that present a newer API version than it
++ * supports, so an FFmpeg built against new SDK headers normally refuses to
++ * run on older drivers. Instead of failing, we query the driver's maximum
++ * supported API version at runtime and present struct version words (and the
++ * session apiVersion) matching what that driver expects, while disabling
++ * features that need a newer API. This keeps one binary working from driver
++ * 470.57.02 (API 11.1, the last driver branch for Kepler / first-generation
++ * NVENC GPUs) up to current drivers, including AV1 support on drivers that
++ * have it.
++ *
++ * The struct version table and the legacy bit-depth field positions below
++ * were derived from and verified against the exact layouts of nv-codec-headers
++ * n11.1.5.3, n12.0.16.1, n12.1.14.0, n12.2.72.0 and n13.0.19.0. All struct
++ * fields FFmpeg touches sit at identical offsets in all of those versions
++ * (new fields are only ever carved out of reserved space). Building against
++ * any other headers invalidates those assumptions, so refuse it.
++ */
++#if !NVENCAPI_CHECK_VERSION(13, 0) || NVENCAPI_CHECK_VERSION(13, 1)
++#error "The nvenc runtime API negotiation patch requires nv-codec-headers 13.0.x. Re-verify the struct version table and legacy bit-depth field layout before moving to other headers."
++#endif
++
++typedef struct NvencApiVersion {
++ uint32_t packed; // (major << 4) | minor, the NvEncodeAPIGetMaxSupportedVersion encoding
++ uint32_t api; // NVENCAPI_VERSION of that SDK: major | (minor << 24)
++ uint32_t function_list; // NV_ENCODE_API_FUNCTION_LIST_VER
++ uint32_t open_session_params; // NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER
++ uint32_t caps_param; // NV_ENC_CAPS_PARAM_VER
++ uint32_t initialize_params; // NV_ENC_INITIALIZE_PARAMS_VER
++ uint32_t config; // NV_ENC_CONFIG_VER
++ uint32_t preset_config; // NV_ENC_PRESET_CONFIG_VER
++ uint32_t pic_params; // NV_ENC_PIC_PARAMS_VER
++ uint32_t lock_bitstream; // NV_ENC_LOCK_BITSTREAM_VER
++ uint32_t lock_input_buffer; // NV_ENC_LOCK_INPUT_BUFFER_VER
++ uint32_t map_input_resource; // NV_ENC_MAP_INPUT_RESOURCE_VER
++ uint32_t register_resource; // NV_ENC_REGISTER_RESOURCE_VER
++ uint32_t create_input_buffer; // NV_ENC_CREATE_INPUT_BUFFER_VER
++ uint32_t create_bitstream_buffer; // NV_ENC_CREATE_BITSTREAM_BUFFER_VER
++ uint32_t reconfigure_params; // NV_ENC_RECONFIGURE_PARAMS_VER
++ uint32_t sequence_param_payload; // NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER
++} NvencApiVersion;
++
++#define NVENC_API_PACKED(major, minor) (((uint32_t)(major) << 4) | (minor))
++#define NVENC_API_AT_LEAST(ctx, major, minor) \
++ ((ctx)->api_version >= NVENC_API_PACKED(major, minor))
++
+ typedef struct NvencSurface
+ {
+ NV_ENC_INPUT_PTR input_surface;
+@@ -216,6 +264,11 @@ typedef struct NvencContext
+ NvencDynLoadFunctions nvenc_dload_funcs;
+
+ NV_ENC_INITIALIZE_PARAMS init_encode_params;
++ /* The pre-12.2 NV_ENC_INITIALIZE_PARAMS layout is 8 bytes larger than the
++ * 12.2+ layout this is compiled against. Drivers negotiated to API < 12.2
++ * read that larger footprint, so keep zeroed spare space directly after
++ * the struct. Must stay right after init_encode_params and stay zero. */
++ uint32_t init_encode_params_compat_pad[2];
+ NV_ENC_CONFIG encode_config;
+ CUcontext cu_context;
+ CUcontext cu_context_internal;
+@@ -321,6 +374,11 @@ typedef struct NvencContext
+ int cbr_padding;
+ int multiview, multiview_supported;
+ int display_sei_sent;
++
++ /* Runtime-negotiated NVENC API version: the newest version supported by
++ * both the driver and this build. Set in nvenc_load_libraries. */
++ const NvencApiVersion *api_vers;
++ uint32_t api_version; // negotiated, (major << 4) | minor
+ } NvencContext;
+
+ int ff_nvenc_encode_init(AVCodecContext *avctx);
diff --git a/subprojects/packagefiles/ffmpeg/meson.build b/subprojects/packagefiles/ffmpeg/meson.build
new file mode 100644
index 0000000..38df7e9
--- /dev/null
+++ b/subprojects/packagefiles/ffmpeg/meson.build
@@ -0,0 +1,35 @@
+project('ffmpeg', 'c', version : '8.1')
+
+# Neither ffmpeg nor the libraries it depends on use meson, so they are built with their
+# own build systems into a shared prefix and then exposed to gpu-screen-recorder as
+# regular pkg-config dependencies.
+
+install_prefix = meson.current_build_dir() / 'install'
+c_compiler = ' '.join(meson.get_compiler('c').cmd_array())
+build_library = find_program(meson.global_source_root() / 'extra' / 'build_ffmpeg.sh')
+
+# srt links against openssl and ffmpeg links against all of them, so the order matters.
+libraries = ['nv-codec-headers', 'x264', 'opus', 'openssl', 'srt', 'ffmpeg']
+
+foreach library : libraries
+ source_dir = library == 'ffmpeg' ? meson.current_source_dir() : subproject(library).get_variable('source_dir')
+ build_result = run_command(build_library, library, source_dir, meson.current_build_dir() / library, install_prefix, c_compiler, check : true)
+ if build_result.stderr().strip() != ''
+ message(build_result.stderr().strip())
+ endif
+endforeach
+
+ffmpeg_libs = ['libavformat', 'libavfilter', 'libavcodec', 'libswresample', 'libavutil']
+
+pkgconfig = find_program('pkg-config')
+pkgconfig_env = environment()
+pkgconfig_env.prepend('PKG_CONFIG_PATH', install_prefix / 'lib' / 'pkgconfig')
+
+ffmpeg_cflags = run_command(pkgconfig, '--cflags', ffmpeg_libs, env : pkgconfig_env, check : true).stdout().split()
+ffmpeg_ldflags = run_command(pkgconfig, '--static', '--libs', ffmpeg_libs, env : pkgconfig_env, check : true).stdout().split()
+
+ffmpeg_dep = declare_dependency(compile_args : ffmpeg_cflags, link_args : ffmpeg_ldflags)
+
+foreach ffmpeg_lib : ffmpeg_libs
+ meson.override_dependency(ffmpeg_lib, ffmpeg_dep)
+endforeach
diff --git a/subprojects/packagefiles/nv-codec-headers/meson.build b/subprojects/packagefiles/nv-codec-headers/meson.build
new file mode 100644
index 0000000..cc59db7
--- /dev/null
+++ b/subprojects/packagefiles/nv-codec-headers/meson.build
@@ -0,0 +1,3 @@
+project('nv-codec-headers')
+
+source_dir = meson.current_source_dir()
diff --git a/subprojects/packagefiles/openssl/meson.build b/subprojects/packagefiles/openssl/meson.build
new file mode 100644
index 0000000..d7ebb0c
--- /dev/null
+++ b/subprojects/packagefiles/openssl/meson.build
@@ -0,0 +1,3 @@
+project('openssl')
+
+source_dir = meson.current_source_dir()
diff --git a/subprojects/packagefiles/opus/meson.build b/subprojects/packagefiles/opus/meson.build
new file mode 100644
index 0000000..858205d
--- /dev/null
+++ b/subprojects/packagefiles/opus/meson.build
@@ -0,0 +1,3 @@
+project('opus')
+
+source_dir = meson.current_source_dir()
diff --git a/subprojects/packagefiles/srt/meson.build b/subprojects/packagefiles/srt/meson.build
new file mode 100644
index 0000000..3fa707e
--- /dev/null
+++ b/subprojects/packagefiles/srt/meson.build
@@ -0,0 +1,3 @@
+project('srt')
+
+source_dir = meson.current_source_dir()
diff --git a/subprojects/packagefiles/x264/meson.build b/subprojects/packagefiles/x264/meson.build
new file mode 100644
index 0000000..87f3a80
--- /dev/null
+++ b/subprojects/packagefiles/x264/meson.build
@@ -0,0 +1,3 @@
+project('x264')
+
+source_dir = meson.current_source_dir()
diff --git a/subprojects/srt.wrap b/subprojects/srt.wrap
new file mode 100644
index 0000000..583b2e8
--- /dev/null
+++ b/subprojects/srt.wrap
@@ -0,0 +1,6 @@
+[wrap-file]
+directory = srt-1.5.6
+source_url = https://github.com/Haivision/srt/archive/refs/tags/v1.5.6.tar.gz
+source_filename = srt-1.5.6.tar.gz
+source_hash = 2c4980c2c4cfd142d21b829d939dc51db9c6628af5967fff62fd7290769569c7
+patch_directory = srt
diff --git a/subprojects/x264.wrap b/subprojects/x264.wrap
new file mode 100644
index 0000000..f9428cb
--- /dev/null
+++ b/subprojects/x264.wrap
@@ -0,0 +1,6 @@
+[wrap-file]
+directory = x264-b35605ace3ddf7c1a5d67a2eb553f034aef41d55
+source_url = https://code.videolan.org/videolan/x264/-/archive/b35605ace3ddf7c1a5d67a2eb553f034aef41d55/x264-b35605ace3ddf7c1a5d67a2eb553f034aef41d55.tar.bz2
+source_filename = x264-b35605ace3ddf7c1a5d67a2eb553f034aef41d55.tar.bz2
+source_hash = 6eeb82934e69fd51e043bd8c5b0d152839638d1ce7aa4eea65a3fedcf83ff224
+patch_directory = x264