VideoFilter inherits from
[platform/upstream/gstreamer.git] / gst / videofilter / gstvideoflip.c
index b6685b2..d0f3263 100644 (file)
 #include "config.h"
 #endif
 
-/*#define DEBUG_ENABLED */
 #include "gstvideoflip.h"
-#include <string.h>
 
-/* GstVideoflip signals and args */
-enum
-{
-  /* FILL ME */
-  LAST_SIGNAL
-};
+#include <gst/video/video.h>
 
+/* GstVideoflip signals and args */
 enum
 {
   ARG_0,
@@ -45,18 +39,30 @@ enum
       /* FILL ME */
 };
 
-static void gst_videoflip_base_init (gpointer g_class);
-static void gst_videoflip_class_init (gpointer g_class, gpointer class_data);
-static void gst_videoflip_init (GTypeInstance * instance, gpointer g_class);
+GST_DEBUG_CATEGORY (videoflip_debug);
+#define GST_CAT_DEFAULT videoflip_debug
 
-static void gst_videoflip_set_property (GObject * object, guint prop_id,
-    const GValue * value, GParamSpec * pspec);
-static void gst_videoflip_get_property (GObject * object, guint prop_id,
-    GValue * value, GParamSpec * pspec);
+static GstElementDetails videoflip_details =
+GST_ELEMENT_DETAILS ("Video Flipper",
+    "Filter/Effect/Video",
+    "Flips and rotates video",
+    "David Schleef <ds@schleef.org>");
+
+static GstStaticPadTemplate gst_videoflip_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ IYUV, I420, YV12 }"))
+    );
+
+static GstStaticPadTemplate gst_videoflip_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ IYUV, I420, YV12 }"))
+    );
 
-static void gst_videoflip_planar411 (GstVideofilter * videofilter, void *dest,
-    void *src);
-static void gst_videoflip_setup (GstVideofilter * videofilter);
+static GstVideofilterClass *parent_class = NULL;
 
 #define GST_TYPE_VIDEOFLIP_METHOD (gst_videoflip_method_get_type())
 
@@ -65,16 +71,17 @@ gst_videoflip_method_get_type (void)
 {
   static GType videoflip_method_type = 0;
   static GEnumValue videoflip_methods[] = {
-    {GST_VIDEOFLIP_METHOD_IDENTITY, "0", "Identity (no rotation)"},
-    {GST_VIDEOFLIP_METHOD_90R, "1", "Rotate clockwise 90 degrees"},
-    {GST_VIDEOFLIP_METHOD_180, "2", "Rotate 180 degrees"},
-    {GST_VIDEOFLIP_METHOD_90L, "3", "Rotate counter-clockwise 90 degrees"},
-    {GST_VIDEOFLIP_METHOD_HORIZ, "4", "Flip horizontally"},
-    {GST_VIDEOFLIP_METHOD_VERT, "5", "Flip vertically"},
-    {GST_VIDEOFLIP_METHOD_TRANS, "6",
-        "Flip across upper left/lower right diagonal"},
-    {GST_VIDEOFLIP_METHOD_OTHER, "7",
-        "Flip across upper right/lower left diagonal"},
+    {GST_VIDEOFLIP_METHOD_IDENTITY, "Identity (no rotation)", "none"},
+    {GST_VIDEOFLIP_METHOD_90R, "Rotate clockwise 90 degrees", "clockwise"},
+    {GST_VIDEOFLIP_METHOD_180, "Rotate 180 degrees", "rotate-180"},
+    {GST_VIDEOFLIP_METHOD_90L, "Rotate counter-clockwise 90 degrees",
+        "counterclockwise"},
+    {GST_VIDEOFLIP_METHOD_HORIZ, "Flip horizontally", "horizontal-flip"},
+    {GST_VIDEOFLIP_METHOD_VERT, "Flip vertically", "vertical-flip"},
+    {GST_VIDEOFLIP_METHOD_TRANS,
+        "Flip across upper left/lower right diagonal", "upper-left-diagonal"},
+    {GST_VIDEOFLIP_METHOD_OTHER,
+        "Flip across upper right/lower left diagonal", "upper-right-diagonal"},
     {0, NULL, NULL},
   };
 
@@ -85,250 +92,149 @@ gst_videoflip_method_get_type (void)
   return videoflip_method_type;
 }
 
-GType
-gst_videoflip_get_type (void)
-{
-  static GType videoflip_type = 0;
-
-  if (!videoflip_type) {
-    static const GTypeInfo videoflip_info = {
-      sizeof (GstVideoflipClass),
-      gst_videoflip_base_init,
-      NULL,
-      gst_videoflip_class_init,
-      NULL,
-      NULL,
-      sizeof (GstVideoflip),
-      0,
-      gst_videoflip_init,
-    };
-
-    videoflip_type = g_type_register_static (GST_TYPE_VIDEOFILTER,
-        "GstVideoflip", &videoflip_info, 0);
-  }
-  return videoflip_type;
-}
-
-static GstVideofilterFormat gst_videoflip_formats[] = {
-  /* planar */
-  {"YV12", 12, gst_videoflip_planar411,},
-  {"I420", 12, gst_videoflip_planar411,},
-  {"IYUV", 12, gst_videoflip_planar411,},
-};
-
-static void
-gst_videoflip_base_init (gpointer g_class)
+static gboolean
+gst_videoflip_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
+    GstCaps * outcaps)
 {
-  static GstElementDetails videoflip_details =
-      GST_ELEMENT_DETAILS ("Video Flipper",
-      "Filter/Effect/Video",
-      "Flips and rotates video",
-      "David Schleef <ds@schleef.org>");
-  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
-  GstVideofilterClass *videofilter_class = GST_VIDEOFILTER_CLASS (g_class);
-  int i;
-
-  gst_element_class_set_details (element_class, &videoflip_details);
-
-  for (i = 0; i < G_N_ELEMENTS (gst_videoflip_formats); i++) {
-    gst_videofilter_class_add_format (videofilter_class,
-        gst_videoflip_formats + i);
+  GstVideoflip *vf;
+  GstStructure *in_s, *out_s;
+  gboolean ret = FALSE;
+
+  vf = GST_VIDEOFLIP (btrans);
+
+  in_s = gst_caps_get_structure (incaps, 0);
+  out_s = gst_caps_get_structure (outcaps, 0);
+
+  if (gst_structure_get_int (in_s, "width", &vf->from_width) &&
+      gst_structure_get_int (in_s, "height", &vf->from_height) &&
+      gst_structure_get_int (out_s, "width", &vf->to_width) &&
+      gst_structure_get_int (out_s, "height", &vf->to_height)) {
+    /* Check that they are correct */
+    switch (vf->method) {
+      case GST_VIDEOFLIP_METHOD_90R:
+      case GST_VIDEOFLIP_METHOD_90L:
+      case GST_VIDEOFLIP_METHOD_TRANS:
+      case GST_VIDEOFLIP_METHOD_OTHER:
+        if ((vf->from_width != vf->to_height) ||
+            (vf->from_height != vf->to_width)) {
+          GST_DEBUG_OBJECT (vf, "we are inverting width and height but caps "
+              "are not correct : %dx%d to %dx%d", vf->from_width,
+              vf->from_height, vf->to_width, vf->to_height);
+          goto beach;
+        }
+        break;
+      case GST_VIDEOFLIP_METHOD_IDENTITY:
+
+        break;
+      case GST_VIDEOFLIP_METHOD_180:
+      case GST_VIDEOFLIP_METHOD_HORIZ:
+      case GST_VIDEOFLIP_METHOD_VERT:
+        if ((vf->from_width != vf->to_width) ||
+            (vf->from_height != vf->to_height)) {
+          GST_DEBUG_OBJECT (vf, "we are keeping width and height but caps "
+              "are not correct : %dx%d to %dx%d", vf->from_width,
+              vf->from_height, vf->to_width, vf->to_height);
+          goto beach;
+        }
+        break;
+      default:
+        g_assert_not_reached ();
+        break;
+    }
   }
 
-  gst_videofilter_class_add_pad_templates (GST_VIDEOFILTER_CLASS (g_class));
-}
+  ret = TRUE;
 
-static void
-gst_videoflip_class_init (gpointer g_class, gpointer class_data)
-{
-  GObjectClass *gobject_class;
-  GstVideofilterClass *videofilter_class;
-
-  gobject_class = G_OBJECT_CLASS (g_class);
-  videofilter_class = GST_VIDEOFILTER_CLASS (g_class);
-
-  gobject_class->set_property = gst_videoflip_set_property;
-  gobject_class->get_property = gst_videoflip_get_property;
-
-  g_object_class_install_property (gobject_class, ARG_METHOD,
-      g_param_spec_enum ("method", "method", "method",
-          GST_TYPE_VIDEOFLIP_METHOD, GST_VIDEOFLIP_METHOD_90R,
-          G_PARAM_READWRITE));
-
-  videofilter_class->setup = gst_videoflip_setup;
-}
-
-static void
-gst_videoflip_init (GTypeInstance * instance, gpointer g_class)
-{
-  GstVideoflip *videoflip = GST_VIDEOFLIP (instance);
-  GstVideofilter *videofilter;
-
-  GST_DEBUG ("gst_videoflip_init");
-
-  videofilter = GST_VIDEOFILTER (videoflip);
-
-  /* do stuff */
+beach:
+  return ret;
 }
 
-static void
-gst_videoflip_set_property (GObject * object, guint prop_id,
-    const GValue * value, GParamSpec * pspec)
+static GstCaps *
+gst_videoflip_transform_caps (GstBaseTransform * trans,
+    GstPadDirection direction, GstCaps * caps)
 {
-  GstVideoflip *src;
-
-  /* it's not null if we got it, but it might not be ours */
-  g_return_if_fail (GST_IS_VIDEOFLIP (object));
-  src = GST_VIDEOFLIP (object);
-
-  GST_DEBUG ("gst_videoflip_set_property");
-  switch (prop_id) {
-    case ARG_METHOD:
-      src->method = g_value_get_enum (value);
-      /* FIXME is this ok? (threading issues) */
-      gst_videoflip_setup (GST_VIDEOFILTER (src));
-      break;
-    default:
-      break;
-  }
-}
-
-static void
-gst_videoflip_get_property (GObject * object, guint prop_id, GValue * value,
-    GParamSpec * pspec)
-{
-  GstVideoflip *src;
-
-  /* it's not null if we got it, but it might not be ours */
-  g_return_if_fail (GST_IS_VIDEOFLIP (object));
-  src = GST_VIDEOFLIP (object);
-
-  switch (prop_id) {
-    case ARG_METHOD:
-      g_value_set_enum (value, src->method);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
+  GstVideoflip *videoflip;
+  GstCaps *ret;
+  gint width, height, i;
+
+  videoflip = GST_VIDEOFLIP (trans);
+
+  ret = gst_caps_copy (caps);
+
+  for (i = 0; i < gst_caps_get_size (ret); i++) {
+    GstStructure *structure = gst_caps_get_structure (ret, i);
+
+    if (gst_structure_get_int (structure, "width", &width) &&
+        gst_structure_get_int (structure, "height", &height)) {
+
+      switch (videoflip->method) {
+        case GST_VIDEOFLIP_METHOD_90R:
+        case GST_VIDEOFLIP_METHOD_90L:
+        case GST_VIDEOFLIP_METHOD_TRANS:
+        case GST_VIDEOFLIP_METHOD_OTHER:
+          gst_structure_set (structure, "width", G_TYPE_INT, height,
+              "height", G_TYPE_INT, width, NULL);
+          break;
+        case GST_VIDEOFLIP_METHOD_IDENTITY:
+        case GST_VIDEOFLIP_METHOD_180:
+        case GST_VIDEOFLIP_METHOD_HORIZ:
+        case GST_VIDEOFLIP_METHOD_VERT:
+          gst_structure_set (structure, "width", G_TYPE_INT, width,
+              "height", G_TYPE_INT, height, NULL);
+          break;
+        default:
+          g_assert_not_reached ();
+          break;
+      }
+    }
   }
-}
 
-static gboolean
-plugin_init (GstPlugin * plugin)
-{
-  if (!gst_library_load ("gstvideofilter"))
-    return FALSE;
+  GST_DEBUG_OBJECT (videoflip, "transformed %" GST_PTR_FORMAT " to %"
+      GST_PTR_FORMAT, caps, ret);
 
-  return gst_element_register (plugin, "videoflip", GST_RANK_NONE,
-      GST_TYPE_VIDEOFLIP);
+  return ret;
 }
 
-GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
-    GST_VERSION_MINOR,
-    "videoflip",
-    "Flips and rotates video",
-    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)
+/* Useful macros */
+#define GST_VIDEO_I420_Y_ROWSTRIDE(width) (GST_ROUND_UP_4(width))
+#define GST_VIDEO_I420_U_ROWSTRIDE(width) (GST_ROUND_UP_8(width)/2)
+#define GST_VIDEO_I420_V_ROWSTRIDE(width) ((GST_ROUND_UP_8(GST_VIDEO_I420_Y_ROWSTRIDE(width)))/2)
 
-     static void gst_videoflip_flip (GstVideoflip * videoflip,
-    unsigned char *dest, unsigned char *src, int sw, int sh, int dw, int dh);
+#define GST_VIDEO_I420_Y_OFFSET(w,h) (0)
+#define GST_VIDEO_I420_U_OFFSET(w,h) (GST_VIDEO_I420_Y_OFFSET(w,h)+(GST_VIDEO_I420_Y_ROWSTRIDE(w)*GST_ROUND_UP_2(h)))
+#define GST_VIDEO_I420_V_OFFSET(w,h) (GST_VIDEO_I420_U_OFFSET(w,h)+(GST_VIDEO_I420_U_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
 
+#define GST_VIDEO_I420_SIZE(w,h)     (GST_VIDEO_I420_V_OFFSET(w,h)+(GST_VIDEO_I420_V_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
 
-     static void gst_videoflip_setup (GstVideofilter * videofilter)
+static gboolean
+gst_videoflip_get_unit_size (GstBaseTransform * btrans, GstCaps * caps,
+    guint * size)
 {
-  int from_width, from_height;
   GstVideoflip *videoflip;
+  GstStructure *structure;
+  gboolean ret = FALSE;
+  gint width, height;
 
-  GST_DEBUG ("gst_videoflip_setup");
+  videoflip = GST_VIDEOFLIP (btrans);
 
-  videoflip = GST_VIDEOFLIP (videofilter);
+  structure = gst_caps_get_structure (caps, 0);
 
-  from_width = gst_videofilter_get_input_width (videofilter);
-  from_height = gst_videofilter_get_input_height (videofilter);
-
-  if (from_width == 0 || from_height == 0) {
-    return;
+  if (gst_structure_get_int (structure, "width", &width) &&
+      gst_structure_get_int (structure, "height", &height)) {
+    *size = GST_VIDEO_I420_SIZE (width, height);
+    ret = TRUE;
+    GST_DEBUG_OBJECT (videoflip, "our frame size is %d bytes (%dx%d)", *size,
+        width, height);
   }
 
-  switch (videoflip->method) {
-    case GST_VIDEOFLIP_METHOD_90R:
-    case GST_VIDEOFLIP_METHOD_90L:
-    case GST_VIDEOFLIP_METHOD_TRANS:
-    case GST_VIDEOFLIP_METHOD_OTHER:
-      gst_videofilter_set_output_size (videofilter, from_height, from_width);
-      break;
-    case GST_VIDEOFLIP_METHOD_IDENTITY:
-    case GST_VIDEOFLIP_METHOD_180:
-    case GST_VIDEOFLIP_METHOD_HORIZ:
-    case GST_VIDEOFLIP_METHOD_VERT:
-      gst_videofilter_set_output_size (videofilter, from_width, from_height);
-      break;
-    default:
-      g_assert_not_reached ();
-      break;
-  }
-
-  GST_DEBUG ("format=%p \"%s\" from %dx%d to %dx%d",
-      videofilter->format, videofilter->format->fourcc,
-      from_width, from_height, videofilter->to_width, videofilter->to_height);
-
-  if (videoflip->method == GST_VIDEOFLIP_METHOD_IDENTITY) {
-    GST_DEBUG ("videoflip: using passthru");
-    videofilter->passthru = TRUE;
-  } else {
-    videofilter->passthru = FALSE;
-  }
-
-  videofilter->from_buf_size =
-      (videofilter->from_width * videofilter->from_height *
-      videofilter->format->depth) / 8;
-  videofilter->to_buf_size =
-      (videofilter->to_width * videofilter->to_height *
-      videofilter->format->depth) / 8;
-
-  videofilter->inited = TRUE;
+  return ret;
 }
 
-static void
-gst_videoflip_planar411 (GstVideofilter * videofilter, void *dest, void *src)
-{
-  GstVideoflip *videoflip;
-  int sw;
-  int sh;
-  int dw;
-  int dh;
-
-  g_return_if_fail (GST_IS_VIDEOFLIP (videofilter));
-  videoflip = GST_VIDEOFLIP (videofilter);
-
-  sw = videofilter->from_width;
-  sh = videofilter->from_height;
-  dw = videofilter->to_width;
-  dh = videofilter->to_height;
-
-  GST_DEBUG ("videoflip: scaling planar 4:1:1 %dx%d to %dx%d", sw, sh, dw, dh);
-
-  gst_videoflip_flip (videoflip, dest, src, sw, sh, dw, dh);
-
-  src += sw * sh;
-  dest += dw * dh;
-
-  dh = dh >> 1;
-  dw = dw >> 1;
-  sh = sh >> 1;
-  sw = sw >> 1;
-
-  gst_videoflip_flip (videoflip, dest, src, sw, sh, dw, dh);
-
-  src += sw * sh;
-  dest += dw * dh;
-
-  gst_videoflip_flip (videoflip, dest, src, sw, sh, dw, dh);
-}
-
-static void
+static GstFlowReturn
 gst_videoflip_flip (GstVideoflip * videoflip, unsigned char *dest,
     unsigned char *src, int sw, int sh, int dw, int dh)
 {
+  GstFlowReturn ret = GST_FLOW_OK;
   int x, y;
 
   switch (videoflip->method) {
@@ -382,7 +288,265 @@ gst_videoflip_flip (GstVideoflip * videoflip, unsigned char *dest,
       }
       break;
     default:
-      /* FIXME */
+      ret = GST_FLOW_ERROR;
+      break;
+  }
+
+  return ret;
+}
+
+static GstFlowReturn
+gst_videoflip_transform (GstBaseTransform * trans, GstBuffer * in,
+    GstBuffer * out)
+{
+  GstVideoflip *videoflip;
+  gpointer dest, src;
+  int sw, sh, dw, dh;
+  GstFlowReturn ret = GST_FLOW_OK;
+
+  videoflip = GST_VIDEOFLIP (trans);
+
+  gst_buffer_stamp (out, in);
+
+  src = GST_BUFFER_DATA (in);
+  dest = GST_BUFFER_DATA (out);
+  sw = videoflip->from_width;
+  sh = videoflip->from_height;
+  dw = videoflip->to_width;
+  dh = videoflip->to_height;
+
+  GST_LOG_OBJECT (videoflip, "videoflip: scaling planar 4:1:1 %dx%d to %dx%d",
+      sw, sh, dw, dh);
+
+  ret = gst_videoflip_flip (videoflip, dest, src, sw, sh, dw, dh);
+  if (ret != GST_FLOW_OK)
+    goto beach;
+
+  src += sw * sh;
+  dest += dw * dh;
+
+  dh = dh >> 1;
+  dw = dw >> 1;
+  sh = sh >> 1;
+  sw = sw >> 1;
+
+  ret = gst_videoflip_flip (videoflip, dest, src, sw, sh, dw, dh);
+  if (ret != GST_FLOW_OK)
+    goto beach;
+
+  src += sw * sh;
+  dest += dw * dh;
+
+  ret = gst_videoflip_flip (videoflip, dest, src, sw, sh, dw, dh);
+
+beach:
+  return ret;
+}
+
+static gboolean
+gst_videoflip_handle_src_event (GstPad * pad, GstEvent * event)
+{
+  GstVideoflip *vf;
+  gboolean ret;
+  gdouble x, y;
+  GstStructure *structure;
+
+  vf = GST_VIDEOFLIP (gst_pad_get_parent (pad));
+
+  GST_DEBUG_OBJECT (vf, "handling %s event", GST_EVENT_TYPE_NAME (event));
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_NAVIGATION:
+      event =
+          GST_EVENT (gst_mini_object_make_writable (GST_MINI_OBJECT (event)));
+
+      structure = (GstStructure *) gst_event_get_structure (event);
+      if (gst_structure_get_double (structure, "pointer_x", &x) &&
+          gst_structure_get_double (structure, "pointer_y", &y)) {
+        switch (vf->method) {
+          case GST_VIDEOFLIP_METHOD_90R:
+          case GST_VIDEOFLIP_METHOD_OTHER:
+            x = y;
+            y = vf->to_width - x;
+            break;
+          case GST_VIDEOFLIP_METHOD_90L:
+          case GST_VIDEOFLIP_METHOD_TRANS:
+            x = vf->to_height - y;
+            y = x;
+            break;
+          case GST_VIDEOFLIP_METHOD_180:
+            x = vf->to_width - x;
+            y = vf->to_height - y;
+            break;
+          case GST_VIDEOFLIP_METHOD_HORIZ:
+            x = vf->to_width - x;
+            y = y;
+            break;
+          case GST_VIDEOFLIP_METHOD_VERT:
+            x = x;
+            y = vf->to_height - y;
+            break;
+          default:
+            x = x;
+            y = y;
+            break;
+        }
+        gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x,
+            "pointer_y", G_TYPE_DOUBLE, y, NULL);
+      }
+      break;
+    default:
+      break;
+  }
+
+  ret = gst_pad_event_default (pad, event);
+
+  gst_object_unref (vf);
+
+  return ret;
+}
+
+static void
+gst_videoflip_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstVideoflip *videoflip;
+  GstVideofilter *videofilter;
+
+  g_return_if_fail (GST_IS_VIDEOFLIP (object));
+  videoflip = GST_VIDEOFLIP (object);
+  videofilter = GST_VIDEOFILTER (object);
+
+  switch (prop_id) {
+    case ARG_METHOD:
+    {
+      GstVideoflipMethod method;
+
+      method = g_value_get_enum (value);
+      if (method != videoflip->method) {
+        GstBaseTransform *btrans = GST_BASE_TRANSFORM (videoflip);
+
+        g_mutex_lock (btrans->transform_lock);
+        gst_pad_set_caps (btrans->sinkpad, NULL);
+        gst_pad_set_caps (btrans->srcpad, NULL);
+        g_mutex_unlock (btrans->transform_lock);
+        videoflip->method = method;
+      }
+    }
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_videoflip_get_property (GObject * object, guint prop_id, GValue * value,
+    GParamSpec * pspec)
+{
+  GstVideoflip *videoflip;
+
+  g_return_if_fail (GST_IS_VIDEOFLIP (object));
+  videoflip = GST_VIDEOFLIP (object);
+
+  switch (prop_id) {
+    case ARG_METHOD:
+      g_value_set_enum (value, videoflip->method);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
   }
 }
+
+static void
+gst_videoflip_base_init (gpointer g_class)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+  gst_element_class_set_details (element_class, &videoflip_details);
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&gst_videoflip_sink_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&gst_videoflip_src_template));
+}
+
+static void
+gst_videoflip_class_init (gpointer klass, gpointer class_data)
+{
+  GObjectClass *gobject_class;
+  GstBaseTransformClass *trans_class;
+
+  gobject_class = (GObjectClass *) klass;
+  trans_class = (GstBaseTransformClass *) klass;
+
+  parent_class = g_type_class_peek_parent (klass);
+
+  gobject_class->set_property = gst_videoflip_set_property;
+  gobject_class->get_property = gst_videoflip_get_property;
+
+  g_object_class_install_property (gobject_class, ARG_METHOD,
+      g_param_spec_enum ("method", "method", "method",
+          GST_TYPE_VIDEOFLIP_METHOD, GST_VIDEOFLIP_METHOD_90R,
+          G_PARAM_READWRITE));
+
+  trans_class->transform_caps =
+      GST_DEBUG_FUNCPTR (gst_videoflip_transform_caps);
+  trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_videoflip_set_caps);
+  trans_class->get_unit_size = GST_DEBUG_FUNCPTR (gst_videoflip_get_unit_size);
+  trans_class->transform = GST_DEBUG_FUNCPTR (gst_videoflip_transform);
+}
+
+static void
+gst_videoflip_init (GTypeInstance * instance, gpointer g_class)
+{
+  GstVideoflip *videoflip = GST_VIDEOFLIP (instance);
+  GstBaseTransform *btrans = GST_BASE_TRANSFORM (instance);
+
+  GST_DEBUG_OBJECT (videoflip, "gst_videoflip_init");
+
+  videoflip->method = GST_VIDEOFLIP_METHOD_90R;
+
+  gst_pad_set_event_function (btrans->srcpad,
+      GST_DEBUG_FUNCPTR (gst_videoflip_handle_src_event));
+}
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (videoflip_debug, "videoflip", 0, "videoflip");
+
+  return gst_element_register (plugin, "videoflip", GST_RANK_NONE,
+      GST_TYPE_VIDEOFLIP);
+}
+
+GType
+gst_videoflip_get_type (void)
+{
+  static GType videoflip_type = 0;
+
+  if (!videoflip_type) {
+    static const GTypeInfo videoflip_info = {
+      sizeof (GstVideoflipClass),
+      gst_videoflip_base_init,
+      NULL,
+      gst_videoflip_class_init,
+      NULL,
+      NULL,
+      sizeof (GstVideoflip),
+      0,
+      gst_videoflip_init,
+    };
+
+    videoflip_type = g_type_register_static (GST_TYPE_VIDEOFILTER,
+        "GstVideoflip", &videoflip_info, 0);
+  }
+  return videoflip_type;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    "videoflip",
+    "Flips and rotates video",
+    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);