/* Copyright (C) 2005 Edward Hervey (edward@fluendo.com) Copyright (C) 2006 Mark Nauwelaerts (manauw@skynet.be) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #ifdef HAVE_FFMPEG_UNINSTALLED #include #include #else #include #include #endif typedef struct _PostProcDetails PostProcDetails; struct _PostProcDetails { const char *shortname; const char *longname; const char *description; }; static PostProcDetails filterdetails[] = { {"hb", "hdeblock", "horizontal deblocking filter"}, {"vb", "vdeblock", "vertical deblocking filter"}, {"h1", "x1hdeblock", "experimental horizontal deblocking filter 1"}, {"v1", "x1vdeblock", "experimental vertical deblocking filter 1"}, {"ha", "ahdeblock", "another horizontal deblocking filter"}, {"va", "avdeblock", "another vertical deblocking filter"}, {"dr", "dering", "deringing filter"}, {"al", "autolevels", "automatic brightness/contrast filter"}, {"lb", "linblenddeint", "linear blend interpolater"}, {"li", "linipoldeint", "linear interpolation deinterlacer"}, {"ci", "cubicipoldeint", "cubic interpolation deinterlacer"}, {"md", "mediandeint", "median deinterlacer"}, {"fd", "ffmpegdeint", "ffmpeg deinterlacer"}, {"l5", "lowpass5", "FIR lowpass deinterlacer"}, {"tn", "tmpnoise", "temporal noise reducer"}, {"fq", "forcequant", "force quantizer"}, {"de", "default", "default filters"}, {NULL, NULL, NULL} }; typedef struct _GstPostProc GstPostProc; struct _GstPostProc { GstVideoFilter element; GstPad *sinkpad, *srcpad; guint quality; gint width, height; gint ystride, ustride, vstride; gint ysize, usize, vsize; pp_mode_t *mode; pp_context_t *context; /* props of various filters */ gboolean autoq; guint scope; /* though not all needed at once, * this avoids union or ugly re-use for simplicity */ gint diff, flat; gint t1, t2, t3; gboolean range; gint quant; /* argument string for pp */ gchar *cargs, *args; }; typedef struct _GstPostProcClass GstPostProcClass; struct _GstPostProcClass { GstVideoFilterClass parent_class; gint filterid; }; /* properties for the various pp filters */ /* common props */ enum { PROP_0, PROP_QUALITY, PROP_AUTOQ, PROP_SCOPE, PROP_MAX }; /* possible filter scopes */ enum { SCOPE_BOTH, SCOPE_CHROMA, SCOPE_LUMA }; #define DEFAULT_QUALITY PP_QUALITY_MAX #define DEFAULT_AUTOQ FALSE #define DEFAULT_SCOPE SCOPE_BOTH /* deblocking props */ enum { PROP_DIFF = PROP_MAX, PROP_FLAT }; #define DEFAULT_DIFF -1 #define DEFAULT_FLAT -1 /* denoise props */ enum { PROP_T1 = PROP_MAX, PROP_T2, PROP_T3 }; #define DEFAULT_T1 -1 #define DEFAULT_T2 -1 #define DEFAULT_T3 -1 /* autolevels */ enum { PROP_RANGE = PROP_MAX }; #define DEFAULT_RANGE FALSE /* forceq props */ enum { PROP_QUANT = PROP_MAX }; #define DEFAULT_QUANT -1 /* hashtable, key = gtype, value = filterdetails index */ static GHashTable *global_plugins; /* TODO : add support for the other format supported by libpostproc */ static GstStaticPadTemplate gst_post_proc_src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ IYUV, I420, YV12, Y42B, Y41B }")) ); static GstStaticPadTemplate gst_post_proc_sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ IYUV, I420, YV12, Y42B, Y41B }")) ); GST_DEBUG_CATEGORY (postproc_debug); #define GST_CAT_DEFAULT postproc_debug static void gst_post_proc_class_init (GstPostProcClass * klass); static void gst_post_proc_base_init (GstPostProcClass * klass); static void gst_post_proc_init (GstPostProc * pproc); static void gst_post_proc_dispose (GObject * object); static gboolean gst_post_proc_setcaps (GstBaseTransform * btrans, GstCaps * incaps, GstCaps * outcaps); static GstFlowReturn gst_post_proc_transform_ip (GstBaseTransform * btrans, GstBuffer * in); /* static GstStateChangeReturn gst_post_proc_change_state (GstElement * element, */ /* GstStateChange transition); */ static void gst_post_proc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_post_proc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static void gst_post_proc_deblock_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_post_proc_deblock_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static void gst_post_proc_autolevels_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_post_proc_autolevels_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static void gst_post_proc_tmpnoise_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_post_proc_tmpnoise_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static void gst_post_proc_forcequant_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_post_proc_forcequant_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static GstElementClass *parent_class = NULL; #define GST_TYPE_PP_SCOPE (gst_pp_scope_get_type()) static GType gst_pp_scope_get_type (void) { static GType pp_scope_type = 0; static const GEnumValue pp_scope[] = { {0, "Chrominance and Luminance filtering", "both"}, {1, "Chrominance only filtering", "chroma"}, {2, "Luminance only filtering", "luma"}, {0, NULL, NULL}, }; if (!pp_scope_type) { pp_scope_type = g_enum_register_static ("GstPostProcPPScope", pp_scope); } return pp_scope_type; } #ifndef GST_DISABLE_GST_DEBUG static void gst_ffmpeg_log_callback (void *ptr, int level, const char *fmt, va_list vl) { GstDebugLevel gst_level; switch (level) { case AV_LOG_QUIET: gst_level = GST_LEVEL_NONE; break; case AV_LOG_ERROR: gst_level = GST_LEVEL_ERROR; break; case AV_LOG_INFO: gst_level = GST_LEVEL_INFO; break; case AV_LOG_DEBUG: gst_level = GST_LEVEL_DEBUG; break; default: gst_level = GST_LEVEL_INFO; break; } gst_debug_log_valist (postproc_debug, gst_level, "", "", 0, NULL, fmt, vl); } #endif #define ROUND_UP_2(x) (((x)+1)&~1) #define ROUND_UP_4(x) (((x)+3)&~3) #define ROUND_UP_8(x) (((x)+7)&~7) static void change_context (GstPostProc * postproc, gint width, gint height) { guint flags; gint ppflags; GST_DEBUG_OBJECT (postproc, "change_context, width:%d, height:%d", width, height); if ((width != postproc->width) && (height != postproc->height)) { if (postproc->context) pp_free_context (postproc->context); flags = oil_cpu_get_flags (); ppflags = (flags & OIL_IMPL_FLAG_MMX ? PP_CPU_CAPS_MMX : 0) | (flags & OIL_IMPL_FLAG_MMXEXT ? PP_CPU_CAPS_MMX2 : 0) | (flags & OIL_IMPL_FLAG_3DNOW ? PP_CPU_CAPS_3DNOW : 0) | (flags & OIL_IMPL_FLAG_ALTIVEC ? PP_CPU_CAPS_ALTIVEC : 0); postproc->context = pp_get_context (width, height, PP_FORMAT_420 | ppflags); postproc->width = width; postproc->height = height; postproc->ystride = ROUND_UP_4 (width); postproc->ustride = ROUND_UP_8 (width) / 2; postproc->vstride = ROUND_UP_8 (postproc->ystride) / 2; postproc->ysize = postproc->ystride * ROUND_UP_2 (height); postproc->usize = postproc->ustride * ROUND_UP_2 (height) / 2; postproc->vsize = postproc->vstride * ROUND_UP_2 (height) / 2; GST_DEBUG_OBJECT (postproc, "new strides are (YUV) : %d %d %d", postproc->ystride, postproc->ustride, postproc->vstride); } } /* append app to *base, and places result in *base */ /* all input strings are free'd */ static void inline append (gchar ** base, gchar * app) { gchar *res; const gchar *sep; if (**base && *app) sep = ":"; else sep = ""; res = g_strconcat (*base, sep, app, NULL); g_free (*base); g_free (app); *base = res; } static void change_mode (GstPostProc * postproc) { GstPostProcClass *klass; gchar *name; klass = (GstPostProcClass *) G_OBJECT_GET_CLASS (G_OBJECT (postproc)); if (postproc->mode) pp_free_mode (postproc->mode); name = g_strdup (filterdetails[klass->filterid].shortname); append (&name, g_strdup (postproc->cargs)); append (&name, g_strdup (postproc->args)); GST_DEBUG_OBJECT (postproc, "requesting pp %s", name); postproc->mode = pp_get_mode_by_name_and_quality (name, postproc->quality); g_free (name); g_assert (postproc->mode); } static void gst_post_proc_base_init (GstPostProcClass * klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GstElementClass *element_class = GST_ELEMENT_CLASS (klass); gint ppidx; gchar *longname, *description; ppidx = GPOINTER_TO_INT (g_hash_table_lookup (global_plugins, GINT_TO_POINTER (G_OBJECT_CLASS_TYPE (gobject_class)))); longname = g_strdup_printf ("LibPostProc %s filter", filterdetails[ppidx].longname); description = g_strdup_printf ("LibPostProc %s", filterdetails[ppidx].description); gst_element_class_set_details_simple (element_class, longname, "Filter/Video", description, "Edward Hervey , Mark Nauwelaerts (manauw@skynet.be)"); g_free (longname); g_free (description); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_post_proc_src_template)); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&gst_post_proc_sink_template)); klass->filterid = ppidx; } static void gst_post_proc_class_init (GstPostProcClass * klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); /* GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); */ GstBaseTransformClass *btrans_class = GST_BASE_TRANSFORM_CLASS (klass); gint ppidx; parent_class = g_type_class_peek_parent (klass); gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_post_proc_set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_post_proc_get_property); /* common props */ g_object_class_install_property (gobject_class, PROP_QUALITY, g_param_spec_uint ("quality", "Quality", "Quality level of filter (higher is better)", 0, PP_QUALITY_MAX, DEFAULT_QUALITY, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_AUTOQ, g_param_spec_boolean ("autoq", "AutoQ", "Automatically switch filter off if CPU too slow", DEFAULT_AUTOQ, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_SCOPE, g_param_spec_enum ("scope", "Scope", "Operate on chrominance and/or luminance", GST_TYPE_PP_SCOPE, DEFAULT_SCOPE, G_PARAM_READWRITE)); ppidx = klass->filterid; /* per filter props */ if (g_strrstr (filterdetails[ppidx].longname, "deblock") != NULL && filterdetails[ppidx].longname[0] != 'x') { /* deblocking */ g_object_class_install_property (gobject_class, PROP_DIFF, g_param_spec_int ("difference", "Difference Factor", "Higher values mean more deblocking (-1 = pp default)", -1, G_MAXINT, DEFAULT_DIFF, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_FLAT, g_param_spec_int ("flatness", "Flatness Threshold", "Lower values mean more deblocking (-1 = pp default)", -1, G_MAXINT, DEFAULT_FLAT, G_PARAM_READWRITE)); gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_post_proc_deblock_set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_post_proc_deblock_get_property); } else if (!(g_ascii_strcasecmp (filterdetails[ppidx].shortname, "tn"))) { /* tmpnoise */ g_object_class_install_property (gobject_class, PROP_T1, g_param_spec_int ("threshold-1", "Threshold One", "Higher values mean stronger filtering (-1 = pp default)", -1, G_MAXINT, DEFAULT_T1, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_T2, g_param_spec_int ("threshold-2", "Threshold Two", "Higher values mean stronger filtering (-1 = pp default)", -1, G_MAXINT, DEFAULT_T2, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_T3, g_param_spec_int ("threshold-3", "Threshold Three", "Higher values mean stronger filtering (-1 = pp default)", -1, G_MAXINT, DEFAULT_T3, G_PARAM_READWRITE)); gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_post_proc_tmpnoise_set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_post_proc_tmpnoise_get_property); } else if (!(g_ascii_strcasecmp (filterdetails[ppidx].shortname, "al"))) { /* autolevels */ g_object_class_install_property (gobject_class, PROP_RANGE, g_param_spec_boolean ("fully-range", "Fully Range", "Stretch luminance to (0-255)", DEFAULT_RANGE, G_PARAM_READWRITE)); gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_post_proc_autolevels_set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_post_proc_autolevels_get_property); } else if (!(g_ascii_strcasecmp (filterdetails[ppidx].shortname, "fq"))) { /* forcequant */ g_object_class_install_property (gobject_class, PROP_QUANT, g_param_spec_int ("quantizer", "Force Quantizer", "Quantizer to use (-1 = pp default)", -1, G_MAXINT, DEFAULT_QUANT, G_PARAM_READWRITE)); gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_post_proc_forcequant_set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_post_proc_forcequant_get_property); } gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_post_proc_dispose); btrans_class->set_caps = GST_DEBUG_FUNCPTR (gst_post_proc_setcaps); btrans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_post_proc_transform_ip); } static void gst_post_proc_init (GstPostProc * postproc) { /* properties */ postproc->quality = DEFAULT_QUALITY; postproc->autoq = DEFAULT_AUTOQ; postproc->scope = DEFAULT_SCOPE; postproc->diff = DEFAULT_DIFF; postproc->flat = DEFAULT_FLAT; postproc->quant = DEFAULT_QUANT; postproc->t1 = DEFAULT_T1; postproc->t2 = DEFAULT_T2; postproc->t3 = DEFAULT_T3; postproc->range = DEFAULT_RANGE; postproc->mode = NULL; postproc->cargs = g_strdup (""); postproc->args = g_strdup (""); change_mode (postproc); postproc->context = NULL; postproc->width = 0; postproc->height = 0; postproc->ystride = 0; postproc->ustride = 0; postproc->vstride = 0; postproc->ysize = 0; postproc->usize = 0; postproc->vsize = 0; } static void gst_post_proc_dispose (GObject * object) { GstPostProc *postproc = (GstPostProc *) object; if (postproc->mode) pp_free_mode (postproc->mode); if (postproc->context) pp_free_context (postproc->context); g_free (postproc->cargs); postproc->cargs = NULL; g_free (postproc->args); postproc->args = NULL; G_OBJECT_CLASS (parent_class)->dispose (object); } static gboolean gst_post_proc_setcaps (GstBaseTransform * btrans, GstCaps * incaps, GstCaps * outcaps) { GstPostProc *postproc = (GstPostProc *) (btrans); GstStructure *structure; gboolean ret = FALSE; gint width, height; structure = gst_caps_get_structure (incaps, 0); if (gst_structure_get_int (structure, "width", &width) && gst_structure_get_int (structure, "height", &height)) { change_context (postproc, width, height); ret = TRUE; } return ret; } static GstFlowReturn gst_post_proc_transform_ip (GstBaseTransform * btrans, GstBuffer * in) { GstPostProc *postproc; gint stride[3]; guint8 *outplane[3]; guint8 *inplane[3]; /* postprocess the buffer ! */ postproc = (GstPostProc *) btrans; stride[0] = postproc->ystride; stride[1] = postproc->ustride; stride[2] = postproc->vstride; outplane[0] = inplane[0] = GST_BUFFER_DATA (in); outplane[1] = inplane[1] = outplane[0] + postproc->ysize; outplane[2] = inplane[2] = outplane[1] + postproc->usize; GST_DEBUG_OBJECT (postproc, "calling pp_postprocess, width:%d, height:%d", postproc->width, postproc->height); pp_postprocess ((const guint8 **) inplane, stride, outplane, stride, postproc->width, postproc->height, (int8_t *) "", 0, postproc->mode, postproc->context, 0); return GST_FLOW_OK; } static void gst_post_proc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstPostProc *postproc = (GstPostProc *) object; gint quality; gchar *args; switch (prop_id) { case PROP_QUALITY: quality = g_value_get_uint (value); break; case PROP_AUTOQ: postproc->autoq = g_value_get_boolean (value); break; case PROP_SCOPE: postproc->scope = g_value_get_enum (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } /* construct common args */ args = postproc->autoq ? g_strdup ("autoq") : g_strdup (""); switch (postproc->scope) { case SCOPE_BOTH: break; case SCOPE_CHROMA: append (&args, g_strdup ("noluma")); break; case SCOPE_LUMA: append (&args, g_strdup ("nochrom")); break; default: g_assert_not_reached (); break; } g_free (postproc->cargs); postproc->cargs = args; change_mode (postproc); } static void gst_post_proc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstPostProc *postproc = (GstPostProc *) object; switch (prop_id) { case PROP_QUALITY: g_value_set_uint (value, postproc->quality); break; case PROP_AUTOQ: g_value_set_boolean (value, postproc->autoq); break; case PROP_SCOPE: g_value_set_enum (value, postproc->scope); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gst_post_proc_deblock_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstPostProc *postproc = (GstPostProc *) object; switch (prop_id) { case PROP_DIFF: postproc->diff = g_value_get_int (value); break; case PROP_FLAT: postproc->flat = g_value_get_int (value); break; default: gst_post_proc_set_property (object, prop_id, value, pspec); break; } /* construct args */ g_free (postproc->args); if (postproc->diff >= 0) { postproc->args = g_strdup_printf ("%d", postproc->diff); if (postproc->flat >= 0) append (&postproc->args, g_strdup_printf ("%d", postproc->flat)); } else postproc->args = g_strdup (""); change_mode (postproc); } static void gst_post_proc_deblock_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstPostProc *postproc = (GstPostProc *) object; switch (prop_id) { case PROP_DIFF: g_value_set_int (value, postproc->diff); break; case PROP_FLAT: g_value_set_int (value, postproc->flat); break; default: gst_post_proc_get_property (object, prop_id, value, pspec); break; } } static void gst_post_proc_tmpnoise_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstPostProc *postproc = (GstPostProc *) object; switch (prop_id) { case PROP_T1: postproc->t1 = g_value_get_int (value); break; case PROP_T2: postproc->t2 = g_value_get_int (value); break; case PROP_T3: postproc->t3 = g_value_get_int (value); break; default: gst_post_proc_set_property (object, prop_id, value, pspec); break; } /* construct args */ g_free (postproc->args); if (postproc->t1 >= 0) { postproc->args = g_strdup_printf ("%d", postproc->t1); if (postproc->t2 >= 0) { append (&postproc->args, g_strdup_printf ("%d", postproc->t2)); if (postproc->t3 >= 0) append (&postproc->args, g_strdup_printf ("%d", postproc->t3)); } } else postproc->args = g_strdup (""); change_mode (postproc); } static void gst_post_proc_tmpnoise_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstPostProc *postproc = (GstPostProc *) object; switch (prop_id) { case PROP_T1: g_value_set_int (value, postproc->t1); break; case PROP_T2: g_value_set_int (value, postproc->t2); break; case PROP_T3: g_value_set_int (value, postproc->t3); break; default: gst_post_proc_get_property (object, prop_id, value, pspec); break; } } static void gst_post_proc_autolevels_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstPostProc *postproc = (GstPostProc *) object; switch (prop_id) { case PROP_RANGE: postproc->range = g_value_get_boolean (value); break; default: gst_post_proc_set_property (object, prop_id, value, pspec); break; } /* construct args */ g_free (postproc->args); if (postproc->range) postproc->args = g_strdup ("f"); else postproc->args = g_strdup (""); change_mode (postproc); } static void gst_post_proc_autolevels_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstPostProc *postproc = (GstPostProc *) object; switch (prop_id) { case PROP_RANGE: g_value_set_boolean (value, postproc->range); break; default: gst_post_proc_get_property (object, prop_id, value, pspec); break; } } static void gst_post_proc_forcequant_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstPostProc *postproc = (GstPostProc *) object; switch (prop_id) { case PROP_QUANT: postproc->quant = g_value_get_int (value); break; default: gst_post_proc_set_property (object, prop_id, value, pspec); break; } /* construct args */ g_free (postproc->args); if (postproc->quant >= 0) postproc->args = g_strdup_printf ("%d", postproc->quant); else postproc->args = g_strdup (""); change_mode (postproc); } static void gst_post_proc_forcequant_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstPostProc *postproc = (GstPostProc *) object; switch (prop_id) { case PROP_QUANT: g_value_set_int (value, postproc->quant); break; default: gst_post_proc_get_property (object, prop_id, value, pspec); break; } } static gboolean gst_post_proc_register (GstPlugin * plugin) { GTypeInfo typeinfo = { sizeof (GstPostProcClass), (GBaseInitFunc) gst_post_proc_base_init, NULL, (GClassInitFunc) gst_post_proc_class_init, NULL, NULL, sizeof (GstPostProc), 0, (GInstanceInitFunc) gst_post_proc_init, }; GType type; int i; global_plugins = g_hash_table_new (NULL, NULL); for (i = 0; filterdetails[i].shortname; i++) { gchar *type_name; g_hash_table_insert (global_plugins, GINT_TO_POINTER (0), GINT_TO_POINTER (i)); /* create type_name */ type_name = g_strdup_printf ("postproc_%s", filterdetails[i].longname); if (g_type_from_name (type_name)) { g_free (type_name); continue; } /* create gtype */ type = g_type_register_static (GST_TYPE_VIDEO_FILTER, type_name, &typeinfo, 0); g_hash_table_insert (global_plugins, GINT_TO_POINTER (type), GINT_TO_POINTER (i)); /* register element */ if (!gst_element_register (plugin, type_name, GST_RANK_PRIMARY, type)) { g_free (type_name); return FALSE; } g_free (type_name); } g_hash_table_remove (global_plugins, GINT_TO_POINTER (0)); return TRUE; } static gboolean plugin_init (GstPlugin * plugin) { GST_DEBUG_CATEGORY_INIT (postproc_debug, "postproc", 0, "video postprocessing elements"); #ifndef GST_DISABLE_GST_DEBUG av_log_set_callback (gst_ffmpeg_log_callback); #endif /* Register the filters */ gst_post_proc_register (plugin); /* Now we can return the pointer to the newly created Plugin object. */ return TRUE; } GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, "postproc", "postprocessing elements (" FFMPEG_SOURCE ")", plugin_init, PACKAGE_VERSION, "GPL", "FFMpeg", "http://ffmpeg.sourceforge.net/")