diff --git a/libavcodec/qsv.c b/libavcodec/qsv.c
index c180ca8..c61a29c 100644
--- a/libavcodec/qsv.c
+++ b/libavcodec/qsv.c
@@ -172,11 +172,16 @@ int ff_qsv_init_internal_session(void *avctx, QSVSession *qs)
 {
     mfxIMPL impl   = MFX_IMPL_AUTO_ANY;
     mfxVersion ver = { { QSV_VERSION_MINOR, QSV_VERSION_MAJOR } };
+    mfxInitParam par;
 
     const char *desc;
     int ret;
 
-    ret = MFXInit(impl, &ver, &qs->session);
+    memset(&par, 0, sizeof(par));
+    par.Implementation = impl;
+    par.Version = ver;
+    par.GPUCopy = qs->gpu_copy;
+    ret = MFXInitEx(par, &qs->session);
     if (ret < 0) {
         av_log(avctx, AV_LOG_ERROR, "Error initializing an internal MFX session\n");
         return ff_qsv_error(ret);
diff --git a/libavcodec/qsv_internal.h b/libavcodec/qsv_internal.h
index 58589df..39778a9 100644
--- a/libavcodec/qsv_internal.h
+++ b/libavcodec/qsv_internal.h
@@ -73,6 +73,7 @@ typedef struct QSVSession {
     int        fd_display;
     VADisplay  va_display;
 #endif
+    int        gpu_copy;
 } QSVSession;
 
 /**
diff --git a/libavcodec/qsvdec.c b/libavcodec/qsvdec.c
index 2075a23..08a5eaa 100644
--- a/libavcodec/qsvdec.c
+++ b/libavcodec/qsvdec.c
@@ -176,13 +176,25 @@ static int alloc_frame(AVCodecContext *avctx, QSVFrame *frame)
 {
     int ret;
 
-    ret = ff_get_buffer(avctx, frame->frame, AV_GET_BUFFER_FLAG_REF);
-    if (ret < 0)
-        return ret;
-
-    if (frame->frame->format == AV_PIX_FMT_QSV) {
+    if (avctx->pix_fmt == AV_PIX_FMT_QSV) {
+        ret = ff_get_buffer(avctx, frame->frame, AV_GET_BUFFER_FLAG_REF);
+        if (ret < 0)
+            return ret;
         frame->surface = (mfxFrameSurface1*)frame->frame->data[3];
     } else {
+        /*
+         * Align frame's width x height to 128x64.
+         * It's recommended to do so if GPU_Copy is turned on.
+         */
+        frame->frame->format = avctx->pix_fmt;
+        frame->frame->width  = FFALIGN(avctx->width, 128);
+        frame->frame->height = FFALIGN(avctx->height, 64);
+        ret = av_frame_get_buffer(frame->frame, 64);
+        if (ret < 0)
+            return ret;
+        frame->frame->width  = avctx->width;
+        frame->frame->height = avctx->height;
+
         frame->surface_internal.Info.BitDepthLuma   = 8;
         frame->surface_internal.Info.BitDepthChroma = 8;
         frame->surface_internal.Info.FourCC         = MFX_FOURCC_NV12;
diff --git a/libavcodec/qsvdec_h2645.c b/libavcodec/qsvdec_h2645.c
index 208302b..a4adc10 100755
--- a/libavcodec/qsvdec_h2645.c
+++ b/libavcodec/qsvdec_h2645.c
@@ -248,6 +248,12 @@ static const AVOption hevc_options[] = {
 
     { "load_plugins", "A :-separate list of hexadecimal plugin UIDs to load in an internal session",
         OFFSET(qsv.load_plugins), AV_OPT_TYPE_STRING, { .str = "" }, 0, 0, VD },
+
+    { "gpu_copy", "Enable gpu copy in sysmem mode [default = off]", OFFSET(qsv.internal_qs.gpu_copy), AV_OPT_TYPE_INT, { .i64 = MFX_GPUCOPY_OFF }, MFX_GPUCOPY_DEFAULT, MFX_GPUCOPY_OFF, .flags = VD, "gpu_copy" },
+    { "default",  NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_DEFAULT }, 0, 0, .flags = VD, "gpu_copy" },
+    { "on",       NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_ON },      0, 0, .flags = VD, "gpu_copy" },
+    { "off",      NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_OFF },     0, 0, .flags = VD, "gpu_copy" },
+
     { NULL },
 };
 
@@ -286,6 +292,12 @@ AVHWAccel ff_h264_qsv_hwaccel = {
 
 static const AVOption options[] = {
     { "async_depth", "Internal parallelization depth, the higher the value the higher the latency.", OFFSET(qsv.async_depth), AV_OPT_TYPE_INT, { .i64 = ASYNC_DEPTH_DEFAULT }, 0, INT_MAX, VD },
+
+    { "gpu_copy", "Enable gpu copy in sysmem mode [default = off]", OFFSET(qsv.internal_qs.gpu_copy), AV_OPT_TYPE_INT, { .i64 = MFX_GPUCOPY_OFF }, MFX_GPUCOPY_DEFAULT, MFX_GPUCOPY_OFF, .flags = VD, "gpu_copy" },
+    { "default",  NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_DEFAULT }, 0, 0, .flags = VD, "gpu_copy" },
+    { "on",       NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_ON },      0, 0, .flags = VD, "gpu_copy" },
+    { "off",      NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_OFF },     0, 0, .flags = VD, "gpu_copy" },
+
     { NULL },
 };
 
diff --git a/libavcodec/qsvdec_mpeg2.c b/libavcodec/qsvdec_mpeg2.c
index 70ccbc5..5e2354a 100644
--- a/libavcodec/qsvdec_mpeg2.c
+++ b/libavcodec/qsvdec_mpeg2.c
@@ -72,6 +72,12 @@ AVHWAccel ff_mpeg2_qsv_hwaccel = {
 #define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM
 static const AVOption options[] = {
     { "async_depth", "Internal parallelization depth, the higher the value the higher the latency.", OFFSET(qsv.async_depth), AV_OPT_TYPE_INT, { .i64 = ASYNC_DEPTH_DEFAULT }, 0, INT_MAX, VD },
+
+    { "gpu_copy", "Enable gpu copy in sysmem mode [default = off]", OFFSET(qsv.internal_qs.gpu_copy), AV_OPT_TYPE_INT, { .i64 = MFX_GPUCOPY_OFF }, MFX_GPUCOPY_DEFAULT, MFX_GPUCOPY_OFF, .flags = VD, "gpu_copy" },
+    { "default",  NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_DEFAULT }, 0, 0, .flags = VD, "gpu_copy" },
+    { "on",       NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_ON },      0, 0, .flags = VD, "gpu_copy" },
+    { "off",      NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_OFF },     0, 0, .flags = VD, "gpu_copy" },
+
     { NULL },
 };
 
diff --git a/libavcodec/qsvdec_vc1.c b/libavcodec/qsvdec_vc1.c
index fcf101f..4948b00 100644
--- a/libavcodec/qsvdec_vc1.c
+++ b/libavcodec/qsvdec_vc1.c
@@ -69,6 +69,12 @@ AVHWAccel ff_vc1_qsv_hwaccel = {
 #define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM
 static const AVOption options[] = {
     { "async_depth", "Internal parallelization depth, the higher the value the higher the latency.", OFFSET(qsv.async_depth), AV_OPT_TYPE_INT, { .i64 = ASYNC_DEPTH_DEFAULT }, 0, INT_MAX, VD },
+
+    { "gpu_copy", "Enable gpu copy in sysmem mode [default = off]", OFFSET(qsv.internal_qs.gpu_copy), AV_OPT_TYPE_INT, { .i64 = MFX_GPUCOPY_OFF }, MFX_GPUCOPY_DEFAULT, MFX_GPUCOPY_OFF, .flags = VD, "gpu_copy" },
+    { "default",  NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_DEFAULT }, 0, 0, .flags = VD, "gpu_copy" },
+    { "on",       NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_ON },      0, 0, .flags = VD, "gpu_copy" },
+    { "off",      NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_OFF },     0, 0, .flags = VD, "gpu_copy" },
+
     { NULL },
 };
 
diff --git a/libavcodec/qsvenc.h b/libavcodec/qsvenc.h
index 2d7bd32..9510d5c 100644
--- a/libavcodec/qsvenc.h
+++ b/libavcodec/qsvenc.h
@@ -70,6 +70,10 @@
 { "adaptive_b",     "Adaptive B-frame placement",             OFFSET(qsv.adaptive_b),     AV_OPT_TYPE_INT, { .i64 = -1 }, -1,          1, VE },                         \
 { "b_strategy",     "Strategy to choose between I/P/B-frames", OFFSET(qsv.b_strategy),    AV_OPT_TYPE_INT, { .i64 = -1 }, -1,          1, VE },                         \
 { "cavlc",          "Enable CAVLC",                           OFFSET(qsv.cavlc),          AV_OPT_TYPE_INT, { .i64 = 0 },   0,          1, VE },                         \
+{ "gpu_copy", "Enable gpu copy in sysmem mode [default = off]", OFFSET(qsv.internal_qs.gpu_copy), AV_OPT_TYPE_INT, { .i64 = MFX_GPUCOPY_OFF }, MFX_GPUCOPY_DEFAULT, MFX_GPUCOPY_OFF, .flags = VE, "gpu_copy" },\
+{ "default",  NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_DEFAULT }, 0, 0, .flags = VE, "gpu_copy" },\
+{ "on",       NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_ON },      0, 0, .flags = VE, "gpu_copy" },\
+{ "off",      NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_OFF },     0, 0, .flags = VE, "gpu_copy" },\
 
 typedef int SetEncodeCtrlCB (AVCodecContext *avctx,
                              const AVFrame *frame, mfxEncodeCtrl* enc_ctrl);
diff --git a/libavfilter/vf_qsvvpp.c b/libavfilter/vf_qsvvpp.c
index b1245d2..ce2ddeb 100644
--- a/libavfilter/vf_qsvvpp.c
+++ b/libavfilter/vf_qsvvpp.c
@@ -127,6 +127,12 @@ static const AVOption qsvvpp_options[] = {
     { "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 },
+
+    { "gpu_copy", "Enable gpu copy in sysmem mode [default = off]", OFFSET(internal_qs.gpu_copy), AV_OPT_TYPE_INT, { .i64 = MFX_GPUCOPY_OFF }, MFX_GPUCOPY_DEFAULT, MFX_GPUCOPY_OFF, .flags = FLAGS, "gpu_copy" },
+    { "default",  NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_DEFAULT }, 0, 0, .flags = FLAGS, "gpu_copy" },
+    { "on",       NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_ON },      0, 0, .flags = FLAGS, "gpu_copy" },
+    { "off",      NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_OFF },     0, 0, .flags = FLAGS, "gpu_copy" },
+
     { NULL }
 };
 
@@ -665,7 +671,7 @@ static AVFrame *vidmem_buffer_alloc(AVFilterContext *ctx, mfxFrameSurface1 *pSur
                         vidmem_buffer_free, NULL, 0);
     if (!frame->buf[0]) {
         av_frame_free(&frame);
-        return AVERROR(ENOMEM);
+        return NULL;
     }
 
     frame->data[3] = frame->buf[0]->data;
