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.c...@intel.com> Signed-off-by: Tong Wu <tong1...@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 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".