diff options
-rw-r--r-- | README | 5 | ||||
-rw-r--r-- | debian.upstream/control.in | 1 | ||||
-rw-r--r-- | gst/vaapi/Makefile.am | 2 | ||||
-rw-r--r-- | gst/vaapi/gstvaapi.c | 4 | ||||
-rw-r--r-- | gst/vaapi/gstvaapipostproc.c | 714 | ||||
-rw-r--r-- | gst/vaapi/gstvaapipostproc.h | 123 |
6 files changed, 848 insertions, 1 deletions
@@ -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 */ |