From 05f7615ab03f9cbb3e8b2d0f160eb6f41de071f7 Mon Sep 17 00:00:00 2001
From: wang-bin <wbsecg1@gmail.com>
Date: Wed, 12 Jun 2019 10:43:50 +0800
Subject: [PATCH 1/3] nvenc: use runtime api version to support old drivers

There are reserved bit fields in nvEncoderAPI.h structs, so driver's abi
is stable. Requesting runtime version of these structs should work. I've
compared 7.0~9.x headers, and confirmed on some devices.
---
 libavcodec/nvenc.c | 72 ++++++++++++++++++++++++++--------------------
 libavcodec/nvenc.h |  3 ++
 2 files changed, 44 insertions(+), 31 deletions(-)

diff --git a/libavcodec/nvenc.c b/libavcodec/nvenc.c
index c6740c1842..ac35cb9f48 100644
--- a/libavcodec/nvenc.c
+++ b/libavcodec/nvenc.c
@@ -193,14 +193,26 @@ static void nvenc_print_driver_requirement(AVCodecContext *avctx, int level)
     av_log(avctx, level, "The minimum required Nvidia driver for nvenc is %s or newer\n", minver);
 }
 
+static inline uint32_t struct_ver_rt(NvencContext* ctx, uint32_t struct_ver)
+{
+    return ((uint32_t)ctx->apiver_rt | ((struct_ver)<<16) | (0x7 << 28));
+}
+
+static inline uint32_t api_ver(uint32_t major_ver, uint32_t minor_ver)
+{
+    return major_ver | (minor_ver << 24);
+}
+
 static av_cold int nvenc_load_libraries(AVCodecContext *avctx)
 {
     NvencContext *ctx            = avctx->priv_data;
     NvencDynLoadFunctions *dl_fn = &ctx->nvenc_dload_funcs;
     NVENCSTATUS err;
     uint32_t nvenc_max_ver;
+    uint32_t nvenc_max_major;
+    uint32_t nvenc_max_minor;
+    uint32_t func_ver = NV_ENCODE_API_FUNCTION_LIST_VER;
     int ret;
-
     ret = cuda_load_functions(&dl_fn->cuda_dl, avctx);
     if (ret < 0)
         return ret;
@@ -214,19 +226,17 @@ static av_cold int nvenc_load_libraries(AVCodecContext *avctx)
     err = dl_fn->nvenc_dl->NvEncodeAPIGetMaxSupportedVersion(&nvenc_max_ver);
     if (err != NV_ENC_SUCCESS)
         return nvenc_print_error(avctx, err, "Failed to query nvenc max version");
+    nvenc_max_major = nvenc_max_ver >> 4;
+    nvenc_max_minor = nvenc_max_ver & 0xf;
+    av_log(avctx, AV_LOG_VERBOSE, "nvenc build version: %d.%d, runtime version: %d.%d\n", NVENCAPI_MAJOR_VERSION, NVENCAPI_MINOR_VERSION, nvenc_max_major, nvenc_max_minor);
 
-    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) {
-        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,
-               nvenc_max_ver >> 4, nvenc_max_ver & 0xf);
-        nvenc_print_driver_requirement(avctx, AV_LOG_ERROR);
-        return AVERROR(ENOSYS);
-    }
+    ctx->apiver_rt = api_ver(nvenc_max_major, nvenc_max_minor); /* NVENCAPI_VERSION */
+    ctx->config_ver_rt = struct_ver_rt(ctx, 7) | (1<<31); /* NV_ENC_CONFIG_VER */
+    if (ctx->apiver_rt < api_ver(8, 1))
+        ctx->config_ver_rt = struct_ver_rt(ctx, 6) | (1<<31);
+    func_ver = struct_ver_rt(ctx, 2);
 
-    dl_fn->nvenc_funcs.version = NV_ENCODE_API_FUNCTION_LIST_VER;
+    dl_fn->nvenc_funcs.version = func_ver;
 
     err = dl_fn->nvenc_dl->NvEncodeAPICreateInstance(&dl_fn->nvenc_funcs);
     if (err != NV_ENC_SUCCESS)
@@ -267,8 +277,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    = struct_ver_rt(ctx, 1); // NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER
+    params.apiVersion = ctx->apiver_rt;
     if (ctx->d3d11_device) {
         params.device     = ctx->d3d11_device;
         params.deviceType = NV_ENC_DEVICE_TYPE_DIRECTX;
@@ -329,7 +339,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     = struct_ver_rt(ctx, 1); // NV_ENC_CAPS_PARAM_VER;
     params.capsToQuery = cap;
 
     ret = p_nvenc->nvEncGetEncodeCaps(ctx->nvencoder, ctx->init_encode_params.encodeGUID, &params, &val);
@@ -688,7 +698,7 @@ static av_cold void set_constqp(AVCodecContext *avctx)
     NV_ENC_RC_PARAMS *rc = &ctx->encode_config.rcParams;
 
     rc->rateControlMode = NV_ENC_PARAMS_RC_CONSTQP;
-
+    /*rc->reservedBitField1 = 0;*/
     if (ctx->init_qp_p >= 0) {
         rc->constQP.qpInterP = ctx->init_qp_p;
         if (ctx->init_qp_i >= 0 && ctx->init_qp_b >= 0) {
@@ -1221,8 +1231,8 @@ 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->config_ver_rt;//NV_ENC_CONFIG_VER;
+    ctx->init_encode_params.version = struct_ver_rt(ctx, 5) | (1<<31);//NV_ENC_INITIALIZE_PARAMS_VER;
 
     ctx->init_encode_params.encodeHeight = avctx->height;
     ctx->init_encode_params.encodeWidth = avctx->width;
@@ -1231,8 +1241,8 @@ static av_cold int nvenc_setup_encoder(AVCodecContext *avctx)
 
     nvenc_map_preset(ctx);
 
-    preset_config.version = NV_ENC_PRESET_CONFIG_VER;
-    preset_config.presetCfg.version = NV_ENC_CONFIG_VER;
+    preset_config.version = struct_ver_rt(ctx, 4) | (1<<31);// NV_ENC_PRESET_CONFIG_VER;
+    preset_config.presetCfg.version = ctx->config_ver_rt;//NV_ENC_CONFIG_VER;
 
     if (IS_SDK10_PRESET(ctx->preset)) {
 #ifdef NVENC_HAVE_NEW_PRESETS
@@ -1260,7 +1270,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->config_ver_rt;//NV_ENC_CONFIG_VER;
 
     compute_dar(avctx, &dw, &dh);
     ctx->init_encode_params.darHeight = dh;
@@ -1404,7 +1414,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 = struct_ver_rt(ctx, 1);//NV_ENC_CREATE_BITSTREAM_BUFFER_VER;
 
     if (avctx->pix_fmt == AV_PIX_FMT_CUDA || avctx->pix_fmt == AV_PIX_FMT_D3D11) {
         ctx->surfaces[idx].in_ref = av_frame_alloc();
@@ -1420,7 +1430,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 = struct_ver_rt(ctx, 1);//NV_ENC_CREATE_INPUT_BUFFER_VER;
         allocSurf.width = avctx->width;
         allocSurf.height = avctx->height;
         allocSurf.bufferFmt = ctx->surfaces[idx].format;
@@ -1503,7 +1513,7 @@ static av_cold int nvenc_setup_extradata(AVCodecContext *avctx)
     uint32_t outSize = 0;
     char tmpHeader[256];
     NV_ENC_SEQUENCE_PARAM_PAYLOAD payload = { 0 };
-    payload.version = NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER;
+    payload.version = struct_ver_rt(ctx, 1);//NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER;
 
     payload.spsppsBuffer = tmpHeader;
     payload.inBufferSize = sizeof(tmpHeader);
@@ -1535,7 +1545,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        = struct_ver_rt(ctx, 4) | (1<<31),
                                             .encodePicFlags = NV_ENC_PIC_FLAG_EOS };
 
         res = nvenc_push_context(avctx);
@@ -1747,7 +1757,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            = struct_ver_rt(ctx, 3);// NV_ENC_REGISTER_RESOURCE_VER;
     reg.width              = frames_ctx->width;
     reg.height             = frames_ctx->height;
     reg.pitch              = frame->linesize[0];
@@ -1802,7 +1812,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 = struct_ver_rt(ctx, 4);// NV_ENC_MAP_INPUT_RESOURCE_VER;
             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) {
@@ -1822,7 +1832,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 = struct_ver_rt(ctx, 1);//NV_ENC_LOCK_INPUT_BUFFER_VER;
         lockBufferParams.inputBuffer = nvenc_frame->input_surface;
 
         nv_status = p_nvenc->nvEncLockInputBuffer(ctx->nvencoder, &lockBufferParams);
@@ -1936,7 +1946,7 @@ static int process_output_surface(AVCodecContext *avctx, AVPacket *pkt, NvencSur
         goto error;
     }
 
-    lock_params.version = NV_ENC_LOCK_BITSTREAM_VER;
+    lock_params.version = struct_ver_rt(ctx, 1);//NV_ENC_LOCK_BITSTREAM_VER;
 
     lock_params.doNotWait = 0;
     lock_params.outputBitstream = tmpoutsurf->output_surface;
@@ -2054,7 +2064,7 @@ static void reconfig_encoder(AVCodecContext *avctx, const AVFrame *frame)
     int reconfig_bitrate = 0, reconfig_dar = 0;
     int dw, dh;
 
-    params.version = NV_ENC_RECONFIGURE_PARAMS_VER;
+    params.version = struct_ver_rt(ctx, 1) | (1<<31);//NV_ENC_RECONFIGURE_PARAMS_VER;
     params.reInitEncodeParams = ctx->init_encode_params;
 
     compute_dar(avctx, &dw, &dh);
@@ -2148,7 +2158,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 = struct_ver_rt(ctx, 4) | (1<<31);//NV_ENC_PIC_PARAMS_VER;
 
     if ((!ctx->cu_context && !ctx->d3d11_device) || !ctx->nvencoder)
         return AVERROR(EINVAL);
diff --git a/libavcodec/nvenc.h b/libavcodec/nvenc.h
index fb3820f7cf..6a39391276 100644
--- a/libavcodec/nvenc.h
+++ b/libavcodec/nvenc.h
@@ -146,6 +146,9 @@ typedef struct NvencContext
 {
     AVClass *avclass;
 
+    uint32_t apiver_rt;
+    uint32_t config_ver_rt;
+
     NvencDynLoadFunctions nvenc_dload_funcs;
 
     NV_ENC_INITIALIZE_PARAMS init_encode_params;
-- 
2.17.1

