diff --git a/ffmpeg_qsv.c b/ffmpeg_qsv.c
index ec8a41b..70a3ecf 100644
--- a/ffmpeg_qsv.c
+++ b/ffmpeg_qsv.c
@@ -387,7 +387,7 @@ static mfxStatus frame_alloc(mfxHDL pthis, mfxFrameAllocRequest *request, mfxFra
     unsigned int va_fourcc = 0;
     mfxU32 fourcc = request->Info.FourCC;
     QSVContext *q = pthis;
-    AVQSVContext *qsv = q->ost->enc_ctx->hwaccel_context;
+    AVQSVContext *qsv = NULL;
     mfxU16 numAllocated = 0;
     bool bCreateSrfSucceeded = false;
     mfxU32 mfx_fourcc;
@@ -395,17 +395,40 @@ static mfxStatus frame_alloc(mfxHDL pthis, mfxFrameAllocRequest *request, mfxFra
     int width32;
     int height32;
     void *avctx = NULL;
+    FilterGraph *fg = q->ost->filter->graph;
 
-    av_log(avctx, AV_LOG_INFO, "=========vaapi alloc frame==============\n");
     if (!request || !response || !request->NumFrameSuggested)
         return MFX_ERR_MEMORY_ALLOC;
 
     memset(response, 0, sizeof(*response));
     surface_num = request->NumFrameSuggested;
-    if ((request->Type & MFX_MEMTYPE_EXTERNAL_FRAME) &&
-            (request->Type & MFX_MEMTYPE_FROM_DECODE))
-        surface_num += (qsv->nb_encoder_surfaces + qsv->nb_decoder_surfaces);
+    if (request->Type & MFX_MEMTYPE_FROM_DECODE) {
+        avctx = input_streams[q->ost->source_index]->dec_ctx;
+        if (request->Type & MFX_MEMTYPE_EXTERNAL_FRAME) {
+            AVFilterContext *vpp = avfilter_graph_get_filter(fg->graph, "Parsed_vpp_0");
+            qsv = input_streams[q->ost->source_index]->dec_ctx->hwaccel_context;
+            surface_num += qsv->nb_decoder_surfaces;
+            if (vpp) {
+                qsv = vpp->hw_device_ctx->data;
+                surface_num += qsv->nb_vpp_surfaces;
+            } else {
+                qsv = q->ost->enc_ctx->hwaccel_context;
+                surface_num += qsv->nb_encoder_surfaces;
+            }
+        }
+    } else if (request->Type & MFX_MEMTYPE_FROM_VPPOUT) {
+        AVFilterContext *vpp = avfilter_graph_get_filter(fg->graph, "Parsed_vpp_0");
+        avctx = vpp;
+        if (request->Type & MFX_MEMTYPE_EXTERNAL_FRAME) {
+            qsv = q->ost->enc_ctx->hwaccel_context;
+            surface_num += qsv->nb_encoder_surfaces;
+        }
+    } else if (request->Type & MFX_MEMTYPE_FROM_ENCODE) {
+        avctx = q->ost->enc_ctx;
+    } else
+        av_log(avctx, AV_LOG_WARNING, "FrameAlloc: may get a bug.\n");
 
+    av_log(avctx, AV_LOG_INFO, "=========vaapi alloc frame==============\n");
     av_log(avctx, AV_LOG_INFO, "VAAPI: va_dpy =%p, surface_num=%d, width=%d, height=%d\n",
             g_session.va_display, surface_num, request->Info.Width, request->Info.Height);
     av_log(avctx, AV_LOG_INFO, "VAAPI: request->Type=%x\n",request->Type);
@@ -721,7 +744,7 @@ static int qsv_check_filters(const OutputStream *ost)
     AVFilterInOut *inputs, *outputs;
     int ret = 0;
     int i;
-    const char *filter_list = "buffer|buffersink|null|format|setpts";
+    const char *filter_list = "buffer|buffersink|null|format|setpts|vpp";
 
     if (!ost->avfilter)
         return -1;
@@ -821,6 +844,7 @@ int qsv_transcode_init_vidmem(OutputStream *ost)
 
     QSVContext *qsv = NULL;
     AVQSVContext *enc_hwctx = NULL;
+    AVQSVContext *vpp_hwctx = NULL;
 
     /* check if the encoder supports QSV */
     if (!ost->enc->pix_fmts)
@@ -837,6 +861,8 @@ int qsv_transcode_init_vidmem(OutputStream *ost)
 
     /* check if the decoder supports QSV and the output only goes to this stream */
     ist = input_streams[ost->source_index];
+    if (ist->hwaccel_id == HWACCEL_NONE || ist->hwaccel_id == HWACCEL_AUTO)
+        ist->hwaccel_id = HWACCEL_QSV;
     if (ist->nb_filters || ist->hwaccel_id != HWACCEL_QSV ||
         !ist->dec || !ist->dec->pix_fmts)
         return 0;
@@ -855,7 +881,8 @@ int qsv_transcode_init_vidmem(OutputStream *ost)
 
     qsv   = av_mallocz(sizeof(*qsv));
     enc_hwctx = av_qsv_alloc_context();
-    if (!qsv || !enc_hwctx)
+    vpp_hwctx = av_qsv_alloc_context();
+    if (!qsv || !enc_hwctx || !vpp_hwctx)
         goto fail;
 
     err = ff_qsv_init_internal_session(NULL, &g_session);
@@ -892,6 +919,11 @@ int qsv_transcode_init_vidmem(OutputStream *ost)
     ist->resample_pix_fmt            = AV_PIX_FMT_QSV;
     ist->hwaccel_ctx                 = qsv;
 
+    vpp_hwctx->session               = qsv->session;
+    vpp_hwctx->iopattern             = MFX_IOPATTERN_IN_VIDEO_MEMORY;
+    vpp_hwctx->pFrameAllocator       = &qsv->frame_allocator;
+    hw_device_ctx = av_buffer_create(vpp_hwctx, sizeof(*vpp_hwctx), av_buffer_default_free, NULL, 0);
+
     return 0;
 
 fail:
diff --git a/libavcodec/libavcodec.v b/libavcodec/libavcodec.v
index 304c2ef..1a4cac8 100644
--- a/libavcodec/libavcodec.v
+++ b/libavcodec/libavcodec.v
@@ -4,6 +4,7 @@ LIBAVCODEC_MAJOR {
         #deprecated, remove after next bump
         audio_resample;
         audio_resample_close;
+        ff_qsv_*;
     local:
         *;
 };
diff --git a/libavcodec/qsv.h b/libavcodec/qsv.h
index ee968d0..3f7b3c8 100644
--- a/libavcodec/qsv.h
+++ b/libavcodec/qsv.h
@@ -96,7 +96,9 @@ typedef struct AVQSVContext {
      */
     int opaque_alloc_type;
 
+    mfxFrameAllocator *pFrameAllocator;
     int nb_decoder_surfaces;
+    int nb_vpp_surfaces;
     int nb_encoder_surfaces;
 } AVQSVContext;
 
diff --git a/libavfilter/vf_vpp.c b/libavfilter/vf_vpp.c
index 0cfeafc..5047a9a 100644
--- a/libavfilter/vf_vpp.c
+++ b/libavfilter/vf_vpp.c
@@ -21,10 +21,80 @@
  */
 
 #include "internal.h"
+#include <mfx/mfxvideo.h>
+#include <mfx/mfxplugin.h>
 #include <float.h>
 #include "libavutil/parseutils.h"
 #include "libavutil/timestamp.h"
+#include "libavutil/avassert.h"
+#include "libavutil/opt.h"
+#include "libavutil/time.h"
+#include "libavutil/avstring.h"
+#include "libavutil/error.h"
 #include "libavcodec/qsv.h"
+#include "libavcodec/qsv_internal.h"
+
+// number of video enhancement filters (denoise, procamp, detail, video_analysis, image stab)
+#define ENH_FILTERS_COUNT           5
+
+typedef struct {
+    const AVClass *class;
+
+    AVFilterContext *ctx;
+
+    mfxSession session;
+    QSVSession internal_qs;
+    int iopattern;
+
+    AVRational framerate;                           // target framerate
+
+    QSVFrame *in_work_frames;                       // used for video memory
+    QSVFrame *out_work_frames;                      // used for video memory
+
+    mfxFrameSurface1 *in_surface;
+    mfxFrameSurface1 *out_surface;
+
+    mfxFrameAllocRequest req[2];                    // [0] - in, [1] - out
+    mfxFrameAllocator *pFrameAllocator;
+    mfxFrameAllocResponse *in_response;
+    mfxFrameAllocResponse *out_response;
+
+    int num_surfaces_in;                            // input surfaces
+    int num_surfaces_out;                           // output surfaces
+    int sysmem_cur_out_idx;
+    int frame_number;
+    int vpp_ready;
+    mfxVideoParam *pVppParam;
+
+    AVBufferRef *hw_device_ctx;
+
+    /* VPP extension */
+    mfxExtBuffer*       pExtBuf[1+ENH_FILTERS_COUNT];
+    mfxExtVppAuxData    extVPPAuxData;
+
+    /* Video Enhancement Algorithms */
+    mfxExtVPPDeinterlacing  deinterlace_conf;
+    mfxExtVPPFrameRateConversion frc_conf;
+    mfxExtVPPDenoise denoise_conf;
+    mfxExtVPPDetail detail_conf;
+    mfxExtVPPComposite composite_conf;
+
+    int out_width;
+    int out_height;
+    int dpic;                   // destination picture structure
+                                // -1 = unknown
+                                // 0 = interlaced top field first
+                                // 1 = progressive
+                                // 2 = interlaced bottom field first
+
+    int deinterlace;            // deinterlace mode : 0=off, 1=bob, 2=advanced
+    int denoise;                // Enable Denoise algorithm. Level is the optional value from the interval [0; 100]
+    int detail;                 // Enable Detail Enhancement algorithm.
+                                // Level is the optional value from the interval [0; 100]
+    int async_depth;            // async dept used by encoder
+    int max_b_frames;           // maxiumum number of b frames used by encoder
+    int use_frc;                // use framerate conversion
+} VPPContext;
 
 /**
  * ToDo :
@@ -120,6 +190,7 @@ static int avpix_fmt_to_mfx_fourcc(int format)
 static void vidmem_init_surface(VPPContext *vpp)
 {
     int i;
+    AVQSVContext *qsv = (AVQSVContext*)vpp->hw_device_ctx->data;
 
     av_log(vpp->ctx, AV_LOG_INFO, "vpp: vidmem_init_surface: ");
 
@@ -134,20 +205,17 @@ static void vidmem_init_surface(VPPContext *vpp)
     /*
      * 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 = FFMAX(vpp->req[0].NumFrameSuggested, 1);
+    vpp->req[1].NumFrameSuggested = FFMAX(vpp->req[1].NumFrameSuggested, 1);
+    av_log(vpp->ctx, AV_LOG_INFO, "in.num = %d, out.num = %d\n",
             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);
+    qsv->nb_vpp_surfaces = vpp->req[0].NumFrameSuggested;
 
-    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->pFrameAllocator->Alloc(vpp->pFrameAllocator->pthis, &vpp->req[1], vpp->out_response);
+
+    vpp->num_surfaces_out = vpp->out_response->NumFrameActual;
     vpp->out_surface      = av_mallocz(sizeof(*vpp->out_surface) * vpp->num_surfaces_out);
     VPP_CHECK_POINTER(vpp->out_surface);
 
@@ -333,6 +401,7 @@ static int sysmem_input_get_surface( AVFilterLink *inlink, AVFrame* picref, mfxF
 
 static int vidmem_input_get_surface( AVFilterLink *inlink, AVFrame* picref, mfxFrameSurface1 **surface )
 {
+    if (picref->format == AV_PIX_FMT_QSV && picref->data[3]) {
     if (picref->data[3]) {
         *surface = (mfxFrameSurface1*)picref->data[3];
     } else {
@@ -533,11 +602,19 @@ static int initial_vpp( VPPContext *vpp )
 static int config_vpp(AVFilterLink *inlink, AVFrame * pic)
 {
     AVFilterContext *ctx = inlink->dst;
-    VPPContext *vpp= ctx->priv;
-    mfxVideoParam mfxParamsVideo;
-    int           ret;
+    VPPContext      *vpp = ctx->priv;
+    mfxVideoParam    mfxParamsVideo;
+    int              ret;
 
     av_log(vpp->ctx, AV_LOG_INFO, "vpp configuration and call mfxVideoVPP_Init\n");
+    if (ctx->hw_device_ctx) {
+        AVQSVContext *qsv    = (AVQSVContext*)ctx->hw_device_ctx->data;
+        vpp->hw_device_ctx   = av_buffer_ref(ctx->hw_device_ctx);
+        vpp->pFrameAllocator = qsv->pFrameAllocator;
+        vpp->iopattern       = qsv->iopattern;
+        vpp->session         = qsv->session;
+    }
+
     if (!vpp->session) {
         ret = ff_qsv_init_internal_session(ctx, &vpp->internal_qs);
         if (ret < 0)
@@ -573,6 +650,32 @@ static void deconf_vpp(AVFilterContext *ctx)
     vpp->vpp_ready = 0;
 }
 
+static void vidmem_buffer_free(void *opaque, uint8_t *data)
+{
+    //do nothing
+}
+
+static AVFrame *vidmem_buffer_alloc(AVFilterContext *ctx, mfxFrameSurface1 *pSurface)
+{
+    AVFrame *frame = av_frame_alloc();
+    if (!frame)
+        return NULL;
+
+    frame->buf[0] = av_buffer_create((uint8_t*)pSurface, sizeof(pSurface),
+                        vidmem_buffer_free, NULL, 0);
+    if (!frame->buf[0]) {
+        av_frame_free(&frame);
+        return AVERROR(ENOMEM);
+    }
+
+    frame->data[3] = frame->buf[0]->data;
+    frame->width   = ctx->outputs[0]->w;
+    frame->height  = ctx->outputs[0]->h;
+    frame->format  = ctx->outputs[0]->format;
+
+    return frame;
+}
+
 /*
  * Real filter func.
  * Push frame into mSDK and pop out filtered frames.
@@ -619,11 +722,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *picref)
             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 (!vpp->pFrameAllocator) {
+            /*
+            * 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));
+        } else {
+            out = vidmem_buffer_alloc(ctx, pOutSurface);
+        }
         if (!out) {
             ret = MFX_ERR_MEMORY_ALLOC;
             break;
@@ -670,10 +777,6 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *picref)
             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;
@@ -722,7 +825,6 @@ static int config_output(AVFilterLink *outlink)
     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;
 }
@@ -763,13 +865,18 @@ static av_cold int vpp_init(AVFilterContext *ctx)
     vpp->vpp_ready       = 0;
     vpp->ctx             = ctx;
     vpp->sysmem_cur_out_idx = 0;
+    vpp->hw_device_ctx   = NULL;
 
     return 0;
 }
 
 static av_cold void vpp_uninit(AVFilterContext *ctx)
 {
+    VPPContext *vpp = ctx->priv;
+
     deconf_vpp(ctx);
+    if (vpp->hw_device_ctx)
+        av_buffer_unref(&vpp->hw_device_ctx);
 }
 
 static int vpp_cmd_size(AVFilterContext *ctx, const char *arg)
