[vlc-commits] [Git][videolan/vlc][master] 5 commits: avcodec: va: change vlc_va_Get arguments

Jean-Baptiste Kempf (@jbk) gitlab at videolan.org
Mon Jul 12 08:48:30 UTC 2021



Jean-Baptiste Kempf pushed to branch master at VideoLAN / VLC


Commits:
e3b5b5b0 by Thomas Guillem at 2021-07-12T09:52:31+02:00
avcodec: va: change vlc_va_Get arguments

Provide the AVCodecContext and the full AVFrame. It will be used by the
new vaapi va module.

- - - - -
03b9c4bc by Thomas Guillem at 2021-07-12T09:52:31+02:00
avcodec: video: remove unused data

frame->buf[0] must be valid but doesn't have to contain data since the
data will be accessed via frame->data[] directly.

Furthermore, all va modules are only setting frame->data[3], therefore,
frame->data[0] was always NULL.

- - - - -
fe63b86f by Thomas Guillem at 2021-07-12T09:52:31+02:00
avcodec: video: also store the pic reference in opaque_ref

The future vaapi va module will create AVFrame from hwframes_ctx with a
frame->buf[0] set internally by avcodec. If this is the case, don't
override it and use opaque_ref (an user field) to keep the reference on
the VLC picture_t.

- - - - -
906ffbaa by Thomas Guillem at 2021-07-12T09:52:31+02:00
avcodec: video: fix swfmt detection when using a hw_device_ctx

- - - - -
08cbeca2 by Thomas Guillem at 2021-07-12T09:52:31+02:00
avcodec: vaapi: adapt to the hw_device_ctx API

Contrary to all other hardware decoders, vaapi can't work with a
hwaccel_context anymore. It requires a hw_device_ctx or a hw_frames_ctx
to work.

This first implementation use a hw_device_ctx and let avcodec handle its
frames pool. It is also possible to handle hw_frames_ctx ourself to get
more controls.

Note: Most of the code could be reused by other va modules if we decide
to a hw_device_ctx.

Fixes #25707

- - - - -


7 changed files:

- modules/codec/Makefile.am
- modules/codec/avcodec/d3d11va.c
- modules/codec/avcodec/dxva2.c
- modules/codec/avcodec/va.h
- modules/codec/avcodec/vaapi.c
- modules/codec/avcodec/video.c
- modules/hw/vdpau/avcodec.c


Changes:

=====================================
modules/codec/Makefile.am
=====================================
@@ -387,7 +387,7 @@ libvaapi_plugin_la_SOURCES = \
 	codec/avcodec/va_surface.c codec/avcodec/va_surface.h
 libvaapi_plugin_la_CPPFLAGS = $(AM_CPPFLAGS)
 libvaapi_plugin_la_CFLAGS = $(AM_CFLAGS) $(AVCODEC_CFLAGS)
-libvaapi_plugin_la_LIBADD = $(LIBVA_LIBS)
+libvaapi_plugin_la_LIBADD = $(LIBVA_LIBS) $(AVCODEC_LIBS)
 if HAVE_AVCODEC_VAAPI
 codec_LTLIBRARIES += libvaapi_plugin.la
 endif


=====================================
modules/codec/avcodec/d3d11va.c
=====================================
@@ -192,8 +192,10 @@ static picture_context_t* NewSurfacePicContext(vlc_va_t *va, vlc_va_surface_t *v
     return &pic_ctx->ctx.s;
 }
 
-static int Get(vlc_va_t *va, picture_t *pic, uint8_t **data)
+static int Get(vlc_va_t *va, picture_t *pic, AVCodecContext *ctx, AVFrame *frame)
 {
+    (void) ctx;
+
     vlc_va_sys_t *sys = va->sys;
     vlc_va_surface_t *va_surface = va_pool_Get(sys->va_pool);
     if (unlikely(va_surface == NULL))
@@ -204,7 +206,7 @@ static int Get(vlc_va_t *va, picture_t *pic, uint8_t **data)
         va_surface_Release(va_surface);
         return VLC_ENOMEM;
     }
-    data[3] = (uint8_t*)sys->hw_surface[va_surface_GetIndex(va_surface)];
+    frame->data[3] = (uint8_t*)sys->hw_surface[va_surface_GetIndex(va_surface)];
     return VLC_SUCCESS;
 }
 


=====================================
modules/codec/avcodec/dxva2.c
=====================================
@@ -191,8 +191,9 @@ static picture_context_t* NewSurfacePicContext(vlc_va_t *va, vlc_va_surface_t *v
     return &pic_ctx->ctx.s;
 }
 
-static int Get(vlc_va_t *va, picture_t *pic, uint8_t **data)
+static int Get(vlc_va_t *va, picture_t *pic, AVCodecContext *ctx, AVFrame *frame)
 {
+    (void) ctx;
     vlc_va_sys_t *sys = va->sys;
 
     /* Check the device */
@@ -215,7 +216,7 @@ static int Get(vlc_va_t *va, picture_t *pic, uint8_t **data)
         va_surface_Release(va_surface);
         return VLC_ENOITEM;
     }
-    data[3] = (uint8_t*)DXVA2_PICCONTEXT_FROM_PICCTX(pic->context)->ctx.picsys.surface;
+    frame->data[3] = (uint8_t*)DXVA2_PICCONTEXT_FROM_PICCTX(pic->context)->ctx.picsys.surface;
     return VLC_SUCCESS;
 }
 


=====================================
modules/codec/avcodec/va.h
=====================================
@@ -31,7 +31,7 @@ typedef struct vlc_decoder_device vlc_decoder_device;
 typedef struct vlc_video_context vlc_video_context;
 
 struct vlc_va_operations {
-    int (*get)(vlc_va_t *, picture_t *pic, uint8_t **surface);
+    int (*get)(vlc_va_t *, picture_t *pic, AVCodecContext *ctx, AVFrame *frame);
     void (*close)(vlc_va_t *);
 };
 
@@ -87,15 +87,17 @@ vlc_va_t *vlc_va_New(vlc_object_t *obj, AVCodecContext *,
  * AV_PIX_FMT_VAAPI       - VASurfaceID
  *
  * @param pic pointer to VLC picture containing the surface [IN/OUT]
- * @param surface pointer to the AVFrame data[0] and data[3] pointers [OUT]
+ * @param ctx pointer to the current AVCodecContext [IN]
+ * @param frame pointer to the AVFrame [IN]
  *
  * @note This function needs not be reentrant.
  *
  * @return VLC_SUCCESS on success, otherwise an error code.
  */
-static inline int vlc_va_Get(vlc_va_t *va, picture_t *pic, uint8_t **surface)
+static inline int vlc_va_Get(vlc_va_t *va, picture_t *pic, AVCodecContext *ctx,
+                             AVFrame *frame)
 {
-    return va->ops->get(va, pic, surface);
+    return va->ops->get(va, pic, ctx, frame);
 }
 
 /**


=====================================
modules/codec/avcodec/vaapi.c
=====================================
@@ -43,19 +43,20 @@
 #endif
 #include <libavcodec/avcodec.h>
 #include <libavcodec/vaapi.h>
+#include <libavutil/hwcontext_vaapi.h>
 
 #include "avcodec.h"
 #include "va.h"
 #include "../../hw/vaapi/vlc_vaapi.h"
 #include "va_surface.h"
 
-typedef struct
+struct vaapi_vctx
 {
-    struct vaapi_context hw_ctx;
-    vlc_video_context *vctx;
-    va_pool_t *va_pool;
-    VASurfaceID render_targets[MAX_SURFACE_COUNT];
-} vlc_va_sys_t;
+    VADisplay va_dpy;
+    AVBufferRef *hwdev_ref;
+    vlc_mutex_t lock;
+    vlc_cond_t wait;
+};
 
 static int GetVaProfile(const AVCodecContext *ctx, const es_format_t *fmt_in,
                         VAProfile *va_profile, int *vlc_chroma,
@@ -127,15 +128,19 @@ static int GetVaProfile(const AVCodecContext *ctx, const es_format_t *fmt_in,
 
 typedef struct {
     struct vaapi_pic_context ctx;
-    vlc_va_surface_t *va_surface;
+    AVFrame *avframe;
 } vaapi_dec_pic_context;
 
 static void vaapi_dec_pic_context_destroy(picture_context_t *context)
 {
     vaapi_dec_pic_context *pic_ctx = container_of(context, vaapi_dec_pic_context, ctx.s);
-    struct vlc_va_surface_t *va_surface = pic_ctx->va_surface;
+    av_frame_free(&pic_ctx->avframe);
+
+    struct vaapi_vctx *vaapi_vctx =
+        vlc_video_context_GetPrivate(pic_ctx->ctx.s.vctx, VLC_VIDEO_CONTEXT_VAAPI);
+    vlc_cond_signal(&vaapi_vctx->wait);
+
     free(pic_ctx);
-    va_surface_Release(va_surface);
 }
 
 static picture_context_t *vaapi_dec_pic_context_copy(picture_context_t *src)
@@ -144,100 +149,76 @@ static picture_context_t *vaapi_dec_pic_context_copy(picture_context_t *src)
     vaapi_dec_pic_context *pic_ctx = malloc(sizeof(*pic_ctx));
     if (unlikely(pic_ctx == NULL))
         return NULL;
-    *pic_ctx = *src_ctx;
+
+    pic_ctx->ctx = src_ctx->ctx;
+
+    pic_ctx->avframe = av_frame_clone(src_ctx->avframe);
+    if (!pic_ctx->avframe)
+    {
+        free(pic_ctx);
+        return NULL;
+    }
+
     vlc_video_context_Hold(pic_ctx->ctx.s.vctx);
-    va_surface_AddRef(pic_ctx->va_surface);
     return &pic_ctx->ctx.s;
 }
 
-static int Get(vlc_va_t *va, picture_t *pic, uint8_t **data)
+static int Get(vlc_va_t *va, picture_t *pic, AVCodecContext *ctx, AVFrame *frame)
 {
-    vlc_va_sys_t *sys = va->sys;
-    vlc_va_surface_t *va_surface = va_pool_Get(sys->va_pool);
-    if (unlikely(va_surface == NULL))
-        return VLC_ENOITEM;
-    vaapi_dec_pic_context *vaapi_ctx = malloc(sizeof(*vaapi_ctx));
-    if (unlikely(vaapi_ctx == NULL))
+    vlc_video_context *vctx = va->sys;
+    struct vaapi_vctx *vaapi_vctx =
+        vlc_video_context_GetPrivate(vctx, VLC_VIDEO_CONTEXT_VAAPI);
+
+    assert(ctx->hw_frames_ctx);
+
+    int ret;
+    vlc_mutex_lock(&vaapi_vctx->lock);
+    while ((ret = av_hwframe_get_buffer(ctx->hw_frames_ctx, frame, 0)) == AVERROR(ENOMEM))
+        vlc_cond_wait(&vaapi_vctx->wait, &vaapi_vctx->lock);
+    vlc_mutex_unlock(&vaapi_vctx->lock);
+
+    if (ret)
     {
-        va_surface_Release(va_surface);
-        return VLC_ENOMEM;
+        msg_Err(va, "vaapi_va: av_hwframe_get_buffer failed: %d\n", ret);
+        return ret;
     }
-    vaapi_ctx->ctx.s = (picture_context_t) {
-        vaapi_dec_pic_context_destroy, vaapi_dec_pic_context_copy,
-        sys->vctx,
+
+    vaapi_dec_pic_context *vaapi_pic_ctx = malloc(sizeof(*vaapi_pic_ctx));
+    if (unlikely(vaapi_pic_ctx == NULL))
+        return VLC_ENOMEM;
+    vaapi_pic_ctx->ctx.s = (picture_context_t) {
+        vaapi_dec_pic_context_destroy, vaapi_dec_pic_context_copy, vctx,
     };
-    vaapi_ctx->ctx.surface = sys->render_targets[va_surface_GetIndex(va_surface)];
-    vaapi_ctx->ctx.va_dpy = sys->hw_ctx.display;
-    vaapi_ctx->va_surface = va_surface;
-    vlc_vaapi_PicSetContext(pic, &vaapi_ctx->ctx);
-    data[3] = (void *) (uintptr_t) vaapi_ctx->ctx.surface;
+
+    vaapi_pic_ctx->ctx.surface = (uintptr_t) frame->data[3];
+    vaapi_pic_ctx->ctx.va_dpy = vaapi_vctx->va_dpy;
+    vaapi_pic_ctx->avframe = av_frame_clone(frame);
+    vlc_vaapi_PicSetContext(pic, &vaapi_pic_ctx->ctx);
 
     return VLC_SUCCESS;
 }
 
 static void Delete(vlc_va_t *va)
 {
-    vlc_va_sys_t *sys = va->sys;
-
-    vlc_video_context_Release(sys->vctx);
-    va_pool_Close(sys->va_pool);
+    vlc_video_context_Release(va->sys);
 }
 
-static const struct vlc_va_operations ops = { Get, Delete, };
-
-static int VAAPICreateDevice(vlc_va_t *va)
+static void vaapi_ctx_destroy(void *priv)
 {
-    VLC_UNUSED(va);
-    return VLC_SUCCESS;
-}
+    struct vaapi_vctx *vaapi_vctx = priv;
 
-static void VAAPIDestroyDevice(void *opaque)
-{
-    vlc_va_sys_t *sys = opaque;
-    if (sys->hw_ctx.context_id != VA_INVALID_ID)
-        vlc_vaapi_DestroyContext(NULL, sys->hw_ctx.display, sys->hw_ctx.context_id);
-    if (sys->hw_ctx.config_id != VA_INVALID_ID)
-        vlc_vaapi_DestroyConfig(NULL, sys->hw_ctx.display, sys->hw_ctx.config_id);
-    free(sys);
+    av_buffer_unref(&vaapi_vctx->hwdev_ref);
 }
 
-static int VAAPICreateDecoderSurfaces(vlc_va_t *va, int codec_id,
-                                      const video_format_t *fmt,
-                                      size_t count)
-{
-    VLC_UNUSED(codec_id);
-    vlc_va_sys_t *sys = va->sys;
-
-    unsigned va_rt_format;
-    int va_fourcc;
-    vlc_chroma_to_vaapi(fmt->i_chroma, &va_rt_format, &va_fourcc);
-
-    VASurfaceAttrib fourcc_attribs[1] = {
-        {
-            .type = VASurfaceAttribPixelFormat,
-            .flags = VA_SURFACE_ATTRIB_SETTABLE,
-            .value.type    = VAGenericValueTypeInteger,
-            .value.value.i = va_fourcc,
-        }
-    };
-
-    VA_CALL(VLC_OBJECT(va), vaCreateSurfaces, sys->hw_ctx.display, va_rt_format,
-            fmt->i_visible_width, fmt->i_visible_height,
-            sys->render_targets, count,
-            fourcc_attribs, 1);
-
-    return VLC_SUCCESS;
-error:
-    return VLC_EGENERIC;
-}
+static const struct vlc_va_operations ops = { Get, Delete, };
 
-static void VAAPISetupAVCodecContext(void *opaque, AVCodecContext *avctx)
+static const struct vlc_video_context_operations vaapi_ctx_ops =
 {
-    vlc_va_sys_t *sys = opaque;
-    avctx->hwaccel_context = &sys->hw_ctx;
-}
+    .destroy = vaapi_ctx_destroy,
+};
 
-static int Create(vlc_va_t *va, AVCodecContext *ctx, enum AVPixelFormat hwfmt, const AVPixFmtDescriptor *desc,
+static int Create(vlc_va_t *va, AVCodecContext *ctx, enum AVPixelFormat hwfmt,
+                  const AVPixFmtDescriptor *desc,
                   const es_format_t *fmt_in, vlc_decoder_device *dec_device,
                   video_format_t *fmt_out, vlc_video_context **vtcx_out)
 {
@@ -246,77 +227,54 @@ static int Create(vlc_va_t *va, AVCodecContext *ctx, enum AVPixelFormat hwfmt, c
         dec_device->type != VLC_DECODER_DEVICE_VAAPI)
         return VLC_EGENERIC;
 
-    vlc_va_sys_t *sys = malloc(sizeof *sys);
-    if (unlikely(sys == NULL))
-        return VLC_ENOMEM;
-    memset(sys, 0, sizeof (*sys));
-
-    vlc_object_t *o = VLC_OBJECT(va);
-
-    int ret = VLC_EGENERIC;
-
-    VADisplay va_dpy = dec_device->opaque;
-
     VAProfile i_profile;
     unsigned count;
     int i_vlc_chroma;
     if (GetVaProfile(ctx, fmt_in, &i_profile, &i_vlc_chroma, &count) != VLC_SUCCESS)
-        goto error;
+        return VLC_EGENERIC;
 
-    /* */
-    sys->hw_ctx.display = va_dpy;
-    sys->hw_ctx.config_id = VA_INVALID_ID;
-    sys->hw_ctx.context_id = VA_INVALID_ID;
-    va->sys = sys;
-
-    struct va_pool_cfg pool_cfg = {
-        VAAPICreateDevice,
-        VAAPIDestroyDevice,
-        VAAPICreateDecoderSurfaces,
-        VAAPISetupAVCodecContext,
-        sys,
-    };
-    sys->va_pool = va_pool_Create(va, &pool_cfg);
-    if (sys->va_pool == NULL)
-        goto error;
+    VADisplay va_dpy = dec_device->opaque;
 
-    fmt_out->i_chroma = i_vlc_chroma;
-    int err = va_pool_SetupDecoder(va, sys->va_pool, ctx, fmt_out, count);
-    if (err != VLC_SUCCESS)
+    AVBufferRef *hwdev_ref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VAAPI);
+    if (hwdev_ref == NULL)
         goto error;
 
-    VASurfaceID *render_targets = sys->render_targets;
+    AVHWDeviceContext *hwdev_ctx = (void *) hwdev_ref->data;
+    AVVAAPIDeviceContext *vadev_ctx = hwdev_ctx->hwctx;
+    vadev_ctx->display = va_dpy;
 
-    sys->hw_ctx.config_id =
-        vlc_vaapi_CreateConfigChecked(o, sys->hw_ctx.display, i_profile,
-                                      VAEntrypointVLD, i_vlc_chroma);
-    if (sys->hw_ctx.config_id == VA_INVALID_ID)
+    if (av_hwdevice_ctx_init(hwdev_ref) < 0)
         goto error;
 
-    /* Create a context */
-    sys->hw_ctx.context_id =
-        vlc_vaapi_CreateContext(o, sys->hw_ctx.display, sys->hw_ctx.config_id,
-                                ctx->coded_width, ctx->coded_height, VA_PROGRESSIVE,
-                                render_targets, count);
-    if (sys->hw_ctx.context_id == VA_INVALID_ID)
+    vlc_video_context *vctx =
+        vlc_video_context_Create(dec_device, VLC_VIDEO_CONTEXT_VAAPI,
+                                 sizeof(struct vaapi_vctx), &vaapi_ctx_ops);
+    if (vctx == NULL)
         goto error;
 
-    msg_Info(va, "Using %s", vaQueryVendorString(sys->hw_ctx.display));
+    struct vaapi_vctx *vaapi_vctx =
+        vlc_video_context_GetPrivate(vctx, VLC_VIDEO_CONTEXT_VAAPI);
 
-    sys->vctx = vlc_video_context_Create( dec_device, VLC_VIDEO_CONTEXT_VAAPI, 0, NULL );
-    if (sys->vctx == NULL)
-        goto error;
+    vaapi_vctx->va_dpy = va_dpy;
+    vaapi_vctx->hwdev_ref = hwdev_ref;
+    vlc_mutex_init(&vaapi_vctx->lock);
+    vlc_cond_init(&vaapi_vctx->wait);
+
+    msg_Info(va, "Using %s", vaQueryVendorString(va_dpy));
+
+    fmt_out->i_chroma = i_vlc_chroma;
+
+    ctx->hw_device_ctx = hwdev_ref;
 
     va->ops = &ops;
-    *vtcx_out = sys->vctx;
+    va->sys = vctx;
+    *vtcx_out = vctx;
     return VLC_SUCCESS;
 
 error:
-    if (sys->va_pool != NULL)
-        va_pool_Close(sys->va_pool);
-    else
-        free(sys);
-    return ret;
+    if (hwdev_ref != NULL)
+        av_buffer_unref(&hwdev_ref);
+    return VLC_EGENERIC;
 }
 
 vlc_module_begin ()


=====================================
modules/codec/avcodec/video.c
=====================================
@@ -1395,20 +1395,26 @@ static int lavc_va_GetFrame(struct AVCodecContext *ctx, AVFrame *frame)
         return -1;
 
     /* data[3] will contains the format-specific surface handle. */
-    if (vlc_va_Get(va, pic, &frame->data[0]))
+    if (vlc_va_Get(va, pic, ctx, frame))
     {
         msg_Err(dec, "hardware acceleration picture allocation failed");
         picture_Release(pic);
         return -1;
     }
 
-    frame->buf[0] = av_buffer_create(frame->data[0], 0, lavc_ReleaseFrame, pic, 0);
-    if (unlikely(frame->buf[0] == NULL))
+    AVBufferRef *buf = av_buffer_create(NULL, 0, lavc_ReleaseFrame, pic, 0);
+    if (unlikely(buf == NULL))
     {
-        lavc_ReleaseFrame(pic, frame->data[0]);
+        lavc_ReleaseFrame(pic, NULL);
         return -1;
     }
 
+    /* frame->buf[0] must be valid but can be previously set by the VA module. */
+    if (frame->buf[0] == NULL)
+        frame->buf[0] = buf;
+    else
+        frame->opaque_ref = buf;
+
     frame->opaque = pic;
     return 0;
 }
@@ -1550,7 +1556,8 @@ static enum AVPixelFormat ffmpeg_GetFormat( AVCodecContext *p_context,
     video_format_t fmt;
 
     /* Enumerate available formats */
-    enum AVPixelFormat swfmt = avcodec_default_get_format(p_context, pi_fmt);
+    enum AVPixelFormat defaultfmt = avcodec_default_get_format(p_context, pi_fmt);
+    enum AVPixelFormat swfmt = AV_PIX_FMT_NONE;
     bool can_hwaccel = false;
 
     for (size_t i = 0; pi_fmt[i] != AV_PIX_FMT_NONE; i++)
@@ -1563,9 +1570,23 @@ static enum AVPixelFormat ffmpeg_GetFormat( AVCodecContext *p_context,
         msg_Dbg( p_dec, "available %sware decoder output format %d (%s)",
                  hwaccel ? "hard" : "soft", pi_fmt[i], dsc->name );
         if (hwaccel)
+        {
+            /* The default fmt is a hw format, it can happen with some va
+             * implementations (when using a hw_device_ctx). */
+            if (defaultfmt == pi_fmt[i])
+                defaultfmt = AV_PIX_FMT_NONE;
+
             can_hwaccel = true;
+        }
+        else if (swfmt == AV_PIX_FMT_NONE)
+            swfmt = pi_fmt[i];
     }
 
+    /* Use the default fmt in priority of any sw fmt if the default fmt is a hw
+     * one */
+    if (defaultfmt != AV_PIX_FMT_NONE)
+        swfmt = defaultfmt;
+
     if (p_sys->pix_fmt == AV_PIX_FMT_NONE)
         goto no_reuse;
 


=====================================
modules/hw/vdpau/avcodec.c
=====================================
@@ -109,8 +109,10 @@ static vlc_vdp_video_field_t *Get(vlc_va_sys_t *sys)
     return field;
 }
 
-static int Lock(vlc_va_t *va, picture_t *pic, uint8_t **data)
+static int Lock(vlc_va_t *va, picture_t *pic, AVCodecContext *ctx, AVFrame *frame)
 {
+    (void) ctx;
+
     vlc_va_sys_t *sys = va->sys;
     vlc_vdp_video_field_t *field = Get(sys);
     if (field == NULL)
@@ -120,7 +122,7 @@ static int Lock(vlc_va_t *va, picture_t *pic, uint8_t **data)
     field->context.vctx = vlc_video_context_Hold(sys->vctx);
 
     pic->context = &field->context;
-    data[3] = (void *)(uintptr_t)field->frame->surface;
+    frame->data[3] = (void *)(uintptr_t)field->frame->surface;
     return VLC_SUCCESS;
 }
 



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/2721256e9bee998db4d4fd880987be225e246078...08cbeca2b732d8dcd3ae698a378cdfee5071a0c3

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/2721256e9bee998db4d4fd880987be225e246078...08cbeca2b732d8dcd3ae698a378cdfee5071a0c3
You're receiving this email because of your account on code.videolan.org.




More information about the vlc-commits mailing list