fix up more enums
[platform/upstream/gst-plugins-good.git] / gst / videobox / gstvideobox.c
index c1d9c3b..87a72e8 100644 (file)
 #include "config.h"
 #endif
 #include <gst/gst.h>
+#include <gst/base/gstbasetransform.h>
 #include <gst/video/video.h>
 
+#include <liboil/liboil.h>
 #include <string.h>
 
+GST_DEBUG_CATEGORY (videobox_debug);
+#define GST_CAT_DEFAULT videobox_debug
+
 #define GST_TYPE_VIDEO_BOX \
   (gst_video_box_get_type())
 #define GST_VIDEO_BOX(obj) \
@@ -49,11 +54,7 @@ GstVideoBoxFill;
 
 struct _GstVideoBox
 {
-  GstElement element;
-
-  /* pads */
-  GstPad *sinkpad;
-  GstPad *srcpad;
+  GstBaseTransform element;
 
   /* caps */
   gint in_width, in_height;
@@ -73,7 +74,7 @@ struct _GstVideoBox
 
 struct _GstVideoBoxClass
 {
-  GstElementClass parent_class;
+  GstBaseTransformClass parent_class;
 };
 
 /* elementfactory information */
@@ -84,13 +85,6 @@ GST_ELEMENT_DETAILS ("video box filter",
     "Wim Taymans <wim@fluendo.com>");
 
 
-/* VideoBox signals and args */
-enum
-{
-  /* FILL ME */
-  LAST_SIGNAL
-};
-
 #define DEFAULT_LEFT      0
 #define DEFAULT_RIGHT     0
 #define DEFAULT_TOP       0
@@ -101,14 +95,14 @@ enum
 
 enum
 {
-  ARG_0,
-  ARG_LEFT,
-  ARG_RIGHT,
-  ARG_TOP,
-  ARG_BOTTOM,
-  ARG_FILL_TYPE,
-  ARG_ALPHA,
-  ARG_BORDER_ALPHA,
+  PROP_0,
+  PROP_LEFT,
+  PROP_RIGHT,
+  PROP_TOP,
+  PROP_BOTTOM,
+  PROP_FILL_TYPE,
+  PROP_ALPHA,
+  PROP_BORDER_ALPHA,
   /* FILL ME */
 };
 
@@ -116,7 +110,7 @@ static GstStaticPadTemplate gst_video_box_src_template =
 GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
     GST_PAD_ALWAYS,
-    GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ I420, AYUV }"))
+    GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ AYUV, I420 }"))
     );
 
 static GstStaticPadTemplate gst_video_box_sink_template =
@@ -127,33 +121,33 @@ GST_STATIC_PAD_TEMPLATE ("sink",
     );
 
 
-static void gst_video_box_base_init (gpointer g_class);
-static void gst_video_box_class_init (GstVideoBoxClass * klass);
-static void gst_video_box_init (GstVideoBox * video_box);
+GST_BOILERPLATE (GstVideoBox, gst_video_box, GstBaseTransform,
+    GST_TYPE_BASE_TRANSFORM);
 
 static void gst_video_box_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec);
 static void gst_video_box_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec);
 
-static GstPadLinkReturn
-gst_video_box_sink_link (GstPad * pad, const GstCaps * caps);
-static void gst_video_box_chain (GstPad * pad, GstData * _data);
-
-static GstElementStateReturn gst_video_box_change_state (GstElement * element);
+static GstCaps *gst_video_box_transform_caps (GstBaseTransform * trans,
+    GstPadDirection direction, GstCaps * from);
+static gboolean gst_video_box_set_caps (GstBaseTransform * trans,
+    GstCaps * in, GstCaps * out);
+static gboolean gst_video_box_get_unit_size (GstBaseTransform * trans,
+    GstCaps * caps, guint * size);
+static GstFlowReturn gst_video_box_transform (GstBaseTransform * trans,
+    GstBuffer * in, GstBuffer * out);
 
 
-static GstElementClass *parent_class = NULL;
-
 #define GST_TYPE_VIDEO_BOX_FILL (gst_video_box_fill_get_type())
 static GType
 gst_video_box_fill_get_type (void)
 {
   static GType video_box_fill_type = 0;
   static GEnumValue video_box_fill[] = {
-    {VIDEO_BOX_FILL_BLACK, "0", "Black"},
-    {VIDEO_BOX_FILL_GREEN, "1", "Colorkey green"},
-    {VIDEO_BOX_FILL_BLUE, "2", "Colorkey blue"},
+    {VIDEO_BOX_FILL_BLACK, "Black", "black"},
+    {VIDEO_BOX_FILL_GREEN, "Colorkey green", "green"},
+    {VIDEO_BOX_FILL_BLUE, "Colorkey blue", "blue"},
     {0, NULL, NULL},
   };
 
@@ -164,32 +158,6 @@ gst_video_box_fill_get_type (void)
   return video_box_fill_type;
 }
 
-/* static guint gst_video_box_signals[LAST_SIGNAL] = { 0 }; */
-
-GType
-gst_video_box_get_type (void)
-{
-  static GType video_box_type = 0;
-
-  if (!video_box_type) {
-    static const GTypeInfo video_box_info = {
-      sizeof (GstVideoBoxClass),
-      gst_video_box_base_init,
-      NULL,
-      (GClassInitFunc) gst_video_box_class_init,
-      NULL,
-      NULL,
-      sizeof (GstVideoBox),
-      0,
-      (GInstanceInitFunc) gst_video_box_init,
-    };
-
-    video_box_type =
-        g_type_register_static (GST_TYPE_ELEMENT, "GstVideoBox",
-        &video_box_info, 0);
-  }
-  return video_box_type;
-}
 
 static void
 gst_video_box_base_init (gpointer g_class)
@@ -203,88 +171,80 @@ gst_video_box_base_init (gpointer g_class)
   gst_element_class_add_pad_template (element_class,
       gst_static_pad_template_get (&gst_video_box_src_template));
 }
+
 static void
 gst_video_box_class_init (GstVideoBoxClass * klass)
 {
   GObjectClass *gobject_class;
-  GstElementClass *gstelement_class;
+  GstBaseTransformClass *trans_class;
 
   gobject_class = (GObjectClass *) klass;
-  gstelement_class = (GstElementClass *) klass;
+  trans_class = (GstBaseTransformClass *) klass;
 
-  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+  gobject_class->set_property = gst_video_box_set_property;
+  gobject_class->get_property = gst_video_box_get_property;
 
-  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FILL_TYPE,
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FILL_TYPE,
       g_param_spec_enum ("fill", "Fill", "How to fill the borders",
           GST_TYPE_VIDEO_BOX_FILL, DEFAULT_FILL_TYPE,
           (GParamFlags) G_PARAM_READWRITE));
-  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LEFT,
-      g_param_spec_int ("left", "Left", "Pixels to box at left",
-          G_MININT, G_MAXINT, DEFAULT_LEFT, G_PARAM_READWRITE));
-  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_RIGHT,
-      g_param_spec_int ("right", "Right", "Pixels to box at right",
-          G_MININT, G_MAXINT, DEFAULT_RIGHT, G_PARAM_READWRITE));
-  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TOP,
-      g_param_spec_int ("top", "Top", "Pixels to box at top",
-          G_MININT, G_MAXINT, DEFAULT_TOP, G_PARAM_READWRITE));
-  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BOTTOM,
-      g_param_spec_int ("bottom", "Bottom", "Pixels to box at bottom",
-          G_MININT, G_MAXINT, DEFAULT_BOTTOM, G_PARAM_READWRITE));
-  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ALPHA,
-      g_param_spec_double ("alpha", "Alpha", "Alpha value picture",
-          0.0, 1.0, DEFAULT_ALPHA, G_PARAM_READWRITE));
-  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BORDER_ALPHA,
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LEFT,
+      g_param_spec_int ("left", "Left",
+          "Pixels to box at left (<0  = add a border)", G_MININT, G_MAXINT,
+          DEFAULT_LEFT, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RIGHT,
+      g_param_spec_int ("right", "Right",
+          "Pixels to box at right (<0 = add a border)", G_MININT, G_MAXINT,
+          DEFAULT_RIGHT, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TOP,
+      g_param_spec_int ("top", "Top",
+          "Pixels to box at top (<0 = add a border)", G_MININT, G_MAXINT,
+          DEFAULT_TOP, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BOTTOM,
+      g_param_spec_int ("bottom", "Bottom",
+          "Pixels to box at bottom (<0 = add a border)", G_MININT, G_MAXINT,
+          DEFAULT_BOTTOM, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_ALPHA,
+      g_param_spec_double ("alpha", "Alpha", "Alpha value picture", 0.0, 1.0,
+          DEFAULT_ALPHA, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BORDER_ALPHA,
       g_param_spec_double ("border_alpha", "Border Alpha",
           "Alpha value of the border", 0.0, 1.0, DEFAULT_BORDER_ALPHA,
           G_PARAM_READWRITE));
 
-  gobject_class->set_property = gst_video_box_set_property;
-  gobject_class->get_property = gst_video_box_get_property;
+  trans_class->transform_caps = gst_video_box_transform_caps;
+  trans_class->set_caps = gst_video_box_set_caps;
+  trans_class->get_unit_size = gst_video_box_get_unit_size;
+  trans_class->transform = gst_video_box_transform;
 
-  gstelement_class->change_state = gst_video_box_change_state;
+  GST_DEBUG_CATEGORY_INIT (videobox_debug, "videobox", 0,
+      "Resizes a video by adding borders or cropping");
 }
 
 static void
-gst_video_box_init (GstVideoBox * video_box)
+gst_video_box_init (GstVideoBox * video_box, GstVideoBoxClass * g_class)
 {
-  /* create the sink and src pads */
-  video_box->sinkpad =
-      gst_pad_new_from_template (gst_static_pad_template_get
-      (&gst_video_box_sink_template), "sink");
-  gst_element_add_pad (GST_ELEMENT (video_box), video_box->sinkpad);
-  gst_pad_set_chain_function (video_box->sinkpad, gst_video_box_chain);
-  gst_pad_set_link_function (video_box->sinkpad, gst_video_box_sink_link);
-
-  video_box->srcpad =
-      gst_pad_new_from_template (gst_static_pad_template_get
-      (&gst_video_box_src_template), "src");
-  gst_element_add_pad (GST_ELEMENT (video_box), video_box->srcpad);
-
   video_box->box_right = DEFAULT_RIGHT;
   video_box->box_left = DEFAULT_LEFT;
   video_box->box_top = DEFAULT_TOP;
   video_box->box_bottom = DEFAULT_BOTTOM;
+  video_box->crop_right = 0;
+  video_box->crop_left = 0;
+  video_box->crop_top = 0;
+  video_box->crop_bottom = 0;
   video_box->fill_type = DEFAULT_FILL_TYPE;
   video_box->alpha = DEFAULT_ALPHA;
   video_box->border_alpha = DEFAULT_BORDER_ALPHA;
-
-  GST_FLAG_SET (video_box, GST_ELEMENT_EVENT_AWARE);
 }
 
-/* do we need this function? */
 static void
 gst_video_box_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec)
 {
-  GstVideoBox *video_box;
-
-  /* it's not null if we got it, but it might not be ours */
-  g_return_if_fail (GST_IS_VIDEO_BOX (object));
-
-  video_box = GST_VIDEO_BOX (object);
+  GstVideoBox *video_box = GST_VIDEO_BOX (object);
 
   switch (prop_id) {
-    case ARG_LEFT:
+    case PROP_LEFT:
       video_box->box_left = g_value_get_int (value);
       if (video_box->box_left < 0) {
         video_box->border_left = -video_box->box_left;
@@ -294,7 +254,7 @@ gst_video_box_set_property (GObject * object, guint prop_id,
         video_box->crop_left = video_box->box_left;
       }
       break;
-    case ARG_RIGHT:
+    case PROP_RIGHT:
       video_box->box_right = g_value_get_int (value);
       if (video_box->box_right < 0) {
         video_box->border_right = -video_box->box_right;
@@ -304,7 +264,7 @@ gst_video_box_set_property (GObject * object, guint prop_id,
         video_box->crop_right = video_box->box_right;
       }
       break;
-    case ARG_TOP:
+    case PROP_TOP:
       video_box->box_top = g_value_get_int (value);
       if (video_box->box_top < 0) {
         video_box->border_top = -video_box->box_top;
@@ -314,7 +274,7 @@ gst_video_box_set_property (GObject * object, guint prop_id,
         video_box->crop_top = video_box->box_top;
       }
       break;
-    case ARG_BOTTOM:
+    case PROP_BOTTOM:
       video_box->box_bottom = g_value_get_int (value);
       if (video_box->box_bottom < 0) {
         video_box->border_bottom = -video_box->box_bottom;
@@ -324,13 +284,13 @@ gst_video_box_set_property (GObject * object, guint prop_id,
         video_box->crop_bottom = video_box->box_bottom;
       }
       break;
-    case ARG_FILL_TYPE:
+    case PROP_FILL_TYPE:
       video_box->fill_type = g_value_get_enum (value);
       break;
-    case ARG_ALPHA:
+    case PROP_ALPHA:
       video_box->alpha = g_value_get_double (value);
       break;
-    case ARG_BORDER_ALPHA:
+    case PROP_BORDER_ALPHA:
       video_box->border_alpha = g_value_get_double (value);
       break;
     default:
@@ -338,37 +298,33 @@ gst_video_box_set_property (GObject * object, guint prop_id,
       break;
   }
 }
+
 static void
 gst_video_box_get_property (GObject * object, guint prop_id, GValue * value,
     GParamSpec * pspec)
 {
-  GstVideoBox *video_box;
-
-  /* it's not null if we got it, but it might not be ours */
-  g_return_if_fail (GST_IS_VIDEO_BOX (object));
-
-  video_box = GST_VIDEO_BOX (object);
+  GstVideoBox *video_box = GST_VIDEO_BOX (object);
 
   switch (prop_id) {
-    case ARG_LEFT:
+    case PROP_LEFT:
       g_value_set_int (value, video_box->box_left);
       break;
-    case ARG_RIGHT:
+    case PROP_RIGHT:
       g_value_set_int (value, video_box->box_right);
       break;
-    case ARG_TOP:
+    case PROP_TOP:
       g_value_set_int (value, video_box->box_top);
       break;
-    case ARG_BOTTOM:
+    case PROP_BOTTOM:
       g_value_set_int (value, video_box->box_bottom);
       break;
-    case ARG_FILL_TYPE:
+    case PROP_FILL_TYPE:
       g_value_set_enum (value, video_box->fill_type);
       break;
-    case ARG_ALPHA:
+    case PROP_ALPHA:
       g_value_set_double (value, video_box->alpha);
       break;
-    case ARG_BORDER_ALPHA:
+    case PROP_BORDER_ALPHA:
       g_value_set_double (value, video_box->border_alpha);
       break;
     default:
@@ -377,45 +333,178 @@ gst_video_box_get_property (GObject * object, guint prop_id, GValue * value,
   }
 }
 
-static GstPadLinkReturn
-gst_video_box_sink_link (GstPad * pad, const GstCaps * caps)
+static GstCaps *
+gst_video_box_transform_caps (GstBaseTransform * trans,
+    GstPadDirection direction, GstCaps * from)
+{
+  GstVideoBox *video_box;
+  GstCaps *to;
+  GstStructure *structure;
+  GValue list_value = { 0 }, value = {
+  0};
+  gint dir, i, tmp;
+
+  video_box = GST_VIDEO_BOX (trans);
+
+  g_value_init (&list_value, GST_TYPE_LIST);
+  g_value_init (&value, GST_TYPE_FOURCC);
+  gst_value_set_fourcc (&value, GST_MAKE_FOURCC ('I', '4', '2', '0'));
+  gst_value_list_append_value (&list_value, &value);
+  g_value_unset (&value);
+
+  to = gst_caps_copy (from);
+  dir = (direction == GST_PAD_SINK) ? -1 : 1;
+
+  for (i = 0; i < gst_caps_get_size (to); i++) {
+    structure = gst_caps_get_structure (to, i);
+    if (direction == GST_PAD_SINK) {    /* I420 to { I420, AYUV } */
+      g_value_init (&value, GST_TYPE_FOURCC);
+      gst_value_set_fourcc (&value, GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'));
+      gst_value_list_append_value (&list_value, &value);
+      g_value_unset (&value);
+      gst_structure_set_value (structure, "format", &list_value);
+    } else if (direction == GST_PAD_SRC) {
+      gst_structure_set_value (structure, "format", &list_value);
+    }
+    if (gst_structure_get_int (structure, "width", &tmp))
+      gst_structure_set (structure, "width", G_TYPE_INT,
+          tmp + dir * (video_box->box_left + video_box->box_right), NULL);
+    if (gst_structure_get_int (structure, "height", &tmp))
+      gst_structure_set (structure, "height", G_TYPE_INT,
+          tmp + dir * (video_box->box_top + video_box->box_bottom), NULL);
+  }
+
+  g_value_unset (&list_value);
+
+  GST_DEBUG_OBJECT (video_box, "direction %d, transformed %" GST_PTR_FORMAT
+      " to %" GST_PTR_FORMAT, direction, from, to);
+
+  return to;
+}
+
+static gboolean
+gst_video_box_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
 {
   GstVideoBox *video_box;
   GstStructure *structure;
   gboolean ret;
+  guint32 fourcc = 0;
 
-  video_box = GST_VIDEO_BOX (gst_pad_get_parent (pad));
-  structure = gst_caps_get_structure (caps, 0);
+  video_box = GST_VIDEO_BOX (trans);
 
+  structure = gst_caps_get_structure (in, 0);
   ret = gst_structure_get_int (structure, "width", &video_box->in_width);
   ret &= gst_structure_get_int (structure, "height", &video_box->in_height);
 
-  return GST_PAD_LINK_OK;
+  structure = gst_caps_get_structure (out, 0);
+  ret &= gst_structure_get_int (structure, "width", &video_box->out_width);
+  ret &= gst_structure_get_int (structure, "height", &video_box->out_height);
+  ret &= gst_structure_get_fourcc (structure, "format", &fourcc);
+
+  if (fourcc == GST_STR_FOURCC ("AYUV")) {
+    video_box->use_alpha = TRUE;
+  } else {
+    if (video_box->box_left == 0 && video_box->box_right == 0 &&
+        video_box->box_top == 0 && video_box->box_bottom == 0) {
+      gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (video_box), TRUE);
+      GST_LOG ("we are using passthrough");
+    } else {
+      gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (video_box),
+          FALSE);
+      GST_LOG ("we are not using passthrough");
+    }
+  }
+
+  return ret;
 }
 
-#define GST_VIDEO_I420_Y_OFFSET(width,height) (0)
-#define GST_VIDEO_I420_U_OFFSET(width,height) ((width)*(height))
-#define GST_VIDEO_I420_V_OFFSET(width,height) ((width)*(height) + ((width/2)*(height/2)))
+/* see gst-plugins/gst/games/gstvideoimage.c, paint_setup_I420() */
+#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)
 
-#define GST_VIDEO_I420_Y_ROWSTRIDE(width) (width)
-#define GST_VIDEO_I420_U_ROWSTRIDE(width) ((width)/2)
-#define GST_VIDEO_I420_V_ROWSTRIDE(width) ((width)/2)
+#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 gboolean
+gst_video_box_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
+    guint * size)
+{
+  GstVideoBox *video_box;
+  GstStructure *structure = NULL;
+  guint32 fourcc;
+  gint width, height;
+
+  g_return_val_if_fail (size, FALSE);
+  video_box = GST_VIDEO_BOX (trans);
+
+  structure = gst_caps_get_structure (caps, 0);
+  gst_structure_get_fourcc (structure, "format", &fourcc);
+  gst_structure_get_int (structure, "width", &width);
+  gst_structure_get_int (structure, "height", &height);
+
+  switch (fourcc) {
+    case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
+      *size = width * height * 4;
+      break;
+    case GST_MAKE_FOURCC ('I', '4', '2', '0'):
+      *size = GST_VIDEO_I420_SIZE (width, height);
+      break;
+    default:
+      return FALSE;
+      break;
+  }
+
+  return TRUE;
+}
 
 static int yuv_colors_Y[] = { 16, 150, 29 };
 static int yuv_colors_U[] = { 128, 46, 255 };
 static int yuv_colors_V[] = { 128, 21, 107 };
 
 static void
+gst_video_box_copy_plane_i420 (GstVideoBox * video_box, guint8 * src,
+    guint8 * dest, gint br, gint bl, gint bt, gint bb, gint src_crop_width,
+    gint src_crop_height, gint src_stride, gint dest_width, gint dest_stride,
+    guint8 fill_color)
+{
+  gint j;
+
+  /* top border */
+  for (j = 0; j < bt; j++) {
+    oil_splat_u8_ns (dest, &fill_color, dest_width);
+    dest += dest_stride;
+  }
+
+  /* copy and add left and right border */
+  for (j = 0; j < src_crop_height; j++) {
+    oil_splat_u8_ns (dest, &fill_color, bl);
+    oil_memcpy (dest + bl, src, src_crop_width);
+    oil_splat_u8_ns (dest + bl + src_crop_width, &fill_color, br);
+    dest += dest_stride;
+    src += src_stride;
+  }
+
+  /* bottom border */
+  for (j = 0; j < bb; j++) {
+    oil_splat_u8_ns (dest, &fill_color, dest_width);
+    dest += dest_stride;
+  }
+}
+
+static void
 gst_video_box_i420 (GstVideoBox * video_box, guint8 * src, guint8 * dest)
 {
   guint8 *srcY, *srcU, *srcV;
   guint8 *destY, *destU, *destV;
   gint crop_width, crop_height;
   gint out_width, out_height;
-  gint src_stride;
+  gint src_width, src_height;
+  gint src_stride, dest_stride;
   gint br, bl, bt, bb;
-  gint j;
-  gint color1, color2;
 
   br = video_box->border_right;
   bl = video_box->border_left;
@@ -425,104 +514,61 @@ gst_video_box_i420 (GstVideoBox * video_box, guint8 * src, guint8 * dest)
   out_width = video_box->out_width;
   out_height = video_box->out_height;
 
-  destY = dest + GST_VIDEO_I420_Y_OFFSET (out_width, out_height);
+  src_width = video_box->in_width;
+  src_height = video_box->in_height;
 
-  srcY =
-      src + GST_VIDEO_I420_Y_OFFSET (video_box->in_width, video_box->in_height);
-  src_stride = GST_VIDEO_I420_Y_ROWSTRIDE (video_box->in_width);
+  crop_width = src_width - (video_box->crop_left + video_box->crop_right);
+  crop_height = src_height - (video_box->crop_top + video_box->crop_bottom);
 
-  crop_width =
-      video_box->in_width - (video_box->crop_left + video_box->crop_right);
-  crop_height =
-      video_box->in_height - (video_box->crop_top + video_box->crop_bottom);
+  /* Y plane */
+  src_stride = GST_VIDEO_I420_Y_ROWSTRIDE (src_width);
+  dest_stride = GST_VIDEO_I420_Y_ROWSTRIDE (out_width);
 
+  destY = dest + GST_VIDEO_I420_Y_OFFSET (out_width, out_height);
+
+  srcY = src + GST_VIDEO_I420_Y_OFFSET (src_width, src_height);
   srcY += src_stride * video_box->crop_top + video_box->crop_left;
 
-  color1 = yuv_colors_Y[video_box->fill_type];
+  gst_video_box_copy_plane_i420 (video_box, srcY, destY, br, bl, bt, bb,
+      crop_width, crop_height, src_stride, out_width, dest_stride,
+      yuv_colors_Y[video_box->fill_type]);
 
-  /* copy Y plane first */
-  for (j = 0; j < bt; j++) {
-    memset (destY, color1, out_width);
-    destY += out_width;
-  }
-  for (j = 0; j < crop_height; j++) {
-    memset (destY, color1, bl);
-    destY += bl;
-    memcpy (destY, srcY, crop_width);
-    destY += crop_width;
-    memset (destY, color1, br);
-    destY += br;
-    srcY += src_stride;
-  }
-  for (j = 0; j < bb; j++) {
-    memset (destY, color1, out_width);
-    destY += out_width;
-  }
-
-  src_stride = GST_VIDEO_I420_U_ROWSTRIDE (video_box->in_width);
+  /* U plane */
+  src_stride = GST_VIDEO_I420_U_ROWSTRIDE (src_width);
+  dest_stride = GST_VIDEO_I420_U_ROWSTRIDE (out_width);
 
   destU = dest + GST_VIDEO_I420_U_OFFSET (out_width, out_height);
-  destV = dest + GST_VIDEO_I420_V_OFFSET (out_width, out_height);
 
-  crop_width /= 2;
-  crop_height /= 2;
-  out_width /= 2;
-  out_height /= 2;
-  bb /= 2;
-  bt /= 2;
-  br /= 2;
-  bl /= 2;
-
-  srcU =
-      src + GST_VIDEO_I420_U_OFFSET (video_box->in_width, video_box->in_height);
-  srcV =
-      src + GST_VIDEO_I420_V_OFFSET (video_box->in_width, video_box->in_height);
+  srcU = src + GST_VIDEO_I420_U_OFFSET (src_width, src_height);
   srcU += src_stride * (video_box->crop_top / 2) + (video_box->crop_left / 2);
-  srcV += src_stride * (video_box->crop_top / 2) + (video_box->crop_left / 2);
 
-  color1 = yuv_colors_U[video_box->fill_type];
-  color2 = yuv_colors_V[video_box->fill_type];
+  gst_video_box_copy_plane_i420 (video_box, srcU, destU, br / 2, bl / 2, bt / 2,
+      bb / 2, crop_width / 2, crop_height / 2, src_stride, out_width / 2,
+      dest_stride, yuv_colors_U[video_box->fill_type]);
 
-  for (j = 0; j < bt; j++) {
-    memset (destU, color1, out_width);
-    memset (destV, color2, out_width);
-    destU += out_width;
-    destV += out_width;
-  }
-  for (j = 0; j < crop_height; j++) {
-    memset (destU, color1, bl);
-    destU += bl;
-    /* copy U plane */
-    memcpy (destU, srcU, crop_width);
-    destU += crop_width;
-    memset (destU, color1, br);
-    destU += br;
-    srcU += src_stride;
-
-    memset (destV, color2, bl);
-    destV += bl;
-    /* copy V plane */
-    memcpy (destV, srcV, crop_width);
-    destV += crop_width;
-    memset (destV, color2, br);
-    destV += br;
-    srcV += src_stride;
-  }
-  for (j = 0; j < bb; j++) {
-    memset (destU, color1, out_width);
-    memset (destV, color2, out_width);
-    destU += out_width;
-    destV += out_width;
-  }
+  /* V plane */
+  src_stride = GST_VIDEO_I420_V_ROWSTRIDE (src_width);
+  dest_stride = GST_VIDEO_I420_V_ROWSTRIDE (out_width);
+
+  destV = dest + GST_VIDEO_I420_V_OFFSET (out_width, out_height);
+
+  srcV = src + GST_VIDEO_I420_V_OFFSET (src_width, src_height);
+  srcV += src_stride * (video_box->crop_top / 2) + (video_box->crop_left / 2);
+
+  gst_video_box_copy_plane_i420 (video_box, srcV, destV, br / 2, bl / 2, bt / 2,
+      bb / 2, crop_width / 2, crop_height / 2, src_stride, out_width / 2,
+      dest_stride, yuv_colors_V[video_box->fill_type]);
 }
 
+/* Note the source image is always I420, we
+ * are converting to AYUV on the fly here */
 static void
 gst_video_box_ayuv (GstVideoBox * video_box, guint8 * src, guint8 * dest)
 {
   guint8 *srcY, *srcU, *srcV;
   gint crop_width, crop_width2, crop_height;
   gint out_width, out_height;
-  gint src_stride, src_stride2;
+  gint src_stridey, src_strideu, src_stridev;
   gint br, bl, bt, bb;
   gint colorY, colorU, colorV;
   gint i, j;
@@ -539,8 +585,9 @@ gst_video_box_ayuv (GstVideoBox * video_box, guint8 * src, guint8 * dest)
   out_width = video_box->out_width;
   out_height = video_box->out_height;
 
-  src_stride = GST_VIDEO_I420_Y_ROWSTRIDE (video_box->in_width);
-  src_stride2 = src_stride / 2;
+  src_stridey = GST_VIDEO_I420_Y_ROWSTRIDE (video_box->in_width);
+  src_strideu = GST_VIDEO_I420_U_ROWSTRIDE (video_box->in_width);
+  src_stridev = GST_VIDEO_I420_V_ROWSTRIDE (video_box->in_width);
 
   crop_width =
       video_box->in_width - (video_box->crop_left + video_box->crop_right);
@@ -550,13 +597,13 @@ gst_video_box_ayuv (GstVideoBox * video_box, guint8 * src, guint8 * dest)
 
   srcY =
       src + GST_VIDEO_I420_Y_OFFSET (video_box->in_width, video_box->in_height);
-  srcY += src_stride * video_box->crop_top + video_box->crop_left;
+  srcY += src_stridey * video_box->crop_top + video_box->crop_left;
   srcU =
       src + GST_VIDEO_I420_U_OFFSET (video_box->in_width, video_box->in_height);
-  srcU += src_stride2 * (video_box->crop_top / 2) + (video_box->crop_left / 2);
+  srcU += src_strideu * (video_box->crop_top / 2) + (video_box->crop_left / 2);
   srcV =
       src + GST_VIDEO_I420_V_OFFSET (video_box->in_width, video_box->in_height);
-  srcV += src_stride2 * (video_box->crop_top / 2) + (video_box->crop_left / 2);
+  srcV += src_stridev * (video_box->crop_top / 2) + (video_box->crop_left / 2);
 
   colorY = yuv_colors_Y[video_box->fill_type];
   colorU = yuv_colors_U[video_box->fill_type];
@@ -567,24 +614,28 @@ gst_video_box_ayuv (GstVideoBox * video_box, guint8 * src, guint8 * dest)
       colorV);
 
   /* top border */
-  for (i = 0; i < bt; i++) {
-    for (j = 0; j < out_width; j++) {
-      *destp++ = ayuv;
-    }
+  if (bt) {
+    size_t nb_pixels = bt * out_width;
+
+    oil_splat_u32_ns (destp, &ayuv, nb_pixels);
+    destp += nb_pixels;
   }
   for (i = 0; i < crop_height; i++) {
     /* left border */
-    for (j = 0; j < bl; j++) {
-      *destp++ = ayuv;
+    if (bl) {
+      oil_splat_u32_ns (destp, &ayuv, bl);
+      destp += bl;
     }
     dest = (guint8 *) destp;
     /* center */
+    /* We can splat the alpha channel for the whole line */
+    oil_splat_u8 (dest, 4, &i_alpha, crop_width);
     for (j = 0; j < crop_width2; j++) {
-      *dest++ = i_alpha;
+      dest++;
       *dest++ = *srcY++;
       *dest++ = *srcU;
       *dest++ = *srcV;
-      *dest++ = i_alpha;
+      dest++;
       *dest++ = *srcY++;
       *dest++ = *srcU++;
       *dest++ = *srcV++;
@@ -593,131 +644,42 @@ gst_video_box_ayuv (GstVideoBox * video_box, guint8 * src, guint8 * dest)
       srcU -= crop_width2;
       srcV -= crop_width2;
     } else {
-      srcU += src_stride2 - crop_width2;
-      srcV += src_stride2 - crop_width2;
+      srcU += src_strideu - crop_width2;
+      srcV += src_stridev - crop_width2;
     }
-    srcY += src_stride - crop_width;
+    srcY += src_stridey - crop_width;
 
     destp = (guint32 *) dest;
     /* right border */
-    for (j = 0; j < br; j++) {
-      *destp++ = ayuv;
+    if (br) {
+      oil_splat_u32_ns (destp, &ayuv, br);
+      destp += br;
     }
   }
   /* bottom border */
-  for (i = 0; i < bb; i++) {
-    for (j = 0; j < out_width; j++) {
-      *destp++ = ayuv;
-    }
+  if (bb) {
+    size_t nb_pixels = bb * out_width;
+
+    oil_splat_u32_ns (destp, &ayuv, nb_pixels);
+    destp += nb_pixels;
   }
 }
 
-static void
-gst_video_box_chain (GstPad * pad, GstData * _data)
+static GstFlowReturn
+gst_video_box_transform (GstBaseTransform * trans, GstBuffer * in,
+    GstBuffer * out)
 {
-  GstBuffer *buffer;
   GstVideoBox *video_box;
-  GstBuffer *outbuf;
-  gint new_width, new_height;
-
-  video_box = GST_VIDEO_BOX (gst_pad_get_parent (pad));
-
-  if (GST_IS_EVENT (_data)) {
-    GstEvent *event = GST_EVENT (_data);
-
-    switch (GST_EVENT_TYPE (event)) {
-      default:
-        gst_pad_event_default (pad, event);
-        break;
-    }
-    return;
-  }
-
-  buffer = GST_BUFFER (_data);
-
-  new_width =
-      video_box->in_width - (video_box->box_left + video_box->box_right);
-  new_height =
-      video_box->in_height - (video_box->box_top + video_box->box_bottom);
 
-  if (new_width != video_box->out_width ||
-      new_height != video_box->out_height ||
-      !GST_PAD_CAPS (video_box->srcpad)) {
-    GstCaps *newcaps;
-
-    newcaps = gst_caps_copy (gst_pad_get_negotiated_caps (video_box->sinkpad));
-
-    video_box->use_alpha = TRUE;
-
-    /* try AYUV first */
-    gst_caps_set_simple (newcaps,
-        "format", GST_TYPE_FOURCC, GST_STR_FOURCC ("AYUV"),
-        "width", G_TYPE_INT, new_width, "height", G_TYPE_INT, new_height, NULL);
-
-    if (GST_PAD_LINK_FAILED (gst_pad_try_set_caps (video_box->srcpad, newcaps))) {
-      video_box->use_alpha = FALSE;
-      newcaps =
-          gst_caps_copy (gst_pad_get_negotiated_caps (video_box->sinkpad));
-      gst_caps_set_simple (newcaps, "format", GST_TYPE_FOURCC,
-          GST_STR_FOURCC ("I420"), "width", G_TYPE_INT, new_width, "height",
-          G_TYPE_INT, new_height, NULL);
-
-      if (GST_PAD_LINK_FAILED (gst_pad_try_set_caps (video_box->srcpad,
-                  newcaps))) {
-        GST_ELEMENT_ERROR (video_box, CORE, NEGOTIATION, (NULL), (NULL));
-        return;
-      }
-    }
-
-    video_box->out_width = new_width;
-    video_box->out_height = new_height;
-  }
+  video_box = GST_VIDEO_BOX (trans);
 
   if (video_box->use_alpha) {
-    outbuf = gst_buffer_new_and_alloc (new_width * new_height * 4);
-
-    gst_video_box_ayuv (video_box,
-        GST_BUFFER_DATA (buffer), GST_BUFFER_DATA (outbuf));
+    gst_video_box_ayuv (video_box, GST_BUFFER_DATA (in), GST_BUFFER_DATA (out));
   } else {
-    outbuf = gst_buffer_new_and_alloc ((new_width * new_height * 3) / 2);
-
-    gst_video_box_i420 (video_box,
-        GST_BUFFER_DATA (buffer), GST_BUFFER_DATA (outbuf));
-  }
-  GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer);
-  GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buffer);
-
-
-  gst_buffer_unref (buffer);
-
-  gst_pad_push (video_box->srcpad, GST_DATA (outbuf));
-}
-
-static GstElementStateReturn
-gst_video_box_change_state (GstElement * element)
-{
-  GstVideoBox *video_box;
-
-  video_box = GST_VIDEO_BOX (element);
-
-  switch (GST_STATE_TRANSITION (element)) {
-    case GST_STATE_NULL_TO_READY:
-      break;
-    case GST_STATE_READY_TO_PAUSED:
-      break;
-    case GST_STATE_PAUSED_TO_PLAYING:
-      break;
-    case GST_STATE_PLAYING_TO_PAUSED:
-      break;
-    case GST_STATE_PAUSED_TO_READY:
-      break;
-    case GST_STATE_READY_TO_NULL:
-      break;
+    gst_video_box_i420 (video_box, GST_BUFFER_DATA (in), GST_BUFFER_DATA (out));
   }
 
-  parent_class->change_state (element);
-
-  return GST_STATE_SUCCESS;
+  return GST_FLOW_OK;
 }
 
 static gboolean
@@ -731,4 +693,4 @@ GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
     GST_VERSION_MINOR,
     "videobox",
     "resizes a video by adding borders or cropping",
-    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)
+    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)