documentation: fixed a heap o' typos
[platform/upstream/gstreamer.git] / sys / msdk / gstmsdkvpp.c
index 4ec260c..66b32e0 100644 (file)
 #include "gstmsdkcontextutil.h"
 #include "gstmsdkvpputil.h"
 
+#define EXT_FORMATS     ""
+
+#ifndef _WIN32
+#include "gstmsdkallocator_libva.h"
+#if VA_CHECK_VERSION(1, 4, 1)
+#undef EXT_FORMATS
+#define EXT_FORMATS     ", BGR10A2_LE"
+#endif
+#endif
+
+#if (MFX_VERSION >= 1028)
+#define EXT_SINK_FORMATS        ", RGB16, Y410, Y210"
+#define EXT_SRC_FORMATS         ", Y410, Y210"
+#elif (MFX_VERSION >= 1027)
+#define EXT_SINK_FORMATS        ", Y410, Y210"
+#define EXT_SRC_FORMATS         ", Y410, Y210"
+#else
+#define EXT_SINK_FORMATS        ""
+#define EXT_SRC_FORMATS         ""
+#endif
+
 GST_DEBUG_CATEGORY_EXTERN (gst_msdkvpp_debug);
 #define GST_CAT_DEFAULT gst_msdkvpp_debug
 
+#define SUPPORTED_SYSTEM_FORMAT \
+    "{ NV12, YV12, I420, YUY2, UYVY, VUYA, BGRA, BGRx, P010_10LE" EXT_SINK_FORMATS "}"
+#define SUPPORTED_DMABUF_FORMAT \
+    "{ NV12, BGRA, YUY2, UYVY, VUYA, P010_10LE" EXT_SINK_FORMATS "}"
+#define SRC_SYSTEM_FORMAT \
+    "{ BGRA, NV12, YUY2, UYVY, VUYA, BGRx, P010_10LE" EXT_FORMATS EXT_SRC_FORMATS "}"
+#define SRC_DMABUF_FORMAT       \
+    "{ BGRA, YUY2, UYVY, NV12, VUYA, BGRx, P010_10LE" EXT_FORMATS EXT_SRC_FORMATS "}"
+
+#ifndef _WIN32
+#define DMABUF_SINK_CAPS_STR \
+  GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_DMABUF, \
+      SUPPORTED_DMABUF_FORMAT)
+#else
+#define DMABUF_SINK_CAPS_STR ""
+#endif
+
+#ifndef _WIN32
+#define DMABUF_SRC_CAPS_STR \
+  GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_DMABUF, \
+      SRC_DMABUF_FORMAT) ";"
+#else
+#define DMABUF_SRC_CAPS_STR ""
+#endif
+
+
 static GstStaticPadTemplate gst_msdkvpp_sink_factory =
-GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
     GST_PAD_ALWAYS,
-    GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ NV12, I420, YUY2, UYVY, BGRA }")
-        ", " "interlace-mode = (string){ progressive, interleaved, mixed }"));
+    GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (SUPPORTED_SYSTEM_FORMAT)
+        ", " "interlace-mode = (string){ progressive, interleaved, mixed }" ";"
+        DMABUF_SINK_CAPS_STR));
 
 static GstStaticPadTemplate gst_msdkvpp_src_factory =
-GST_STATIC_PAD_TEMPLATE ("src",
+    GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
     GST_PAD_ALWAYS,
-    GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ NV12, BGRA }") ", "
-        "interlace-mode = (string){ progressive, interleaved, mixed }"));
+    GST_STATIC_CAPS (DMABUF_SRC_CAPS_STR
+        GST_VIDEO_CAPS_MAKE (SRC_SYSTEM_FORMAT) ", "
+        "interlace-mode = (string){ progressive, interleaved, mixed }" ";"));
 
 enum
 {
@@ -67,7 +116,9 @@ enum
   PROP_HARDWARE,
   PROP_ASYNC_DEPTH,
   PROP_DENOISE,
+#ifndef GST_REMOVE_DEPRECATED
   PROP_ROTATION,
+#endif
   PROP_DEINTERLACE_MODE,
   PROP_DEINTERLACE_METHOD,
   PROP_HUE,
@@ -75,15 +126,27 @@ enum
   PROP_BRIGHTNESS,
   PROP_CONTRAST,
   PROP_DETAIL,
+#ifndef GST_REMOVE_DEPRECATED
   PROP_MIRRORING,
+#endif
   PROP_SCALING_MODE,
+  PROP_FORCE_ASPECT_RATIO,
+  PROP_FRC_ALGORITHM,
+  PROP_VIDEO_DIRECTION,
+  PROP_CROP_LEFT,
+  PROP_CROP_RIGHT,
+  PROP_CROP_TOP,
+  PROP_CROP_BOTTOM,
   PROP_N,
 };
 
 #define PROP_HARDWARE_DEFAULT            TRUE
 #define PROP_ASYNC_DEPTH_DEFAULT         1
 #define PROP_DENOISE_DEFAULT             0
+#ifndef GST_REMOVE_DEPRECATED
 #define PROP_ROTATION_DEFAULT            MFX_ANGLE_0
+#define PROP_MIRRORING_DEFAULT           MFX_MIRRORING_DISABLED
+#endif
 #define PROP_DEINTERLACE_MODE_DEFAULT    GST_MSDKVPP_DEINTERLACE_MODE_AUTO
 #define PROP_DEINTERLACE_METHOD_DEFAULT  MFX_DEINTERLACING_BOB
 #define PROP_HUE_DEFAULT                 0
@@ -91,8 +154,14 @@ enum
 #define PROP_BRIGHTNESS_DEFAULT          0
 #define PROP_CONTRAST_DEFAULT            1
 #define PROP_DETAIL_DEFAULT              0
-#define PROP_MIRRORING_DEFAULT           MFX_MIRRORING_DISABLED
 #define PROP_SCALING_MODE_DEFAULT        MFX_SCALING_MODE_DEFAULT
+#define PROP_FORCE_ASPECT_RATIO_DEFAULT  TRUE
+#define PROP_FRC_ALGORITHM_DEFAULT       _MFX_FRC_ALGORITHM_NONE
+#define PROP_VIDEO_DIRECTION_DEFAULT     GST_VIDEO_ORIENTATION_IDENTITY
+#define PROP_CROP_LEFT_DEFAULT           0
+#define PROP_CROP_RIGHT_DEFAULT          0
+#define PROP_CROP_TOP_DEFAULT            0
+#define PROP_CROP_BOTTOM_DEFAULT         0
 
 #define gst_msdkvpp_parent_class parent_class
 G_DEFINE_TYPE (GstMsdkVPP, gst_msdkvpp, GST_TYPE_BASE_TRANSFORM);
@@ -129,11 +198,24 @@ ensure_context (GstBaseTransform * trans)
     GST_INFO_OBJECT (thiz, "Found context from neighbour %" GST_PTR_FORMAT,
         thiz->context);
 
-    if (gst_msdk_context_get_job_type (thiz->context) & GST_MSDK_JOB_VPP) {
-      GstMsdkContext *parent_context;
+    /* Check GST_MSDK_JOB_VPP and GST_MSDK_JOB_ENCODER together to avoid sharing context
+     * between VPP and ENCODER
+     * Example:
+     * gst-launch-1.0 videotestsrc ! msdkvpp ! video/x-raw,format=YUY2 ! msdkh264enc ! fakesink
+     */
+    if (gst_msdk_context_get_job_type (thiz->context) & (GST_MSDK_JOB_ENCODER |
+            GST_MSDK_JOB_VPP)) {
+      GstMsdkContext *parent_context, *msdk_context;
 
       parent_context = thiz->context;
-      thiz->context = gst_msdk_context_new_with_parent (parent_context);
+      msdk_context = gst_msdk_context_new_with_parent (parent_context);
+
+      if (!msdk_context) {
+        GST_ERROR_OBJECT (thiz, "Context creation failed");
+        return FALSE;
+      }
+
+      thiz->context = msdk_context;
       gst_object_unref (parent_context);
 
       GST_INFO_OBJECT (thiz,
@@ -215,13 +297,16 @@ gst_msdkvpp_create_buffer_pool (GstMsdkVPP * thiz, GstPadDirection direction,
   GstVideoAlignment align;
   GstAllocationParams params = { 0, 31, 0, 0, };
   mfxFrameAllocResponse *alloc_resp = NULL;
+  gboolean use_dmabuf = FALSE;
 
   if (direction == GST_PAD_SINK) {
     alloc_resp = &thiz->in_alloc_resp;
     pool_info = &thiz->sinkpad_buffer_pool_info;
+    use_dmabuf = thiz->use_sinkpad_dmabuf;
   } else if (direction == GST_PAD_SRC) {
     alloc_resp = &thiz->out_alloc_resp;
     pool_info = &thiz->srcpad_buffer_pool_info;
+    use_dmabuf = thiz->use_srcpad_dmabuf;
   }
 
   pool = gst_msdk_buffer_pool_new (thiz->context, alloc_resp);
@@ -231,10 +316,13 @@ gst_msdkvpp_create_buffer_pool (GstMsdkVPP * thiz, GstPadDirection direction,
   if (!gst_video_info_from_caps (&info, caps))
     goto error_no_video_info;
 
-  gst_msdk_set_video_alignment (&info, &align);
+  gst_msdk_set_video_alignment (&info, 0, 0, &align);
   gst_video_info_align (&info, &align);
 
-  if (thiz->use_video_memory)
+  if (use_dmabuf)
+    allocator =
+        gst_msdk_dmabuf_allocator_new (thiz->context, &info, alloc_resp);
+  else if (thiz->use_video_memory)
     allocator = gst_msdk_video_allocator_new (thiz->context, &info, alloc_resp);
   else
     allocator = gst_msdk_system_allocator_new (&info);
@@ -249,9 +337,13 @@ gst_msdkvpp_create_buffer_pool (GstMsdkVPP * thiz, GstPadDirection direction,
   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
   gst_buffer_pool_config_add_option (config,
       GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
-  if (thiz->use_video_memory)
+  if (thiz->use_video_memory) {
     gst_buffer_pool_config_add_option (config,
         GST_BUFFER_POOL_OPTION_MSDK_USE_VIDEO_MEMORY);
+    if (use_dmabuf)
+      gst_buffer_pool_config_add_option (config,
+          GST_BUFFER_POOL_OPTION_MSDK_USE_DMABUF);
+  }
 
   gst_buffer_pool_config_set_video_alignment (config, &align);
   gst_buffer_pool_config_set_allocator (config, allocator, &params);
@@ -260,7 +352,7 @@ gst_msdkvpp_create_buffer_pool (GstMsdkVPP * thiz, GstPadDirection direction,
   if (!gst_buffer_pool_set_config (pool, config))
     goto error_pool_config;
 
-  /* Updating pool_info with algined info of allocator */
+  /* Updating pool_info with aligned info of allocator */
   *pool_info = info;
 
   return pool;
@@ -273,27 +365,41 @@ error_no_pool:
 error_no_video_info:
   {
     GST_INFO_OBJECT (thiz, "Failed to get Video info from caps");
+    gst_object_unref (pool);
     return NULL;
   }
 error_no_allocator:
   {
     GST_INFO_OBJECT (thiz, "Failed to create allocator");
-    if (pool)
-      gst_object_unref (pool);
+    gst_object_unref (pool);
     return NULL;
   }
 error_pool_config:
   {
     GST_INFO_OBJECT (thiz, "Failed to set config");
-    if (pool)
-      gst_object_unref (pool);
-    if (allocator)
-      gst_object_unref (allocator);
+    gst_object_unref (pool);
+    gst_object_unref (allocator);
     return NULL;
   }
 }
 
 static gboolean
+_gst_caps_has_feature (const GstCaps * caps, const gchar * feature)
+{
+  guint i;
+
+  for (i = 0; i < gst_caps_get_size (caps); i++) {
+    GstCapsFeatures *const features = gst_caps_get_features (caps, i);
+    /* Skip ANY features, we need an exact match for correct evaluation */
+    if (gst_caps_features_is_any (features))
+      continue;
+    if (gst_caps_features_contains (features, feature))
+      return TRUE;
+  }
+  return FALSE;
+}
+
+static gboolean
 gst_msdkvpp_decide_allocation (GstBaseTransform * trans, GstQuery * query)
 {
   GstMsdkVPP *thiz = GST_MSDKVPP (trans);
@@ -315,38 +421,48 @@ gst_msdkvpp_decide_allocation (GstBaseTransform * trans, GstQuery * query)
     GST_ERROR_OBJECT (thiz, "Failed to get video info");
     return FALSE;
   }
+  /* if downstream allocation query supports dmabuf-capsfeatures,
+   * we do allocate dmabuf backed memory */
+  if (_gst_caps_has_feature (caps, GST_CAPS_FEATURE_MEMORY_DMABUF)) {
+    GST_INFO_OBJECT (thiz, "MSDK VPP srcpad uses DMABuf memory");
+    thiz->use_srcpad_dmabuf = TRUE;
+  }
 
   if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL))
     thiz->add_video_meta = TRUE;
   else
     thiz->add_video_meta = FALSE;
 
-  if (gst_query_get_n_allocation_pools (query) > 0) {
-    gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min_buffers,
-        &max_buffers);
+  /* Check whether the query has pool */
+  if (gst_query_get_n_allocation_pools (query) > 0)
     update_pool = TRUE;
-    size = MAX (size, GST_VIDEO_INFO_SIZE (&info));
 
-    if (pool && !GST_IS_MSDK_BUFFER_POOL (pool)) {
-      GST_INFO_OBJECT (thiz, "ignoring non-msdk pool: %" GST_PTR_FORMAT, pool);
-      g_clear_object (&pool);
-    }
-  }
+  /* increase the min_buffers with number of concurrent vpp operations */
+  min_buffers += thiz->async_depth;
 
-  if (!pool) {
+  /* invalidate the cached pool if there is an allocation_query */
+  if (thiz->srcpad_buffer_pool)
     gst_object_unref (thiz->srcpad_buffer_pool);
-    pool =
-        gst_msdkvpp_create_buffer_pool (thiz, GST_PAD_SRC, caps, min_buffers);
-    thiz->srcpad_buffer_pool = pool;
-
-    /* get the configured pool properties inorder to set in query */
-    config = gst_buffer_pool_get_config (pool);
-    gst_buffer_pool_config_get_params (config, &caps, &size, &min_buffers,
-        &max_buffers);
-    if (gst_buffer_pool_config_get_allocator (config, &allocator, &params))
-      gst_query_add_allocation_param (query, allocator, &params);
-    gst_structure_free (config);
-  }
+
+  /* Always create a pool for vpp out buffers. Each of the msdk element
+   * has to create it's own mfxsurfacepool which is an msdk constraint.
+   * For eg: Each Msdk component (vpp, dec and enc) will invoke the external
+   * Frame allocator for video-memory usage.So sharing the pool between
+   * gst-msdk elements might not be a good idea, rather each element
+   * can check the buffer type (whether it is from msdk-buffer pool)
+   * to make sure there is no copy. Since we share the context between
+   * msdk elements, using buffers from one sdk's framealloator in another
+   * sdk-components is perfectly fine */
+  pool = gst_msdkvpp_create_buffer_pool (thiz, GST_PAD_SRC, caps, min_buffers);
+  thiz->srcpad_buffer_pool = pool;
+
+  /* get the configured pool properties inorder to set in query */
+  config = gst_buffer_pool_get_config (pool);
+  gst_buffer_pool_config_get_params (config, &caps, &size, &min_buffers,
+      &max_buffers);
+  if (gst_buffer_pool_config_get_allocator (config, &allocator, &params))
+    gst_query_add_allocation_param (query, allocator, &params);
+  gst_structure_free (config);
 
   if (update_pool)
     gst_query_set_nth_allocation_pool (query, 0, pool, size, min_buffers,
@@ -373,8 +489,9 @@ gst_msdkvpp_propose_allocation (GstBaseTransform * trans,
   GstCaps *caps;
   GstStructure *config;
   gboolean need_pool;
-  guint size;
   GstAllocationParams params;
+  guint size;
+  guint min_buffers = thiz->async_depth + 1;
 
   gst_query_parse_allocation (query, &caps, &need_pool);
   if (!caps) {
@@ -387,37 +504,44 @@ gst_msdkvpp_propose_allocation (GstBaseTransform * trans,
     return FALSE;
   }
 
-  size = MAX (info.size, GST_VIDEO_INFO_SIZE (&thiz->sinkpad_buffer_pool_info));
+  /* if upstream allocation query supports dmabuf-capsfeatures,
+   * we do allocate dmabuf backed memory */
+  if (_gst_caps_has_feature (caps, GST_CAPS_FEATURE_MEMORY_DMABUF)) {
+    GST_INFO_OBJECT (thiz, "MSDK VPP srcpad uses DMABuf memory");
+    thiz->use_sinkpad_dmabuf = TRUE;
+  }
 
-  /* We already created a pool while setting the caps
-   * just to make sure the pipeline works even if there is
-   * no allocation query from upstream (theoratical ??).Provide the
-   * same pool in query if required/possible */
+  if (need_pool) {
+    /* alwys provide a new pool for upstream to help re-negotiation
+     * more info here: https://bugzilla.gnome.org/show_bug.cgi?id=748344 */
+    pool = gst_msdkvpp_create_buffer_pool (thiz, GST_PAD_SINK, caps,
+        min_buffers);
+  }
+
+  /* Update the internal pool if any allocation attribute changed */
   if (!gst_video_info_is_equal (&thiz->sinkpad_buffer_pool_info, &info)) {
     gst_object_unref (thiz->sinkpad_buffer_pool);
-    thiz->sinkpad_buffer_pool =
-        gst_msdkvpp_create_buffer_pool (thiz, GST_PAD_SINK, caps,
-        thiz->in_num_surfaces);
+    thiz->sinkpad_buffer_pool = gst_msdkvpp_create_buffer_pool (thiz,
+        GST_PAD_SINK, caps, min_buffers);
   }
 
-  pool = thiz->sinkpad_buffer_pool;
-
+  /* get the size and allocator params from configured pool and set it in query */
+  if (!need_pool)
+    pool = gst_object_ref (thiz->sinkpad_buffer_pool);
   config = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (pool));
-
   gst_buffer_pool_config_get_params (config, NULL, &size, NULL, NULL);
-
   if (gst_buffer_pool_config_get_allocator (config, &allocator, &params))
     gst_query_add_allocation_param (query, allocator, &params);
   gst_structure_free (config);
 
-  /* if upstream does't have a pool requirement, set only
+  /* if upstream doesn't have a pool requirement, set only
    *  size, min_buffers and max_buffers in query */
-  if (!need_pool)
-    pool = NULL;
-
-  gst_query_add_allocation_pool (query, pool, size, thiz->in_num_surfaces, 0);
+  gst_query_add_allocation_pool (query, need_pool ? pool : NULL, size,
+      min_buffers, 0);
   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
 
+  gst_object_unref (pool);
+
   return GST_BASE_TRANSFORM_CLASS (parent_class)->propose_allocation (trans,
       decide_query, query);
 }
@@ -455,11 +579,78 @@ get_surface_from_pool (GstMsdkVPP * thiz, GstBufferPool * pool,
   return msdk_surface;
 }
 
+#ifndef _WIN32
+static gboolean
+import_dmabuf_to_msdk_surface (GstMsdkVPP * thiz, GstBuffer * buf,
+    MsdkSurface * msdk_surface)
+{
+  GstMemory *mem = NULL;
+  GstVideoInfo vinfo;
+  GstVideoMeta *vmeta;
+  GstMsdkMemoryID *msdk_mid = NULL;
+  mfxFrameSurface1 *mfx_surface = NULL;
+  gint fd, i;
+
+  mem = gst_buffer_peek_memory (buf, 0);
+  fd = gst_dmabuf_memory_get_fd (mem);
+  if (fd < 0)
+    return FALSE;
+
+  vinfo = thiz->sinkpad_info;
+
+  /* Update offset/stride/size if there is VideoMeta attached to
+   * the buffer */
+  vmeta = gst_buffer_get_video_meta (buf);
+  if (vmeta) {
+    if (GST_VIDEO_INFO_FORMAT (&vinfo) != vmeta->format ||
+        GST_VIDEO_INFO_WIDTH (&vinfo) != vmeta->width ||
+        GST_VIDEO_INFO_HEIGHT (&vinfo) != vmeta->height ||
+        GST_VIDEO_INFO_N_PLANES (&vinfo) != vmeta->n_planes) {
+      GST_ERROR_OBJECT (thiz, "VideoMeta attached to buffer is not matching"
+          "the negotiated width/height/format");
+      return FALSE;
+    }
+    for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&vinfo); ++i) {
+      GST_VIDEO_INFO_PLANE_OFFSET (&vinfo, i) = vmeta->offset[i];
+      GST_VIDEO_INFO_PLANE_STRIDE (&vinfo, i) = vmeta->stride[i];
+    }
+    GST_VIDEO_INFO_SIZE (&vinfo) = gst_buffer_get_size (buf);
+  }
+
+  /* Upstream neither accepted the msdk pool nor the msdk buffer size restrictions.
+   * Current media-driver and GMMLib will fail due to strict memory size restrictions.
+   * Ideally, media-driver should accept what ever memory coming from other drivers
+   * in case of dmabuf-import and this is how the intel-vaapi-driver works.
+   * For now, in order to avoid any crash we check the buffer size and fallback
+   * to copy frame method.
+   *
+   * See this: https://github.com/intel/media-driver/issues/169
+   * */
+  if (GST_VIDEO_INFO_SIZE (&vinfo) <
+      GST_VIDEO_INFO_SIZE (&thiz->sinkpad_buffer_pool_info))
+    return FALSE;
+
+  mfx_surface = msdk_surface->surface;
+  msdk_mid = (GstMsdkMemoryID *) mfx_surface->Data.MemId;
+
+  /* release the internal memory storage of associated mfxSurface */
+  gst_msdk_replace_mfx_memid (thiz->context, mfx_surface, VA_INVALID_ID);
+
+  /* export dmabuf to vasurface */
+  if (!gst_msdk_export_dmabuf_to_vasurface (thiz->context, &vinfo, fd,
+          msdk_mid->surface))
+    return FALSE;
+
+  return TRUE;
+}
+#endif
+
 static MsdkSurface *
 get_msdk_surface_from_input_buffer (GstMsdkVPP * thiz, GstBuffer * inbuf)
 {
   GstVideoFrame src_frame, out_frame;
   MsdkSurface *msdk_surface;
+  GstMemory *mem = NULL;
 
   if (gst_msdk_is_msdk_buffer (inbuf)) {
     msdk_surface = g_slice_new0 (MsdkSurface);
@@ -469,12 +660,26 @@ get_msdk_surface_from_input_buffer (GstMsdkVPP * thiz, GstBuffer * inbuf)
   }
 
   /* If upstream hasn't accpeted the proposed msdk bufferpool,
-   * just copy frame to msdk buffer and take a surface from it.
-   */
+   * just copy frame (if not dmabuf backed) to msdk buffer and
+   * take a surface from it.   */
   if (!(msdk_surface =
           get_surface_from_pool (thiz, thiz->sinkpad_buffer_pool, NULL)))
     goto error;
 
+#ifndef _WIN32
+  /************ dmabuf-import ************* */
+  /* if upstream provided a dmabuf backed memory, but not an msdk
+   * buffer, we could export the dmabuf to underlined vasurface */
+  mem = gst_buffer_peek_memory (inbuf, 0);
+  if (gst_is_dmabuf_memory (mem)) {
+    if (import_dmabuf_to_msdk_surface (thiz, inbuf, msdk_surface))
+      return msdk_surface;
+    else
+      GST_INFO_OBJECT (thiz, "Upstream dmabuf-backed memory is not imported"
+          "to the msdk surface, fall back to the copy input frame method");
+  }
+#endif
+
   if (!gst_video_frame_map (&src_frame, &thiz->sinkpad_info, inbuf,
           GST_MAP_READ)) {
     GST_ERROR_OBJECT (thiz, "failed to map the frame for source");
@@ -509,12 +714,17 @@ gst_msdkvpp_transform (GstBaseTransform * trans, GstBuffer * inbuf,
     GstBuffer * outbuf)
 {
   GstMsdkVPP *thiz = GST_MSDKVPP (trans);
+  GstClockTime timestamp;
+  GstFlowReturn ret = GST_FLOW_OK;
   mfxSession session;
   mfxSyncPoint sync_point = NULL;
   mfxStatus status;
+  mfxFrameInfo *in_info = NULL;
   MsdkSurface *in_surface = NULL;
   MsdkSurface *out_surface = NULL;
 
+  timestamp = GST_BUFFER_TIMESTAMP (inbuf);
+
   in_surface = get_msdk_surface_from_input_buffer (thiz, inbuf);
   if (!in_surface)
     return GST_FLOW_ERROR;
@@ -524,51 +734,94 @@ gst_msdkvpp_transform (GstBaseTransform * trans, GstBuffer * inbuf,
     out_surface->surface = gst_msdk_get_surface_from_buffer (outbuf);
   } else {
     GST_ERROR ("Failed to get msdk outsurface!");
+    free_msdk_surface (in_surface);
     return GST_FLOW_ERROR;
   }
 
-  session = gst_msdk_context_get_session (thiz->context);
-  for (;;) {
-    status =
-        MFXVideoVPP_RunFrameVPPAsync (session, in_surface->surface,
-        out_surface->surface, NULL, &sync_point);
-    if (status != MFX_WRN_DEVICE_BUSY)
-      break;
-    /* If device is busy, wait 1ms and retry, as per MSDK's recomendation */
-    g_usleep (1000);
-  };
-
-  if (status != MFX_ERR_NONE && status != MFX_ERR_MORE_DATA
-      && status != MFX_ERR_MORE_SURFACE)
-    goto vpp_error;
-
-  /* No output generated */
-  if (status == MFX_ERR_MORE_DATA)
-    goto error_more_data;
-  if (sync_point)
-    MFXVideoCORE_SyncOperation (session, sync_point, 10000);
+  /* update surface crop info (NOTE: msdk min frame size is 2x2) */
+  in_info = &in_surface->surface->Info;
+  if ((thiz->crop_left + thiz->crop_right >= in_info->CropW - 1)
+      || (thiz->crop_top + thiz->crop_bottom >= in_info->CropH - 1)) {
+    GST_WARNING_OBJECT (thiz, "ignoring crop... cropping too much!");
+  } else {
+    in_info->CropX = thiz->crop_left;
+    in_info->CropY = thiz->crop_top;
+    in_info->CropW -= thiz->crop_left + thiz->crop_right;
+    in_info->CropH -= thiz->crop_top + thiz->crop_bottom;
+  }
 
-  /* More than one output buffers are generated */
-  if (status == MFX_ERR_MORE_SURFACE)
-    status = MFX_ERR_NONE;
+  session = gst_msdk_context_get_session (thiz->context);
 
-  gst_buffer_copy_into (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
+  /* outer loop is for handling FrameRate Control and deinterlace use cases */
+  do {
+    for (;;) {
+      status =
+          MFXVideoVPP_RunFrameVPPAsync (session, in_surface->surface,
+          out_surface->surface, NULL, &sync_point);
+      if (status != MFX_WRN_DEVICE_BUSY)
+        break;
+      /* If device is busy, wait 1ms and retry, as per MSDK's recommendation */
+      g_usleep (1000);
+    };
+
+    if (status == MFX_WRN_INCOMPATIBLE_VIDEO_PARAM)
+      GST_WARNING_OBJECT (thiz, "VPP returned: %s",
+          msdk_status_to_string (status));
+    else if (status != MFX_ERR_NONE && status != MFX_ERR_MORE_DATA
+        && status != MFX_ERR_MORE_SURFACE)
+      goto vpp_error;
+
+    /* No output generated */
+    if (status == MFX_ERR_MORE_DATA)
+      goto error_more_data;
+
+    /* Wait for vpp operation to complete, the magic number 300000 below
+     * is used in MSDK samples
+     * #define MSDK_VPP_WAIT_INTERVAL 300000
+     */
+    if (sync_point &&
+        MFXVideoCORE_SyncOperation (session, sync_point,
+            300000) != MFX_ERR_NONE)
+      GST_WARNING_OBJECT (thiz, "failed to do sync operation");
+
+    /* More than one output buffers are generated */
+    if (status == MFX_ERR_MORE_SURFACE) {
+      GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
+      GST_BUFFER_DURATION (outbuf) = thiz->buffer_duration;
+      timestamp += thiz->buffer_duration;
+      ret = gst_pad_push (GST_BASE_TRANSFORM_SRC_PAD (trans), outbuf);
+      if (ret != GST_FLOW_OK)
+        goto error_push_buffer;
+      outbuf = create_output_buffer (thiz);
+    } else {
+      GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
+      GST_BUFFER_DURATION (outbuf) = thiz->buffer_duration;
+    }
+  } while (status == MFX_ERR_MORE_SURFACE);
 
-  free_msdk_surface (in_surface);
-  return GST_FLOW_OK;
+  goto transform_end;
 
 vpp_error:
   GST_ERROR_OBJECT (thiz, "MSDK Failed to do VPP");
-  free_msdk_surface (in_surface);
-  free_msdk_surface (out_surface);
-  return GST_FLOW_ERROR;
+  ret = GST_FLOW_ERROR;
+  goto transform_end;
 
 error_more_data:
   GST_WARNING_OBJECT (thiz,
-      "MSDK Requries additional input for processing, "
+      "MSDK Requires additional input for processing, "
       "Retruning FLOW_DROPPED since no output buffer was generated");
+  ret = GST_BASE_TRANSFORM_FLOW_DROPPED;
+  goto transform_end;
+
+error_push_buffer:
+  GST_DEBUG_OBJECT (thiz, "failed to push output buffer: %s",
+      gst_flow_get_name (ret));
+
+transform_end:
   free_msdk_surface (in_surface);
-  return GST_BASE_TRANSFORM_FLOW_DROPPED;
+  free_msdk_surface (out_surface);
+
+  return ret;
 }
 
 static void
@@ -579,10 +832,15 @@ gst_msdkvpp_close (GstMsdkVPP * thiz)
   if (!thiz->context)
     return;
 
+  if (thiz->use_video_memory) {
+    gst_msdk_frame_free (thiz->context, &thiz->in_alloc_resp);
+    gst_msdk_frame_free (thiz->context, &thiz->out_alloc_resp);
+  }
+
   GST_DEBUG_OBJECT (thiz, "Closing VPP 0x%p", thiz->context);
   status = MFXVideoVPP_Close (gst_msdk_context_get_session (thiz->context));
   if (status != MFX_ERR_NONE && status != MFX_ERR_NOT_INITIALIZED) {
-    GST_WARNING_OBJECT (thiz, "Encoder close failed (%s)",
+    GST_WARNING_OBJECT (thiz, "VPP close failed (%s)",
         msdk_status_to_string (status));
   }
 
@@ -598,7 +856,7 @@ gst_msdkvpp_close (GstMsdkVPP * thiz)
     gst_object_unref (thiz->srcpad_buffer_pool);
   thiz->srcpad_buffer_pool = NULL;
 
-  thiz->field_duration = GST_CLOCK_TIME_NONE;
+  thiz->buffer_duration = GST_CLOCK_TIME_NONE;
   gst_video_info_init (&thiz->sinkpad_info);
   gst_video_info_init (&thiz->srcpad_info);
 }
@@ -606,8 +864,6 @@ gst_msdkvpp_close (GstMsdkVPP * thiz)
 static void
 ensure_filters (GstMsdkVPP * thiz)
 {
-  guint n_filters = 0;
-
   /* Denoise */
   if (thiz->flags & GST_MSDK_FLAG_DENOISE) {
     mfxExtVPPDenoise *mfx_denoise = &thiz->mfx_denoise;
@@ -615,19 +871,15 @@ ensure_filters (GstMsdkVPP * thiz)
     mfx_denoise->Header.BufferSz = sizeof (mfxExtVPPDenoise);
     mfx_denoise->DenoiseFactor = thiz->denoise_factor;
     gst_msdkvpp_add_extra_param (thiz, (mfxExtBuffer *) mfx_denoise);
-    thiz->max_filter_algorithms[n_filters] = MFX_EXTBUFF_VPP_DENOISE;
-    n_filters++;
   }
 
   /* Rotation */
-  if (thiz->flags & GST_MSDK_FLAG_ROTATION) {
+  if (thiz->rotation != MFX_ANGLE_0) {
     mfxExtVPPRotation *mfx_rotation = &thiz->mfx_rotation;
     mfx_rotation->Header.BufferId = MFX_EXTBUFF_VPP_ROTATION;
     mfx_rotation->Header.BufferSz = sizeof (mfxExtVPPRotation);
     mfx_rotation->Angle = thiz->rotation;
     gst_msdkvpp_add_extra_param (thiz, (mfxExtBuffer *) mfx_rotation);
-    thiz->max_filter_algorithms[n_filters] = MFX_EXTBUFF_VPP_ROTATION;
-    n_filters++;
   }
 
   /* Deinterlace */
@@ -637,8 +889,6 @@ ensure_filters (GstMsdkVPP * thiz)
     mfx_deinterlace->Header.BufferSz = sizeof (mfxExtVPPDeinterlacing);
     mfx_deinterlace->Mode = thiz->deinterlace_method;
     gst_msdkvpp_add_extra_param (thiz, (mfxExtBuffer *) mfx_deinterlace);
-    thiz->max_filter_algorithms[n_filters] = MFX_EXTBUFF_VPP_DEINTERLACING;
-    n_filters++;
   }
 
   /* Colorbalance(ProcAmp) */
@@ -652,8 +902,6 @@ ensure_filters (GstMsdkVPP * thiz)
     mfx_procamp->Brightness = thiz->brightness;
     mfx_procamp->Contrast = thiz->contrast;
     gst_msdkvpp_add_extra_param (thiz, (mfxExtBuffer *) mfx_procamp);
-    thiz->max_filter_algorithms[n_filters] = MFX_EXTBUFF_VPP_PROCAMP;
-    n_filters++;
   }
 
   /* Detail/Edge enhancement */
@@ -663,29 +911,15 @@ ensure_filters (GstMsdkVPP * thiz)
     mfx_detail->Header.BufferSz = sizeof (mfxExtVPPDetail);
     mfx_detail->DetailFactor = thiz->detail;
     gst_msdkvpp_add_extra_param (thiz, (mfxExtBuffer *) mfx_detail);
-    thiz->max_filter_algorithms[n_filters] = MFX_EXTBUFF_VPP_DETAIL;
-    n_filters++;
-  }
-
-  /* mfxExtVPPDoUse */
-  if (n_filters) {
-    mfxExtVPPDoUse *mfx_vpp_douse = &thiz->mfx_vpp_douse;
-    mfx_vpp_douse->Header.BufferId = MFX_EXTBUFF_VPP_DOUSE;
-    mfx_vpp_douse->Header.BufferSz = sizeof (mfxExtVPPDoUse);
-    mfx_vpp_douse->NumAlg = n_filters;
-    mfx_vpp_douse->AlgList = thiz->max_filter_algorithms;
-    gst_msdkvpp_add_extra_param (thiz, (mfxExtBuffer *) mfx_vpp_douse);
   }
 
   /* Mirroring */
-  if (thiz->flags & GST_MSDK_FLAG_MIRRORING) {
+  if (thiz->mirroring != MFX_MIRRORING_DISABLED) {
     mfxExtVPPMirroring *mfx_mirroring = &thiz->mfx_mirroring;
     mfx_mirroring->Header.BufferId = MFX_EXTBUFF_VPP_MIRRORING;
     mfx_mirroring->Header.BufferSz = sizeof (mfxExtVPPMirroring);
     mfx_mirroring->Type = thiz->mirroring;
     gst_msdkvpp_add_extra_param (thiz, (mfxExtBuffer *) mfx_mirroring);
-    thiz->max_filter_algorithms[n_filters] = MFX_EXTBUFF_VPP_MIRRORING;
-    n_filters++;
   }
 
   /* Scaling Mode */
@@ -695,8 +929,15 @@ ensure_filters (GstMsdkVPP * thiz)
     mfx_scaling->Header.BufferSz = sizeof (mfxExtVPPScaling);
     mfx_scaling->ScalingMode = thiz->scaling_mode;
     gst_msdkvpp_add_extra_param (thiz, (mfxExtBuffer *) mfx_scaling);
-    thiz->max_filter_algorithms[n_filters] = MFX_EXTBUFF_VPP_SCALING;
-    n_filters++;
+  }
+
+  /* FRC */
+  if (thiz->flags & GST_MSDK_FLAG_FRC) {
+    mfxExtVPPFrameRateConversion *mfx_frc = &thiz->mfx_frc;
+    mfx_frc->Header.BufferId = MFX_EXTBUFF_VPP_FRAME_RATE_CONVERSION;
+    mfx_frc->Header.BufferSz = sizeof (mfxExtVPPFrameRateConversion);
+    mfx_frc->Algorithm = thiz->frc_algm;
+    gst_msdkvpp_add_extra_param (thiz, (mfxExtBuffer *) mfx_frc);
   }
 }
 
@@ -709,6 +950,14 @@ gst_msdkvpp_set_passthrough (GstMsdkVPP * thiz)
   if (thiz->flags)
     passthrough = FALSE;
 
+  /* vpp could be needed in some specific circumstances, for eg:
+   * input surface is dmabuf and output must be videomemory. So far
+   * the underline iHD driver doesn't seems to support dmabuf mapping,
+   * so we could explicitly ask msdkvpp to provide non-dambuf videomemory
+   * surfaces as output thourgh capsfileters */
+  if (thiz->need_vpp)
+    passthrough = FALSE;
+
   /* no passthrough if there is change in out width,height or format */
   if (GST_VIDEO_INFO_WIDTH (&thiz->sinkpad_info) !=
       GST_VIDEO_INFO_WIDTH (&thiz->srcpad_info)
@@ -718,9 +967,7 @@ gst_msdkvpp_set_passthrough (GstMsdkVPP * thiz)
       GST_VIDEO_INFO_FORMAT (&thiz->srcpad_info))
     passthrough = FALSE;
 
-  GST_OBJECT_UNLOCK (thiz);
   gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (thiz), passthrough);
-  GST_OBJECT_LOCK (thiz);
 }
 
 static gboolean
@@ -738,6 +985,19 @@ gst_msdkvpp_initialize (GstMsdkVPP * thiz)
   GST_OBJECT_LOCK (thiz);
   session = gst_msdk_context_get_session (thiz->context);
 
+  /* Close the current session if the session has been initialized,
+   * otherwise the subsequent function call of MFXVideoVPP_Init() will
+   * fail
+   */
+  if (thiz->initialized) {
+    if (thiz->use_video_memory) {
+      gst_msdk_frame_free (thiz->context, &thiz->in_alloc_resp);
+      gst_msdk_frame_free (thiz->context, &thiz->out_alloc_resp);
+    }
+
+    MFXVideoVPP_Close (session);
+  }
+
   if (thiz->use_video_memory) {
     gst_msdk_set_frame_allocator (thiz->context);
     thiz->param.IOPattern =
@@ -754,15 +1014,37 @@ gst_msdkvpp_initialize (GstMsdkVPP * thiz)
   /* update output video attributes, only CSC and Scaling are supported for now */
   gst_msdk_set_mfx_frame_info_from_video_info (&thiz->param.vpp.Out,
       &thiz->srcpad_info);
-  thiz->param.vpp.Out.FrameRateExtN =
-      GST_VIDEO_INFO_FPS_N (&thiz->sinkpad_info);
-  thiz->param.vpp.Out.FrameRateExtD =
-      GST_VIDEO_INFO_FPS_D (&thiz->sinkpad_info);
+
+  /* use msdk frame rarte control if there is a mismatch in In & OUt fps  */
+  if (GST_VIDEO_INFO_FPS_N (&thiz->srcpad_info) &&
+      (GST_VIDEO_INFO_FPS_N (&thiz->sinkpad_info) !=
+          GST_VIDEO_INFO_FPS_N (&thiz->srcpad_info)
+          || GST_VIDEO_INFO_FPS_D (&thiz->sinkpad_info) !=
+          GST_VIDEO_INFO_FPS_D (&thiz->srcpad_info))) {
+    thiz->flags |= GST_MSDK_FLAG_FRC;
+    /* So far this is the only algorithm which is working somewhat good */
+    thiz->frc_algm = MFX_FRCALGM_PRESERVE_TIMESTAMP;
+  }
+
+  /* work-around to avoid zero fps in msdk structure */
+  if (!thiz->param.vpp.In.FrameRateExtN)
+    thiz->param.vpp.In.FrameRateExtN = 30;
+  if (!thiz->param.vpp.Out.FrameRateExtN)
+    thiz->param.vpp.Out.FrameRateExtN = thiz->param.vpp.In.FrameRateExtN;
 
   /* set vpp out picstruct as progressive if deinterlacing enabled */
   if (thiz->flags & GST_MSDK_FLAG_DEINTERLACE)
     thiz->param.vpp.Out.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
 
+  /* Enable the required filters */
+  ensure_filters (thiz);
+
+  /* Add extended buffers */
+  if (thiz->num_extra_params) {
+    thiz->param.NumExtParam = thiz->num_extra_params;
+    thiz->param.ExtParam = thiz->extra_params;
+  }
+
   /* validate parameters and allow the Media SDK to make adjustments */
   status = MFXVideoVPP_Query (session, &thiz->param, &thiz->param);
   if (status < MFX_ERR_NONE) {
@@ -774,18 +1056,6 @@ gst_msdkvpp_initialize (GstMsdkVPP * thiz)
         msdk_status_to_string (status));
   }
 
-  /* Enable the required filters */
-  ensure_filters (thiz);
-
-  /* set passthrough according to filter operation change */
-  gst_msdkvpp_set_passthrough (thiz);
-
-  /* Add exteneded buffers */
-  if (thiz->num_extra_params) {
-    thiz->param.NumExtParam = thiz->num_extra_params;
-    thiz->param.ExtParam = thiz->extra_params;
-  }
-
   status = MFXVideoVPP_QueryIOSurf (session, &thiz->param, request);
   if (status < MFX_ERR_NONE) {
     GST_ERROR_OBJECT (thiz, "VPP Query IO surfaces failed (%s)",
@@ -798,8 +1068,15 @@ gst_msdkvpp_initialize (GstMsdkVPP * thiz)
 
   if (thiz->use_video_memory) {
     /* Input surface pool pre-allocation */
+    request[0].Type |= MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET;
+    if (thiz->use_sinkpad_dmabuf)
+      request[0].Type |= MFX_MEMTYPE_EXPORT_FRAME;
     gst_msdk_frame_alloc (thiz->context, &(request[0]), &thiz->in_alloc_resp);
+
     /* Output surface pool pre-allocation */
+    request[1].Type |= MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET;
+    if (thiz->use_srcpad_dmabuf)
+      request[1].Type |= MFX_MEMTYPE_EXPORT_FRAME;
     gst_msdk_frame_alloc (thiz->context, &(request[1]), &thiz->out_alloc_resp);
   }
 
@@ -816,6 +1093,7 @@ gst_msdkvpp_initialize (GstMsdkVPP * thiz)
         msdk_status_to_string (status));
   }
 
+  thiz->initialized = TRUE;
   GST_OBJECT_UNLOCK (thiz);
   return TRUE;
 
@@ -836,6 +1114,9 @@ gst_msdkvpp_set_caps (GstBaseTransform * trans, GstCaps * caps,
   gboolean srcpad_info_changed = FALSE;
   gboolean deinterlace;
 
+  if (gst_caps_get_features (caps, 0) != gst_caps_get_features (out_caps, 0))
+    thiz->need_vpp = 1;
+
   gst_video_info_from_caps (&in_info, caps);
   gst_video_info_from_caps (&out_info, out_caps);
 
@@ -844,6 +1125,9 @@ gst_msdkvpp_set_caps (GstBaseTransform * trans, GstCaps * caps,
   if (!gst_video_info_is_equal (&out_info, &thiz->srcpad_info))
     srcpad_info_changed = TRUE;
 
+  if (!sinkpad_info_changed && !srcpad_info_changed && thiz->initialized)
+    return TRUE;
+
   thiz->sinkpad_info = in_info;
   thiz->srcpad_info = out_info;
 #ifndef _WIN32
@@ -852,21 +1136,25 @@ gst_msdkvpp_set_caps (GstBaseTransform * trans, GstCaps * caps,
   thiz->use_video_memory = FALSE;
 #endif
 
-  if (!sinkpad_info_changed && !srcpad_info_changed)
-    return TRUE;
-
   /* check for deinterlace requirement */
   deinterlace = gst_msdkvpp_is_deinterlace_enabled (thiz, &in_info);
   if (deinterlace)
     thiz->flags |= GST_MSDK_FLAG_DEINTERLACE;
-  thiz->field_duration = GST_VIDEO_INFO_FPS_N (&in_info) > 0 ?
-      gst_util_uint64_scale (GST_SECOND, GST_VIDEO_INFO_FPS_D (&in_info),
-      (1 + deinterlace) * GST_VIDEO_INFO_FPS_N (&in_info)) : 0;
+
+  thiz->buffer_duration = GST_VIDEO_INFO_FPS_N (&out_info) > 0 ?
+      gst_util_uint64_scale (GST_SECOND, GST_VIDEO_INFO_FPS_D (&out_info),
+      GST_VIDEO_INFO_FPS_N (&out_info)) : 0;
 
   if (!gst_msdkvpp_initialize (thiz))
     return FALSE;
 
+  /* set passthrough according to filter operation change */
+  gst_msdkvpp_set_passthrough (thiz);
+
   /* Ensure sinkpad buffer pool */
+  if (thiz->sinkpad_buffer_pool)
+    gst_object_unref (thiz->sinkpad_buffer_pool);
+
   thiz->sinkpad_buffer_pool =
       gst_msdkvpp_create_buffer_pool (thiz, GST_PAD_SINK, caps,
       thiz->in_num_surfaces);
@@ -875,6 +1163,9 @@ gst_msdkvpp_set_caps (GstBaseTransform * trans, GstCaps * caps,
     return FALSE;
   }
   /* Ensure a srcpad buffer pool */
+  if (thiz->srcpad_buffer_pool)
+    gst_object_unref (thiz->srcpad_buffer_pool);
+
   thiz->srcpad_buffer_pool =
       gst_msdkvpp_create_buffer_pool (thiz, GST_PAD_SRC, out_caps,
       thiz->out_num_surfaces);
@@ -886,21 +1177,77 @@ gst_msdkvpp_set_caps (GstBaseTransform * trans, GstCaps * caps,
   return TRUE;
 }
 
+static gboolean
+pad_can_dmabuf (GstMsdkVPP * thiz, GstPadDirection direction, GstCaps * filter)
+{
+  gboolean ret = FALSE;
+  GstCaps *caps, *out_caps;
+  GstPad *pad;
+  GstBaseTransform *trans = GST_BASE_TRANSFORM (thiz);
+
+  if (direction == GST_PAD_SRC)
+    pad = GST_BASE_TRANSFORM_SRC_PAD (trans);
+  else
+    pad = GST_BASE_TRANSFORM_SINK_PAD (trans);
+
+  /* make a copy of filter caps since we need to alter the structure
+   * by adding dmabuf-capsfeatures */
+  caps = gst_caps_copy (filter);
+  gst_caps_set_features (caps, 0,
+      gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_DMABUF));
+
+  out_caps = gst_pad_peer_query_caps (pad, caps);
+  if (!out_caps)
+    goto done;
+
+  if (gst_caps_is_any (out_caps) || gst_caps_is_empty (out_caps)
+      || out_caps == caps)
+    goto done;
+
+  if (_gst_caps_has_feature (out_caps, GST_CAPS_FEATURE_MEMORY_DMABUF))
+    ret = TRUE;
+done:
+  if (caps)
+    gst_caps_unref (caps);
+  if (out_caps)
+    gst_caps_unref (out_caps);
+  return ret;
+}
+
 static GstCaps *
 gst_msdkvpp_fixate_caps (GstBaseTransform * trans,
     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
 {
   GstMsdkVPP *thiz = GST_MSDKVPP (trans);
   GstCaps *result = NULL;
+  gboolean *use_dmabuf;
 
-  if (direction == GST_PAD_SRC)
+  if (direction == GST_PAD_SRC) {
     result = gst_caps_fixate (result);
-  else {
+    use_dmabuf = &thiz->use_sinkpad_dmabuf;
+  } else {
+    /*
+     * Override mirroring & rotation properties once video-direction
+     * is set explicitly
+     */
+    if (thiz->flags & GST_MSDK_FLAG_VIDEO_DIRECTION)
+      gst_msdk_get_mfx_video_orientation_from_video_direction
+          (thiz->video_direction, &thiz->mirroring, &thiz->rotation);
+
     result = gst_msdkvpp_fixate_srccaps (thiz, caps, othercaps);
+    use_dmabuf = &thiz->use_srcpad_dmabuf;
   }
 
   GST_DEBUG_OBJECT (trans, "fixated to %" GST_PTR_FORMAT, result);
   gst_caps_unref (othercaps);
+
+  if (pad_can_dmabuf (thiz,
+          direction == GST_PAD_SRC ? GST_PAD_SINK : GST_PAD_SRC, result)) {
+    gst_caps_set_features (result, 0,
+        gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_DMABUF, NULL));
+    *use_dmabuf = TRUE;
+  }
+
   return result;
 }
 
@@ -966,10 +1313,16 @@ gst_msdkvpp_set_property (GObject * object, guint prop_id,
       thiz->denoise_factor = g_value_get_uint (value);
       thiz->flags |= GST_MSDK_FLAG_DENOISE;
       break;
+#ifndef GST_REMOVE_DEPRECATED
     case PROP_ROTATION:
       thiz->rotation = g_value_get_enum (value);
       thiz->flags |= GST_MSDK_FLAG_ROTATION;
       break;
+    case PROP_MIRRORING:
+      thiz->mirroring = g_value_get_enum (value);
+      thiz->flags |= GST_MSDK_FLAG_MIRRORING;
+      break;
+#endif
     case PROP_DEINTERLACE_MODE:
       thiz->deinterlace_mode = g_value_get_enum (value);
       break;
@@ -996,14 +1349,32 @@ gst_msdkvpp_set_property (GObject * object, guint prop_id,
       thiz->detail = g_value_get_uint (value);
       thiz->flags |= GST_MSDK_FLAG_DETAIL;
       break;
-    case PROP_MIRRORING:
-      thiz->mirroring = g_value_get_enum (value);
-      thiz->flags |= GST_MSDK_FLAG_MIRRORING;
-      break;
     case PROP_SCALING_MODE:
       thiz->scaling_mode = g_value_get_enum (value);
       thiz->flags |= GST_MSDK_FLAG_SCALING_MODE;
       break;
+    case PROP_FORCE_ASPECT_RATIO:
+      thiz->keep_aspect = g_value_get_boolean (value);
+      break;
+    case PROP_FRC_ALGORITHM:
+      thiz->frc_algm = g_value_get_enum (value);
+      break;
+    case PROP_VIDEO_DIRECTION:
+      thiz->video_direction = g_value_get_enum (value);
+      thiz->flags |= GST_MSDK_FLAG_VIDEO_DIRECTION;
+      break;
+    case PROP_CROP_LEFT:
+      thiz->crop_left = g_value_get_uint (value);
+      break;
+    case PROP_CROP_RIGHT:
+      thiz->crop_right = g_value_get_uint (value);
+      break;
+    case PROP_CROP_TOP:
+      thiz->crop_top = g_value_get_uint (value);
+      break;
+    case PROP_CROP_BOTTOM:
+      thiz->crop_bottom = g_value_get_uint (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1026,9 +1397,14 @@ gst_msdkvpp_get_property (GObject * object, guint prop_id,
     case PROP_DENOISE:
       g_value_set_uint (value, thiz->denoise_factor);
       break;
+#ifndef GST_REMOVE_DEPRECATED
     case PROP_ROTATION:
       g_value_set_enum (value, thiz->rotation);
       break;
+    case PROP_MIRRORING:
+      g_value_set_enum (value, thiz->mirroring);
+      break;
+#endif
     case PROP_DEINTERLACE_MODE:
       g_value_set_enum (value, thiz->deinterlace_mode);
       break;
@@ -1050,12 +1426,30 @@ gst_msdkvpp_get_property (GObject * object, guint prop_id,
     case PROP_DETAIL:
       g_value_set_uint (value, thiz->detail);
       break;
-    case PROP_MIRRORING:
-      g_value_set_enum (value, thiz->mirroring);
-      break;
     case PROP_SCALING_MODE:
       g_value_set_enum (value, thiz->scaling_mode);
       break;
+    case PROP_FORCE_ASPECT_RATIO:
+      g_value_set_boolean (value, thiz->keep_aspect);
+      break;
+    case PROP_FRC_ALGORITHM:
+      g_value_set_enum (value, thiz->frc_algm);
+      break;
+    case PROP_VIDEO_DIRECTION:
+      g_value_set_enum (value, thiz->video_direction);
+      break;
+    case PROP_CROP_LEFT:
+      g_value_set_uint (value, thiz->crop_left);
+      break;
+    case PROP_CROP_RIGHT:
+      g_value_set_uint (value, thiz->crop_right);
+      break;
+    case PROP_CROP_TOP:
+      g_value_set_uint (value, thiz->crop_top);
+      break;
+    case PROP_CROP_BOTTOM:
+      g_value_set_uint (value, thiz->crop_bottom);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1141,10 +1535,20 @@ gst_msdkvpp_class_init (GstMsdkVPPClass * klass)
       "Denoising Factor",
       0, 100, PROP_DENOISE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
+#ifndef GST_REMOVE_DEPRECATED
   obj_properties[PROP_ROTATION] =
       g_param_spec_enum ("rotation", "Rotation",
-      "Rotation Angle", gst_msdkvpp_rotation_get_type (),
-      PROP_ROTATION_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+      "Rotation Angle (DEPRECATED, use video-direction instead)",
+      gst_msdkvpp_rotation_get_type (), PROP_ROTATION_DEFAULT,
+      G_PARAM_DEPRECATED | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  obj_properties[PROP_MIRRORING] =
+      g_param_spec_enum ("mirroring", "Mirroring",
+      "The Mirroring type (DEPRECATED, use video-direction instead)",
+      gst_msdkvpp_mirroring_get_type (), PROP_MIRRORING_DEFAULT,
+      G_PARAM_DEPRECATED | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+#endif
 
   obj_properties[PROP_DEINTERLACE_MODE] =
       g_param_spec_enum ("deinterlace-mode", "Deinterlace Mode",
@@ -1185,36 +1589,90 @@ gst_msdkvpp_class_init (GstMsdkVPPClass * klass)
       "The factor of detail/edge enhancement filter algorithm",
       0, 100, PROP_DETAIL_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
-  obj_properties[PROP_MIRRORING] =
-      g_param_spec_enum ("mirroring", "Mirroring",
-      "The Mirroring type", gst_msdkvpp_mirroring_get_type (),
-      PROP_MIRRORING_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-
   obj_properties[PROP_SCALING_MODE] =
       g_param_spec_enum ("scaling-mode", "Scaling Mode",
       "The Scaling mode to use", gst_msdkvpp_scaling_mode_get_type (),
       PROP_SCALING_MODE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
+  obj_properties[PROP_FORCE_ASPECT_RATIO] =
+      g_param_spec_boolean ("force-aspect-ratio", "Force Aspect Ratio",
+      "When enabled, scaling will respect original aspect ratio",
+      PROP_FORCE_ASPECT_RATIO_DEFAULT,
+      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  obj_properties[PROP_FRC_ALGORITHM] =
+      g_param_spec_enum ("frc-algorithm", "FrameRateControl Algorithm",
+      "The Framerate Control Alogorithm to use",
+      gst_msdkvpp_frc_algorithm_get_type (), PROP_FRC_ALGORITHM_DEFAULT,
+      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  /*
+   * The video-direction to use, expressed as an enum value. See
+   * #GstVideoOrientationMethod.
+   */
+  obj_properties[PROP_VIDEO_DIRECTION] = g_param_spec_enum ("video-direction",
+      "Video Direction", "Video direction: rotation and flipping"
+#ifndef GST_REMOVE_DEPRECATED
+      ", it will override both mirroring & rotation properties if set explicitly"
+#endif
+      ,
+      GST_TYPE_VIDEO_ORIENTATION_METHOD,
+      PROP_VIDEO_DIRECTION_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  obj_properties[PROP_CROP_LEFT] = g_param_spec_uint ("crop-left",
+      "Crop Left", "Pixels to crop at left",
+      0, G_MAXUINT16, PROP_CROP_LEFT_DEFAULT,
+      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  obj_properties[PROP_CROP_RIGHT] = g_param_spec_uint ("crop-right",
+      "Crop Right", "Pixels to crop at right",
+      0, G_MAXUINT16, PROP_CROP_RIGHT_DEFAULT,
+      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  obj_properties[PROP_CROP_TOP] = g_param_spec_uint ("crop-top",
+      "Crop Top", "Pixels to crop at top",
+      0, G_MAXUINT16, PROP_CROP_TOP_DEFAULT,
+      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  obj_properties[PROP_CROP_BOTTOM] = g_param_spec_uint ("crop-bottom",
+      "Crop Bottom", "Pixels to crop at bottom",
+      0, G_MAXUINT16, PROP_CROP_BOTTOM_DEFAULT,
+      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
   g_object_class_install_properties (gobject_class, PROP_N, obj_properties);
 }
 
 static void
 gst_msdkvpp_init (GstMsdkVPP * thiz)
 {
+  thiz->initialized = FALSE;
   thiz->hardware = PROP_HARDWARE_DEFAULT;
   thiz->async_depth = PROP_ASYNC_DEPTH_DEFAULT;
   thiz->denoise_factor = PROP_DENOISE_DEFAULT;
+#ifndef GST_REMOVE_DEPRECATED
   thiz->rotation = PROP_ROTATION_DEFAULT;
+  thiz->mirroring = PROP_MIRRORING_DEFAULT;
+#else
+  thiz->rotation = MFX_ANGLE_0;
+  thiz->mirroring = MFX_MIRRORING_DISABLED;
+#endif
   thiz->deinterlace_mode = PROP_DEINTERLACE_MODE_DEFAULT;
   thiz->deinterlace_method = PROP_DEINTERLACE_METHOD_DEFAULT;
-  thiz->field_duration = GST_CLOCK_TIME_NONE;
+  thiz->buffer_duration = GST_CLOCK_TIME_NONE;
   thiz->hue = PROP_HUE_DEFAULT;
   thiz->saturation = PROP_SATURATION_DEFAULT;
   thiz->brightness = PROP_BRIGHTNESS_DEFAULT;
   thiz->contrast = PROP_CONTRAST_DEFAULT;
   thiz->detail = PROP_DETAIL_DEFAULT;
-  thiz->mirroring = PROP_MIRRORING_DEFAULT;
   thiz->scaling_mode = PROP_SCALING_MODE_DEFAULT;
+  thiz->keep_aspect = PROP_FORCE_ASPECT_RATIO_DEFAULT;
+  thiz->frc_algm = PROP_FRC_ALGORITHM_DEFAULT;
+  thiz->video_direction = PROP_VIDEO_DIRECTION_DEFAULT;
+  thiz->crop_left = PROP_CROP_LEFT_DEFAULT;
+  thiz->crop_right = PROP_CROP_RIGHT_DEFAULT;
+  thiz->crop_top = PROP_CROP_TOP_DEFAULT;
+  thiz->crop_bottom = PROP_CROP_BOTTOM_DEFAULT;
+
   gst_video_info_init (&thiz->sinkpad_info);
   gst_video_info_init (&thiz->srcpad_info);
 }