vaapipostproc: add new element for video postprocessing.
authorGwenole Beauchesne <gwenole.beauchesne@intel.com>
Mon, 26 Mar 2012 12:37:24 +0000 (14:37 +0200)
committerGwenole Beauchesne <gwenole.beauchesne@intel.com>
Mon, 26 Mar 2012 12:37:24 +0000 (14:37 +0200)
Add vaapipostproc element for video postprocessing. So far, only basic
bob deinterlacing is implemented. Interlaced mode is automatically
detected based on sink caps ("interlaced" field).

README
debian.upstream/control.in
gst/vaapi/Makefile.am
gst/vaapi/gstvaapi.c
gst/vaapi/gstvaapipostproc.c [new file with mode: 0644]
gst/vaapi/gstvaapipostproc.h [new file with mode: 0644]

diff --git a/README b/README
index 682d904..ce09558 100644 (file)
--- a/README
+++ b/README
@@ -26,9 +26,12 @@ GStreamer and helper libraries.
   * `vaapiupload' is used to convert from video/x-raw-yuv pixels to
     video/x-vaapi-surface surfaces.
 
-  * `vaapidownload' is used to convert from video-x-vaapi-surface
+  * `vaapidownload' is used to convert from video/x-vaapi-surface
     surfaces to video/x-raw-yuv pixels.
 
+  * `vaapipostproc' is used to postprocess video/x-vaapi-surface
+    surfaces, for e.g. deinterlacing.
+
   * `vaapisink' is used to display video/x-vaapi-surface surfaces to
     screen.
 
index 1cf3477..9ef5de6 100644 (file)
@@ -21,6 +21,7 @@ Description: VA-API plugins for GStreamer
  - `vaapidecode': decode bitstreams using VA-API
  - `vaapiupload': converts from YUV pixels to VA surfaces 
  - `vaapidownload': convert from VA surfaces to YUV pixels
+ - `vaapipostproc': postprocess VA surfaces, e.g. deinterlacing
  - `vaapisink': a VA-API based video sink
 
 Package: gstreamer@GST_MAJORMINOR@-vaapi-doc
index 5be0076..3879fb6 100644 (file)
@@ -20,6 +20,7 @@ libgstvaapi_la_SOURCES =      \
        gstvaapidecode.c        \
        gstvaapidownload.c      \
        gstvaapipluginutil.c    \
+       gstvaapipostproc.c      \
        gstvaapisink.c          \
        gstvaapiupload.c        \
        $(NULL)
@@ -28,6 +29,7 @@ noinst_HEADERS =              \
        gstvaapidecode.h        \
        gstvaapidownload.h      \
        gstvaapipluginutil.h    \
+       gstvaapipostproc.h      \
        gstvaapisink.h          \
        gstvaapiupload.h        \
        $(NULL)
index 2ca04c5..23be2b3 100644 (file)
@@ -30,6 +30,7 @@
 #include "gstvaapidownload.h"
 #include "gstvaapiupload.h"
 #include "gstvaapidecode.h"
+#include "gstvaapipostproc.h"
 #include "gstvaapisink.h"
 
 static gboolean
@@ -44,6 +45,9 @@ plugin_init (GstPlugin *plugin)
     gst_element_register(plugin, "vaapidecode",
                          GST_RANK_PRIMARY,
                          GST_TYPE_VAAPIDECODE);
+    gst_element_register(plugin, "vaapipostproc",
+                         GST_RANK_PRIMARY,
+                         GST_TYPE_VAAPIPOSTPROC);
     gst_element_register(plugin, "vaapisink",
                          GST_RANK_PRIMARY,
                          GST_TYPE_VAAPISINK);
diff --git a/gst/vaapi/gstvaapipostproc.c b/gst/vaapi/gstvaapipostproc.c
new file mode 100644 (file)
index 0000000..e0f9f67
--- /dev/null
@@ -0,0 +1,714 @@
+/*
+ *  gstvaapipostproc.c - VA-API video postprocessing
+ *
+ *  Copyright (C) 2012 Intel Corporation
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1
+ *  of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free
+ *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+/**
+ * SECTION:gstvaapipostproc
+ * @short_description: A video postprocessing filter
+ *
+ * vaapipostproc consists in various postprocessing algorithms to be
+ * applied to VA surfaces. So far, only basic bob deinterlacing is
+ * implemented.
+ */
+
+#include "config.h"
+#include <gst/video/video.h>
+#include <gst/video/videocontext.h>
+#include <gst/vaapi/gstvaapivideosink.h>
+#include <gst/vaapi/gstvaapivideobuffer.h>
+
+#include "gstvaapipluginutil.h"
+#include "gstvaapipostproc.h"
+
+#define GST_PLUGIN_NAME "vaapipostproc"
+#define GST_PLUGIN_DESC "A video postprocessing filter"
+
+GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapipostproc);
+#define GST_CAT_DEFAULT gst_debug_vaapipostproc
+
+/* ElementFactory information */
+static const GstElementDetails gst_vaapipostproc_details =
+    GST_ELEMENT_DETAILS(
+        "VA-API video postprocessing",
+        "Filter/Converter/Video",
+        GST_PLUGIN_DESC,
+        "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
+
+/* Default templates */
+static const char gst_vaapipostproc_sink_caps_str[] =
+    GST_VAAPI_SURFACE_CAPS ", "
+    "interlaced = (boolean) { true, false }";
+
+static const char gst_vaapipostproc_src_caps_str[] =
+    GST_VAAPI_SURFACE_CAPS ", "
+    "interlaced = (boolean) false";
+
+static GstStaticPadTemplate gst_vaapipostproc_sink_factory =
+    GST_STATIC_PAD_TEMPLATE(
+        "sink",
+        GST_PAD_SINK,
+        GST_PAD_ALWAYS,
+        GST_STATIC_CAPS(gst_vaapipostproc_sink_caps_str));
+
+static GstStaticPadTemplate gst_vaapipostproc_src_factory =
+    GST_STATIC_PAD_TEMPLATE(
+        "src",
+        GST_PAD_SRC,
+        GST_PAD_ALWAYS,
+        GST_STATIC_CAPS(gst_vaapipostproc_src_caps_str));
+
+#define GstVideoContextClass GstVideoContextInterface
+GST_BOILERPLATE_WITH_INTERFACE(
+    GstVaapiPostproc,
+    gst_vaapipostproc,
+    GstElement,
+    GST_TYPE_ELEMENT,
+    GstVideoContext,
+    GST_TYPE_VIDEO_CONTEXT,
+    gst_video_context);
+
+enum {
+    PROP_0,
+
+    PROP_DEINTERLACE_MODE,
+    PROP_DEINTERLACE_METHOD,
+};
+
+#define DEFAULT_DEINTERLACE_MODE        GST_VAAPI_DEINTERLACE_MODE_AUTO
+#define DEFAULT_DEINTERLACE_METHOD      GST_VAAPI_DEINTERLACE_METHOD_BOB
+
+#define GST_TYPE_VAAPI_DEINTERLACE_MODES \
+    gst_vaapi_deinterlace_modes_get_type()
+
+static GType
+gst_vaapi_deinterlace_modes_get_type(void)
+{
+    static GType deinterlace_modes_type = 0;
+
+    static const GEnumValue modes_types[] = {
+        { GST_VAAPI_DEINTERLACE_MODE_AUTO,
+          "Auto detection", "auto" },
+        { GST_VAAPI_DEINTERLACE_MODE_INTERLACED,
+          "Force deinterlacing", "interlaced" },
+        { GST_VAAPI_DEINTERLACE_MODE_DISABLED,
+          "Never deinterlace", "disabled" },
+        { 0, NULL, NULL },
+    };
+
+    if (!deinterlace_modes_type) {
+        deinterlace_modes_type =
+            g_enum_register_static("GstVaapiDeinterlaceModes", modes_types);
+    }
+    return deinterlace_modes_type;
+}
+
+#define GST_TYPE_VAAPI_DEINTERLACE_METHODS \
+    gst_vaapi_deinterlace_methods_get_type()
+
+static GType
+gst_vaapi_deinterlace_methods_get_type(void)
+{
+    static GType deinterlace_methods_type = 0;
+
+    static const GEnumValue methods_types[] = {
+        { GST_VAAPI_DEINTERLACE_METHOD_BOB,
+          "Bob deinterlacing", "bob" },
+#if 0
+        /* VA/VPP */
+        { GST_VAAPI_DEINTERLACE_METHOD_WEAVE,
+          "Weave deinterlacing", "weave" },
+        { GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE,
+          "Motion adaptive deinterlacing", "motion-adaptive" },
+        { GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED,
+          "Motion compensated deinterlacing", "motion-compensated" },
+#endif
+        { 0, NULL, NULL },
+    };
+
+    if (!deinterlace_methods_type) {
+        deinterlace_methods_type =
+            g_enum_register_static("GstVaapiDeinterlaceMethods", methods_types);
+    }
+    return deinterlace_methods_type;
+}
+
+static inline GstVaapiPostproc *
+get_vaapipostproc_from_pad(GstPad *pad)
+{
+    return GST_VAAPIPOSTPROC(gst_pad_get_parent_element(pad));
+}
+
+/* GstVideoContext interface */
+
+static void
+gst_vaapipostproc_set_video_context(
+    GstVideoContext *context,
+    const gchar     *type,
+    const GValue    *value
+)
+{
+    GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(context);
+
+    gst_vaapi_set_display(type, value, &postproc->display);
+}
+
+static gboolean
+gst_video_context_supported(GstVaapiPostproc *postproc, GType iface_type)
+{
+    return (iface_type == GST_TYPE_VIDEO_CONTEXT);
+}
+
+static void
+gst_video_context_interface_init(GstVideoContextInterface *iface)
+{
+    iface->set_context = gst_vaapipostproc_set_video_context;
+}
+
+static gboolean
+gst_vaapipostproc_create(GstVaapiPostproc *postproc, GstCaps *caps)
+{
+    if (!gst_vaapi_ensure_display(postproc, &postproc->display))
+        return FALSE;
+
+    gst_caps_replace(&postproc->postproc_caps, caps);
+    return TRUE;
+}
+
+static void
+gst_vaapipostproc_destroy(GstVaapiPostproc *postproc)
+{
+    gst_caps_replace(&postproc->postproc_caps, NULL);
+
+    if (postproc->display) {
+        g_object_unref(postproc->display);
+        postproc->display = NULL;
+    }
+}
+
+static gboolean
+gst_vaapipostproc_reset(GstVaapiPostproc *postproc, GstCaps *caps)
+{
+    if (postproc->postproc_caps &&
+        gst_caps_is_always_compatible(caps, postproc->postproc_caps))
+        return TRUE;
+
+    gst_vaapipostproc_destroy(postproc);
+    return gst_vaapipostproc_create(postproc, caps);
+}
+
+static gboolean
+gst_vaapipostproc_start(GstVaapiPostproc *postproc)
+{
+    if (!gst_vaapi_ensure_display(postproc, &postproc->display))
+        return FALSE;
+    return TRUE;
+}
+
+static gboolean
+gst_vaapipostproc_stop(GstVaapiPostproc *postproc)
+{
+    if (postproc->display) {
+        g_object_unref(postproc->display);
+        postproc->display = NULL;
+    }
+    return TRUE;
+}
+
+static GstFlowReturn
+gst_vaapipostproc_process(GstVaapiPostproc *postproc, GstBuffer *buf)
+{
+    GstVaapiVideoBuffer *vbuf = GST_VAAPI_VIDEO_BUFFER(buf);
+    GstVaapiSurfaceProxy *proxy;
+    GstClockTime timestamp;
+    GstFlowReturn ret;
+    GstBuffer *outbuf = NULL;
+    guint outbuf_flags, flags = 0;
+    gboolean tff;
+
+    /* Deinterlacing disabled, push frame */
+    if (!postproc->deinterlace) {
+        gst_vaapi_video_buffer_set_render_flags(vbuf, flags);
+        ret = gst_pad_push(postproc->srcpad, buf);
+        if (ret != GST_FLOW_OK)
+            goto error_push_buffer;
+        return GST_FLOW_OK;
+    }
+
+    timestamp = GST_BUFFER_TIMESTAMP(buf);
+    proxy     = gst_vaapi_video_buffer_get_surface_proxy(vbuf);
+    tff       = gst_vaapi_surface_proxy_get_tff(proxy);
+
+    /* First field */
+    outbuf = gst_vaapi_video_buffer_new_with_surface_proxy(proxy);
+    if (!outbuf)
+        goto error_create_buffer;
+
+    vbuf = GST_VAAPI_VIDEO_BUFFER(outbuf);
+    outbuf_flags = flags;
+    outbuf_flags |= tff ?
+        GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD :
+        GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD;
+    gst_vaapi_video_buffer_set_render_flags(vbuf, outbuf_flags);
+
+    GST_BUFFER_TIMESTAMP(outbuf) = timestamp;
+    GST_BUFFER_DURATION(outbuf)  = postproc->field_duration;
+    gst_buffer_set_caps(outbuf, postproc->srcpad_caps);
+    ret = gst_pad_push(postproc->srcpad, outbuf);
+    if (ret != GST_FLOW_OK)
+        goto error_push_buffer;
+
+    /* Second field */
+    outbuf = gst_vaapi_video_buffer_new_with_surface_proxy(proxy);
+    if (!outbuf)
+        goto error_create_buffer;
+
+    vbuf = GST_VAAPI_VIDEO_BUFFER(outbuf);
+    outbuf_flags = flags;
+    outbuf_flags |= tff ?
+        GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD :
+        GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD;
+    gst_vaapi_video_buffer_set_render_flags(vbuf, outbuf_flags);
+
+    GST_BUFFER_TIMESTAMP(outbuf) = timestamp + postproc->field_duration;
+    GST_BUFFER_DURATION(outbuf)  = postproc->field_duration;
+    gst_buffer_set_caps(outbuf, postproc->srcpad_caps);
+    ret = gst_pad_push(postproc->srcpad, outbuf);
+    if (ret != GST_FLOW_OK)
+        goto error_push_buffer;
+
+    gst_buffer_unref(buf);
+    return GST_FLOW_OK;
+
+    /* ERRORS */
+error_create_buffer:
+    {
+        GST_ERROR("failed to create output buffer");
+        gst_buffer_unref(buf);
+        return GST_FLOW_UNEXPECTED;
+    }
+error_push_buffer:
+    {
+        if (ret != GST_FLOW_WRONG_STATE)
+            GST_ERROR("failed to push output buffer to video sink");
+        gst_buffer_unref(buf);
+        return GST_FLOW_UNEXPECTED;
+    }
+}
+
+static gboolean
+gst_vaapipostproc_update_sink_caps(GstVaapiPostproc *postproc, GstCaps *caps)
+{
+    gint fps_n, fps_d;
+    gboolean interlaced;
+
+    if (!gst_video_parse_caps_framerate(caps, &fps_n, &fps_d))
+        return FALSE;
+    postproc->fps_n = fps_n;
+    postproc->fps_d = fps_d;
+
+    switch (postproc->deinterlace_mode) {
+    case GST_VAAPI_DEINTERLACE_MODE_AUTO:
+        if (!gst_video_format_parse_caps_interlaced(caps, &interlaced))
+            return FALSE;
+        postproc->deinterlace = interlaced;
+        break;
+    case GST_VAAPI_DEINTERLACE_MODE_INTERLACED:
+        postproc->deinterlace = TRUE;
+        break;
+    case GST_VAAPI_DEINTERLACE_MODE_DISABLED:
+        postproc->deinterlace = FALSE;
+        break;
+    }
+
+    postproc->field_duration = gst_util_uint64_scale(
+        GST_SECOND,
+        postproc->fps_d,
+        (1 + postproc->deinterlace) * postproc->fps_n
+    );
+
+    gst_caps_replace(&postproc->sinkpad_caps, caps);
+    return TRUE;
+}
+
+static gboolean
+gst_vaapipostproc_update_src_caps(GstVaapiPostproc *postproc, GstCaps *caps)
+{
+    GstCaps *src_caps;
+    GstStructure *structure;
+    const GValue *v_width, *v_height, *v_par;
+    gint fps_n, fps_d;
+
+    if (postproc->srcpad_caps)
+        src_caps = gst_caps_make_writable(postproc->srcpad_caps);
+    else
+        src_caps = gst_caps_from_string(GST_VAAPI_SURFACE_CAPS_NAME);
+    if (!src_caps)
+        return FALSE;
+    postproc->srcpad_caps = src_caps;
+
+    structure    = gst_caps_get_structure(caps, 0);
+    v_width      = gst_structure_get_value(structure, "width");
+    v_height     = gst_structure_get_value(structure, "height");
+    v_par        = gst_structure_get_value(structure, "pixel-aspect-ratio");
+
+    structure = gst_caps_get_structure(src_caps, 0);
+    if (v_width && v_height) {
+        gst_structure_set_value(structure, "width", v_width);
+        gst_structure_set_value(structure, "height", v_height);
+    }
+    if (v_par)
+        gst_structure_set_value(structure, "pixel-aspect-ratio", v_par);
+
+    gst_structure_set(structure, "type", G_TYPE_STRING, "vaapi", NULL);
+    gst_structure_set(structure, "opengl", G_TYPE_BOOLEAN, USE_VAAPI_GLX, NULL);
+
+    if (!postproc->deinterlace)
+        gst_structure_remove_field(structure, "interlaced");
+    else {
+        /* Set double framerate in interlaced mode */
+        if (!gst_util_fraction_multiply(postproc->fps_n, postproc->fps_d,
+                                        2, 1,
+                                        &fps_n, &fps_d))
+            return FALSE;
+
+        gst_structure_set(
+            structure,
+            "interlaced", G_TYPE_BOOLEAN, FALSE,
+            "framerate", GST_TYPE_FRACTION, fps_n, fps_d,
+            NULL
+        );
+    }
+    return gst_pad_set_caps(postproc->srcpad, src_caps);
+}
+
+static gboolean
+gst_vaapipostproc_ensure_allowed_caps(GstVaapiPostproc *postproc)
+{
+    if (postproc->allowed_caps)
+        return TRUE;
+
+    postproc->allowed_caps =
+        gst_caps_from_string(gst_vaapipostproc_sink_caps_str);
+    if (!postproc->allowed_caps)
+        return FALSE;
+
+    /* XXX: append VA/VPP filters */
+    return TRUE;
+}
+
+static GstCaps *
+gst_vaapipostproc_get_caps(GstPad *pad)
+{
+    GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
+    GstCaps *out_caps;
+
+    if (gst_vaapipostproc_ensure_allowed_caps(postproc))
+        out_caps = gst_caps_ref(postproc->allowed_caps);
+    else
+        out_caps = gst_caps_new_empty();
+
+    gst_object_unref(postproc);
+    return out_caps;
+}
+
+static gboolean
+gst_vaapipostproc_set_caps(GstPad *pad, GstCaps *caps)
+{
+    GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
+    gboolean success = FALSE;
+
+    g_return_val_if_fail(pad == postproc->sinkpad, FALSE);
+
+    do {
+        if (!gst_vaapipostproc_update_sink_caps(postproc, caps))
+            break;
+        if (!gst_vaapipostproc_update_src_caps(postproc, caps))
+            break;
+        if (!gst_vaapipostproc_reset(postproc, postproc->sinkpad_caps))
+            break;
+        success = TRUE;
+    } while (0);
+    gst_object_unref(postproc);
+    return success;
+}
+
+static GstFlowReturn
+gst_vaapipostproc_chain(GstPad *pad, GstBuffer *buf)
+{
+    GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
+    GstFlowReturn ret;
+
+    ret = gst_vaapipostproc_process(postproc, buf);
+    gst_object_unref(postproc);
+    return ret;
+}
+
+static gboolean
+gst_vaapipostproc_sink_event(GstPad *pad, GstEvent *event)
+{
+    GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
+    gboolean success;
+
+    GST_DEBUG("handle sink event '%s'", GST_EVENT_TYPE_NAME(event));
+
+    /* Propagate event downstream */
+    success = gst_pad_push_event(postproc->srcpad, event);
+    gst_object_unref(postproc);
+    return success;
+}
+
+static gboolean
+gst_vaapipostproc_src_event(GstPad *pad, GstEvent *event)
+{
+    GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
+    gboolean success;
+
+    GST_DEBUG("handle src event '%s'", GST_EVENT_TYPE_NAME(event));
+
+    /* Propagate event upstream */
+    success = gst_pad_push_event(postproc->sinkpad, event);
+    gst_object_unref(postproc);
+    return success;
+}
+
+static gboolean
+gst_vaapipostproc_query(GstPad *pad, GstQuery *query)
+{
+    GstVaapiPostproc * const postproc = get_vaapipostproc_from_pad(pad);
+    gboolean success;
+
+    GST_DEBUG("sharing display %p", postproc->display);
+
+    if (gst_vaapi_reply_to_query(query, postproc->display))
+        success = TRUE;
+    else
+        success = gst_pad_query_default(pad, query);
+
+    gst_object_unref(postproc);
+    return success;
+}
+
+static void
+gst_vaapipostproc_finalize(GObject *object)
+{
+    GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
+
+    gst_vaapipostproc_destroy(postproc);
+
+    gst_caps_replace(&postproc->sinkpad_caps, NULL);
+    gst_caps_replace(&postproc->srcpad_caps,  NULL);
+    gst_caps_replace(&postproc->allowed_caps, NULL);
+
+    G_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+static void
+gst_vaapipostproc_set_property(
+    GObject      *object,
+    guint         prop_id,
+    const GValue *value,
+    GParamSpec   *pspec
+)
+{
+    GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
+
+    switch (prop_id) {
+    case PROP_DEINTERLACE_MODE:
+        postproc->deinterlace_mode = g_value_get_enum(value);
+        break;
+    case PROP_DEINTERLACE_METHOD:
+        postproc->deinterlace_method = g_value_get_enum(value);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+gst_vaapipostproc_get_property(
+    GObject    *object,
+    guint       prop_id,
+    GValue     *value,
+    GParamSpec *pspec
+)
+{
+    GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(object);
+
+    switch (prop_id) {
+    case PROP_DEINTERLACE_MODE:
+        g_value_set_enum(value, postproc->deinterlace_mode);
+        break;
+    case PROP_DEINTERLACE_METHOD:
+        g_value_set_enum(value, postproc->deinterlace_method);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        break;
+    }
+}
+
+static GstStateChangeReturn
+gst_vaapipostproc_change_state(GstElement *element, GstStateChange transition)
+{
+    GstVaapiPostproc * const postproc = GST_VAAPIPOSTPROC(element);
+    GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+    switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+        if (!gst_vaapipostproc_start(postproc))
+            return GST_STATE_CHANGE_FAILURE;
+        break;
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+        break;
+    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+        break;
+    default:
+        break;
+    }
+
+    ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
+    if (ret != GST_STATE_CHANGE_SUCCESS)
+        return ret;
+
+    switch (transition) {
+    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+        break;
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+        break;
+    case GST_STATE_CHANGE_READY_TO_NULL:
+        if (!gst_vaapipostproc_stop(postproc))
+            return GST_STATE_CHANGE_FAILURE;
+        break;
+    default:
+        break;
+    }
+    return GST_STATE_CHANGE_SUCCESS;
+}
+
+static void
+gst_vaapipostproc_class_init(GstVaapiPostprocClass *klass)
+{
+    GObjectClass * const object_class = G_OBJECT_CLASS(klass);
+    GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
+
+    GST_DEBUG_CATEGORY_INIT(gst_debug_vaapipostproc,
+                            GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
+
+    object_class->finalize      = gst_vaapipostproc_finalize;
+    object_class->set_property  = gst_vaapipostproc_set_property;
+    object_class->get_property  = gst_vaapipostproc_get_property;
+
+    element_class->change_state = gst_vaapipostproc_change_state;
+
+    /**
+     * GstVaapiSink:deinterlace-mode:
+     *
+     * This selects whether the deinterlacing should always be applied or if
+     * they should only be applied on content that has the "interlaced" flag
+     * on the caps.
+     */
+    g_object_class_install_property
+        (object_class,
+         PROP_DEINTERLACE_MODE,
+         g_param_spec_enum("deinterlace",
+                           "Deinterlace",
+                           "Deinterlace mode to use",
+                           GST_TYPE_VAAPI_DEINTERLACE_MODES,
+                           DEFAULT_DEINTERLACE_MODE,
+                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+    /**
+     * GstVaapiSink:deinterlace-method:
+     *
+     * This selects the deinterlacing method to apply.
+     */
+    g_object_class_install_property
+        (object_class,
+         PROP_DEINTERLACE_METHOD,
+         g_param_spec_enum("deinterlace-method",
+                           "Deinterlace method",
+                           "Deinterlace method to use",
+                           GST_TYPE_VAAPI_DEINTERLACE_METHODS,
+                           DEFAULT_DEINTERLACE_METHOD,
+                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gst_vaapipostproc_base_init(gpointer klass)
+{
+    GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
+    GstPadTemplate *pad_template;
+
+    gst_element_class_set_details(element_class, &gst_vaapipostproc_details);
+
+    /* sink pad */
+    pad_template = gst_static_pad_template_get(&gst_vaapipostproc_sink_factory);
+    gst_element_class_add_pad_template(element_class, pad_template);
+    gst_object_unref(pad_template);
+
+    /* src pad */
+    pad_template = gst_static_pad_template_get(&gst_vaapipostproc_src_factory);
+    gst_element_class_add_pad_template(element_class, pad_template);
+    gst_object_unref(pad_template);
+}
+
+static void
+gst_vaapipostproc_init(GstVaapiPostproc *postproc, GstVaapiPostprocClass *klass)
+{
+    GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
+
+    postproc->allowed_caps              = NULL;
+    postproc->postproc_caps             = NULL;
+    postproc->display                   = NULL;
+    postproc->surface_width             = 0;
+    postproc->surface_height            = 0;
+    postproc->deinterlace               = FALSE;
+    postproc->deinterlace_mode          = DEFAULT_DEINTERLACE_MODE;
+    postproc->deinterlace_method        = DEFAULT_DEINTERLACE_METHOD;
+    postproc->field_duration            = GST_CLOCK_TIME_NONE;
+    postproc->fps_n                     = 0;
+    postproc->fps_d                     = 0;
+
+    /* Pad through which data comes in to the element */
+    postproc->sinkpad = gst_pad_new_from_template(
+        gst_element_class_get_pad_template(element_class, "sink"),
+        "sink"
+    );
+    postproc->sinkpad_caps = NULL;
+
+    gst_pad_set_getcaps_function(postproc->sinkpad, gst_vaapipostproc_get_caps);
+    gst_pad_set_setcaps_function(postproc->sinkpad, gst_vaapipostproc_set_caps);
+    gst_pad_set_chain_function(postproc->sinkpad, gst_vaapipostproc_chain);
+    gst_pad_set_event_function(postproc->sinkpad, gst_vaapipostproc_sink_event);
+    gst_pad_set_query_function(postproc->sinkpad, gst_vaapipostproc_query);
+    gst_element_add_pad(GST_ELEMENT(postproc), postproc->sinkpad);
+
+    /* Pad through which data goes out of the element */
+    postproc->srcpad = gst_pad_new_from_template(
+        gst_element_class_get_pad_template(element_class, "src"),
+        "src"
+    );
+    postproc->srcpad_caps = NULL;
+
+    gst_pad_set_event_function(postproc->srcpad, gst_vaapipostproc_src_event);
+    gst_pad_set_query_function(postproc->srcpad, gst_vaapipostproc_query);
+    gst_element_add_pad(GST_ELEMENT(postproc), postproc->srcpad);
+}
diff --git a/gst/vaapi/gstvaapipostproc.h b/gst/vaapi/gstvaapipostproc.h
new file mode 100644 (file)
index 0000000..281d286
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ *  gstvaapipostproc.h - VA-API video post processing
+ *
+ *  Copyright (C) 2012 Intel Corporation
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free
+ *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+*/
+
+#ifndef GST_VAAPIPOSTPROC_H
+#define GST_VAAPIPOSTPROC_H
+
+#include <gst/gst.h>
+#include <gst/vaapi/gstvaapidisplay.h>
+#include <gst/vaapi/gstvaapisurface.h>
+#include <gst/vaapi/gstvaapisurfacepool.h>
+#include <gst/vaapi/gstvaapivideobuffer.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_VAAPIPOSTPROC \
+    (gst_vaapipostproc_get_type())
+
+#define GST_VAAPIPOSTPROC(obj)                          \
+    (G_TYPE_CHECK_INSTANCE_CAST((obj),                  \
+                                GST_TYPE_VAAPIPOSTPROC, \
+                                GstVaapiPostproc))
+
+#define GST_VAAPIPOSTPROC_CLASS(klass)                  \
+    (G_TYPE_CHECK_CLASS_CAST((klass),                   \
+                             GST_TYPE_VAAPIPOSTPROC,    \
+                             GstVaapiPostprocClass))
+
+#define GST_IS_VAAPIPOSTPROC(obj) \
+    (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_VAAPIPOSTPROC))
+
+#define GST_IS_VAAPIPOSTPROC_CLASS(klass) \
+    (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_VAAPIPOSTPROC))
+
+#define GST_VAAPIPOSTPROC_GET_CLASS(obj)                \
+    (G_TYPE_INSTANCE_GET_CLASS((obj),                   \
+                               GST_TYPE_VAAPIPOSTPROC,  \
+                               GstVaapiPostprocClass))
+
+typedef struct _GstVaapiPostproc                GstVaapiPostproc;
+typedef struct _GstVaapiPostprocClass           GstVaapiPostprocClass;
+
+typedef enum _GstVaapiDeinterlaceMode           GstVaapiDeinterlaceMode;
+typedef enum _GstVaapiDeinterlaceMethod         GstVaapiDeinterlaceMethod;
+
+/**
+ * GstVaapiDeinterlaceMode:
+ * @GST_VAAPI_DEINTERLACE_MODE_AUTO: Auto detect needs for deinterlacing.
+ * @GST_VAAPI_DEINTERLACE_MODE_INTERLACED: Force deinterlacing.
+ * @GST_VAAPI_DEINTERLACE_MODE_DISABLED: Never perform deinterlacing.
+ */
+enum _GstVaapiDeinterlaceMode {
+    GST_VAAPI_DEINTERLACE_MODE_AUTO = 0,
+    GST_VAAPI_DEINTERLACE_MODE_INTERLACED,
+    GST_VAAPI_DEINTERLACE_MODE_DISABLED,
+};
+
+/**
+ * GstVaapiDeinterlaceMethod:
+ * @GST_VAAPI_DEINTERLACE_METHOD_BOB: Basic bob deinterlacing algorithm.
+ * @GST_VAAPI_DEINTERLACE_METHOD_WEAVE: Weave deinterlacing algorithm.
+ * @GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE: Motion adaptive deinterlacing algorithm.
+ * @GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED: Motion compensated deinterlacing algorithm.
+ */
+enum _GstVaapiDeinterlaceMethod {
+    GST_VAAPI_DEINTERLACE_METHOD_BOB = 1,
+    GST_VAAPI_DEINTERLACE_METHOD_WEAVE,
+    GST_VAAPI_DEINTERLACE_METHOD_MOTION_ADAPTIVE,
+    GST_VAAPI_DEINTERLACE_METHOD_MOTION_COMPENSATED,
+};
+
+struct _GstVaapiPostproc {
+    /*< private >*/
+    GstElement                  parent_instance;
+
+    GstPad                     *sinkpad;
+    GstCaps                    *sinkpad_caps;
+    GstPad                     *srcpad;
+    GstCaps                    *srcpad_caps;
+    GstCaps                    *allowed_caps;
+    GstCaps                    *postproc_caps;
+
+    GstVaapiDisplay            *display;
+    guint                       surface_width;
+    guint                       surface_height;
+
+    /* Deinterlacing */
+    gboolean                    deinterlace;
+    GstVaapiDeinterlaceMode     deinterlace_mode;
+    GstVaapiDeinterlaceMethod   deinterlace_method;
+    GstClockTime                field_duration;
+    gint                        fps_n;
+    gint                        fps_d;
+};
+
+struct _GstVaapiPostprocClass {
+    /*< private >*/
+    GstElementClass             parent_class;
+};
+
+GType
+gst_vaapipostproc_get_type(void);
+
+G_END_DECLS
+
+#endif /* GST_VAAPIPOSTPROC_H */