From 95ebc0ad912447ba83cacb197f506b881f82179e Mon Sep 17 00:00:00 2001
From: 21pages <sunboeasy@gmail.com>
Date: Tue, 10 Dec 2024 15:29:21 +0800
Subject: [PATCH 1/2] dlopen libva

Signed-off-by: 21pages <sunboeasy@gmail.com>
---
 libavcodec/vaapi_decode.c      |  96 ++++++-----
 libavcodec/vaapi_encode.c      | 173 ++++++++++---------
 libavcodec/vaapi_encode_h264.c |   3 +-
 libavcodec/vaapi_encode_h265.c |   6 +-
 libavutil/hwcontext_vaapi.c    | 292 ++++++++++++++++++++++++---------
 libavutil/hwcontext_vaapi.h    |  96 +++++++++++
 6 files changed, 477 insertions(+), 189 deletions(-)

diff --git a/libavcodec/vaapi_decode.c b/libavcodec/vaapi_decode.c
index a59194340f..e202b673f4 100644
--- a/libavcodec/vaapi_decode.c
+++ b/libavcodec/vaapi_decode.c
@@ -38,17 +38,18 @@ int ff_vaapi_decode_make_param_buffer(AVCodecContext *avctx,
                                       size_t size)
 {
     VAAPIDecodeContext *ctx = avctx->internal->hwaccel_priv_data;
+    VAAPIDynLoadFunctions *vaf = ctx->hwctx->funcs;
     VAStatus vas;
     VABufferID buffer;
 
     av_assert0(pic->nb_param_buffers + 1 <= MAX_PARAM_BUFFERS);
 
-    vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context,
+    vas = vaf->vaCreateBuffer(ctx->hwctx->display, ctx->va_context,
                          type, size, 1, (void*)data, &buffer);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to create parameter "
                "buffer (type %d): %d (%s).\n",
-               type, vas, vaErrorStr(vas));
+               type, vas, vaf->vaErrorStr(vas));
         return AVERROR(EIO);
     }
 
@@ -69,6 +70,7 @@ int ff_vaapi_decode_make_slice_buffer(AVCodecContext *avctx,
                                       size_t slice_size)
 {
     VAAPIDecodeContext *ctx = avctx->internal->hwaccel_priv_data;
+    VAAPIDynLoadFunctions *vaf = ctx->hwctx->funcs;
     VAStatus vas;
     int index;
 
@@ -88,13 +90,13 @@ int ff_vaapi_decode_make_slice_buffer(AVCodecContext *avctx,
 
     index = 2 * pic->nb_slices;
 
-    vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context,
+    vas = vaf->vaCreateBuffer(ctx->hwctx->display, ctx->va_context,
                          VASliceParameterBufferType,
                          params_size, nb_params, (void*)params_data,
                          &pic->slice_buffers[index]);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to create slice "
-               "parameter buffer: %d (%s).\n", vas, vaErrorStr(vas));
+               "parameter buffer: %d (%s).\n", vas, vaf->vaErrorStr(vas));
         return AVERROR(EIO);
     }
 
@@ -102,15 +104,15 @@ int ff_vaapi_decode_make_slice_buffer(AVCodecContext *avctx,
            "is %#x.\n", pic->nb_slices, params_size,
            pic->slice_buffers[index]);
 
-    vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context,
+    vas = vaf->vaCreateBuffer(ctx->hwctx->display, ctx->va_context,
                          VASliceDataBufferType,
                          slice_size, 1, (void*)slice_data,
                          &pic->slice_buffers[index + 1]);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to create slice "
                "data buffer (size %zu): %d (%s).\n",
-               slice_size, vas, vaErrorStr(vas));
-        vaDestroyBuffer(ctx->hwctx->display,
+               slice_size, vas, vaf->vaErrorStr(vas));
+        vaf->vaDestroyBuffer(ctx->hwctx->display,
                         pic->slice_buffers[index]);
         return AVERROR(EIO);
     }
@@ -127,26 +129,27 @@ static void ff_vaapi_decode_destroy_buffers(AVCodecContext *avctx,
                                             VAAPIDecodePicture *pic)
 {
     VAAPIDecodeContext *ctx = avctx->internal->hwaccel_priv_data;
+    VAAPIDynLoadFunctions *vaf = ctx->hwctx->funcs;
     VAStatus vas;
     int i;
 
     for (i = 0; i < pic->nb_param_buffers; i++) {
-        vas = vaDestroyBuffer(ctx->hwctx->display,
+        vas = vaf->vaDestroyBuffer(ctx->hwctx->display,
                               pic->param_buffers[i]);
         if (vas != VA_STATUS_SUCCESS) {
             av_log(avctx, AV_LOG_ERROR, "Failed to destroy "
                    "parameter buffer %#x: %d (%s).\n",
-                   pic->param_buffers[i], vas, vaErrorStr(vas));
+                   pic->param_buffers[i], vas, vaf->vaErrorStr(vas));
         }
     }
 
     for (i = 0; i < 2 * pic->nb_slices; i++) {
-        vas = vaDestroyBuffer(ctx->hwctx->display,
+        vas = vaf->vaDestroyBuffer(ctx->hwctx->display,
                               pic->slice_buffers[i]);
         if (vas != VA_STATUS_SUCCESS) {
             av_log(avctx, AV_LOG_ERROR, "Failed to destroy slice "
                    "slice buffer %#x: %d (%s).\n",
-                   pic->slice_buffers[i], vas, vaErrorStr(vas));
+                   pic->slice_buffers[i], vas, vaf->vaErrorStr(vas));
         }
     }
 }
@@ -155,6 +158,7 @@ int ff_vaapi_decode_issue(AVCodecContext *avctx,
                           VAAPIDecodePicture *pic)
 {
     VAAPIDecodeContext *ctx = avctx->internal->hwaccel_priv_data;
+    VAAPIDynLoadFunctions *vaf = ctx->hwctx->funcs;
     VAStatus vas;
     int err;
 
@@ -166,37 +170,37 @@ int ff_vaapi_decode_issue(AVCodecContext *avctx,
     av_log(avctx, AV_LOG_DEBUG, "Decode to surface %#x.\n",
            pic->output_surface);
 
-    vas = vaBeginPicture(ctx->hwctx->display, ctx->va_context,
+    vas = vaf->vaBeginPicture(ctx->hwctx->display, ctx->va_context,
                          pic->output_surface);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to begin picture decode "
-               "issue: %d (%s).\n", vas, vaErrorStr(vas));
+               "issue: %d (%s).\n", vas, vaf->vaErrorStr(vas));
         err = AVERROR(EIO);
         goto fail_with_picture;
     }
 
-    vas = vaRenderPicture(ctx->hwctx->display, ctx->va_context,
+    vas = vaf->vaRenderPicture(ctx->hwctx->display, ctx->va_context,
                           pic->param_buffers, pic->nb_param_buffers);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to upload decode "
-               "parameters: %d (%s).\n", vas, vaErrorStr(vas));
+               "parameters: %d (%s).\n", vas, vaf->vaErrorStr(vas));
         err = AVERROR(EIO);
         goto fail_with_picture;
     }
 
-    vas = vaRenderPicture(ctx->hwctx->display, ctx->va_context,
+    vas = vaf->vaRenderPicture(ctx->hwctx->display, ctx->va_context,
                           pic->slice_buffers, 2 * pic->nb_slices);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to upload slices: "
-               "%d (%s).\n", vas, vaErrorStr(vas));
+               "%d (%s).\n", vas, vaf->vaErrorStr(vas));
         err = AVERROR(EIO);
         goto fail_with_picture;
     }
 
-    vas = vaEndPicture(ctx->hwctx->display, ctx->va_context);
+    vas = vaf->vaEndPicture(ctx->hwctx->display, ctx->va_context);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to end picture decode "
-               "issue: %d (%s).\n", vas, vaErrorStr(vas));
+               "issue: %d (%s).\n", vas, vaf->vaErrorStr(vas));
         err = AVERROR(EIO);
         if (CONFIG_VAAPI_1 || ctx->hwctx->driver_quirks &
             AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS)
@@ -213,10 +217,10 @@ int ff_vaapi_decode_issue(AVCodecContext *avctx,
     goto exit;
 
 fail_with_picture:
-    vas = vaEndPicture(ctx->hwctx->display, ctx->va_context);
+    vas = vaf->vaEndPicture(ctx->hwctx->display, ctx->va_context);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to end picture decode "
-               "after error: %d (%s).\n", vas, vaErrorStr(vas));
+               "after error: %d (%s).\n", vas, vaf->vaErrorStr(vas));
     }
 fail:
     ff_vaapi_decode_destroy_buffers(avctx, pic);
@@ -304,6 +308,7 @@ static int vaapi_decode_find_best_format(AVCodecContext *avctx,
                                          AVHWFramesContext *frames)
 {
     AVVAAPIDeviceContext *hwctx = device->hwctx;
+    VAAPIDynLoadFunctions *vaf = hwctx->funcs;
     VAStatus vas;
     VASurfaceAttrib *attr;
     enum AVPixelFormat source_format, best_format, format;
@@ -313,11 +318,11 @@ static int vaapi_decode_find_best_format(AVCodecContext *avctx,
     source_format = avctx->sw_pix_fmt;
     av_assert0(source_format != AV_PIX_FMT_NONE);
 
-    vas = vaQuerySurfaceAttributes(hwctx->display, config_id,
+    vas = vaf->vaQuerySurfaceAttributes(hwctx->display, config_id,
                                    NULL, &nb_attr);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to query surface attributes: "
-               "%d (%s).\n", vas, vaErrorStr(vas));
+               "%d (%s).\n", vas, vaf->vaErrorStr(vas));
         return AVERROR(ENOSYS);
     }
 
@@ -325,11 +330,11 @@ static int vaapi_decode_find_best_format(AVCodecContext *avctx,
     if (!attr)
         return AVERROR(ENOMEM);
 
-    vas = vaQuerySurfaceAttributes(hwctx->display, config_id,
+    vas = vaf->vaQuerySurfaceAttributes(hwctx->display, config_id,
                                    attr, &nb_attr);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to query surface attributes: "
-               "%d (%s).\n", vas, vaErrorStr(vas));
+               "%d (%s).\n", vas, vaf->vaErrorStr(vas));
         av_freep(&attr);
         return AVERROR(ENOSYS);
     }
@@ -471,6 +476,7 @@ static int vaapi_decode_make_config(AVCodecContext *avctx,
 
     AVHWDeviceContext    *device = (AVHWDeviceContext*)device_ref->data;
     AVVAAPIDeviceContext *hwctx = device->hwctx;
+    VAAPIDynLoadFunctions *vaf = hwctx->funcs;
 
     codec_desc = avcodec_descriptor_get(avctx->codec_id);
     if (!codec_desc) {
@@ -478,7 +484,7 @@ static int vaapi_decode_make_config(AVCodecContext *avctx,
         goto fail;
     }
 
-    profile_count = vaMaxNumProfiles(hwctx->display);
+    profile_count = vaf->vaMaxNumProfiles(hwctx->display);
     profile_list  = av_malloc_array(profile_count,
                                     sizeof(VAProfile));
     if (!profile_list) {
@@ -486,11 +492,11 @@ static int vaapi_decode_make_config(AVCodecContext *avctx,
         goto fail;
     }
 
-    vas = vaQueryConfigProfiles(hwctx->display,
+    vas = vaf->vaQueryConfigProfiles(hwctx->display,
                                 profile_list, &profile_count);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to query profiles: "
-               "%d (%s).\n", vas, vaErrorStr(vas));
+               "%d (%s).\n", vas, vaf->vaErrorStr(vas));
         err = AVERROR(ENOSYS);
         goto fail;
     }
@@ -550,12 +556,12 @@ static int vaapi_decode_make_config(AVCodecContext *avctx,
         }
     }
 
-    vas = vaCreateConfig(hwctx->display, matched_va_profile,
+    vas = vaf->vaCreateConfig(hwctx->display, matched_va_profile,
                          VAEntrypointVLD, NULL, 0,
                          va_config);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to create decode "
-               "configuration: %d (%s).\n", vas, vaErrorStr(vas));
+               "configuration: %d (%s).\n", vas, vaf->vaErrorStr(vas));
         err = AVERROR(EIO);
         goto fail;
     }
@@ -638,7 +644,7 @@ fail:
     av_hwframe_constraints_free(&constraints);
     av_freep(&hwconfig);
     if (*va_config != VA_INVALID_ID) {
-        vaDestroyConfig(hwctx->display, *va_config);
+        vaf->vaDestroyConfig(hwctx->display, *va_config);
         *va_config = VA_INVALID_ID;
     }
     av_freep(&profile_list);
@@ -651,12 +657,14 @@ int ff_vaapi_common_frame_params(AVCodecContext *avctx,
     AVHWFramesContext *hw_frames = (AVHWFramesContext *)hw_frames_ctx->data;
     AVHWDeviceContext *device_ctx = hw_frames->device_ctx;
     AVVAAPIDeviceContext *hwctx;
+    VAAPIDynLoadFunctions *vaf;
     VAConfigID va_config = VA_INVALID_ID;
     int err;
 
     if (device_ctx->type != AV_HWDEVICE_TYPE_VAAPI)
         return AVERROR(EINVAL);
     hwctx = device_ctx->hwctx;
+    vaf = hwctx->funcs;
 
     err = vaapi_decode_make_config(avctx, hw_frames->device_ref, &va_config,
                                    hw_frames_ctx);
@@ -664,7 +672,7 @@ int ff_vaapi_common_frame_params(AVCodecContext *avctx,
         return err;
 
     if (va_config != VA_INVALID_ID)
-        vaDestroyConfig(hwctx->display, va_config);
+        vaf->vaDestroyConfig(hwctx->display, va_config);
 
     return 0;
 }
@@ -672,6 +680,7 @@ int ff_vaapi_common_frame_params(AVCodecContext *avctx,
 int ff_vaapi_decode_init(AVCodecContext *avctx)
 {
     VAAPIDecodeContext *ctx = avctx->internal->hwaccel_priv_data;
+    VAAPIDynLoadFunctions *vaf;
     VAStatus vas;
     int err;
 
@@ -686,13 +695,18 @@ int ff_vaapi_decode_init(AVCodecContext *avctx)
     ctx->hwfc   = ctx->frames->hwctx;
     ctx->device = ctx->frames->device_ctx;
     ctx->hwctx  = ctx->device->hwctx;
+    if (!ctx->hwctx || !ctx->hwctx->funcs) {
+        err = AVERROR(EINVAL);
+        goto fail;
+    }
+    vaf = ctx->hwctx->funcs;
 
     err = vaapi_decode_make_config(avctx, ctx->frames->device_ref,
                                    &ctx->va_config, NULL);
     if (err)
         goto fail;
 
-    vas = vaCreateContext(ctx->hwctx->display, ctx->va_config,
+    vas = vaf->vaCreateContext(ctx->hwctx->display, ctx->va_config,
                           avctx->coded_width, avctx->coded_height,
                           VA_PROGRESSIVE,
                           ctx->hwfc->surface_ids,
@@ -700,7 +714,7 @@ int ff_vaapi_decode_init(AVCodecContext *avctx)
                           &ctx->va_context);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to create decode "
-               "context: %d (%s).\n", vas, vaErrorStr(vas));
+               "context: %d (%s).\n", vas, vaf->vaErrorStr(vas));
         err = AVERROR(EIO);
         goto fail;
     }
@@ -718,22 +732,28 @@ fail:
 int ff_vaapi_decode_uninit(AVCodecContext *avctx)
 {
     VAAPIDecodeContext *ctx = avctx->internal->hwaccel_priv_data;
+    VAAPIDynLoadFunctions *vaf = NULL;
     VAStatus vas;
 
+    if (ctx->hwctx && ctx->hwctx->funcs)
+        vaf = ctx->hwctx->funcs;
+    if (!vaf)
+        return 0;
+
     if (ctx->va_context != VA_INVALID_ID) {
-        vas = vaDestroyContext(ctx->hwctx->display, ctx->va_context);
+        vas = vaf->vaDestroyContext(ctx->hwctx->display, ctx->va_context);
         if (vas != VA_STATUS_SUCCESS) {
             av_log(avctx, AV_LOG_ERROR, "Failed to destroy decode "
                    "context %#x: %d (%s).\n",
-                   ctx->va_context, vas, vaErrorStr(vas));
+                   ctx->va_context, vas, vaf->vaErrorStr(vas));
         }
     }
     if (ctx->va_config != VA_INVALID_ID) {
-        vas = vaDestroyConfig(ctx->hwctx->display, ctx->va_config);
+        vas = vaf->vaDestroyConfig(ctx->hwctx->display, ctx->va_config);
         if (vas != VA_STATUS_SUCCESS) {
             av_log(avctx, AV_LOG_ERROR, "Failed to destroy decode "
                    "configuration %#x: %d (%s).\n",
-                   ctx->va_config, vas, vaErrorStr(vas));
+                   ctx->va_config, vas, vaf->vaErrorStr(vas));
         }
     }
 
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index 16a9a364f0..ccf6fa59d6 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -43,6 +43,7 @@ static int vaapi_encode_make_packed_header(AVCodecContext *avctx,
                                            int type, char *data, size_t bit_len)
 {
     VAAPIEncodeContext *ctx = avctx->priv_data;
+    VAAPIDynLoadFunctions *vaf = ctx->hwctx->funcs;
     VAStatus vas;
     VABufferID param_buffer, data_buffer;
     VABufferID *tmp;
@@ -57,24 +58,24 @@ static int vaapi_encode_make_packed_header(AVCodecContext *avctx,
         return AVERROR(ENOMEM);
     pic->param_buffers = tmp;
 
-    vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context,
+    vas = vaf->vaCreateBuffer(ctx->hwctx->display, ctx->va_context,
                          VAEncPackedHeaderParameterBufferType,
                          sizeof(params), 1, &params, &param_buffer);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to create parameter buffer "
                "for packed header (type %d): %d (%s).\n",
-               type, vas, vaErrorStr(vas));
+               type, vas, vaf->vaErrorStr(vas));
         return AVERROR(EIO);
     }
     pic->param_buffers[pic->nb_param_buffers++] = param_buffer;
 
-    vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context,
+    vas = vaf->vaCreateBuffer(ctx->hwctx->display, ctx->va_context,
                          VAEncPackedHeaderDataBufferType,
                          (bit_len + 7) / 8, 1, data, &data_buffer);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to create data buffer "
                "for packed header (type %d): %d (%s).\n",
-               type, vas, vaErrorStr(vas));
+               type, vas, vaf->vaErrorStr(vas));
         return AVERROR(EIO);
     }
     pic->param_buffers[pic->nb_param_buffers++] = data_buffer;
@@ -89,6 +90,7 @@ static int vaapi_encode_make_param_buffer(AVCodecContext *avctx,
                                           int type, char *data, size_t len)
 {
     VAAPIEncodeContext *ctx = avctx->priv_data;
+    VAAPIDynLoadFunctions *vaf = ctx->hwctx->funcs;
     VAStatus vas;
     VABufferID *tmp;
     VABufferID buffer;
@@ -98,11 +100,11 @@ static int vaapi_encode_make_param_buffer(AVCodecContext *avctx,
         return AVERROR(ENOMEM);
     pic->param_buffers = tmp;
 
-    vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context,
+    vas = vaf->vaCreateBuffer(ctx->hwctx->display, ctx->va_context,
                          type, len, 1, data, &buffer);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to create parameter buffer "
-               "(type %d): %d (%s).\n", type, vas, vaErrorStr(vas));
+               "(type %d): %d (%s).\n", type, vas, vaf->vaErrorStr(vas));
         return AVERROR(EIO);
     }
     pic->param_buffers[pic->nb_param_buffers++] = buffer;
@@ -141,6 +143,7 @@ static int vaapi_encode_wait(AVCodecContext *avctx, FFHWBaseEncodePicture *base_
     FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
 #endif
     VAAPIEncodeContext *ctx = avctx->priv_data;
+    VAAPIDynLoadFunctions *vaf = ctx->hwctx->funcs;
     VAAPIEncodePicture *pic = base_pic->priv;
     VAStatus vas;
 
@@ -156,22 +159,22 @@ static int vaapi_encode_wait(AVCodecContext *avctx, FFHWBaseEncodePicture *base_
            base_pic->encode_order, pic->input_surface);
 
 #if VA_CHECK_VERSION(1, 9, 0)
-    if (base_ctx->async_encode) {
-        vas = vaSyncBuffer(ctx->hwctx->display,
+    if (base_ctx->async_encode && vaf->vaSyncBuffer) {
+        vas = vaf->vaSyncBuffer(ctx->hwctx->display,
                            pic->output_buffer,
                            VA_TIMEOUT_INFINITE);
         if (vas != VA_STATUS_SUCCESS) {
             av_log(avctx, AV_LOG_ERROR, "Failed to sync to output buffer completion: "
-                   "%d (%s).\n", vas, vaErrorStr(vas));
+                   "%d (%s).\n", vas, vaf->vaErrorStr(vas));
             return AVERROR(EIO);
         }
     } else
 #endif
     { // If vaSyncBuffer is not implemented, try old version API.
-        vas = vaSyncSurface(ctx->hwctx->display, pic->input_surface);
+        vas = vaf->vaSyncSurface(ctx->hwctx->display, pic->input_surface);
         if (vas != VA_STATUS_SUCCESS) {
             av_log(avctx, AV_LOG_ERROR, "Failed to sync to picture completion: "
-                "%d (%s).\n", vas, vaErrorStr(vas));
+                "%d (%s).\n", vas, vaf->vaErrorStr(vas));
             return AVERROR(EIO);
         }
     }
@@ -270,6 +273,7 @@ static int vaapi_encode_issue(AVCodecContext *avctx,
 {
     FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
     VAAPIEncodeContext         *ctx = avctx->priv_data;
+    VAAPIDynLoadFunctions *vaf = ctx->hwctx->funcs;
     VAAPIEncodePicture         *pic = base_pic->priv;
     VAAPIEncodeSlice *slice;
     VAStatus vas;
@@ -587,28 +591,28 @@ static int vaapi_encode_issue(AVCodecContext *avctx,
     }
 #endif
 
-    vas = vaBeginPicture(ctx->hwctx->display, ctx->va_context,
+    vas = vaf->vaBeginPicture(ctx->hwctx->display, ctx->va_context,
                          pic->input_surface);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to begin picture encode issue: "
-               "%d (%s).\n", vas, vaErrorStr(vas));
+               "%d (%s).\n", vas, vaf->vaErrorStr(vas));
         err = AVERROR(EIO);
         goto fail_with_picture;
     }
 
-    vas = vaRenderPicture(ctx->hwctx->display, ctx->va_context,
+    vas = vaf->vaRenderPicture(ctx->hwctx->display, ctx->va_context,
                           pic->param_buffers, pic->nb_param_buffers);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to upload encode parameters: "
-               "%d (%s).\n", vas, vaErrorStr(vas));
+               "%d (%s).\n", vas, vaf->vaErrorStr(vas));
         err = AVERROR(EIO);
         goto fail_with_picture;
     }
 
-    vas = vaEndPicture(ctx->hwctx->display, ctx->va_context);
+    vas = vaf->vaEndPicture(ctx->hwctx->display, ctx->va_context);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to end picture encode issue: "
-               "%d (%s).\n", vas, vaErrorStr(vas));
+               "%d (%s).\n", vas, vaf->vaErrorStr(vas));
         err = AVERROR(EIO);
         // vaRenderPicture() has been called here, so we should not destroy
         // the parameter buffers unless separate destruction is required.
@@ -622,12 +626,12 @@ static int vaapi_encode_issue(AVCodecContext *avctx,
     if (CONFIG_VAAPI_1 || ctx->hwctx->driver_quirks &
         AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS) {
         for (i = 0; i < pic->nb_param_buffers; i++) {
-            vas = vaDestroyBuffer(ctx->hwctx->display,
+            vas = vaf->vaDestroyBuffer(ctx->hwctx->display,
                                   pic->param_buffers[i]);
             if (vas != VA_STATUS_SUCCESS) {
                 av_log(avctx, AV_LOG_ERROR, "Failed to destroy "
                        "param buffer %#x: %d (%s).\n",
-                       pic->param_buffers[i], vas, vaErrorStr(vas));
+                       pic->param_buffers[i], vas, vaf->vaErrorStr(vas));
                 // And ignore.
             }
         }
@@ -636,10 +640,10 @@ static int vaapi_encode_issue(AVCodecContext *avctx,
     return 0;
 
 fail_with_picture:
-    vaEndPicture(ctx->hwctx->display, ctx->va_context);
+    vaf->vaEndPicture(ctx->hwctx->display, ctx->va_context);
 fail:
     for(i = 0; i < pic->nb_param_buffers; i++)
-        vaDestroyBuffer(ctx->hwctx->display, pic->param_buffers[i]);
+        vaf->vaDestroyBuffer(ctx->hwctx->display, pic->param_buffers[i]);
     if (pic->slices) {
         for (i = 0; i < pic->nb_slices; i++)
             av_freep(&pic->slices[i].codec_slice_params);
@@ -657,16 +661,17 @@ fail_at_end:
 static int vaapi_encode_get_coded_buffer_size(AVCodecContext *avctx, VABufferID buf_id)
 {
     VAAPIEncodeContext *ctx = avctx->priv_data;
+    VAAPIDynLoadFunctions *vaf = ctx->hwctx->funcs;
     VACodedBufferSegment *buf_list, *buf;
     int size = 0;
     VAStatus vas;
     int err;
 
-    vas = vaMapBuffer(ctx->hwctx->display, buf_id,
+    vas = vaf->vaMapBuffer(ctx->hwctx->display, buf_id,
                       (void**)&buf_list);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to map output buffers: "
-               "%d (%s).\n", vas, vaErrorStr(vas));
+               "%d (%s).\n", vas, vaf->vaErrorStr(vas));
         err = AVERROR(EIO);
         return err;
     }
@@ -674,10 +679,10 @@ static int vaapi_encode_get_coded_buffer_size(AVCodecContext *avctx, VABufferID
     for (buf = buf_list; buf; buf = buf->next)
         size += buf->size;
 
-    vas = vaUnmapBuffer(ctx->hwctx->display, buf_id);
+    vas = vaf->vaUnmapBuffer(ctx->hwctx->display, buf_id);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to unmap output buffers: "
-               "%d (%s).\n", vas, vaErrorStr(vas));
+               "%d (%s).\n", vas, vaf->vaErrorStr(vas));
         err = AVERROR(EIO);
         return err;
     }
@@ -689,15 +694,16 @@ static int vaapi_encode_get_coded_buffer_data(AVCodecContext *avctx,
                                               VABufferID buf_id, uint8_t **dst)
 {
     VAAPIEncodeContext *ctx = avctx->priv_data;
+    VAAPIDynLoadFunctions *vaf = ctx->hwctx->funcs;
     VACodedBufferSegment *buf_list, *buf;
     VAStatus vas;
     int err;
 
-    vas = vaMapBuffer(ctx->hwctx->display, buf_id,
+    vas = vaf->vaMapBuffer(ctx->hwctx->display, buf_id,
                       (void**)&buf_list);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to map output buffers: "
-               "%d (%s).\n", vas, vaErrorStr(vas));
+               "%d (%s).\n", vas, vaf->vaErrorStr(vas));
         err = AVERROR(EIO);
         return err;
     }
@@ -710,10 +716,10 @@ static int vaapi_encode_get_coded_buffer_data(AVCodecContext *avctx,
         *dst += buf->size;
     }
 
-    vas = vaUnmapBuffer(ctx->hwctx->display, buf_id);
+    vas = vaf->vaUnmapBuffer(ctx->hwctx->display, buf_id);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to unmap output buffers: "
-               "%d (%s).\n", vas, vaErrorStr(vas));
+               "%d (%s).\n", vas, vaf->vaErrorStr(vas));
         err = AVERROR(EIO);
         return err;
     }
@@ -936,6 +942,7 @@ static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
 {
     FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
     VAAPIEncodeContext         *ctx = avctx->priv_data;
+    VAAPIDynLoadFunctions *vaf = ctx->hwctx->funcs;
     VAProfile       *va_profiles    = NULL;
     VAEntrypoint    *va_entrypoints = NULL;
     VAStatus vas;
@@ -977,16 +984,16 @@ static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
     av_log(avctx, AV_LOG_VERBOSE, "Input surface format is %s.\n",
            desc->name);
 
-    n = vaMaxNumProfiles(ctx->hwctx->display);
+    n = vaf->vaMaxNumProfiles(ctx->hwctx->display);
     va_profiles = av_malloc_array(n, sizeof(VAProfile));
     if (!va_profiles) {
         err = AVERROR(ENOMEM);
         goto fail;
     }
-    vas = vaQueryConfigProfiles(ctx->hwctx->display, va_profiles, &n);
+    vas = vaf->vaQueryConfigProfiles(ctx->hwctx->display, va_profiles, &n);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to query profiles: %d (%s).\n",
-               vas, vaErrorStr(vas));
+               vas, vaf->vaErrorStr(vas));
         err = AVERROR_EXTERNAL;
         goto fail;
     }
@@ -1007,7 +1014,7 @@ static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
             continue;
 
 #if VA_CHECK_VERSION(1, 0, 0)
-        profile_string = vaProfileStr(profile->va_profile);
+        profile_string = vaf->vaProfileStr(profile->va_profile);
 #else
         profile_string = "(no profile names)";
 #endif
@@ -1037,18 +1044,18 @@ static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
     av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI profile %s (%d).\n",
            profile_string, ctx->va_profile);
 
-    n = vaMaxNumEntrypoints(ctx->hwctx->display);
+    n = vaf->vaMaxNumEntrypoints(ctx->hwctx->display);
     va_entrypoints = av_malloc_array(n, sizeof(VAEntrypoint));
     if (!va_entrypoints) {
         err = AVERROR(ENOMEM);
         goto fail;
     }
-    vas = vaQueryConfigEntrypoints(ctx->hwctx->display, ctx->va_profile,
+    vas = vaf->vaQueryConfigEntrypoints(ctx->hwctx->display, ctx->va_profile,
                                    va_entrypoints, &n);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to query entrypoints for "
                "profile %s (%d): %d (%s).\n", profile_string,
-               ctx->va_profile, vas, vaErrorStr(vas));
+               ctx->va_profile, vas, vaf->vaErrorStr(vas));
         err = AVERROR_EXTERNAL;
         goto fail;
     }
@@ -1070,7 +1077,7 @@ static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
 
     ctx->va_entrypoint = va_entrypoints[i];
 #if VA_CHECK_VERSION(1, 0, 0)
-    entrypoint_string = vaEntrypointStr(ctx->va_entrypoint);
+    entrypoint_string = vaf->vaEntrypointStr(ctx->va_entrypoint);
 #else
     entrypoint_string = "(no entrypoint names)";
 #endif
@@ -1095,12 +1102,12 @@ static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
     }
 
     rt_format_attr = (VAConfigAttrib) { VAConfigAttribRTFormat };
-    vas = vaGetConfigAttributes(ctx->hwctx->display,
+    vas = vaf->vaGetConfigAttributes(ctx->hwctx->display,
                                 ctx->va_profile, ctx->va_entrypoint,
                                 &rt_format_attr, 1);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to query RT format "
-               "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+               "config attribute: %d (%s).\n", vas, vaf->vaErrorStr(vas));
         err = AVERROR_EXTERNAL;
         goto fail;
     }
@@ -1157,6 +1164,7 @@ static const VAAPIEncodeRCMode vaapi_encode_rc_modes[] = {
 static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
 {
     VAAPIEncodeContext *ctx = avctx->priv_data;
+    VAAPIDynLoadFunctions *vaf = ctx->hwctx->funcs;
     uint32_t supported_va_rc_modes;
     const VAAPIEncodeRCMode *rc_mode;
     int64_t rc_bits_per_second;
@@ -1170,12 +1178,12 @@ static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
     VAStatus vas;
     char supported_rc_modes_string[64];
 
-    vas = vaGetConfigAttributes(ctx->hwctx->display,
+    vas = vaf->vaGetConfigAttributes(ctx->hwctx->display,
                                 ctx->va_profile, ctx->va_entrypoint,
                                 &rc_attr, 1);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to query rate control "
-               "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+               "config attribute: %d (%s).\n", vas, vaf->vaErrorStr(vas));
         return AVERROR_EXTERNAL;
     }
     if (rc_attr.value == VA_ATTRIB_NOT_SUPPORTED) {
@@ -1516,6 +1524,7 @@ static av_cold int vaapi_encode_init_max_frame_size(AVCodecContext *avctx)
 {
 #if VA_CHECK_VERSION(1, 5, 0)
     VAAPIEncodeContext  *ctx = avctx->priv_data;
+    VAAPIDynLoadFunctions *vaf = ctx->hwctx->funcs;
     VAConfigAttrib      attr = { VAConfigAttribMaxFrameSize };
     VAStatus vas;
 
@@ -1526,14 +1535,14 @@ static av_cold int vaapi_encode_init_max_frame_size(AVCodecContext *avctx)
         return AVERROR(EINVAL);
     }
 
-    vas = vaGetConfigAttributes(ctx->hwctx->display,
+    vas = vaf->vaGetConfigAttributes(ctx->hwctx->display,
                                 ctx->va_profile,
                                 ctx->va_entrypoint,
                                 &attr, 1);
     if (vas != VA_STATUS_SUCCESS) {
         ctx->max_frame_size = 0;
         av_log(avctx, AV_LOG_ERROR, "Failed to query max frame size "
-               "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+               "config attribute: %d (%s).\n", vas, vaf->vaErrorStr(vas));
         return AVERROR_EXTERNAL;
     }
 
@@ -1573,18 +1582,19 @@ static av_cold int vaapi_encode_init_gop_structure(AVCodecContext *avctx)
 {
     FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
     VAAPIEncodeContext         *ctx = avctx->priv_data;
+    VAAPIDynLoadFunctions *vaf = ctx->hwctx->funcs;
     VAStatus vas;
     VAConfigAttrib attr = { VAConfigAttribEncMaxRefFrames };
     uint32_t ref_l0, ref_l1;
     int prediction_pre_only, err;
 
-    vas = vaGetConfigAttributes(ctx->hwctx->display,
+    vas = vaf->vaGetConfigAttributes(ctx->hwctx->display,
                                 ctx->va_profile,
                                 ctx->va_entrypoint,
                                 &attr, 1);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to query reference frames "
-               "attribute: %d (%s).\n", vas, vaErrorStr(vas));
+               "attribute: %d (%s).\n", vas, vaf->vaErrorStr(vas));
         return AVERROR_EXTERNAL;
     }
 
@@ -1602,13 +1612,13 @@ static av_cold int vaapi_encode_init_gop_structure(AVCodecContext *avctx)
     if (!(ctx->codec->flags & FF_HW_FLAG_INTRA_ONLY ||
         avctx->gop_size <= 1)) {
         attr = (VAConfigAttrib) { VAConfigAttribPredictionDirection };
-        vas = vaGetConfigAttributes(ctx->hwctx->display,
+        vas = vaf->vaGetConfigAttributes(ctx->hwctx->display,
                                     ctx->va_profile,
                                     ctx->va_entrypoint,
                                     &attr, 1);
         if (vas != VA_STATUS_SUCCESS) {
             av_log(avctx, AV_LOG_WARNING, "Failed to query prediction direction "
-                   "attribute: %d (%s).\n", vas, vaErrorStr(vas));
+                   "attribute: %d (%s).\n", vas, vaf->vaErrorStr(vas));
             return AVERROR_EXTERNAL;
         } else if (attr.value == VA_ATTRIB_NOT_SUPPORTED) {
             av_log(avctx, AV_LOG_VERBOSE, "Driver does not report any additional "
@@ -1758,6 +1768,7 @@ static av_cold int vaapi_encode_init_slice_structure(AVCodecContext *avctx)
 {
     FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
     VAAPIEncodeContext *ctx = avctx->priv_data;
+    VAAPIDynLoadFunctions *vaf = ctx->hwctx->funcs;
     VAConfigAttrib attr[3] = { { VAConfigAttribEncMaxSlices },
                                { VAConfigAttribEncSliceStructure },
 #if VA_CHECK_VERSION(1, 1, 0)
@@ -1789,13 +1800,13 @@ static av_cold int vaapi_encode_init_slice_structure(AVCodecContext *avctx)
         return 0;
     }
 
-    vas = vaGetConfigAttributes(ctx->hwctx->display,
+    vas = vaf->vaGetConfigAttributes(ctx->hwctx->display,
                                 ctx->va_profile,
                                 ctx->va_entrypoint,
                                 attr, FF_ARRAY_ELEMS(attr));
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to query slice "
-               "attributes: %d (%s).\n", vas, vaErrorStr(vas));
+               "attributes: %d (%s).\n", vas, vaf->vaErrorStr(vas));
         return AVERROR_EXTERNAL;
     }
     max_slices      = attr[0].value;
@@ -1849,16 +1860,17 @@ static av_cold int vaapi_encode_init_slice_structure(AVCodecContext *avctx)
 static av_cold int vaapi_encode_init_packed_headers(AVCodecContext *avctx)
 {
     VAAPIEncodeContext *ctx = avctx->priv_data;
+    VAAPIDynLoadFunctions *vaf = ctx->hwctx->funcs;
     VAStatus vas;
     VAConfigAttrib attr = { VAConfigAttribEncPackedHeaders };
 
-    vas = vaGetConfigAttributes(ctx->hwctx->display,
+    vas = vaf->vaGetConfigAttributes(ctx->hwctx->display,
                                 ctx->va_profile,
                                 ctx->va_entrypoint,
                                 &attr, 1);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to query packed headers "
-               "attribute: %d (%s).\n", vas, vaErrorStr(vas));
+               "attribute: %d (%s).\n", vas, vaf->vaErrorStr(vas));
         return AVERROR_EXTERNAL;
     }
 
@@ -1910,17 +1922,18 @@ static av_cold int vaapi_encode_init_quality(AVCodecContext *avctx)
 {
 #if VA_CHECK_VERSION(0, 36, 0)
     VAAPIEncodeContext *ctx = avctx->priv_data;
+    VAAPIDynLoadFunctions *vaf = ctx->hwctx->funcs;
     VAStatus vas;
     VAConfigAttrib attr = { VAConfigAttribEncQualityRange };
     int quality = avctx->compression_level;
 
-    vas = vaGetConfigAttributes(ctx->hwctx->display,
+    vas = vaf->vaGetConfigAttributes(ctx->hwctx->display,
                                 ctx->va_profile,
                                 ctx->va_entrypoint,
                                 &attr, 1);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to query quality "
-               "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+               "config attribute: %d (%s).\n", vas, vaf->vaErrorStr(vas));
         return AVERROR_EXTERNAL;
     }
 
@@ -1958,16 +1971,17 @@ static av_cold int vaapi_encode_init_roi(AVCodecContext *avctx)
 #if VA_CHECK_VERSION(1, 0, 0)
     FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
     VAAPIEncodeContext         *ctx = avctx->priv_data;
+    VAAPIDynLoadFunctions *vaf = ctx->hwctx->funcs;
     VAStatus vas;
     VAConfigAttrib attr = { VAConfigAttribEncROI };
 
-    vas = vaGetConfigAttributes(ctx->hwctx->display,
+    vas = vaf->vaGetConfigAttributes(ctx->hwctx->display,
                                 ctx->va_profile,
                                 ctx->va_entrypoint,
                                 &attr, 1);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to query ROI "
-               "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+               "config attribute: %d (%s).\n", vas, vaf->vaErrorStr(vas));
         return AVERROR_EXTERNAL;
     }
 
@@ -1992,10 +2006,11 @@ static void vaapi_encode_free_output_buffer(FFRefStructOpaque opaque,
 {
     AVCodecContext   *avctx = opaque.nc;
     VAAPIEncodeContext *ctx = avctx->priv_data;
+    VAAPIDynLoadFunctions *vaf = ctx->hwctx->funcs;
     VABufferID *buffer_id_ref = obj;
     VABufferID buffer_id = *buffer_id_ref;
 
-    vaDestroyBuffer(ctx->hwctx->display, buffer_id);
+    vaf->vaDestroyBuffer(ctx->hwctx->display, buffer_id);
 
     av_log(avctx, AV_LOG_DEBUG, "Freed output buffer %#x\n", buffer_id);
 }
@@ -2005,6 +2020,7 @@ static int vaapi_encode_alloc_output_buffer(FFRefStructOpaque opaque, void *obj)
     AVCodecContext   *avctx = opaque.nc;
     FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
     VAAPIEncodeContext         *ctx = avctx->priv_data;
+    VAAPIDynLoadFunctions *vaf = ctx->hwctx->funcs;
     VABufferID *buffer_id = obj;
     VAStatus vas;
 
@@ -2012,13 +2028,13 @@ static int vaapi_encode_alloc_output_buffer(FFRefStructOpaque opaque, void *obj)
     // to hold the largest possible compressed frame.  We assume here
     // that the uncompressed frame plus some header data is an upper
     // bound on that.
-    vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context,
+    vas = vaf->vaCreateBuffer(ctx->hwctx->display, ctx->va_context,
                          VAEncCodedBufferType,
                          3 * base_ctx->surface_width * base_ctx->surface_height +
                          (1 << 16), 1, 0, buffer_id);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to create bitstream "
-               "output buffer: %d (%s).\n", vas, vaErrorStr(vas));
+               "output buffer: %d (%s).\n", vas, vaf->vaErrorStr(vas));
         return AVERROR(ENOMEM);
     }
 
@@ -2092,6 +2108,7 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
 {
     FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
     VAAPIEncodeContext         *ctx = avctx->priv_data;
+    VAAPIDynLoadFunctions *vaf = NULL;
     AVVAAPIFramesContext *recon_hwctx = NULL;
     VAStatus vas;
     int err;
@@ -2107,6 +2124,12 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
 
     ctx->hwctx = base_ctx->device->hwctx;
 
+    if (!ctx->hwctx || !ctx->hwctx->funcs) {
+        err = AVERROR(EINVAL);
+        goto fail;
+    }
+    vaf = ctx->hwctx->funcs;
+
     err = vaapi_encode_profile_entrypoint(avctx);
     if (err < 0)
         goto fail;
@@ -2157,13 +2180,13 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
             goto fail;
     }
 
-    vas = vaCreateConfig(ctx->hwctx->display,
+    vas = vaf->vaCreateConfig(ctx->hwctx->display,
                          ctx->va_profile, ctx->va_entrypoint,
                          ctx->config_attributes, ctx->nb_config_attributes,
                          &ctx->va_config);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to create encode pipeline "
-               "configuration: %d (%s).\n", vas, vaErrorStr(vas));
+               "configuration: %d (%s).\n", vas, vaf->vaErrorStr(vas));
         err = AVERROR(EIO);
         goto fail;
     }
@@ -2173,7 +2196,7 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
         goto fail;
 
     recon_hwctx = base_ctx->recon_frames->hwctx;
-    vas = vaCreateContext(ctx->hwctx->display, ctx->va_config,
+    vas = vaf->vaCreateContext(ctx->hwctx->display, ctx->va_config,
                           base_ctx->surface_width, base_ctx->surface_height,
                           VA_PROGRESSIVE,
                           recon_hwctx->surface_ids,
@@ -2181,7 +2204,7 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
                           &ctx->va_context);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(avctx, AV_LOG_ERROR, "Failed to create encode pipeline "
-               "context: %d (%s).\n", vas, vaErrorStr(vas));
+               "context: %d (%s).\n", vas, vaf->vaErrorStr(vas));
         err = AVERROR(EIO);
         goto fail;
     }
@@ -2255,14 +2278,16 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
 
 #if VA_CHECK_VERSION(1, 9, 0)
     // check vaSyncBuffer function
-    vas = vaSyncBuffer(ctx->hwctx->display, VA_INVALID_ID, 0);
-    if (vas != VA_STATUS_ERROR_UNIMPLEMENTED) {
-        base_ctx->async_encode = 1;
-        base_ctx->encode_fifo = av_fifo_alloc2(base_ctx->async_depth,
-                                               sizeof(VAAPIEncodePicture*),
-                                               0);
-        if (!base_ctx->encode_fifo)
-            return AVERROR(ENOMEM);
+    if (vaf->vaSyncBuffer) {
+        vas = vaf->vaSyncBuffer(ctx->hwctx->display, VA_INVALID_ID, 0);
+        if (vas != VA_STATUS_ERROR_UNIMPLEMENTED) {
+            base_ctx->async_encode = 1;
+            base_ctx->encode_fifo = av_fifo_alloc2(base_ctx->async_depth,
+                                                sizeof(VAAPIEncodePicture*),
+                                                0);
+            if (!base_ctx->encode_fifo)
+                return AVERROR(ENOMEM);
+        }
     }
 #endif
 
@@ -2291,14 +2316,14 @@ av_cold int ff_vaapi_encode_close(AVCodecContext *avctx)
     ff_refstruct_pool_uninit(&ctx->output_buffer_pool);
 
     if (ctx->va_context != VA_INVALID_ID) {
-        if (ctx->hwctx)
-            vaDestroyContext(ctx->hwctx->display, ctx->va_context);
+        if (ctx->hwctx && ctx->hwctx->funcs)
+            ctx->hwctx->funcs->vaDestroyContext(ctx->hwctx->display, ctx->va_context);
         ctx->va_context = VA_INVALID_ID;
     }
 
     if (ctx->va_config != VA_INVALID_ID) {
-        if (ctx->hwctx)
-            vaDestroyConfig(ctx->hwctx->display, ctx->va_config);
+        if (ctx->hwctx && ctx->hwctx->funcs)
+            ctx->hwctx->funcs->vaDestroyConfig(ctx->hwctx->display, ctx->va_config);
         ctx->va_config = VA_INVALID_ID;
     }
 
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index fb87b68bec..6d4ce630ce 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -868,6 +868,7 @@ static int vaapi_encode_h264_init_slice_params(AVCodecContext *avctx,
 static av_cold int vaapi_encode_h264_configure(AVCodecContext *avctx)
 {
     VAAPIEncodeContext      *ctx = avctx->priv_data;
+    VAAPIDynLoadFunctions   *vaf = ctx->hwctx->funcs;
     VAAPIEncodeH264Context *priv = avctx->priv_data;
     int err;
 
@@ -919,7 +920,7 @@ static av_cold int vaapi_encode_h264_configure(AVCodecContext *avctx)
                vaapi_encode_h264_sei_identifier_uuid,
                sizeof(priv->sei_identifier.uuid_iso_iec_11578));
 
-        driver = vaQueryVendorString(ctx->hwctx->display);
+        driver = vaf->vaQueryVendorString(ctx->hwctx->display);
         if (!driver)
             driver = "unknown driver";
 
diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c
index 2283bcc0b4..7c624f99a9 100644
--- a/libavcodec/vaapi_encode_h265.c
+++ b/libavcodec/vaapi_encode_h265.c
@@ -899,6 +899,8 @@ static int vaapi_encode_h265_init_slice_params(AVCodecContext *avctx,
 static av_cold int vaapi_encode_h265_get_encoder_caps(AVCodecContext *avctx)
 {
     FFHWBaseEncodeContext *base_ctx = avctx->priv_data;
+    VAAPIEncodeContext      *ctx = avctx->priv_data;
+    VAAPIDynLoadFunctions *vaf = ctx->hwctx->funcs;
     VAAPIEncodeH265Context    *priv = avctx->priv_data;
 
 #if VA_CHECK_VERSION(1, 13, 0)
@@ -909,7 +911,7 @@ static av_cold int vaapi_encode_h265_get_encoder_caps(AVCodecContext *avctx)
         VAStatus vas;
 
         attr.type = VAConfigAttribEncHEVCFeatures;
-        vas = vaGetConfigAttributes(ctx->hwctx->display, ctx->va_profile,
+        vas = vaf->vaGetConfigAttributes(ctx->hwctx->display, ctx->va_profile,
                                     ctx->va_entrypoint, &attr, 1);
         if (vas != VA_STATUS_SUCCESS) {
             av_log(avctx, AV_LOG_ERROR, "Failed to query encoder "
@@ -923,7 +925,7 @@ static av_cold int vaapi_encode_h265_get_encoder_caps(AVCodecContext *avctx)
         }
 
         attr.type = VAConfigAttribEncHEVCBlockSizes;
-        vas = vaGetConfigAttributes(ctx->hwctx->display, ctx->va_profile,
+        vas = vaf->vaGetConfigAttributes(ctx->hwctx->display, ctx->va_profile,
                                     ctx->va_entrypoint, &attr, 1);
         if (vas != VA_STATUS_SUCCESS) {
             av_log(avctx, AV_LOG_ERROR, "Failed to query encoder "
diff --git a/libavutil/hwcontext_vaapi.c b/libavutil/hwcontext_vaapi.c
index 95aa38d9d2..13451e8ad7 100644
--- a/libavutil/hwcontext_vaapi.c
+++ b/libavutil/hwcontext_vaapi.c
@@ -48,6 +48,7 @@ typedef HRESULT (WINAPI *PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory)
 #   include <unistd.h>
 #endif
 
+#include <dlfcn.h>
 
 #include "avassert.h"
 #include "buffer.h"
@@ -60,6 +61,128 @@ typedef HRESULT (WINAPI *PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory)
 #include "pixdesc.h"
 #include "pixfmt.h"
 
+////////////////////////////////////////////////////////////
+///             dynamic load functions
+////////////////////////////////////////////////////////////
+
+#define LOAD_SYMBOL(name) do { \
+    funcs->name = dlsym(funcs->handle_va, #name); \
+    if (!funcs->name) { \
+        av_log(NULL, AV_LOG_ERROR, "Failed to load %s\n", #name); \
+        goto fail; \
+    } \
+} while(0)
+
+static void vaapi_free_functions(VAAPIDynLoadFunctions *funcs)
+{
+    if (!funcs)
+        return;
+
+    if (funcs->handle_va_x11)
+        dlclose(funcs->handle_va_x11);
+    if (funcs->handle_va_drm)
+        dlclose(funcs->handle_va_drm);
+    if (funcs->handle_va)
+        dlclose(funcs->handle_va);
+    av_free(funcs);
+}
+
+static VAAPIDynLoadFunctions *vaapi_load_functions(void)
+{
+    VAAPIDynLoadFunctions *funcs = av_mallocz(sizeof(*funcs));
+    if (!funcs)
+        return NULL;
+
+    // Load libva.so
+    funcs->handle_va = dlopen("libva.so.2", RTLD_NOW | RTLD_LOCAL);
+    if (!funcs->handle_va) {
+        av_log(NULL, AV_LOG_ERROR, "Failed to load libva: %s\n", dlerror());
+        goto fail;
+    }
+
+    // Load core functions
+    LOAD_SYMBOL(vaInitialize);
+    LOAD_SYMBOL(vaTerminate);
+    LOAD_SYMBOL(vaCreateConfig);
+    LOAD_SYMBOL(vaDestroyConfig);
+    LOAD_SYMBOL(vaCreateContext);
+    LOAD_SYMBOL(vaDestroyContext);
+    LOAD_SYMBOL(vaCreateBuffer);
+    LOAD_SYMBOL(vaDestroyBuffer);
+    LOAD_SYMBOL(vaMapBuffer);
+    LOAD_SYMBOL(vaUnmapBuffer);
+    LOAD_SYMBOL(vaSyncSurface);
+    LOAD_SYMBOL(vaGetConfigAttributes);
+    LOAD_SYMBOL(vaCreateSurfaces);
+    LOAD_SYMBOL(vaDestroySurfaces);
+    LOAD_SYMBOL(vaBeginPicture);
+    LOAD_SYMBOL(vaRenderPicture);
+    LOAD_SYMBOL(vaEndPicture);
+    LOAD_SYMBOL(vaQueryConfigEntrypoints);
+    LOAD_SYMBOL(vaQueryConfigProfiles);
+    LOAD_SYMBOL(vaGetDisplayAttributes);
+    LOAD_SYMBOL(vaErrorStr);
+    LOAD_SYMBOL(vaMaxNumEntrypoints);
+    LOAD_SYMBOL(vaMaxNumProfiles);
+    LOAD_SYMBOL(vaQueryVendorString);
+    LOAD_SYMBOL(vaQuerySurfaceAttributes);
+    LOAD_SYMBOL(vaDestroyImage);
+    LOAD_SYMBOL(vaDeriveImage);
+    LOAD_SYMBOL(vaPutImage);
+    LOAD_SYMBOL(vaCreateImage);
+    LOAD_SYMBOL(vaGetImage);
+    LOAD_SYMBOL(vaExportSurfaceHandle);
+    LOAD_SYMBOL(vaReleaseBufferHandle);
+    LOAD_SYMBOL(vaAcquireBufferHandle);
+    LOAD_SYMBOL(vaSetErrorCallback);
+    LOAD_SYMBOL(vaSetInfoCallback);
+    LOAD_SYMBOL(vaSetDriverName);
+    LOAD_SYMBOL(vaEntrypointStr);
+    LOAD_SYMBOL(vaQueryImageFormats);
+    LOAD_SYMBOL(vaMaxNumImageFormats);
+    LOAD_SYMBOL(vaProfileStr);
+    
+    // Load libva-x11.so
+    funcs->handle_va_x11 = dlopen("libva-x11.so.2", RTLD_NOW | RTLD_LOCAL);
+    if (!funcs->handle_va_x11) {
+        av_log(NULL, AV_LOG_ERROR, "Failed to load libva-x11: %s\n", dlerror());
+        goto fail;
+    }
+
+    funcs->vaGetDisplay = dlsym(funcs->handle_va_x11, "vaGetDisplay");
+    if (!funcs->vaGetDisplay) {
+        av_log(NULL, AV_LOG_ERROR, "Failed to load vaGetDisplay\n");
+        goto fail;
+    }
+
+    // Load libva-drm.so
+    funcs->handle_va_drm = dlopen("libva-drm.so.2", RTLD_NOW | RTLD_LOCAL);
+    if (!funcs->handle_va_drm) {
+        av_log(NULL, AV_LOG_ERROR, "Failed to load libva-drm: %s\n", dlerror());
+        goto fail;
+    }
+
+    funcs->vaGetDisplayDRM = dlsym(funcs->handle_va_drm, "vaGetDisplayDRM");
+    if (!funcs->vaGetDisplayDRM) {
+        av_log(NULL, AV_LOG_ERROR, "Failed to load vaGetDisplayDRM\n");
+        goto fail;
+    }
+
+    // Optional functions
+    funcs->vaSyncBuffer = dlsym(funcs->handle_va, "vaSyncBuffer");
+    av_log(NULL, AV_LOG_DEBUG, "vaSyncBuffer:%p.\n", funcs->vaSyncBuffer);
+
+    return funcs;
+
+fail:
+    vaapi_free_functions(funcs);
+    return NULL;
+}
+
+////////////////////////////////////////////////////////////
+///             VAAPI API end
+////////////////////////////////////////////////////////////
+
 
 typedef struct VAAPIDevicePriv {
 #if HAVE_VAAPI_X11
@@ -236,6 +359,7 @@ static int vaapi_frames_get_constraints(AVHWDeviceContext *hwdev,
 {
     VAAPIDeviceContext *ctx = hwdev->hwctx;
     AVVAAPIDeviceContext *hwctx = &ctx->p;
+    VAAPIDynLoadFunctions *vaf = hwctx->funcs;
     const AVVAAPIHWConfig *config = hwconfig;
     VASurfaceAttrib *attr_list = NULL;
     VAStatus vas;
@@ -246,11 +370,11 @@ static int vaapi_frames_get_constraints(AVHWDeviceContext *hwdev,
     if (config &&
         !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES)) {
         attr_count = 0;
-        vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
+        vas = vaf->vaQuerySurfaceAttributes(hwctx->display, config->config_id,
                                        0, &attr_count);
         if (vas != VA_STATUS_SUCCESS) {
             av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
-                   "%d (%s).\n", vas, vaErrorStr(vas));
+                   "%d (%s).\n", vas, vaf->vaErrorStr(vas));
             err = AVERROR(ENOSYS);
             goto fail;
         }
@@ -261,11 +385,11 @@ static int vaapi_frames_get_constraints(AVHWDeviceContext *hwdev,
             goto fail;
         }
 
-        vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
+        vas = vaf->vaQuerySurfaceAttributes(hwctx->display, config->config_id,
                                        attr_list, &attr_count);
         if (vas != VA_STATUS_SUCCESS) {
             av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
-                   "%d (%s).\n", vas, vaErrorStr(vas));
+                   "%d (%s).\n", vas, vaf->vaErrorStr(vas));
             err = AVERROR(ENOSYS);
             goto fail;
         }
@@ -396,6 +520,7 @@ static int vaapi_device_init(AVHWDeviceContext *hwdev)
 {
     VAAPIDeviceContext *ctx = hwdev->hwctx;
     AVVAAPIDeviceContext *hwctx = &ctx->p;
+    VAAPIDynLoadFunctions *vaf = hwctx->funcs;
     VAImageFormat *image_list = NULL;
     VAStatus vas;
     const char *vendor_string;
@@ -403,7 +528,7 @@ static int vaapi_device_init(AVHWDeviceContext *hwdev)
     enum AVPixelFormat pix_fmt;
     unsigned int fourcc;
 
-    image_count = vaMaxNumImageFormats(hwctx->display);
+    image_count = vaf->vaMaxNumImageFormats(hwctx->display);
     if (image_count <= 0) {
         err = AVERROR(EIO);
         goto fail;
@@ -413,7 +538,7 @@ static int vaapi_device_init(AVHWDeviceContext *hwdev)
         err = AVERROR(ENOMEM);
         goto fail;
     }
-    vas = vaQueryImageFormats(hwctx->display, image_list, &image_count);
+    vas = vaf->vaQueryImageFormats(hwctx->display, image_list, &image_count);
     if (vas != VA_STATUS_SUCCESS) {
         err = AVERROR(EIO);
         goto fail;
@@ -440,7 +565,7 @@ static int vaapi_device_init(AVHWDeviceContext *hwdev)
         }
     }
 
-    vendor_string = vaQueryVendorString(hwctx->display);
+    vendor_string = vaf->vaQueryVendorString(hwctx->display);
     if (vendor_string)
         av_log(hwdev, AV_LOG_VERBOSE, "VAAPI driver: %s.\n", vendor_string);
 
@@ -493,15 +618,16 @@ static void vaapi_buffer_free(void *opaque, uint8_t *data)
 {
     AVHWFramesContext     *hwfc = opaque;
     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
+    VAAPIDynLoadFunctions *vaf = hwctx->funcs;
     VASurfaceID surface_id;
     VAStatus vas;
 
     surface_id = (VASurfaceID)(uintptr_t)data;
 
-    vas = vaDestroySurfaces(hwctx->display, &surface_id, 1);
+    vas = vaf->vaDestroySurfaces(hwctx->display, &surface_id, 1);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(hwfc, AV_LOG_ERROR, "Failed to destroy surface %#x: "
-               "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
+               "%d (%s).\n", surface_id, vas, vaf->vaErrorStr(vas));
     }
 }
 
@@ -511,6 +637,7 @@ static AVBufferRef *vaapi_pool_alloc(void *opaque, size_t size)
     VAAPIFramesContext     *ctx = hwfc->hwctx;
     AVVAAPIFramesContext  *avfc = &ctx->p;
     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
+    VAAPIDynLoadFunctions *vaf = hwctx->funcs;
     VASurfaceID surface_id;
     VAStatus vas;
     AVBufferRef *ref;
@@ -519,13 +646,13 @@ static AVBufferRef *vaapi_pool_alloc(void *opaque, size_t size)
         avfc->nb_surfaces >= hwfc->initial_pool_size)
         return NULL;
 
-    vas = vaCreateSurfaces(hwctx->display, ctx->rt_format,
+    vas = vaf->vaCreateSurfaces(hwctx->display, ctx->rt_format,
                            hwfc->width, hwfc->height,
                            &surface_id, 1,
                            ctx->attributes, ctx->nb_attributes);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(hwfc, AV_LOG_ERROR, "Failed to create surface: "
-               "%d (%s).\n", vas, vaErrorStr(vas));
+               "%d (%s).\n", vas, vaf->vaErrorStr(vas));
         return NULL;
     }
     av_log(hwfc, AV_LOG_DEBUG, "Created surface %#x.\n", surface_id);
@@ -534,7 +661,7 @@ static AVBufferRef *vaapi_pool_alloc(void *opaque, size_t size)
                            sizeof(surface_id), &vaapi_buffer_free,
                            hwfc, AV_BUFFER_FLAG_READONLY);
     if (!ref) {
-        vaDestroySurfaces(hwctx->display, &surface_id, 1);
+        vaf->vaDestroySurfaces(hwctx->display, &surface_id, 1);
         return NULL;
     }
 
@@ -554,6 +681,7 @@ static int vaapi_frames_init(AVHWFramesContext *hwfc)
     VAAPIFramesContext     *ctx = hwfc->hwctx;
     AVVAAPIFramesContext  *avfc = &ctx->p;
     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
+    VAAPIDynLoadFunctions *vaf = hwctx->funcs;
     const VAAPIFormatDescriptor *desc;
     VAImageFormat *expected_format;
     AVBufferRef *test_surface = NULL;
@@ -669,7 +797,7 @@ static int vaapi_frames_init(AVHWFramesContext *hwfc)
     err = vaapi_get_image_format(hwfc->device_ctx,
                                  hwfc->sw_format, &expected_format);
     if (err == 0) {
-        vas = vaDeriveImage(hwctx->display, test_surface_id, &test_image);
+        vas = vaf->vaDeriveImage(hwctx->display, test_surface_id, &test_image);
         if (vas == VA_STATUS_SUCCESS) {
             if (expected_format->fourcc == test_image.format.fourcc) {
                 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping possible.\n");
@@ -680,11 +808,11 @@ static int vaapi_frames_init(AVHWFramesContext *hwfc)
                        "expected format %08x.\n",
                        expected_format->fourcc, test_image.format.fourcc);
             }
-            vaDestroyImage(hwctx->display, test_image.image_id);
+            vaf->vaDestroyImage(hwctx->display, test_image.image_id);
         } else {
             av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
                    "deriving image does not work: "
-                   "%d (%s).\n", vas, vaErrorStr(vas));
+                   "%d (%s).\n", vas, vaf->vaErrorStr(vas));
         }
     } else {
         av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
@@ -765,33 +893,34 @@ static void vaapi_unmap_frame(AVHWFramesContext *hwfc,
 {
     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
     VAAPIMapping           *map = hwmap->priv;
+    VAAPIDynLoadFunctions *vaf = hwctx->funcs;
     VASurfaceID surface_id;
     VAStatus vas;
 
     surface_id = (VASurfaceID)(uintptr_t)hwmap->source->data[3];
     av_log(hwfc, AV_LOG_DEBUG, "Unmap surface %#x.\n", surface_id);
 
-    vas = vaUnmapBuffer(hwctx->display, map->image.buf);
+    vas = vaf->vaUnmapBuffer(hwctx->display, map->image.buf);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(hwfc, AV_LOG_ERROR, "Failed to unmap image from surface "
-               "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
+               "%#x: %d (%s).\n", surface_id, vas, vaf->vaErrorStr(vas));
     }
 
     if ((map->flags & AV_HWFRAME_MAP_WRITE) &&
         !(map->flags & AV_HWFRAME_MAP_DIRECT)) {
-        vas = vaPutImage(hwctx->display, surface_id, map->image.image_id,
+        vas = vaf->vaPutImage(hwctx->display, surface_id, map->image.image_id,
                          0, 0, hwfc->width, hwfc->height,
                          0, 0, hwfc->width, hwfc->height);
         if (vas != VA_STATUS_SUCCESS) {
             av_log(hwfc, AV_LOG_ERROR, "Failed to write image to surface "
-                   "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
+                   "%#x: %d (%s).\n", surface_id, vas, vaf->vaErrorStr(vas));
         }
     }
 
-    vas = vaDestroyImage(hwctx->display, map->image.image_id);
+    vas = vaf->vaDestroyImage(hwctx->display, map->image.image_id);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image from surface "
-               "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
+               "%#x: %d (%s).\n", surface_id, vas, vaf->vaErrorStr(vas));
     }
 
     av_free(map);
@@ -801,6 +930,7 @@ static int vaapi_map_frame(AVHWFramesContext *hwfc,
                            AVFrame *dst, const AVFrame *src, int flags)
 {
     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
+    VAAPIDynLoadFunctions *vaf = hwctx->funcs;
     VAAPIFramesContext *ctx = hwfc->hwctx;
     VASurfaceID surface_id;
     const VAAPIFormatDescriptor *desc;
@@ -839,10 +969,10 @@ static int vaapi_map_frame(AVHWFramesContext *hwfc,
     map->flags = flags;
     map->image.image_id = VA_INVALID_ID;
 
-    vas = vaSyncSurface(hwctx->display, surface_id);
+    vas = vaf->vaSyncSurface(hwctx->display, surface_id);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(hwfc, AV_LOG_ERROR, "Failed to sync surface "
-               "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
+               "%#x: %d (%s).\n", surface_id, vas, vaf->vaErrorStr(vas));
         err = AVERROR(EIO);
         goto fail;
     }
@@ -856,11 +986,11 @@ static int vaapi_map_frame(AVHWFramesContext *hwfc,
     // prefer not to be given direct-mapped memory if they request read access.
     if (ctx->derive_works && dst->format == hwfc->sw_format &&
         ((flags & AV_HWFRAME_MAP_DIRECT) || !(flags & AV_HWFRAME_MAP_READ))) {
-        vas = vaDeriveImage(hwctx->display, surface_id, &map->image);
+        vas = vaf->vaDeriveImage(hwctx->display, surface_id, &map->image);
         if (vas != VA_STATUS_SUCCESS) {
             av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
                    "surface %#x: %d (%s).\n",
-                   surface_id, vas, vaErrorStr(vas));
+                   surface_id, vas, vaf->vaErrorStr(vas));
             err = AVERROR(EIO);
             goto fail;
         }
@@ -873,41 +1003,32 @@ static int vaapi_map_frame(AVHWFramesContext *hwfc,
         }
         map->flags |= AV_HWFRAME_MAP_DIRECT;
     } else {
-        vas = vaCreateImage(hwctx->display, image_format,
+        vas = vaf->vaCreateImage(hwctx->display, image_format,
                             hwfc->width, hwfc->height, &map->image);
         if (vas != VA_STATUS_SUCCESS) {
             av_log(hwfc, AV_LOG_ERROR, "Failed to create image for "
                    "surface %#x: %d (%s).\n",
-                   surface_id, vas, vaErrorStr(vas));
+                   surface_id, vas, vaf->vaErrorStr(vas));
             err = AVERROR(EIO);
             goto fail;
         }
         if (!(flags & AV_HWFRAME_MAP_OVERWRITE)) {
-            vas = vaGetImage(hwctx->display, surface_id, 0, 0,
+            vas = vaf->vaGetImage(hwctx->display, surface_id, 0, 0,
                              hwfc->width, hwfc->height, map->image.image_id);
             if (vas != VA_STATUS_SUCCESS) {
                 av_log(hwfc, AV_LOG_ERROR, "Failed to read image from "
                        "surface %#x: %d (%s).\n",
-                       surface_id, vas, vaErrorStr(vas));
+                       surface_id, vas, vaf->vaErrorStr(vas));
                 err = AVERROR(EIO);
                 goto fail;
             }
         }
     }
 
-#if VA_CHECK_VERSION(1, 21, 0)
-    if (flags & AV_HWFRAME_MAP_READ)
-        vaflags |= VA_MAPBUFFER_FLAG_READ;
-    if (flags & AV_HWFRAME_MAP_WRITE)
-        vaflags |= VA_MAPBUFFER_FLAG_WRITE;
-    // On drivers not implementing vaMapBuffer2 libva calls vaMapBuffer instead.
-    vas = vaMapBuffer2(hwctx->display, map->image.buf, &address, vaflags);
-#else
-    vas = vaMapBuffer(hwctx->display, map->image.buf, &address);
-#endif
+    vas = vaf->vaMapBuffer(hwctx->display, map->image.buf, &address);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(hwfc, AV_LOG_ERROR, "Failed to map image from surface "
-               "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
+               "%#x: %d (%s).\n", surface_id, vas, vaf->vaErrorStr(vas));
         err = AVERROR(EIO);
         goto fail;
     }
@@ -936,9 +1057,9 @@ static int vaapi_map_frame(AVHWFramesContext *hwfc,
 fail:
     if (map) {
         if (address)
-            vaUnmapBuffer(hwctx->display, map->image.buf);
+            vaf->vaUnmapBuffer(hwctx->display, map->image.buf);
         if (map->image.image_id != VA_INVALID_ID)
-            vaDestroyImage(hwctx->display, map->image.image_id);
+            vaf->vaDestroyImage(hwctx->display, map->image.image_id);
         av_free(map);
     }
     return err;
@@ -1080,12 +1201,12 @@ static void vaapi_unmap_from_drm(AVHWFramesContext *dst_fc,
                                  HWMapDescriptor *hwmap)
 {
     AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
-
+    VAAPIDynLoadFunctions *vaf = dst_dev->funcs;
     VASurfaceID surface_id = (VASurfaceID)(uintptr_t)hwmap->priv;
 
     av_log(dst_fc, AV_LOG_DEBUG, "Destroy surface %#x.\n", surface_id);
 
-    vaDestroySurfaces(dst_dev->display, &surface_id, 1);
+    vaf->vaDestroySurfaces(dst_dev->display, &surface_id, 1);
 }
 
 static int vaapi_map_from_drm(AVHWFramesContext *src_fc, AVFrame *dst,
@@ -1100,6 +1221,7 @@ static int vaapi_map_from_drm(AVHWFramesContext *src_fc, AVFrame *dst,
     AVHWFramesContext      *dst_fc =
         (AVHWFramesContext*)dst->hw_frames_ctx->data;
     AVVAAPIDeviceContext  *dst_dev = dst_fc->device_ctx->hwctx;
+    VAAPIDynLoadFunctions *vaf = dst_dev->funcs;
     const AVDRMFrameDescriptor *desc;
     const VAAPIFormatDescriptor *format_desc;
     VASurfaceID surface_id;
@@ -1216,7 +1338,7 @@ static int vaapi_map_from_drm(AVHWFramesContext *src_fc, AVFrame *dst,
          * Gallium seem to do the correct error checks, so lets just try the
          * PRIME_2 import first.
          */
-        vas = vaCreateSurfaces(dst_dev->display, format_desc->rt_format,
+        vas = vaf->vaCreateSurfaces(dst_dev->display, format_desc->rt_format,
                                src->width, src->height, &surface_id, 1,
                                prime_attrs, FF_ARRAY_ELEMS(prime_attrs));
         if (vas != VA_STATUS_SUCCESS)
@@ -1267,7 +1389,7 @@ static int vaapi_map_from_drm(AVHWFramesContext *src_fc, AVFrame *dst,
             FFSWAP(uint32_t, buffer_desc.offsets[1], buffer_desc.offsets[2]);
         }
 
-        vas = vaCreateSurfaces(dst_dev->display, format_desc->rt_format,
+        vas = vaf->vaCreateSurfaces(dst_dev->display, format_desc->rt_format,
                                src->width, src->height,
                                &surface_id, 1,
                                buffer_attrs, FF_ARRAY_ELEMS(buffer_attrs));
@@ -1298,14 +1420,14 @@ static int vaapi_map_from_drm(AVHWFramesContext *src_fc, AVFrame *dst,
         FFSWAP(uint32_t, buffer_desc.offsets[1], buffer_desc.offsets[2]);
     }
 
-    vas = vaCreateSurfaces(dst_dev->display, format_desc->rt_format,
+    vas = vaf->vaCreateSurfaces(dst_dev->display, format_desc->rt_format,
                            src->width, src->height,
                            &surface_id, 1,
                            attrs, FF_ARRAY_ELEMS(attrs));
 #endif
     if (vas != VA_STATUS_SUCCESS) {
         av_log(dst_fc, AV_LOG_ERROR, "Failed to create surface from DRM "
-               "object: %d (%s).\n", vas, vaErrorStr(vas));
+               "object: %d (%s).\n", vas, vaf->vaErrorStr(vas));
         return AVERROR(EIO);
     }
     av_log(dst_fc, AV_LOG_DEBUG, "Create surface %#x.\n", surface_id);
@@ -1343,6 +1465,7 @@ static int vaapi_map_to_drm_esh(AVHWFramesContext *hwfc, AVFrame *dst,
                                 const AVFrame *src, int flags)
 {
     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
+    VAAPIDynLoadFunctions *vaf = hwctx->funcs;
     VASurfaceID surface_id;
     VAStatus vas;
     VADRMPRIMESurfaceDescriptor va_desc;
@@ -1356,10 +1479,10 @@ static int vaapi_map_to_drm_esh(AVHWFramesContext *hwfc, AVFrame *dst,
     if (flags & AV_HWFRAME_MAP_READ) {
         export_flags |= VA_EXPORT_SURFACE_READ_ONLY;
 
-        vas = vaSyncSurface(hwctx->display, surface_id);
+        vas = vaf->vaSyncSurface(hwctx->display, surface_id);
         if (vas != VA_STATUS_SUCCESS) {
             av_log(hwfc, AV_LOG_ERROR, "Failed to sync surface "
-                   "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
+                   "%#x: %d (%s).\n", surface_id, vas, vaf->vaErrorStr(vas));
             return AVERROR(EIO);
         }
     }
@@ -1367,14 +1490,14 @@ static int vaapi_map_to_drm_esh(AVHWFramesContext *hwfc, AVFrame *dst,
     if (flags & AV_HWFRAME_MAP_WRITE)
         export_flags |= VA_EXPORT_SURFACE_WRITE_ONLY;
 
-    vas = vaExportSurfaceHandle(hwctx->display, surface_id,
+    vas = vaf->vaExportSurfaceHandle(hwctx->display, surface_id,
                                 VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
                                 export_flags, &va_desc);
     if (vas != VA_STATUS_SUCCESS) {
         if (vas == VA_STATUS_ERROR_UNIMPLEMENTED)
             return AVERROR(ENOSYS);
         av_log(hwfc, AV_LOG_ERROR, "Failed to export surface %#x: "
-               "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
+               "%d (%s).\n", surface_id, vas, vaf->vaErrorStr(vas));
         return AVERROR(EIO);
     }
 
@@ -1437,6 +1560,7 @@ static void vaapi_unmap_to_drm_abh(AVHWFramesContext *hwfc,
                                   HWMapDescriptor *hwmap)
 {
     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
+    VAAPIDynLoadFunctions *vaf = hwctx->funcs;
     VAAPIDRMImageBufferMapping *mapping = hwmap->priv;
     VASurfaceID surface_id;
     VAStatus vas;
@@ -1448,19 +1572,19 @@ static void vaapi_unmap_to_drm_abh(AVHWFramesContext *hwfc,
     // DRM PRIME file descriptors are closed by vaReleaseBufferHandle(),
     // so we shouldn't close them separately.
 
-    vas = vaReleaseBufferHandle(hwctx->display, mapping->image.buf);
+    vas = vaf->vaReleaseBufferHandle(hwctx->display, mapping->image.buf);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(hwfc, AV_LOG_ERROR, "Failed to release buffer "
                "handle of image %#x (derived from surface %#x): "
                "%d (%s).\n", mapping->image.buf, surface_id,
-               vas, vaErrorStr(vas));
+               vas, vaf->vaErrorStr(vas));
     }
 
-    vas = vaDestroyImage(hwctx->display, mapping->image.image_id);
+    vas = vaf->vaDestroyImage(hwctx->display, mapping->image.image_id);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image "
                "derived from surface %#x: %d (%s).\n",
-               surface_id, vas, vaErrorStr(vas));
+               surface_id, vas, vaf->vaErrorStr(vas));
     }
 
     av_free(mapping);
@@ -1470,6 +1594,7 @@ static int vaapi_map_to_drm_abh(AVHWFramesContext *hwfc, AVFrame *dst,
                                 const AVFrame *src, int flags)
 {
     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
+    VAAPIDynLoadFunctions *vaf = hwctx->funcs;
     VAAPIDRMImageBufferMapping *mapping = NULL;
     VASurfaceID surface_id;
     VAStatus vas;
@@ -1483,12 +1608,12 @@ static int vaapi_map_to_drm_abh(AVHWFramesContext *hwfc, AVFrame *dst,
     if (!mapping)
         return AVERROR(ENOMEM);
 
-    vas = vaDeriveImage(hwctx->display, surface_id,
+    vas = vaf->vaDeriveImage(hwctx->display, surface_id,
                         &mapping->image);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
                "surface %#x: %d (%s).\n",
-               surface_id, vas, vaErrorStr(vas));
+               surface_id, vas, vaf->vaErrorStr(vas));
         err = AVERROR(EIO);
         goto fail;
     }
@@ -1543,13 +1668,13 @@ static int vaapi_map_to_drm_abh(AVHWFramesContext *hwfc, AVFrame *dst,
         }
     }
 
-    vas = vaAcquireBufferHandle(hwctx->display, mapping->image.buf,
+    vas = vaf->vaAcquireBufferHandle(hwctx->display, mapping->image.buf,
                                 &mapping->buffer_info);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(hwfc, AV_LOG_ERROR, "Failed to get buffer "
                "handle from image %#x (derived from surface %#x): "
                "%d (%s).\n", mapping->image.buf, surface_id,
-               vas, vaErrorStr(vas));
+               vas, vaf->vaErrorStr(vas));
         err = AVERROR(EIO);
         goto fail_derived;
     }
@@ -1578,9 +1703,9 @@ static int vaapi_map_to_drm_abh(AVHWFramesContext *hwfc, AVFrame *dst,
     return 0;
 
 fail_mapped:
-    vaReleaseBufferHandle(hwctx->display, mapping->image.buf);
+    vaf->vaReleaseBufferHandle(hwctx->display, mapping->image.buf);
 fail_derived:
-    vaDestroyImage(hwctx->display, mapping->image.image_id);
+    vaf->vaDestroyImage(hwctx->display, mapping->image.image_id);
 fail:
     av_freep(&mapping);
     return err;
@@ -1634,9 +1759,15 @@ static void vaapi_device_free(AVHWDeviceContext *ctx)
 {
     AVVAAPIDeviceContext *hwctx = ctx->hwctx;
     VAAPIDevicePriv      *priv  = ctx->user_opaque;
+    VAAPIDynLoadFunctions *vaf = hwctx->funcs;
 
-    if (hwctx->display)
-        vaTerminate(hwctx->display);
+    if (hwctx && hwctx->display && vaf && vaf->vaTerminate)
+        vaf->vaTerminate(hwctx->display);
+
+    if (hwctx && hwctx->funcs) {
+        vaapi_free_functions(hwctx->funcs);
+        hwctx->funcs = NULL;
+    }
 
 #if HAVE_VAAPI_X11
     if (priv->x11_display)
@@ -1669,20 +1800,21 @@ static int vaapi_device_connect(AVHWDeviceContext *ctx,
                                 VADisplay display)
 {
     AVVAAPIDeviceContext *hwctx = ctx->hwctx;
+    VAAPIDynLoadFunctions *vaf = hwctx->funcs;
     int major, minor;
     VAStatus vas;
 
 #if CONFIG_VAAPI_1
-    vaSetErrorCallback(display, &vaapi_device_log_error, ctx);
-    vaSetInfoCallback (display, &vaapi_device_log_info,  ctx);
+    vaf->vaSetErrorCallback(display, &vaapi_device_log_error, ctx);
+    vaf->vaSetInfoCallback (display, &vaapi_device_log_info,  ctx);
 #endif
 
     hwctx->display = display;
 
-    vas = vaInitialize(display, &major, &minor);
+    vas = vaf->vaInitialize(display, &major, &minor);
     if (vas != VA_STATUS_SUCCESS) {
         av_log(ctx, AV_LOG_ERROR, "Failed to initialise VAAPI "
-               "connection: %d (%s).\n", vas, vaErrorStr(vas));
+               "connection: %d (%s).\n", vas, vaf->vaErrorStr(vas));
         return AVERROR(EIO);
     }
     av_log(ctx, AV_LOG_VERBOSE, "Initialised VAAPI connection: "
@@ -1698,6 +1830,16 @@ static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device,
     VADisplay display = NULL;
     const AVDictionaryEntry *ent;
     int try_drm, try_x11, try_win32, try_all;
+    VAAPIDeviceContext *hwctx = ctx->hwctx;
+    VAAPIDynLoadFunctions *vaf;
+
+    hwctx->p.funcs = vaapi_load_functions();
+    if (!hwctx->p.funcs) {
+        av_log(NULL, AV_LOG_ERROR, "Failed to load libva: %s\n", dlerror());
+        return AVERROR_EXTERNAL;
+    }
+
+    vaf = hwctx->p.funcs;
 
     priv = av_mallocz(sizeof(*priv));
     if (!priv)
@@ -1843,7 +1985,7 @@ static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device,
                 break;
         }
 
-        display = vaGetDisplayDRM(priv->drm_fd);
+        display = vaf->vaGetDisplayDRM(priv->drm_fd);
         if (!display) {
             av_log(ctx, AV_LOG_VERBOSE, "Cannot open a VA display "
                    "from DRM device %s.\n", device);
@@ -1861,7 +2003,7 @@ static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device,
             av_log(ctx, AV_LOG_VERBOSE, "Cannot open X11 display "
                    "%s.\n", XDisplayName(device));
         } else {
-            display = vaGetDisplay(priv->x11_display);
+            display = vaf->vaGetDisplay(priv->x11_display);
             if (!display) {
                 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
                        "from X11 display %s.\n", XDisplayName(device));
@@ -1950,11 +2092,11 @@ static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device,
     if (ent) {
 #if VA_CHECK_VERSION(0, 38, 0)
         VAStatus vas;
-        vas = vaSetDriverName(display, ent->value);
+        vas = vaf->vaSetDriverName(display, ent->value);
         if (vas != VA_STATUS_SUCCESS) {
             av_log(ctx, AV_LOG_ERROR, "Failed to set driver name to "
-                   "%s: %d (%s).\n", ent->value, vas, vaErrorStr(vas));
-            vaTerminate(display);
+                   "%s: %d (%s).\n", ent->value, vas, vaf->vaErrorStr(vas));
+            vaf->vaTerminate(display);
             return AVERROR_EXTERNAL;
         }
 #else
@@ -1970,6 +2112,8 @@ static int vaapi_device_derive(AVHWDeviceContext *ctx,
                                AVHWDeviceContext *src_ctx,
                                AVDictionary *opts, int flags)
 {
+    VAAPIDeviceContext *hwctx = ctx->hwctx;
+    VAAPIDynLoadFunctions *vaf = hwctx->p.funcs;
 #if HAVE_VAAPI_DRM
     if (src_ctx->type == AV_HWDEVICE_TYPE_DRM) {
         AVDRMDeviceContext *src_hwctx = src_ctx->hwctx;
@@ -2041,7 +2185,7 @@ static int vaapi_device_derive(AVHWDeviceContext *ctx,
         ctx->user_opaque = priv;
         ctx->free        = &vaapi_device_free;
 
-        display = vaGetDisplayDRM(fd);
+        display = vaf->vaGetDisplayDRM(fd);
         if (!display) {
             av_log(ctx, AV_LOG_ERROR, "Failed to open a VA display from "
                    "DRM device.\n");
diff --git a/libavutil/hwcontext_vaapi.h b/libavutil/hwcontext_vaapi.h
index 0b2e071cb3..2c51223d45 100644
--- a/libavutil/hwcontext_vaapi.h
+++ b/libavutil/hwcontext_vaapi.h
@@ -20,6 +20,100 @@
 #define AVUTIL_HWCONTEXT_VAAPI_H
 
 #include <va/va.h>
+#include <va/va_x11.h>
+#include <va/va_drm.h>
+#include <va/va_str.h>
+
+
+////////////////////////////////////////////////////////////
+///             VAAPI dynamic load functions start
+////////////////////////////////////////////////////////////
+
+typedef struct VAAPIDynLoadFunctions {
+    // Core VA functions
+    VAStatus (*vaInitialize)(VADisplay dpy, int *major_version, int *minor_version);
+    VAStatus (*vaTerminate)(VADisplay dpy);
+    VAStatus (*vaCreateConfig)(VADisplay dpy, VAProfile profile, VAEntrypoint entrypoint,
+                              VAConfigAttrib *attrib_list, int num_attribs, VAConfigID *config_id);
+    VAStatus (*vaDestroyConfig)(VADisplay dpy, VAConfigID config_id);
+    VAStatus (*vaCreateContext)(VADisplay dpy, VAConfigID config_id, int picture_width,
+                               int picture_height, int flag, VASurfaceID *render_targets,
+                               int num_render_targets, VAContextID *context);
+    VAStatus (*vaDestroyContext)(VADisplay dpy, VAContextID context);
+    VAStatus (*vaCreateBuffer)(VADisplay dpy, VAContextID context, VABufferType type,
+                              unsigned int size, unsigned int num_elements, void *data,
+                              VABufferID *buf_id);
+    VAStatus (*vaDestroyBuffer)(VADisplay dpy, VABufferID buf_id);
+    VAStatus (*vaMapBuffer)(VADisplay dpy, VABufferID buf_id, void **pbuf);
+    VAStatus (*vaUnmapBuffer)(VADisplay dpy, VABufferID buf_id);
+    VAStatus (*vaSyncSurface)(VADisplay dpy, VASurfaceID render_target);
+    VAStatus (*vaGetConfigAttributes)(VADisplay dpy, VAProfile profile,
+                                     VAEntrypoint entrypoint, VAConfigAttrib *attrib_list,
+                                     int num_attribs);
+    VAStatus (*vaCreateSurfaces)(VADisplay dpy, unsigned int format,
+                                unsigned int width, unsigned int height,
+                                VASurfaceID *surfaces, unsigned int num_surfaces,
+                                VASurfaceAttrib *attrib_list, unsigned int num_attribs);
+    VAStatus (*vaDestroySurfaces)(VADisplay dpy, VASurfaceID *surfaces, int num_surfaces);
+    VAStatus (*vaBeginPicture)(VADisplay dpy, VAContextID context, VASurfaceID render_target);
+    VAStatus (*vaRenderPicture)(VADisplay dpy, VAContextID context,
+                               VABufferID *buffers, int num_buffers);
+    VAStatus (*vaEndPicture)(VADisplay dpy, VAContextID context);
+    VAStatus (*vaQueryConfigEntrypoints)(VADisplay dpy, VAProfile profile,
+                                        VAEntrypoint *entrypoint_list, int *num_entrypoints);
+    VAStatus (*vaQueryConfigProfiles)(VADisplay dpy, VAProfile *profile_list, int *num_profiles);
+    VAStatus (*vaGetDisplayAttributes)(VADisplay dpy, VADisplayAttribute *attr_list, int num_attributes);
+    const char *(*vaErrorStr)(VAStatus error_status);
+    int (*vaMaxNumEntrypoints)(VADisplay dpy);
+    int (*vaMaxNumProfiles)(VADisplay dpy);
+    const char *(*vaQueryVendorString)(VADisplay dpy);
+    VAStatus (*vaQuerySurfaceAttributes)(VADisplay dpy, VAConfigID config_id,
+                                        VASurfaceAttrib *attrib_list, int *num_attribs);
+    VAStatus (*vaDestroyImage)(VADisplay dpy, VAImageID image);
+    VAStatus (*vaDeriveImage)(VADisplay dpy, VASurfaceID surface, VAImage *image);
+    VAStatus (*vaPutImage)(VADisplay dpy, VASurfaceID surface, VAImageID image,
+                          int src_x, int src_y, unsigned int src_width, unsigned int src_height,
+                          int dest_x, int dest_y, unsigned int dest_width, unsigned int dest_height);
+    VAStatus (*vaCreateImage)(VADisplay dpy, VAImageFormat *format, int width, int height, VAImage *image);
+    VAStatus (*vaGetImage)(VADisplay dpy, VASurfaceID surface,
+                          int x, int y, unsigned int width, unsigned int height,
+                          VAImageID image);
+    VAStatus (*vaExportSurfaceHandle)(VADisplay dpy, VASurfaceID surface_id,
+                                     uint32_t mem_type, uint32_t flags,
+                                     void *descriptor);
+    VAStatus (*vaReleaseBufferHandle)(VADisplay dpy, VABufferID buf_id);
+    VAStatus (*vaAcquireBufferHandle)(VADisplay dpy, VABufferID buf_id, 
+                                     VABufferInfo *buf_info);
+    VAStatus (*vaSetErrorCallback)(VADisplay dpy, VAMessageCallback callback, void *user_context);
+    VAStatus (*vaSetInfoCallback)(VADisplay dpy, VAMessageCallback callback, void *user_context);
+    VAStatus (*vaSetDriverName)(VADisplay dpy, const char *driver_name);
+    const char *(*vaEntrypointStr)(VAEntrypoint entrypoint);
+    VAStatus (*vaQueryImageFormats)(VADisplay dpy, VAImageFormat *format_list, int *num_formats);
+    int (*vaMaxNumImageFormats)(VADisplay dpy);
+    const char *(*vaProfileStr)(VAProfile profile);
+
+
+    // Optional functions
+    VAStatus (*vaSyncBuffer)(VADisplay dpy, VABufferID buf_id, uint64_t timeout_ns);
+
+    // X11 specific functions
+    VADisplay (*vaGetDisplay)(Display *dpy);
+
+    // DRM specific functions
+    VADisplay (*vaGetDisplayDRM)(int fd);
+
+
+
+    // Library handles
+    void *handle_va;
+    void *handle_va_drm;
+    void *handle_va_x11;
+} VAAPIDynLoadFunctions;
+
+
+////////////////////////////////////////////////////////////
+///             VAAPI API end
+////////////////////////////////////////////////////////////
 
 /**
  * @file
@@ -78,6 +172,8 @@ typedef struct AVVAAPIDeviceContext {
      * operations using VAAPI with the same VADisplay.
      */
     unsigned int driver_quirks;
+
+    VAAPIDynLoadFunctions *funcs;
 } AVVAAPIDeviceContext;
 
 /**
-- 
2.34.1

