diff mbox series

[FFmpeg-devel] libavcodec/qsvenc: Add support to qsv to encode external surface.

Message ID 20220627072419.589256-1-wenbin.chen@intel.com
State New
Headers show
Series [FFmpeg-devel] libavcodec/qsvenc: Add support to qsv to encode external surface. | expand

Checks

Context Check Description
yinshiyou/make_loongarch64 success Make finished
yinshiyou/make_fate_loongarch64 success Make fate finished
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished
andriy/make_armv7_RPi4 success Make finished
andriy/make_fate_armv7_RPi4 success Make fate finished

Commit Message

Wenbin Chen June 27, 2022, 7:24 a.m. UTC
Qsv encoder only encode the frame that are pre-allocated, so qsv encoder
cannot encode the frame mapped from other components. In fact, MediaSDK
can encode frame that are dynamically created. I add the support to qsv
to encode external frame. Now the following command line works:

ffmpeg -v verbose -init_hw_device vaapi=va -init_hw_device qsv=qs@va \
-hwaccel qsv -hwaccel_device qs -c:v h264_qsv -i input.264 -vf \
"hwmap=derive_device=vaapi,format=vaapi,hwmap=derive_device=vulkan, \
format=vulkan,scale_vulkan=w=1920:h=1080,hwmap=derive_device=vaapi, \
format=vaapi,hwmap=derive_device=qsv:reverse=1:extra_hw_frames=16, \
format=qsv" -c:v h264_qsv output.264

Signed-off-by: Wenbin Chen <wenbin.chen@intel.com>
Signed-off-by: Tong Wu <tong1.wu@intel.com>
---
 libavcodec/qsv_internal.h |  1 +
 libavcodec/qsvenc.c       | 19 ++++++++--
 libavutil/hwcontext_qsv.c | 79 ++++++++++++++++++++++++++++++++++-----
 3 files changed, 86 insertions(+), 13 deletions(-)
diff mbox series

Patch

diff --git a/libavcodec/qsv_internal.h b/libavcodec/qsv_internal.h
index 8131acdae9..2ee523ec3d 100644
--- a/libavcodec/qsv_internal.h
+++ b/libavcodec/qsv_internal.h
@@ -90,6 +90,7 @@  typedef struct QSVFrame {
 
     int queued;
     int used;
+    int external_frame;
 
     struct QSVFrame *next;
 } QSVFrame;
diff --git a/libavcodec/qsvenc.c b/libavcodec/qsvenc.c
index 2382c2f5f7..e269ece5d9 100644
--- a/libavcodec/qsvenc.c
+++ b/libavcodec/qsvenc.c
@@ -1421,6 +1421,10 @@  static void clear_unused_frames(QSVEncContext *q)
             memset(&cur->enc_ctrl, 0, sizeof(cur->enc_ctrl));
             cur->enc_ctrl.Payload = cur->payloads;
             cur->enc_ctrl.ExtParam = cur->extparam;
+            if (cur->external_frame) {
+                av_freep(&cur->surface.Data.MemId);
+                cur->external_frame = 0;
+            }
             if (cur->frame->format == AV_PIX_FMT_QSV) {
                 av_frame_unref(cur->frame);
             }
@@ -1486,10 +1490,17 @@  static int submit_frame(QSVEncContext *q, const AVFrame *frame,
 
         if (q->frames_ctx.mids) {
             ret = ff_qsv_find_surface_idx(&q->frames_ctx, qf);
-            if (ret < 0)
-                return ret;
-
-            qf->surface.Data.MemId = &q->frames_ctx.mids[ret];
+            if (ret >= 0)
+                qf->surface.Data.MemId = &q->frames_ctx.mids[ret];
+        }
+        if (!q->frames_ctx.mids || ret < 0) {
+            QSVMid *mid = NULL;
+            mid = (QSVMid *)av_mallocz(sizeof(*mid));
+            if (!mid)
+                return AVERROR(ENOMEM);
+            mid->handle_pair = (mfxHDLPair *)qf->surface.Data.MemId;
+            qf->surface.Data.MemId = mid;
+            qf->external_frame = 1;
         }
     } else {
         /* make a copy if the input is not padded as libmfx requires */
diff --git a/libavutil/hwcontext_qsv.c b/libavutil/hwcontext_qsv.c
index 56dffa1f25..565c79a4e0 100644
--- a/libavutil/hwcontext_qsv.c
+++ b/libavutil/hwcontext_qsv.c
@@ -1337,11 +1337,24 @@  static int qsv_frames_derive_to(AVHWFramesContext *dst_ctx,
     return 0;
 }
 
+static void qsv_umap_external_surface(AVHWFramesContext *dst_fc,
+                                 HWMapDescriptor *hwmap)
+{
+    mfxFrameSurface1 *new_sur = (mfxFrameSurface1 *)hwmap->priv;
+    mfxHDLPair *hdlpair = (mfxHDLPair *)new_sur->Data.MemId;
+    if (hwmap->source->format == AV_PIX_FMT_VAAPI)
+        av_freep(&hdlpair->first);
+    av_freep(&new_sur->Data.MemId);
+    av_freep(&new_sur);
+}
+
 static int qsv_map_to(AVHWFramesContext *dst_ctx,
                       AVFrame *dst, const AVFrame *src, int flags)
 {
     AVQSVFramesContext *hwctx = dst_ctx->hwctx;
     int i, err, index = -1;
+    mfxFrameSurface1 *new_sur = NULL;
+    mfxHDLPair *new_hdlpair = NULL;
 
     for (i = 0; i < hwctx->nb_surfaces && index < 0; i++) {
         switch(src->format) {
@@ -1379,22 +1392,70 @@  static int qsv_map_to(AVHWFramesContext *dst_ctx,
 #endif
         }
     }
+    /* If the surface is not in the pool, create a new surface */
     if (index < 0) {
-        av_log(dst_ctx, AV_LOG_ERROR, "Trying to map from a surface which "
-               "is not in the mapped frames context.\n");
-        return AVERROR(EINVAL);
+        new_sur = (mfxFrameSurface1 *)av_mallocz(sizeof(*new_sur));
+        if (!new_sur) {
+            err = AVERROR(ENOMEM);
+            goto fail;
+        }
+        err = qsv_init_surface(dst_ctx, new_sur);
+        if (err < 0)
+            goto fail;
+
+        new_hdlpair = (mfxHDLPair *)av_mallocz(sizeof(*new_hdlpair));
+        if (!new_hdlpair) {
+            err = AVERROR(ENOMEM);
+            goto fail;
+        }
+        switch (src->format) {
+#if CONFIG_VAAPI
+        case AV_PIX_FMT_VAAPI:
+            new_hdlpair->first = (VASurfaceID *)av_mallocz(sizeof(VASurfaceID));
+            if (!new_hdlpair->first) {
+                err = AVERROR(ENOMEM);
+                goto fail;
+            }
+            *(VASurfaceID*)new_hdlpair->first = (VASurfaceID)(uintptr_t)src->data[3];
+            break;
+#endif
+        case AV_PIX_FMT_D3D11:
+            new_hdlpair->first = src->data[0];
+            new_hdlpair->second = src->data[1];
+            break;
+        case AV_PIX_FMT_DXVA2_VLD:
+            new_hdlpair->first = src->data[3];
+            break;
+        default:
+            return AVERROR(ENOSYS);
+        }
+        av_log(dst_ctx, AV_LOG_DEBUG, "Trying to map from a surface which "
+            "is not in the mapped frames context, so create a new surface\n");
+        new_sur->Data.MemId = new_hdlpair;
+        err = ff_hwframe_map_create(dst->hw_frames_ctx, dst, src,
+                                    &qsv_umap_external_surface,
+                                    (void*)new_sur);
+        if (err)
+            goto fail;
+    } else {
+        err = ff_hwframe_map_create(dst->hw_frames_ctx,
+                                    dst, src, NULL, NULL);
+        if (err)
+            return err;
     }
 
-    err = ff_hwframe_map_create(dst->hw_frames_ctx,
-                                dst, src, NULL, NULL);
-    if (err)
-        return err;
-
     dst->width   = src->width;
     dst->height  = src->height;
-    dst->data[3] = (uint8_t*)&hwctx->surfaces[index];
+    dst->data[3] = (uint8_t *)((index < 0) ? new_sur : &hwctx->surfaces[index]);
 
     return 0;
+
+fail:
+    av_freep(&new_sur);
+    if (src->format == AV_PIX_FMT_VAAPI && new_hdlpair)
+        av_freep(&new_hdlpair->first);
+    av_freep(&new_hdlpair);
+    return err;
 }
 
 static int qsv_frames_get_constraints(AVHWDeviceContext *ctx,