summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README5
-rw-r--r--debian.upstream/control.in1
-rw-r--r--gst/vaapi/Makefile.am2
-rw-r--r--gst/vaapi/gstvaapi.c4
-rw-r--r--gst/vaapi/gstvaapipostproc.c714
-rw-r--r--gst/vaapi/gstvaapipostproc.h123
6 files changed, 848 insertions, 1 deletions
diff --git a/README b/README
index 682d904b..ce09558c 100644
--- 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.
diff --git a/debian.upstream/control.in b/debian.upstream/control.in
index 1cf3477d..9ef5de62 100644
--- a/debian.upstream/control.in
+++ b/debian.upstream/control.in
@@ -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
diff --git a/gst/vaapi/Makefile.am b/gst/vaapi/Makefile.am
index 5be00766..3879fb62 100644
--- a/gst/vaapi/Makefile.am
+++ b/gst/vaapi/Makefile.am
@@ -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)
diff --git a/gst/vaapi/gstvaapi.c b/gst/vaapi/gstvaapi.c
index 2ca04c5f..23be2b38 100644
--- a/gst/vaapi/gstvaapi.c
+++ b/gst/vaapi/gstvaapi.c
@@ -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
index 00000000..e0f9f679
--- /dev/null
+++ b/gst/vaapi/gstvaapipostproc.c
@@ -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
index 00000000..281d2863
--- /dev/null
+++ b/gst/vaapi/gstvaapipostproc.h
@@ -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 */