plugins: expose I420 format for interop with SW elements.
[platform/upstream/gstreamer-vaapi.git] / gst / vaapi / gstvaapipluginutil.c
index 504d53a..fe84771 100644 (file)
@@ -1,7 +1,8 @@
 /*
  *  gstvaapipluginutil.h - VA-API plugin helpers
  *
- *  Copyright (C) 2011-2012 Intel Corporation
+ *  Copyright (C) 2011-2014 Intel Corporation
+ *    Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
  *  Copyright (C) 2011 Collabora
  *    Author: Nicolas Dufresne <nicolas.dufresne@collabora.co.uk>
  *
  *  Boston, MA 02110-1301 USA
  */
 
-#ifdef HAVE_CONFIG_H
-# include "config.h"
+#include "gst/vaapi/sysdeps.h"
+#include "gstvaapivideocontext.h"
+#if USE_DRM
+# include <gst/vaapi/gstvaapidisplay_drm.h>
 #endif
-#include <string.h>
-#include <gst/video/videocontext.h>
 #if USE_X11
 # include <gst/vaapi/gstvaapidisplay_x11.h>
 #endif
 # include <gst/vaapi/gstvaapidisplay_wayland.h>
 #endif
 #include "gstvaapipluginutil.h"
+#include "gstvaapipluginbase.h"
 
 /* Preferred first */
 static const char *display_types[] = {
-    "gst-vaapi-display",
-    "vaapi-display",
+  "gst-vaapi-display",
+  "vaapi-display",
 #if USE_WAYLAND
-    "wl-display",
-    "wl-display-name",
+  "wl-display",
+  "wl-display-name",
 #endif
 #if USE_X11
-    "x11-display",
-    "x11-display-name",
+  "x11-display",
+  "x11-display-name",
 #endif
-    NULL
+#if USE_DRM
+  "drm-device",
+  "drm-device-path",
+#endif
+  NULL
 };
 
-typedef struct {
-    const gchar        *type_str;
-    GstVaapiDisplayType type;
-    GstVaapiDisplay * (*create_display)(const gchar *);
+typedef struct
+{
+  const gchar *type_str;
+  GstVaapiDisplayType type;
+  GstVaapiDisplay *(*create_display) (const gchar *);
 } DisplayMap;
 
 static const DisplayMap g_display_map[] = {
 #if USE_WAYLAND
-    { "wayland",
-      GST_VAAPI_DISPLAY_TYPE_WAYLAND,
-      gst_vaapi_display_wayland_new },
+  {"wayland",
+        GST_VAAPI_DISPLAY_TYPE_WAYLAND,
+      gst_vaapi_display_wayland_new},
+#endif
+#if USE_GLX
+  {"glx",
+        GST_VAAPI_DISPLAY_TYPE_GLX,
+      gst_vaapi_display_glx_new},
 #endif
 #if USE_X11
-    { "x11",
-      GST_VAAPI_DISPLAY_TYPE_X11,
-      gst_vaapi_display_x11_new },
+  {"x11",
+        GST_VAAPI_DISPLAY_TYPE_X11,
+      gst_vaapi_display_x11_new},
 #endif
-#if USE_GLX
-    { "glx",
-      GST_VAAPI_DISPLAY_TYPE_GLX,
-      gst_vaapi_display_glx_new },
+#if USE_DRM
+  {"drm",
+        GST_VAAPI_DISPLAY_TYPE_DRM,
+      gst_vaapi_display_drm_new},
 #endif
-    { NULL, }
+  {NULL,}
 };
 
+static GstVaapiDisplay *
+gst_vaapi_create_display (GstVaapiDisplayType display_type)
+{
+  GstVaapiDisplay *display = NULL;
+  const DisplayMap *m;
+
+  for (m = g_display_map; m->type_str != NULL; m++) {
+    if (display_type != GST_VAAPI_DISPLAY_TYPE_ANY && display_type != m->type)
+      continue;
+
+    display = m->create_display (NULL);
+    if (display || display_type != GST_VAAPI_DISPLAY_TYPE_ANY)
+      break;
+  }
+  return display;
+}
+
 gboolean
-gst_vaapi_ensure_display(
-    gpointer             element,
-    GstVaapiDisplayType  display_type,
-    GstVaapiDisplay    **display_ptr
-)
+gst_vaapi_ensure_display (gpointer element, GstVaapiDisplayType type)
 {
-    GstVaapiDisplay *display;
-    GstVideoContext *context;
-    const DisplayMap *m;
-
-    g_return_val_if_fail(GST_IS_VIDEO_CONTEXT(element), FALSE);
-    g_return_val_if_fail(display_ptr != NULL, FALSE);
-
-    /* Already exist ? */
-    display = *display_ptr;
-    if (display)
-        return TRUE;
-
-    context = GST_VIDEO_CONTEXT(element);
-    g_return_val_if_fail(context != NULL, FALSE);
-
-    gst_video_context_prepare(context, display_types);
-
-    /* Neighbour found and it updated the display */
-    if (*display_ptr)
-        return TRUE;
-
-    /* If no neighboor, or application not interested, use system default */
-    for (m = g_display_map; m->type_str != NULL; m++) {
-        if (display_type != GST_VAAPI_DISPLAY_TYPE_ANY &&
-            display_type != m->type)
-            continue;
-
-        display = m->create_display(NULL);
-        if (display) {
-            /* FIXME: allocator should return NULL if an error occurred */
-            if (gst_vaapi_display_get_display(display)) {
-                display_type = m->type;
-                break;
-            }
-            g_object_unref(display);
-            display = NULL;
-        }
+  GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (element);
+  GstVaapiDisplay *display;
+  GstVideoContext *context;
 
-        if (display_type != GST_VAAPI_DISPLAY_TYPE_ANY)
-            break;
-    }
+  g_return_val_if_fail (GST_IS_VIDEO_CONTEXT (element), FALSE);
 
-    if (display_ptr)
-        *display_ptr = display;
-    return display != NULL;
+  context = GST_VIDEO_CONTEXT (element);
+  g_return_val_if_fail (context != NULL, FALSE);
+
+  gst_vaapi_video_context_prepare (context, display_types);
+
+  /* Neighbour found and it updated the display */
+  if (plugin->display
+      && gst_vaapi_display_type_is_compatible (plugin->display_type, type))
+    return TRUE;
+
+  /* If no neighboor, or application not interested, use system default */
+  display = gst_vaapi_create_display (type);
+  if (!display)
+    return FALSE;
+
+  gst_vaapi_video_context_propagate (context, display);
+  GST_VAAPI_PLUGIN_BASE_DISPLAY_REPLACE (plugin, display);
+  gst_vaapi_display_unref (display);
+  return TRUE;
 }
 
 void
-gst_vaapi_set_display(
-    const gchar      *type,
-    const GValue     *value,
-    GstVaapiDisplay **display
-)
+gst_vaapi_set_display (const gchar * type,
+    const GValue * value, GstVaapiDisplay ** display_ptr)
 {
-    GstVaapiDisplay *dpy = NULL;
+  GstVaapiDisplay *display = NULL;
 
-    if (!strcmp(type, "vaapi-display")) {
-        g_return_if_fail(G_VALUE_HOLDS_POINTER(value));
-        dpy = gst_vaapi_display_new_with_display(g_value_get_pointer(value));
-    }
-    else if (!strcmp(type, "gst-vaapi-display")) {
-        g_return_if_fail(G_VALUE_HOLDS_OBJECT(value));
-        dpy = g_value_dup_object(value);
-    }
+  if (!strcmp (type, "vaapi-display")) {
+    g_return_if_fail (G_VALUE_HOLDS_POINTER (value));
+    display = gst_vaapi_display_new_with_display (g_value_get_pointer (value));
+  } else if (!strcmp (type, "gst-vaapi-display")) {
+    g_return_if_fail (G_VALUE_HOLDS_POINTER (value));
+    display = gst_vaapi_display_ref (g_value_get_pointer (value));
+  }
+#if USE_DRM
+  else if (!strcmp (type, "drm-device")) {
+    gint device;
+    g_return_if_fail (G_VALUE_HOLDS_INT (value));
+    device = g_value_get_int (value);
+    display = gst_vaapi_display_drm_new_with_device (device);
+  } else if (!strcmp (type, "drm-device-path")) {
+    const gchar *device_path;
+    g_return_if_fail (G_VALUE_HOLDS_STRING (value));
+    device_path = g_value_get_string (value);
+    display = gst_vaapi_display_drm_new (device_path);
+  }
+#endif
 #if USE_X11
-    else if (!strcmp(type, "x11-display-name")) {
-        g_return_if_fail(G_VALUE_HOLDS_STRING(value));
+  else if (!strcmp (type, "x11-display-name")) {
+    g_return_if_fail (G_VALUE_HOLDS_STRING (value));
 #if USE_GLX
-        dpy = gst_vaapi_display_glx_new(g_value_get_string(value));
+    display = gst_vaapi_display_glx_new (g_value_get_string (value));
 #endif
-        if (!dpy)
-            dpy = gst_vaapi_display_x11_new(g_value_get_string(value));
-    }
-    else if (!strcmp(type, "x11-display")) {
-        g_return_if_fail(G_VALUE_HOLDS_POINTER(value));
+    if (!display)
+      display = gst_vaapi_display_x11_new (g_value_get_string (value));
+  } else if (!strcmp (type, "x11-display")) {
+    g_return_if_fail (G_VALUE_HOLDS_POINTER (value));
 #if USE_GLX
-        dpy = gst_vaapi_display_glx_new_with_display(g_value_get_pointer(value));
+    display =
+        gst_vaapi_display_glx_new_with_display (g_value_get_pointer (value));
 #endif
-        if (!dpy)
-            dpy = gst_vaapi_display_x11_new_with_display(g_value_get_pointer(value));
-    }
+    if (!display)
+      display =
+          gst_vaapi_display_x11_new_with_display (g_value_get_pointer (value));
+  }
 #endif
 #if USE_WAYLAND
-    else if (!strcmp(type, "wl-display")) {
-        struct wl_display *wl_display;
-        g_return_if_fail(G_VALUE_HOLDS_POINTER(value));
-        wl_display = g_value_get_pointer(value);
-        dpy = gst_vaapi_display_wayland_new_with_display(wl_display);
-    }
-    else if (!strcmp(type, "wl-display-name")) {
-        const gchar *display_name;
-        g_return_if_fail(G_VALUE_HOLDS_STRING(value));
-        display_name = g_value_get_string(value);
-        dpy = gst_vaapi_display_wayland_new(display_name);
-    }
+  else if (!strcmp (type, "wl-display")) {
+    struct wl_display *wl_display;
+    g_return_if_fail (G_VALUE_HOLDS_POINTER (value));
+    wl_display = g_value_get_pointer (value);
+    display = gst_vaapi_display_wayland_new_with_display (wl_display);
+  } else if (!strcmp (type, "wl-display-name")) {
+    const gchar *display_name;
+    g_return_if_fail (G_VALUE_HOLDS_STRING (value));
+    display_name = g_value_get_string (value);
+    display = gst_vaapi_display_wayland_new (display_name);
+  }
 #endif
 
-    if (dpy) {
-        if (*display)
-            g_object_unref(*display);
-        *display = dpy;
-    }
+  if (display) {
+    gst_vaapi_display_replace (display_ptr, display);
+    gst_vaapi_display_unref (display);
+  }
 }
 
 gboolean
-gst_vaapi_reply_to_query(GstQuery *query, GstVaapiDisplay *display)
+gst_vaapi_reply_to_query (GstQuery * query, GstVaapiDisplay * display)
 {
-    GstVaapiDisplayType display_type;
-    const gchar **types;
-    const gchar *type;
-    gint i;
-    gboolean res = FALSE;
+#if GST_CHECK_VERSION(1,1,0)
+  const gchar *type = NULL;
+  GstContext *context;
 
-    if (!display)
-        return FALSE;
+  if (GST_QUERY_TYPE (query) != GST_QUERY_CONTEXT)
+    return FALSE;
 
-    types = gst_video_context_query_get_supported_types(query);
+  if (!display)
+    return FALSE;
 
-    if (!types)
-        return FALSE;
+  if (!gst_query_parse_context_type (query, &type))
+    return FALSE;
 
-    display_type = gst_vaapi_display_get_display_type(display);
-    for (i = 0; types[i] && !res; i++) {
-        type = types[i];
+  if (g_strcmp0 (type, GST_VAAPI_DISPLAY_CONTEXT_TYPE_NAME))
+    return FALSE;
 
-        res = TRUE;
-        if (!strcmp(type, "gst-vaapi-display")) {
-            gst_video_context_query_set_object(query, type, G_OBJECT(display));
-        }
-        else if (!strcmp(type, "vaapi-display")) {
-            VADisplay vadpy = gst_vaapi_display_get_display(display);
-            gst_video_context_query_set_pointer(query, type, vadpy);
+  context = gst_vaapi_video_context_new_with_display (display, FALSE);
+  gst_query_set_context (query, context);
+  gst_context_unref (context);
+
+  return TRUE;
+#else
+  GstVaapiDisplayType display_type;
+  const gchar **types;
+  const gchar *type;
+  gint i;
+  gboolean res = FALSE;
+
+  if (GST_QUERY_TYPE (query) != GST_QUERY_CUSTOM)
+    return FALSE;
+
+  if (!display)
+    return FALSE;
+
+  types = gst_video_context_query_get_supported_types (query);
+
+  if (!types)
+    return FALSE;
+
+  display_type = gst_vaapi_display_get_display_type (display);
+  for (i = 0; types[i] && !res; i++) {
+    type = types[i];
+
+    res = TRUE;
+    if (!strcmp (type, "gst-vaapi-display")) {
+      gst_video_context_query_set_pointer (query, type, display);
+    } else if (!strcmp (type, "vaapi-display")) {
+      VADisplay vadpy = gst_vaapi_display_get_display (display);
+      gst_video_context_query_set_pointer (query, type, vadpy);
+    } else {
+      switch (display_type) {
+#if USE_DRM
+        case GST_VAAPI_DISPLAY_TYPE_DRM:{
+          GstVaapiDisplayDRM *const drm_dpy = GST_VAAPI_DISPLAY_DRM (display);
+          if (!strcmp (type, "drm-device-path"))
+            gst_video_context_query_set_string (query, type,
+                gst_vaapi_display_drm_get_device_path (drm_dpy));
+#if 0
+          /* XXX: gst_video_context_query_set_int() does not exist yet */
+          else if (!strcmp (type, "drm-device"))
+            gst_video_context_query_set_int (query, type,
+                gst_vaapi_display_drm_get_device (drm_dpy));
+#endif
+          else
+            res = FALSE;
+          break;
         }
-        else {
-            switch (display_type) {
+#endif
 #if USE_X11
 #if USE_GLX
-            case GST_VAAPI_DISPLAY_TYPE_GLX:
+        case GST_VAAPI_DISPLAY_TYPE_GLX:
 #endif
-            case GST_VAAPI_DISPLAY_TYPE_X11: {
-                GstVaapiDisplayX11 * const xvadpy =
-                    GST_VAAPI_DISPLAY_X11(display);
-                Display * const x11dpy =
-                    gst_vaapi_display_x11_get_display(xvadpy);
-                if (!strcmp(type, "x11-display"))
-                    gst_video_context_query_set_pointer(query, type, x11dpy);
-                else if (!strcmp(type, "x11-display-name"))
-                    gst_video_context_query_set_string(query, type,
-                        DisplayString(x11dpy));
-                else
-                    res = FALSE;
-                break;
-            }
+        case GST_VAAPI_DISPLAY_TYPE_X11:{
+          GstVaapiDisplayX11 *const xvadpy = GST_VAAPI_DISPLAY_X11 (display);
+          Display *const x11dpy = gst_vaapi_display_x11_get_display (xvadpy);
+          if (!strcmp (type, "x11-display"))
+            gst_video_context_query_set_pointer (query, type, x11dpy);
+          else if (!strcmp (type, "x11-display-name"))
+            gst_video_context_query_set_string (query, type,
+                DisplayString (x11dpy));
+          else
+            res = FALSE;
+          break;
+        }
 #endif
 #if USE_WAYLAND
-            case GST_VAAPI_DISPLAY_TYPE_WAYLAND: {
-                GstVaapiDisplayWayland * const wlvadpy =
-                    GST_VAAPI_DISPLAY_WAYLAND(display);
-                struct wl_display * const wldpy =
-                    gst_vaapi_display_wayland_get_display(wlvadpy);
-                if (!strcmp(type, "wl-display"))
-                    gst_video_context_query_set_pointer(query, type, wldpy);
-                else
-                    res = FALSE;
-                break;
-            }
-#endif
-            default:
-                res = FALSE;
-                break;
-            }
+        case GST_VAAPI_DISPLAY_TYPE_WAYLAND:{
+          GstVaapiDisplayWayland *const wlvadpy =
+              GST_VAAPI_DISPLAY_WAYLAND (display);
+          struct wl_display *const wldpy =
+              gst_vaapi_display_wayland_get_display (wlvadpy);
+          if (!strcmp (type, "wl-display"))
+            gst_video_context_query_set_pointer (query, type, wldpy);
+          else
+            res = FALSE;
+          break;
         }
+#endif
+        default:
+          res = FALSE;
+          break;
+      }
     }
-    return res;
+  }
+  return res;
+#endif /* !GST_CHECK_VERSION(1,1,0) */
 }
 
 gboolean
-gst_vaapi_append_surface_caps(GstCaps *out_caps, GstCaps *in_caps)
+gst_vaapi_append_surface_caps (GstCaps * out_caps, GstCaps * in_caps)
 {
-    GstStructure *structure;
-    const GValue *v_width, *v_height, *v_framerate, *v_par;
-    guint i, n_structures;
-
-    structure   = gst_caps_get_structure(in_caps, 0);
-    v_width     = gst_structure_get_value(structure, "width");
-    v_height    = gst_structure_get_value(structure, "height");
-    v_framerate = gst_structure_get_value(structure, "framerate");
-    v_par       = gst_structure_get_value(structure, "pixel-aspect-ratio");
-    if (!v_width || !v_height)
-        return FALSE;
-
-    n_structures = gst_caps_get_size(out_caps);
-    for (i = 0; i < n_structures; i++) {
-        structure = gst_caps_get_structure(out_caps, i);
-        gst_structure_set_value(structure, "width", v_width);
-        gst_structure_set_value(structure, "height", v_height);
-        if (v_framerate)
-            gst_structure_set_value(structure, "framerate", v_framerate);
-        if (v_par)
-            gst_structure_set_value(structure, "pixel-aspect-ratio", v_par);
-    }
+  GstStructure *structure;
+  const GValue *v_width, *v_height, *v_framerate, *v_par;
+  guint i, n_structures;
+
+  structure = gst_caps_get_structure (in_caps, 0);
+  v_width = gst_structure_get_value (structure, "width");
+  v_height = gst_structure_get_value (structure, "height");
+  v_framerate = gst_structure_get_value (structure, "framerate");
+  v_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
+  if (!v_width || !v_height)
+    return FALSE;
+
+  n_structures = gst_caps_get_size (out_caps);
+  for (i = 0; i < n_structures; i++) {
+    structure = gst_caps_get_structure (out_caps, i);
+    gst_structure_set_value (structure, "width", v_width);
+    gst_structure_set_value (structure, "height", v_height);
+    if (v_framerate)
+      gst_structure_set_value (structure, "framerate", v_framerate);
+    if (v_par)
+      gst_structure_set_value (structure, "pixel-aspect-ratio", v_par);
+  }
+  return TRUE;
+}
+
+gboolean
+gst_vaapi_apply_composition (GstVaapiSurface * surface, GstBuffer * buffer)
+{
+#if GST_CHECK_VERSION(1,0,0)
+  GstVideoOverlayCompositionMeta *const cmeta =
+      gst_buffer_get_video_overlay_composition_meta (buffer);
+  GstVideoOverlayComposition *composition;
+
+  if (!cmeta)
+    return TRUE;
+  composition = cmeta->overlay;
+#else
+  GstVideoOverlayComposition *const composition =
+      gst_video_buffer_get_overlay_composition (buffer);
+#endif
+  if (!composition)
     return TRUE;
+  return gst_vaapi_surface_set_subpictures_from_composition (surface,
+      composition, TRUE);
+}
+
+gboolean
+gst_vaapi_value_set_format (GValue * value, GstVideoFormat format)
+{
+#if GST_CHECK_VERSION(1,0,0)
+  const gchar *str;
+
+  str = gst_video_format_to_string (format);
+  if (!str)
+    return FALSE;
+
+  g_value_init (value, G_TYPE_STRING);
+  g_value_set_string (value, str);
+#else
+  guint32 fourcc;
+
+  fourcc = gst_video_format_to_fourcc (format);
+  if (!fourcc)
+    return FALSE;
+
+  g_value_init (value, GST_TYPE_FOURCC);
+  gst_value_set_fourcc (value, fourcc);
+#endif
+  return TRUE;
+}
+
+gboolean
+gst_vaapi_value_set_format_list (GValue * value, GArray * formats)
+{
+  GValue v_format = G_VALUE_INIT;
+  guint i;
+
+  g_value_init (value, GST_TYPE_LIST);
+  for (i = 0; i < formats->len; i++) {
+    GstVideoFormat const format = g_array_index (formats, GstVideoFormat, i);
+
+    if (!gst_vaapi_value_set_format (&v_format, format))
+      continue;
+    gst_value_list_append_value (value, &v_format);
+    g_value_unset (&v_format);
+  }
+  return TRUE;
+}
+
+void
+set_video_template_caps (GstCaps * caps)
+{
+  GstStructure *const structure = gst_caps_get_structure (caps, 0);
+
+  gst_structure_set (structure,
+      "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
+      "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
+      "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1,
+      "pixel-aspect-ratio", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
+}
+
+GstCaps *
+gst_vaapi_video_format_new_template_caps (GstVideoFormat format)
+{
+#if GST_CHECK_VERSION(1,0,0)
+  GstCaps *caps;
+
+  g_return_val_if_fail (format != GST_VIDEO_FORMAT_UNKNOWN, NULL);
+
+  caps = gst_caps_new_empty_simple ("video/x-raw");
+  if (!caps)
+    return NULL;
+
+  gst_caps_set_simple (caps,
+      "format", G_TYPE_STRING, gst_video_format_to_string (format), NULL);
+  set_video_template_caps (caps);
+  return caps;
+#else
+  return gst_video_format_new_template_caps (format);
+#endif
+}
+
+GstCaps *
+gst_vaapi_video_format_new_template_caps_from_list (GArray * formats)
+{
+#if GST_CHECK_VERSION(1,0,0)
+  GValue v_formats = G_VALUE_INIT;
+  GstCaps *caps;
+
+  caps = gst_caps_new_empty_simple ("video/x-raw");
+  if (!caps)
+    return NULL;
+
+  if (!gst_vaapi_value_set_format_list (&v_formats, formats)) {
+    gst_caps_unref (caps);
+    return NULL;
+  }
+
+  gst_caps_set_value (caps, "format", &v_formats);
+  set_video_template_caps (caps);
+#else
+  GstCaps *caps, *tmp_caps;
+  guint i;
+
+  g_return_val_if_fail (formats != NULL, NULL);
+
+  caps = gst_caps_new_empty ();
+  if (!caps)
+    return NULL;
+
+  for (i = 0; i < formats->len; i++) {
+    const GstVideoFormat format = g_array_index (formats, GstVideoFormat, i);
+    tmp_caps = gst_vaapi_video_format_new_template_caps (format);
+    if (tmp_caps)
+      gst_caps_append (caps, tmp_caps);
+  }
+#endif
+  return caps;
+}
+
+GstCaps *
+gst_vaapi_video_format_new_template_caps_with_features (GstVideoFormat format,
+    const gchar * features_string)
+{
+  GstCaps *caps;
+
+  caps = gst_vaapi_video_format_new_template_caps (format);
+  if (!caps)
+    return NULL;
+
+#if GST_CHECK_VERSION(1,1,0)
+  GstCapsFeatures *const features =
+      gst_caps_features_new (features_string, NULL);
+  if (!features) {
+    gst_caps_unref (caps);
+    return NULL;
+  }
+  gst_caps_set_features (caps, 0, features);
+#endif
+  return caps;
+}
+
+GstVaapiCapsFeature
+gst_vaapi_find_preferred_caps_feature (GstPad * pad, GstVideoFormat format)
+{
+  GstVaapiCapsFeature feature = GST_VAAPI_CAPS_FEATURE_SYSTEM_MEMORY;
+#if GST_CHECK_VERSION(1,1,0)
+  guint i, num_structures;
+  GstCaps *caps = NULL;
+  GstCaps *gl_texture_upload_caps = NULL;
+  GstCaps *sysmem_caps = NULL;
+  GstCaps *vaapi_caps = NULL;
+  GstCaps *out_caps;
+
+  out_caps = gst_pad_peer_query_caps (pad, NULL);
+  if (!out_caps)
+    goto cleanup;
+
+  gl_texture_upload_caps =
+      gst_vaapi_video_format_new_template_caps_with_features
+      (GST_VIDEO_FORMAT_RGBA,
+      GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META);
+  if (!gl_texture_upload_caps)
+    goto cleanup;
+
+  if (format == GST_VIDEO_FORMAT_ENCODED)
+    format = GST_VIDEO_FORMAT_I420;
+
+  vaapi_caps =
+      gst_vaapi_video_format_new_template_caps_with_features (format,
+      GST_CAPS_FEATURE_MEMORY_VAAPI_SURFACE);
+  if (!vaapi_caps)
+    goto cleanup;
+
+  sysmem_caps =
+      gst_vaapi_video_format_new_template_caps_with_features (format,
+      GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY);
+  if (!sysmem_caps)
+    goto cleanup;
+
+  num_structures = gst_caps_get_size (out_caps);
+  for (i = 0; i < num_structures; i++) {
+    GstCapsFeatures *const features = gst_caps_get_features (out_caps, i);
+    GstStructure *const structure = gst_caps_get_structure (out_caps, i);
+
+    caps = gst_caps_new_full (gst_structure_copy (structure), NULL);
+    if (!caps)
+      continue;
+    gst_caps_set_features (caps, 0, gst_caps_features_copy (features));
+
+    if (gst_caps_can_intersect (caps, vaapi_caps) &&
+        feature < GST_VAAPI_CAPS_FEATURE_VAAPI_SURFACE)
+      feature = GST_VAAPI_CAPS_FEATURE_VAAPI_SURFACE;
+    else if (gst_caps_can_intersect (caps, gl_texture_upload_caps) &&
+        feature < GST_VAAPI_CAPS_FEATURE_GL_TEXTURE_UPLOAD_META)
+      feature = GST_VAAPI_CAPS_FEATURE_GL_TEXTURE_UPLOAD_META;
+    else if (gst_caps_can_intersect (caps, sysmem_caps) &&
+        feature < GST_VAAPI_CAPS_FEATURE_SYSTEM_MEMORY)
+      feature = GST_VAAPI_CAPS_FEATURE_SYSTEM_MEMORY;
+    gst_caps_replace (&caps, NULL);
+  }
+
+cleanup:
+  gst_caps_replace (&gl_texture_upload_caps, NULL);
+  gst_caps_replace (&sysmem_caps, NULL);
+  gst_caps_replace (&vaapi_caps, NULL);
+  gst_caps_replace (&caps, NULL);
+  gst_caps_replace (&out_caps, NULL);
+#endif
+  return feature;
+}
+
+gboolean
+gst_caps_set_interlaced (GstCaps * caps, GstVideoInfo * vip)
+{
+#if GST_CHECK_VERSION(1,0,0)
+  GstVideoInterlaceMode mode;
+  const gchar *mode_str;
+
+  mode = vip ? GST_VIDEO_INFO_INTERLACE_MODE (vip) :
+      GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
+  switch (mode) {
+    case GST_VIDEO_INTERLACE_MODE_PROGRESSIVE:
+      mode_str = "progressive";
+      break;
+    case GST_VIDEO_INTERLACE_MODE_INTERLEAVED:
+      mode_str = "interleaved";
+      break;
+    case GST_VIDEO_INTERLACE_MODE_MIXED:
+      mode_str = "mixed";
+      break;
+    default:
+      GST_ERROR ("unsupported `interlace-mode' %d", mode);
+      return FALSE;
+  }
+
+  gst_caps_set_simple (caps, "interlace-mode", G_TYPE_STRING, mode_str, NULL);
+#else
+  gst_caps_set_simple (caps, "interlaced", G_TYPE_BOOLEAN,
+      vip ? GST_VIDEO_INFO_IS_INTERLACED (vip) : FALSE, NULL);
+#endif
+  return TRUE;
 }