[FFmpeg-devel] [PATCH] libavcodec/qsvenc: Add support to qsv to encode external surface.
Wenbin Chen
wenbin.chen at intel.com
Mon Jun 27 10:24:19 EEST 2022
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 at 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 at intel.com>
Signed-off-by: Tong Wu <tong1.wu at intel.com>
---
libavcodec/qsv_internal.h | 1 +
libavcodec/qsvenc.c | 19 ++++++++--
libavutil/hwcontext_qsv.c | 79 ++++++++++++++++++++++++++++++++++-----
3 files changed, 86 insertions(+), 13 deletions(-)
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,
--
2.32.0
More information about the ffmpeg-devel
mailing list