[FFmpeg-devel] [PATCH 2/6] lavf/vpp: Enable vpp filter, an Intel GPU accelerated scaler.
Nablet Developer
sdk at nablet.com
Thu Aug 25 10:07:15 EEST 2016
From: ChaoX A Liu <chaox.a.liu at intel.com>
Signed-off-by: ChaoX A Liu <chaox.a.liu at intel.com>
---
configure | 3 +
libavcodec/qsv.c | 2 +-
libavcodec/qsv_internal.h | 2 +-
libavfilter/Makefile | 1 +
libavfilter/allfilters.c | 1 +
libavfilter/vf_qsvvpp.c | 864 ++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 871 insertions(+), 2 deletions(-)
create mode 100644 libavfilter/vf_qsvvpp.c
diff --git a/configure b/configure
index 5b017fd..525efae 100755
--- a/configure
+++ b/configure
@@ -2086,6 +2086,7 @@ CONFIG_EXTRA="
qsv
qsvdec
qsvenc
+ qsvvpp
rangecoder
riffdec
riffenc
@@ -3044,6 +3045,8 @@ phase_filter_deps="gpl"
pp7_filter_deps="gpl"
pp_filter_deps="gpl postproc"
pullup_filter_deps="gpl"
+qsvvpp_filter_deps="libmfx"
+qsvvpp_filter_select="qsv"
removelogo_filter_deps="avcodec avformat swscale"
repeatfields_filter_deps="gpl"
resample_filter_deps="avresample"
diff --git a/libavcodec/qsv.c b/libavcodec/qsv.c
index b505e14..c180ca8 100644
--- a/libavcodec/qsv.c
+++ b/libavcodec/qsv.c
@@ -168,7 +168,7 @@ static int ff_qsv_set_display_handle(AVCodecContext *avctx, QSVSession *qs)
* @param avctx ffmpeg metadata for this codec context
* @param session the MSDK session used
*/
-int ff_qsv_init_internal_session(AVCodecContext *avctx, QSVSession *qs)
+int ff_qsv_init_internal_session(void *avctx, QSVSession *qs)
{
mfxIMPL impl = MFX_IMPL_AUTO_ANY;
mfxVersion ver = { { QSV_VERSION_MINOR, QSV_VERSION_MAJOR } };
diff --git a/libavcodec/qsv_internal.h b/libavcodec/qsv_internal.h
index 59d1336..e43728b 100644
--- a/libavcodec/qsv_internal.h
+++ b/libavcodec/qsv_internal.h
@@ -80,7 +80,7 @@ int ff_qsv_error(int mfx_err);
int ff_qsv_codec_id_to_mfx(enum AVCodecID codec_id);
-int ff_qsv_init_internal_session(AVCodecContext *avctx, QSVSession *qs);
+int ff_qsv_init_internal_session(void *avctx, QSVSession *qs);
int ff_qsv_load_plugins(mfxSession session, const char *load_plugins);
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 9dc524f..f53ff7a 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -236,6 +236,7 @@ OBJS-$(CONFIG_PP7_FILTER) += vf_pp7.o
OBJS-$(CONFIG_PSNR_FILTER) += vf_psnr.o dualinput.o framesync.o
OBJS-$(CONFIG_PULLUP_FILTER) += vf_pullup.o
OBJS-$(CONFIG_QP_FILTER) += vf_qp.o
+OBJS-$(CONFIG_QSVVPP_FILTER) += vf_qsvvpp.o
OBJS-$(CONFIG_RANDOM_FILTER) += vf_random.o
OBJS-$(CONFIG_READVITC_FILTER) += vf_readvitc.o
OBJS-$(CONFIG_REALTIME_FILTER) += f_realtime.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 05fa9c4..f7de5a5 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -252,6 +252,7 @@ void avfilter_register_all(void)
REGISTER_FILTER(PSNR, psnr, vf);
REGISTER_FILTER(PULLUP, pullup, vf);
REGISTER_FILTER(QP, qp, vf);
+ REGISTER_FILTER(QSVVPP, qsvvpp, vf);
REGISTER_FILTER(RANDOM, random, vf);
REGISTER_FILTER(READVITC, readvitc, vf);
REGISTER_FILTER(REALTIME, realtime, vf);
diff --git a/libavfilter/vf_qsvvpp.c b/libavfilter/vf_qsvvpp.c
new file mode 100644
index 0000000..3a5d4d3
--- /dev/null
+++ b/libavfilter/vf_qsvvpp.c
@@ -0,0 +1,864 @@
+/*
+ * Intel MediaSDK Quick Sync Video VPP filter
+ *
+ * copyright (c) 2015 Sven Dueking
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "internal.h"
+#include <float.h>
+#include "libavutil/parseutils.h"
+#include "libavutil/timestamp.h"
+#include "libavcodec/qsv.h"
+
+/**
+ * ToDo :
+ *
+ * - double check surface pointers for different fourccs
+ * - handle empty extbuffers
+ * - cropping
+ * - use AV_PIX_FMT_QSV to pass surfaces to encoder
+ * - deinterlace check settings etc.
+ * - allocate number of surfaces depending modules and number of b frames
+ */
+
+#define VPP_ZERO_MEMORY(VAR) { memset(&VAR, 0, sizeof(VAR)); }
+#define VPP_ALIGN16(value) (((value + 15) >> 4) << 4) // round up to a multiple of 16
+#define VPP_ALIGN32(value) (((value + 31) >> 5) << 5) // round up to a multiple of 32
+#define VPP_CHECK_POINTER(P, ...) {if (!(P)) {return __VA_ARGS__;}}
+
+#define OFFSET(x) offsetof(VPPContext, x)
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
+
+static const AVOption qsvvpp_options[] = {
+ { "deinterlace", "deinterlace mode: 0=off, 1=bob, 2=advanced", OFFSET(deinterlace), AV_OPT_TYPE_INT, {.i64=0}, 0, MFX_DEINTERLACING_ADVANCED, .flags = FLAGS },
+ { "denoise", "denoise level [0, 100]", OFFSET(denoise), AV_OPT_TYPE_INT, {.i64=0}, 0, 100, .flags = FLAGS },
+ { "detail", "detail enhancement level [0, 100]", OFFSET(detail), AV_OPT_TYPE_INT, {.i64=0}, 0, 100, .flags = FLAGS },
+ { "w", "Output video width", OFFSET(out_width), AV_OPT_TYPE_INT, {.i64=0}, 0, 4096, .flags = FLAGS },
+ { "width", "Output video width", OFFSET(out_width), AV_OPT_TYPE_INT, {.i64=0}, 0, 4096, .flags = FLAGS },
+ { "h", "Output video height", OFFSET(out_height), AV_OPT_TYPE_INT, {.i64=0}, 0, 2304, .flags = FLAGS },
+ { "height", "Output video height : ", OFFSET(out_height), AV_OPT_TYPE_INT, {.i64=0}, 0, 2304, .flags = FLAGS },
+ { "dpic", "dest pic struct: 0=tff, 1=progressive [default], 2=bff", OFFSET(dpic), AV_OPT_TYPE_INT, {.i64 = 1 }, 0, 2, .flags = FLAGS },
+ { "framerate", "output framerate", OFFSET(framerate), AV_OPT_TYPE_RATIONAL, { .dbl = 0.0 },0, DBL_MAX, .flags = FLAGS },
+ { "async_depth", "Maximum processing parallelism [default = 4]", OFFSET(async_depth), AV_OPT_TYPE_INT, { .i64 = ASYNC_DEPTH_DEFAULT }, 0, INT_MAX, .flags = FLAGS },
+ { "max_b_frames","Maximum number of b frames [default = 3]", OFFSET(max_b_frames), AV_OPT_TYPE_INT, { .i64 = 3 }, 0, INT_MAX, .flags = FLAGS },
+ { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(qsvvpp);
+
+static int option_id_to_mfx_pic_struct(int id)
+{
+ switch (id) {
+ case 0:
+ return MFX_PICSTRUCT_FIELD_TFF;
+ case 1:
+ return MFX_PICSTRUCT_PROGRESSIVE;
+ case 2:
+ return MFX_PICSTRUCT_FIELD_BFF;
+ default:
+ return MFX_PICSTRUCT_UNKNOWN;
+ }
+ return MFX_PICSTRUCT_UNKNOWN;
+}
+
+static int get_chroma_fourcc(unsigned int fourcc)
+{
+ switch (fourcc) {
+ case MFX_FOURCC_YUY2:
+ return MFX_CHROMAFORMAT_YUV422;
+ case MFX_FOURCC_RGB4:
+ return MFX_CHROMAFORMAT_YUV444;
+ default:
+ return MFX_CHROMAFORMAT_YUV420;
+ }
+ return MFX_CHROMAFORMAT_YUV420;
+}
+
+static int avframe_id_to_mfx_pic_struct(AVFrame * pic)
+{
+ if (!pic->interlaced_frame)
+ return MFX_PICSTRUCT_PROGRESSIVE;
+
+ if (pic->top_field_first == 1)
+ return MFX_PICSTRUCT_FIELD_TFF;
+
+ return MFX_PICSTRUCT_FIELD_BFF;
+}
+
+static int avpix_fmt_to_mfx_fourcc(int format)
+{
+ switch(format){
+ case AV_PIX_FMT_YUV420P:
+ return MFX_FOURCC_YV12;
+ case AV_PIX_FMT_NV12:
+ return MFX_FOURCC_NV12;
+ case AV_PIX_FMT_YUYV422:
+ return MFX_FOURCC_YUY2;
+ case AV_PIX_FMT_RGB32:
+ return MFX_FOURCC_RGB4;
+ }
+
+ return MFX_FOURCC_NV12;
+}
+
+static void vidmem_init_surface(VPPContext *vpp)
+{
+ int i;
+
+ av_log(vpp->ctx, AV_LOG_INFO, "qsvvpp: vidmem_init_surface: ");
+
+ /*
+ * Input surfaces are allocated by decoder.
+ * We needn't allocate them.
+ */
+ vpp->num_surfaces_in = 0;
+ vpp->in_surface = NULL;
+ vpp->in_response = NULL;
+
+ /*
+ * We should care about next stage vpp or encoder's input surfaces.
+ */
+ av_log(vpp->ctx, AV_LOG_INFO, "in.num = %d, out.num = %d, ",
+ vpp->req[0].NumFrameSuggested, vpp->req[1].NumFrameSuggested);
+ if (vpp->enc_ctx) {
+ vpp->req[1].NumFrameSuggested += vpp->enc_ctx->req.NumFrameSuggested;
+ av_log(vpp->ctx, AV_LOG_INFO, "enc_ctx.num=%d\n", vpp->enc_ctx->req.NumFrameSuggested);
+ } else {
+ av_log(vpp->ctx, AV_LOG_INFO, "enc_ctx.num=%d\n", 0);
+ }
+
+ vpp->req[0].NumFrameSuggested = FFMAX(vpp->req[0].NumFrameSuggested, 1);
+
+ vpp->num_surfaces_out = FFMAX(vpp->req[1].NumFrameSuggested, 1);
+ vpp->out_response = av_mallocz(sizeof(*vpp->out_response));
+ VPP_CHECK_POINTER(vpp->out_response);
+ vpp->out_surface = av_mallocz(sizeof(*vpp->out_surface) * vpp->num_surfaces_out);
+ VPP_CHECK_POINTER(vpp->out_surface);
+
+ vpp->pFrameAllocator->Alloc( vpp->pFrameAllocator->pthis, &(vpp->req[1]), vpp->out_response);
+ for (i = 0; i < vpp->num_surfaces_out; i++) {
+ memcpy(&vpp->out_surface[i].Info, &vpp->pVppParam->vpp.Out, sizeof(vpp->out_surface[i].Info));
+ vpp->out_surface[i].Data.MemId = vpp->out_response->mids[i];
+ }
+}
+
+static void vidmem_free_surface(AVFilterContext *ctx)
+{
+ VPPContext *vpp= ctx->priv;
+
+ av_log(vpp->ctx, AV_LOG_DEBUG, "qsvvpp: vidmem_free_surface\n");
+
+ if (vpp->out_surface)
+ av_freep(&vpp->out_surface);
+
+ if (vpp->out_response) {
+ vpp->pFrameAllocator->Free(vpp->pFrameAllocator->pthis, vpp->out_response);
+ av_freep(&vpp->out_response);
+ }
+
+ vpp->num_surfaces_in = 0;
+ vpp->num_surfaces_out = 0;
+}
+
+static void sysmem_init_surface(VPPContext *vpp)
+{
+ int i, j;
+ av_log(vpp->ctx, AV_LOG_INFO, "qsvvpp: sysmem_init_surface: ");
+ av_log(vpp->ctx, AV_LOG_INFO, "in.num = %d, out.num = %d\n",
+ vpp->req[0].NumFrameSuggested, vpp->req[1].NumFrameSuggested);
+
+ vpp->num_surfaces_in = FFMAX(vpp->req[0].NumFrameSuggested, vpp->async_depth + vpp->max_b_frames + 1);
+ vpp->in_surface = av_mallocz(sizeof(*vpp->in_surface) * vpp->num_surfaces_in);
+ VPP_CHECK_POINTER(vpp->in_surface);
+ for (j = 0; j < vpp->num_surfaces_in; j++)
+ memcpy(&vpp->in_surface[j].Info, &vpp->pVppParam->vpp.In, sizeof(vpp->in_surface[j].Info));
+
+ vpp->num_surfaces_out = FFMAX(vpp->req[1].NumFrameSuggested, 1);
+ vpp->out_surface = av_mallocz(sizeof(*vpp->out_surface) * vpp->num_surfaces_out);
+ VPP_CHECK_POINTER(vpp->out_surface);
+ for (i = 0; i < vpp->num_surfaces_out; i++)
+ memcpy(&vpp->out_surface[i].Info, &vpp->pVppParam->vpp.Out, sizeof(vpp->out_surface[i].Info));
+}
+
+static void sysmem_free_surface(AVFilterContext *ctx)
+{
+ VPPContext *vpp= ctx->priv;
+
+ av_log(vpp->ctx, AV_LOG_DEBUG, "qsvvpp: sysmem_free_surface\n");
+ if (vpp->in_surface)
+ av_freep(&vpp->in_surface);
+
+ if (vpp->out_surface)
+ av_freep(&vpp->out_surface);
+
+ vpp->num_surfaces_in = 0;
+ vpp->num_surfaces_out = 0;
+}
+
+static int get_free_surface_index_in(AVFilterContext *ctx, mfxFrameSurface1 *surface_pool, int pool_size)
+{
+ if (surface_pool) {
+ for (mfxU16 i = 0; i < pool_size; i++) {
+ if (!surface_pool[i].Data.Locked)
+ return i;
+ }
+ }
+
+ av_log(ctx, AV_LOG_ERROR, "Error getting a free surface, pool size is %d\n", pool_size);
+ return MFX_ERR_NOT_FOUND;
+}
+
+static int get_free_surface_index_out(AVFilterContext *ctx, mfxFrameSurface1 *surface_pool, int pool_size)
+{
+ int i;
+ if (surface_pool)
+ for (i = 0; i < pool_size; i++)
+ if (!surface_pool[i].Data.Locked)
+ return i;
+
+ av_log(ctx, AV_LOG_ERROR, "Error getting a free surface, pool size is %d\n", pool_size);
+ return MFX_ERR_NOT_FOUND;
+}
+
+static int sysmem_map_frame_to_surface(VPPContext *vpp, AVFrame *frame, mfxFrameSurface1 *surface)
+{
+ surface->Info.PicStruct = avframe_id_to_mfx_pic_struct(frame);
+
+ switch (frame->format) {
+ case AV_PIX_FMT_NV12:
+ surface->Data.Y = frame->data[0];
+ surface->Data.VU = frame->data[1];
+ break;
+
+ case AV_PIX_FMT_YUV420P:
+ surface->Data.Y = frame->data[0];
+ surface->Data.U = frame->data[1];
+ surface->Data.V = frame->data[2];
+ break;
+
+ case AV_PIX_FMT_YUYV422:
+ surface->Data.Y = frame->data[0];
+ surface->Data.U = frame->data[0] + 1;
+ surface->Data.V = frame->data[0] + 3;
+ break;
+
+ case AV_PIX_FMT_RGB32:
+ surface->Data.B = frame->data[0];
+ surface->Data.G = frame->data[0] + 1;
+ surface->Data.R = frame->data[0] + 2;
+ surface->Data.A = frame->data[0] + 3;
+ break;
+
+ default:
+ return MFX_ERR_UNSUPPORTED;
+ }
+ surface->Data.Pitch = frame->linesize[0];
+
+ return 0;
+}
+
+static int vidmem_map_frame_to_surface(VPPContext *vpp, AVFrame *frame, mfxFrameSurface1 *surface)
+{
+ int i;
+ mfxFrameData data;
+
+ vpp->pFrameAllocator->Lock(vpp->pFrameAllocator->pthis, surface->Data.MemId, &data);
+ switch (frame->format) {
+ case AV_PIX_FMT_NV12:
+ for (i = 0; i<frame->height; i++)
+ memcpy(data.Y + data.Pitch*i, frame->data[0] + frame->linesize[0]*i, frame->linesize[0]);
+ for (i = 0; i<frame->height/2; i++)
+ memcpy(data.UV + data.Pitch*i, frame->data[1] + frame->linesize[1]*i, frame->linesize[1]);
+ break;
+
+ case AV_PIX_FMT_YUV420P:
+ for (i = 0; i<frame->height; i++)
+ memcpy(data.Y + data.Pitch*i, frame->data[0] + frame->linesize[0]*i, frame->linesize[0]);
+ for (i = 0; i<frame->height/2; i++)
+ memcpy(data.U + data.Pitch*i, frame->data[1] + frame->linesize[1]*i, frame->linesize[1]);
+ for (i = 0; i<frame->height/2; i++)
+ memcpy(data.V + data.Pitch*i, frame->data[2] + frame->linesize[2]*i, frame->linesize[2]);
+ break;
+
+ case AV_PIX_FMT_YUYV422:
+ for (i = 0; i<frame->height; i++)
+ memcpy(data.Y + data.Pitch*i, frame->data[0] + frame->linesize[0]*i, frame->linesize[0]);
+ break;
+
+ case AV_PIX_FMT_RGB32:
+ for (i = 0; i<frame->height; i++)
+ memcpy(data.B + data.Pitch*i, frame->data[0] + frame->linesize[0]*i, frame->linesize[0]);
+ break;
+
+ default:
+ return MFX_ERR_UNSUPPORTED;
+ }
+ vpp->pFrameAllocator->Unlock(vpp->pFrameAllocator->pthis, surface->Data.MemId, &data);
+
+ return 0;
+}
+
+static int sysmem_input_get_surface( AVFilterLink *inlink, AVFrame* picref, mfxFrameSurface1 **surface )
+{
+ AVFilterContext *ctx = inlink->dst;
+ VPPContext *vpp = ctx->priv;
+ int surf_idx = 0;
+
+ surf_idx = get_free_surface_index_in(ctx, vpp->in_surface, vpp->num_surfaces_in);
+ if ( MFX_ERR_NOT_FOUND == surf_idx )
+ return MFX_ERR_NOT_FOUND;
+
+ *surface = &vpp->in_surface[surf_idx];
+ sysmem_map_frame_to_surface(vpp, picref, *surface);
+ (*surface)->Data.TimeStamp = av_rescale_q(picref->pts, inlink->time_base, (AVRational){1,90000});
+
+ return 0;
+}
+
+static int vidmem_input_get_surface( AVFilterLink *inlink, AVFrame* picref, mfxFrameSurface1 **surface )
+{
+ if (picref->data[3]) {
+ *surface = (mfxFrameSurface1*)picref->data[3];
+ } else {
+ return MFX_ERR_NOT_FOUND;
+ }
+
+ (*surface)->Data.TimeStamp = av_rescale_q(picref->pts, inlink->time_base, (AVRational){1,90000});
+
+ return 0;
+}
+
+static int sysmem_output_get_surface( AVFilterLink *inlink, mfxFrameSurface1 **surface )
+{
+ AVFilterContext *ctx = inlink->dst;
+ VPPContext *vpp = ctx->priv;
+ int out_idx = 0;
+
+ if (vpp->out_surface) {
+ for (out_idx = vpp->sysmem_cur_out_idx; out_idx < vpp->num_surfaces_out; out_idx++)
+ if (!vpp->out_surface[out_idx].Data.Locked)
+ break;
+ } else {
+ return MFX_ERR_NOT_INITIALIZED;
+ }
+
+ if ( vpp->num_surfaces_out == out_idx )
+ return MFX_ERR_NOT_FOUND;
+
+ *surface = &vpp->out_surface[out_idx];
+
+ vpp->sysmem_cur_out_idx = out_idx + 1;
+ if (vpp->sysmem_cur_out_idx >= vpp->num_surfaces_out)
+ vpp->sysmem_cur_out_idx = 0;
+
+ return 0;
+}
+
+static int vidmem_output_get_surface( AVFilterLink *inlink, mfxFrameSurface1 **surface )
+{
+ AVFilterContext *ctx = inlink->dst;
+ VPPContext *vpp = ctx->priv;
+ int out_idx = 0;
+
+ out_idx = get_free_surface_index_out(ctx, vpp->out_surface, vpp->num_surfaces_out);
+
+ if (MFX_ERR_NOT_FOUND == out_idx)
+ return out_idx;
+
+ *surface = &vpp->out_surface[out_idx];
+
+ return 0;
+}
+
+static int init_vpp_param( VPPContext *vpp, int format, int input_w, int input_h, int frame_rate_num, int frame_rate_den, int pic_struct )
+{
+ // input data
+ vpp->pVppParam->vpp.In.FourCC = avpix_fmt_to_mfx_fourcc(format);
+ vpp->pVppParam->vpp.In.ChromaFormat = get_chroma_fourcc(vpp->pVppParam->vpp.In.FourCC);
+ vpp->pVppParam->vpp.In.CropX = 0;
+ vpp->pVppParam->vpp.In.CropY = 0;
+ vpp->pVppParam->vpp.In.CropW = input_w;
+ vpp->pVppParam->vpp.In.CropH = input_h;
+ vpp->pVppParam->vpp.In.PicStruct = pic_struct;
+ vpp->pVppParam->vpp.In.FrameRateExtN = frame_rate_num;
+ vpp->pVppParam->vpp.In.FrameRateExtD = frame_rate_den;
+ vpp->pVppParam->vpp.In.BitDepthLuma = 8;
+ vpp->pVppParam->vpp.In.BitDepthChroma = 8;
+
+ // width must be a multiple of 16
+ // height must be a multiple of 16 in case of frame picture and a multiple of 32 in case of field picture
+ vpp->pVppParam->vpp.In.Width = VPP_ALIGN16(vpp->pVppParam->vpp.In.CropW);
+ vpp->pVppParam->vpp.In.Height =
+ (MFX_PICSTRUCT_PROGRESSIVE == vpp->pVppParam->vpp.In.PicStruct) ?
+ VPP_ALIGN16(vpp->pVppParam->vpp.In.CropH) :
+ VPP_ALIGN32(vpp->pVppParam->vpp.In.CropH);
+
+ // output data
+ vpp->pVppParam->vpp.Out.FourCC = MFX_FOURCC_NV12;
+ vpp->pVppParam->vpp.Out.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
+ vpp->pVppParam->vpp.Out.CropX = 0;
+ vpp->pVppParam->vpp.Out.CropY = 0;
+ vpp->pVppParam->vpp.Out.CropW = vpp->out_width;
+ vpp->pVppParam->vpp.Out.CropH = vpp->out_height;
+ vpp->pVppParam->vpp.Out.PicStruct = option_id_to_mfx_pic_struct(vpp->dpic);
+ vpp->pVppParam->vpp.Out.FrameRateExtN = vpp->framerate.num;
+ vpp->pVppParam->vpp.Out.FrameRateExtD = vpp->framerate.den;
+ vpp->pVppParam->vpp.Out.BitDepthLuma = 8;
+ vpp->pVppParam->vpp.Out.BitDepthChroma = 8;
+
+ if ((vpp->pVppParam->vpp.In.FrameRateExtN / vpp->pVppParam->vpp.In.FrameRateExtD) !=
+ (vpp->pVppParam->vpp.Out.FrameRateExtN / vpp->pVppParam->vpp.Out.FrameRateExtD)) {
+ vpp->use_frc = 1;
+ } else {
+ vpp->use_frc = 0;
+ }
+
+ // width must be a multiple of 16
+ // height must be a multiple of 16 in case of frame picture and a multiple of 32 in case of field picture
+ vpp->pVppParam->vpp.Out.Width = VPP_ALIGN16(vpp->pVppParam->vpp.Out.CropW);
+ vpp->pVppParam->vpp.Out.Height =
+ (MFX_PICSTRUCT_PROGRESSIVE == vpp->pVppParam->vpp.Out.PicStruct) ?
+ VPP_ALIGN16(vpp->pVppParam->vpp.Out.CropH) :
+ VPP_ALIGN32(vpp->pVppParam->vpp.Out.CropH);
+
+ if (vpp->pFrameAllocator) {
+ vpp->pVppParam->IOPattern = MFX_IOPATTERN_IN_VIDEO_MEMORY | MFX_IOPATTERN_OUT_VIDEO_MEMORY;
+ } else {
+ vpp->pVppParam->IOPattern = MFX_IOPATTERN_IN_SYSTEM_MEMORY | MFX_IOPATTERN_OUT_SYSTEM_MEMORY;
+ }
+
+ av_log(vpp->ctx, AV_LOG_INFO, "QSVVPP: In %dx%d %4.2f fps\t Out %dx%d %4.2f fps\n",
+ (vpp->pVppParam->vpp.In.Width),
+ (vpp->pVppParam->vpp.In.Height),
+ vpp->pVppParam->vpp.In.FrameRateExtN / (float)vpp->pVppParam->vpp.In.FrameRateExtD,
+ (vpp->pVppParam->vpp.Out.Width),
+ (vpp->pVppParam->vpp.Out.Height),
+ vpp->pVppParam->vpp.Out.FrameRateExtN / (float)vpp->pVppParam->vpp.Out.FrameRateExtD);
+
+ if (vpp->use_frc == 1)
+ av_log(vpp->ctx, AV_LOG_INFO, "QSVVPP: Framerate conversion enabled\n");
+
+ return 0;
+}
+
+static int initial_vpp( VPPContext *vpp )
+{
+ int ret = 0;
+ vpp->pVppParam->NumExtParam = 0;
+ vpp->frame_number = 0;
+ vpp->pVppParam->ExtParam = (mfxExtBuffer**)vpp->pExtBuf;
+
+ if (vpp->deinterlace) {
+ av_log(vpp->ctx, AV_LOG_DEBUG, "QSVVPP: Deinterlace enabled\n");
+ memset(&vpp->deinterlace_conf, 0, sizeof(mfxExtVPPDeinterlacing));
+ vpp->deinterlace_conf.Header.BufferId = MFX_EXTBUFF_VPP_DEINTERLACING;
+ vpp->deinterlace_conf.Header.BufferSz = sizeof(mfxExtVPPDeinterlacing);
+ vpp->deinterlace_conf.Mode = vpp->deinterlace == 1 ? MFX_DEINTERLACING_BOB : MFX_DEINTERLACING_ADVANCED;
+
+ vpp->pVppParam->ExtParam[vpp->pVppParam->NumExtParam++] = (mfxExtBuffer*)&(vpp->deinterlace_conf);
+ }
+
+ if (vpp->use_frc) {
+ av_log(vpp->ctx, AV_LOG_DEBUG, "QSVVPP: Framerate conversion enabled\n");
+ memset(&vpp->frc_conf, 0, sizeof(mfxExtVPPFrameRateConversion));
+ vpp->frc_conf.Header.BufferId = MFX_EXTBUFF_VPP_FRAME_RATE_CONVERSION;
+ vpp->frc_conf.Header.BufferSz = sizeof(mfxExtVPPFrameRateConversion);
+ vpp->frc_conf.Algorithm = MFX_FRCALGM_DISTRIBUTED_TIMESTAMP; // make optional
+
+ vpp->pVppParam->ExtParam[vpp->pVppParam->NumExtParam++] = (mfxExtBuffer*)&(vpp->frc_conf);
+ }
+
+ if (vpp->denoise) {
+ av_log(vpp->ctx, AV_LOG_DEBUG, "QSVVPP: Denoise enabled\n");
+ memset(&vpp->denoise_conf, 0, sizeof(mfxExtVPPDenoise));
+ vpp->denoise_conf.Header.BufferId = MFX_EXTBUFF_VPP_DENOISE;
+ vpp->denoise_conf.Header.BufferSz = sizeof(mfxExtVPPDenoise);
+ vpp->denoise_conf.DenoiseFactor = vpp->denoise;
+
+ vpp->pVppParam->ExtParam[vpp->pVppParam->NumExtParam++] = (mfxExtBuffer*)&(vpp->denoise_conf);
+ }
+
+ if (vpp->detail) {
+ av_log(vpp->ctx, AV_LOG_DEBUG, "QSVVPP: Detail enabled\n");
+ memset(&vpp->detail_conf, 0, sizeof(mfxExtVPPDetail));
+ vpp->detail_conf.Header.BufferId = MFX_EXTBUFF_VPP_DETAIL;
+ vpp->detail_conf.Header.BufferSz = sizeof(mfxExtVPPDetail);
+ vpp->detail_conf.DetailFactor = vpp->detail;
+
+ vpp->pVppParam->ExtParam[vpp->pVppParam->NumExtParam++] = (mfxExtBuffer*)&(vpp->detail_conf);
+ }
+
+ memset(&vpp->req, 0, sizeof(mfxFrameAllocRequest) * 2);
+ ret = MFXVideoVPP_QueryIOSurf(vpp->session, vpp->pVppParam, &vpp->req[0]);
+ if (ret < 0) {
+ av_log(vpp->ctx, AV_LOG_ERROR, "QSVVPP: Error querying the VPP IO surface\n");
+ return ff_qsv_error(ret);
+ }
+
+ if (vpp->pFrameAllocator) {
+ vidmem_init_surface(vpp);
+ } else {
+ sysmem_init_surface(vpp);
+ }
+
+ ret = MFXVideoVPP_Init(vpp->session, vpp->pVppParam);
+
+ if (MFX_WRN_PARTIAL_ACCELERATION == ret) {
+ av_log(vpp->ctx, AV_LOG_WARNING, "QSVVPP: VPP will work with partial HW acceleration\n");
+ } else if (ret < 0) {
+ av_log(vpp->ctx, AV_LOG_ERROR, "QSVVPP: Error initializing the VPP %d\n",ret);
+ return ff_qsv_error(ret);
+ }
+
+ vpp->vpp_ready = 1;
+ return 0;
+}
+
+static int config_vpp(AVFilterLink *inlink, AVFrame * pic)
+{
+ AVFilterContext *ctx = inlink->dst;
+ VPPContext *vpp= ctx->priv;
+ mfxVideoParam mfxParamsVideo;
+ int ret;
+
+ av_log(vpp->ctx, AV_LOG_INFO, "QSVVPP: vpp configuration and call mfxVideoVPP_Init\n");
+ if (!vpp->session) {
+ ret = ff_qsv_init_internal_session(ctx, &vpp->internal_qs);
+ if (ret < 0)
+ return ret;
+
+ vpp->session = vpp->internal_qs.session;
+ }
+
+ av_log(ctx, AV_LOG_INFO, "QSVVPP: vpp initializing with session = %p\n", vpp->session);
+ VPP_ZERO_MEMORY(mfxParamsVideo);
+ vpp->pVppParam = &mfxParamsVideo;
+
+ init_vpp_param(vpp, inlink->format, inlink->w, inlink->h,
+ inlink->frame_rate.num, inlink->frame_rate.den,
+ avframe_id_to_mfx_pic_struct(pic) );
+
+ return initial_vpp( vpp );
+}
+
+static void deconf_vpp(AVFilterContext *ctx)
+{
+ VPPContext *vpp = ctx->priv;
+
+ MFXVideoVPP_Close(vpp->session);
+
+ if (vpp->pFrameAllocator) {
+ vidmem_free_surface(ctx);
+ } else {
+ sysmem_free_surface(ctx);
+ }
+
+ ff_qsv_close_internal_session(&vpp->internal_qs);
+ vpp->vpp_ready = 0;
+}
+
+/*
+ * Real filter func.
+ * Push frame into mSDK and pop out filtered frames.
+ */
+static int filter_frame(AVFilterLink *inlink, AVFrame *picref)
+{
+ int ret = 0;
+ int filter_frame_ret = 0;
+ AVFilterContext *ctx = inlink->dst;
+ VPPContext *vpp = ctx->priv;
+ mfxSyncPoint sync = NULL;
+ mfxFrameSurface1 *pInSurface = NULL;
+ mfxFrameSurface1 *pOutSurface = NULL;
+ AVFilterLink *outlink = inlink->dst->outputs[0];
+ AVFrame *out = NULL;
+
+ /*
+ * we re-config local params when getting 1st main frame.
+ */
+ if (!vpp->vpp_ready) {
+ ret = config_vpp(inlink, picref);
+ if (ret < 0) {
+ av_frame_free(&picref);
+ return ret;
+ }
+ vpp->vpp_ready = 1;
+ }
+ av_log(ctx, AV_LOG_DEBUG, "Filtering frame from %s, count %"PRIi64", ts %s\n",
+ inlink->src->name, inlink->frame_count, av_ts2str(picref->pts));
+
+ do {
+ /*get input surface*/
+ if (vpp->pFrameAllocator) {
+ vidmem_input_get_surface( inlink, picref, &pInSurface );
+ vidmem_output_get_surface( inlink, &pOutSurface );
+ } else {
+ sysmem_input_get_surface( inlink, picref, &pInSurface );
+ sysmem_output_get_surface( inlink, &pOutSurface );
+ }
+
+ if (!pInSurface || !pOutSurface) {
+ av_log(ctx, AV_LOG_ERROR, "no free input or output surface\n");
+ ret = MFX_ERR_MEMORY_ALLOC;
+ break;
+ }
+
+ /*
+ * get an AVFrame for output.
+ * @NOTE: frame buffer is aligned with 128x64 to compat with GPU-copy.
+ */
+ out = ff_get_video_buffer(outlink, FFALIGN(vpp->out_width, 128), FFALIGN(vpp->out_height, 64));
+ if (!out) {
+ ret = MFX_ERR_MEMORY_ALLOC;
+ break;
+ }
+ av_frame_copy_props(out, picref);
+ out->width = vpp->out_width;
+ out->height = vpp->out_height;
+
+ /*map avframe->data into outsurface*/
+ if (!vpp->pFrameAllocator) {
+ pOutSurface->Data.Y = out->data[0];
+ pOutSurface->Data.VU = out->data[1];
+ pOutSurface->Data.Pitch = out->linesize[0];
+ }
+
+ do {
+ ret = MFXVideoVPP_RunFrameVPPAsync(vpp->session, pInSurface, pOutSurface, NULL, &sync);
+ if (ret == MFX_WRN_DEVICE_BUSY) {
+ av_usleep(500);
+ continue;
+ }
+ break;
+ } while (1);
+
+ if (ret < 0 && ret != MFX_ERR_MORE_SURFACE) {
+ av_frame_free(&out);
+ /*Ignore more_data error*/
+ if (ret == MFX_ERR_MORE_DATA)
+ ret = 0;
+ break;
+ }
+
+ if (ret == MFX_WRN_INCOMPATIBLE_VIDEO_PARAM)
+ av_log(ctx, AV_LOG_WARNING,
+ "EncodeFrameAsync returned 'incompatible param' code\n");
+
+ MFXVideoCORE_SyncOperation(vpp->session, sync, 60000);
+
+ out->interlaced_frame = !(vpp->dpic == 1 || vpp->dpic == 3);
+ out->top_field_first = vpp->dpic == 1;
+ if (pOutSurface->Data.TimeStamp == MFX_TIMESTAMP_UNKNOWN) {
+ out->pts = AV_NOPTS_VALUE;
+ } else {
+ out->pts = av_rescale_q(pOutSurface->Data.TimeStamp, (AVRational){1,90000}, outlink->time_base);
+ }
+
+ /*For video mem, we use AVFrame->data[3] to transfer surface*/
+ if (vpp->pFrameAllocator)
+ out->data[3] = (void*) pOutSurface;
+
+ filter_frame_ret = ff_filter_frame(inlink->dst->outputs[0], out);
+ if (filter_frame_ret < 0)
+ break;
+
+ vpp->frame_number++;
+ } while (ret == MFX_ERR_MORE_SURFACE);
+ av_frame_free(&picref);
+
+ return ret < 0 ? ff_qsv_error(ret) : filter_frame_ret;
+}
+
+/*
+ * Configure each inputs.
+ */
+static int config_input(AVFilterLink *inlink)
+{
+ AVFilterContext *ctx = inlink->dst;
+ VPPContext *vpp = ctx->priv;
+
+ if (!vpp->framerate.den || !vpp->framerate.num)
+ vpp->framerate = inlink->frame_rate;
+
+ /*
+ * if out_w is not set(<=0), we calc it based on out_h;
+ * if out_h is not set(<=0), we calc it based on out_w;
+ * if both are not set, we set out_rect = in_rect.
+ */
+ if (vpp->out_width <= 0)
+ vpp->out_width = av_rescale(vpp->out_height, inlink->w, inlink->h);
+ if (vpp->out_height <= 0)
+ vpp->out_height = av_rescale(vpp->out_width, inlink->h, inlink->w);
+ if (vpp->out_height <= 0 || vpp->out_width <= 0) {
+ vpp->out_width = inlink->w;
+ vpp->out_height = inlink->h;
+ }
+
+ return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+ AVFilterContext *ctx = outlink->src;
+ VPPContext *vpp = ctx->priv;
+
+ outlink->w = vpp->out_width;
+ outlink->h = vpp->out_height;
+ outlink->frame_rate = vpp->framerate;
+ outlink->time_base = av_inv_q(vpp->framerate);
+ outlink->format = AV_PIX_FMT_NV12;
+
+ return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+ AVFilterFormats *main_fmts, *out_fmts;
+
+ static const enum AVPixelFormat main_in_fmts[] = {
+ AV_PIX_FMT_YUV420P,
+ AV_PIX_FMT_NV12,
+ AV_PIX_FMT_YUYV422,
+ AV_PIX_FMT_RGB32,
+ AV_PIX_FMT_QSV,
+ AV_PIX_FMT_NONE
+ };
+ static const enum AVPixelFormat out_pix_fmts[] = {
+ AV_PIX_FMT_NV12,
+ AV_PIX_FMT_QSV,
+ AV_PIX_FMT_NONE
+ };
+
+ main_fmts = ff_make_format_list(main_in_fmts);
+ out_fmts = ff_make_format_list(out_pix_fmts);
+
+ ff_formats_ref(main_fmts, &ctx->inputs[0]->out_formats);
+ ff_formats_ref(out_fmts, &ctx->outputs[0]->in_formats);
+
+ return 0;
+}
+
+static av_cold int qsvvpp_init(AVFilterContext *ctx)
+{
+ VPPContext *vpp = ctx->priv;
+
+ vpp->frame_number = 0;
+ vpp->pFrameAllocator = NULL;
+ vpp->vpp_ready = 0;
+ vpp->ctx = ctx;
+ vpp->sysmem_cur_out_idx = 0;
+
+ return 0;
+}
+
+static av_cold void qsvvpp_uninit(AVFilterContext *ctx)
+{
+ deconf_vpp(ctx);
+}
+
+static int qsvvpp_cmd_size(AVFilterContext *ctx, const char *arg)
+{
+ VPPContext *vpp = ctx->priv;
+ int w,h,ret;
+
+ ret = av_parse_video_size(&w, &h, arg);
+ if (ret)
+ return ret;
+
+ if (w != vpp->out_width || h != vpp->out_height) {
+ if (vpp->vpp_ready)
+ deconf_vpp(ctx);
+ vpp->out_width = w;
+ vpp->out_height = h;
+ }
+
+ return ret;
+}
+
+static int sqvvpp_process_cmd(AVFilterContext *ctx, const char *cmd, const char *arg, char *res, int res_len, int flags)
+{
+ int ret = 0, i;
+
+#undef NELEMS
+#define NELEMS(x) (sizeof(x)/sizeof((x)[0]))
+ static const struct{
+ const char *short_name;
+ const char *long_name;
+ const char *desc;
+ int (*func)(AVFilterContext*, const char *);
+ int need_arg;
+ const char *arg_desc;
+ }cmdlist[] = {
+ {"h", "help", "Show this help.", NULL, 0, NULL},
+ {"s", "size", "Output resolution", qsvvpp_cmd_size, 1, "wxh"},
+ };
+
+ for (i = 0; i < NELEMS(cmdlist); i++) {
+ if (!av_strcasecmp(cmd, cmdlist[i].long_name)
+ || !av_strcasecmp(cmd, cmdlist[i].short_name))
+ break;
+ }
+
+ if ((i > NELEMS(cmdlist)) || (i <= 0) || (cmdlist[i].need_arg && !arg)) {
+ for (i = 0; i < NELEMS(cmdlist); i++)
+ av_log(ctx, AV_LOG_INFO, "%2s|%-12s %12s\t%s\n", cmdlist[i].short_name,
+ cmdlist[i].long_name, cmdlist[i].desc, cmdlist[i].arg_desc);
+
+ return AVERROR(EINVAL);
+ }
+
+ if (cmdlist[i].func)
+ ret = cmdlist[i].func(ctx, arg);
+ av_log(ctx, AV_LOG_DEBUG, "Dealing with cmd: %s, args: %s, ret: %d.\n", cmd, arg, ret);
+
+ return ret;
+}
+
+static const AVFilterPad qsvvpp_inputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .config_props = config_input,
+ .filter_frame = filter_frame,
+ },
+ { NULL }
+};
+
+static const AVFilterPad qsvvpp_outputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .config_props = config_output,
+ },
+ { NULL }
+};
+
+AVFilter ff_vf_vpp = {
+ .name = "qsvvpp",
+ .description = NULL_IF_CONFIG_SMALL("Quick Sync Video VPP."),
+ .priv_size = sizeof(VPPContext),
+ .query_formats = query_formats,
+ .init = qsvvpp_init,
+ .uninit = qsvvpp_uninit,
+ .inputs = qsvvpp_inputs,
+ .outputs = qsvvpp_outputs,
+ .priv_class = &qsvvpp_class,
+ .process_command = qsvvpp_process_cmd,
+};
+
--
2.5.0
More information about the ffmpeg-devel
mailing list