rtpmux: fix output segment and buffer DTS to correspond to the flattened PTS
[platform/upstream/gst-plugins-good.git] / gst / videobox / gstvideobox.c
index 0cec935..f8f984c 100644 (file)
@@ -1,5 +1,7 @@
 /* GStreamer
- * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
+ * Copyright (C) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -13,8 +15,8 @@
  *
  * You should have received a copy of the GNU Library General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
  */
 /**
  * SECTION:element-videobox
  * 
  * The videobox plugin has many uses such as doing a mosaic of pictures, 
  * letterboxing video, cutting out pieces of video, picture in picture, etc..
+ *
+ * Setting autocrop to true changes the behavior of the plugin so that
+ * caps determine crop properties rather than the other way around: given
+ * input and output dimensions, the crop values are selected so that the
+ * smaller frame is effectively centered in the larger frame.  This
+ * involves either cropping or padding.
+ * 
+ * If you use autocrop there is little point in setting the other
+ * properties manually because they will be overriden if the caps change,
+ * but nothing stops you from doing so.
+ * 
+ * Sample pipeline:
+ * |[
+ * gst-launch-1.0 videotestsrc ! videobox autocrop=true ! \
+ *   "video/x-raw, width=600, height=400" ! videoconvert ! ximagesink
+ * ]|
  */
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
-#include <gst/gst.h>
-#include <gst/base/gstbasetransform.h>
-#include <gst/video/video.h>
+
+#include "gstvideobox.h"
+#include "gstvideoboxorc.h"
+
 #include <math.h>
-#include <liboil/liboil.h>
 #include <string.h>
 
 GST_DEBUG_CATEGORY_STATIC (videobox_debug);
 #define GST_CAT_DEFAULT videobox_debug
 
-#define GST_TYPE_VIDEO_BOX \
-  (gst_video_box_get_type())
-#define GST_VIDEO_BOX(obj) \
-  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_BOX,GstVideoBox))
-#define GST_VIDEO_BOX_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_BOX,GstVideoBoxClass))
-#define GST_IS_VIDEO_BOX(obj) \
-  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_BOX))
-#define GST_IS_VIDEO_BOX_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_BOX))
-
-typedef struct _GstVideoBox GstVideoBox;
-typedef struct _GstVideoBoxClass GstVideoBoxClass;
-
-typedef enum
+/* From videotestsrc.c */
+static const guint8 yuv_sdtv_colors_Y[VIDEO_BOX_FILL_LAST] =
+    { 16, 145, 41, 81, 210, 235 };
+static const guint8 yuv_sdtv_colors_U[VIDEO_BOX_FILL_LAST] =
+    { 128, 54, 240, 90, 16, 128 };
+static const guint8 yuv_sdtv_colors_V[VIDEO_BOX_FILL_LAST] =
+    { 128, 34, 110, 240, 146, 128 };
+
+static const guint8 yuv_hdtv_colors_Y[VIDEO_BOX_FILL_LAST] =
+    { 16, 173, 32, 63, 219, 235 };
+static const guint8 yuv_hdtv_colors_U[VIDEO_BOX_FILL_LAST] =
+    { 128, 42, 240, 102, 16, 128 };
+static const guint8 yuv_hdtv_colors_V[VIDEO_BOX_FILL_LAST] =
+    { 128, 26, 118, 240, 138, 128 };
+
+static const guint8 rgb_colors_R[VIDEO_BOX_FILL_LAST] =
+    { 0, 0, 0, 255, 255, 255 };
+static const guint8 rgb_colors_G[VIDEO_BOX_FILL_LAST] =
+    { 0, 255, 0, 0, 255, 255 };
+static const guint8 rgb_colors_B[VIDEO_BOX_FILL_LAST] =
+    { 0, 0, 255, 0, 0, 255 };
+
+/* Generated by -bad/ext/cog/generate_tables */
+static const int cog_ycbcr_to_rgb_matrix_8bit_hdtv[] = {
+  298, 0, 459, -63514,
+  298, -55, -136, 19681,
+  298, 541, 0, -73988,
+};
+
+static const int cog_ycbcr_to_rgb_matrix_8bit_sdtv[] = {
+  298, 0, 409, -57068,
+  298, -100, -208, 34707,
+  298, 516, 0, -70870,
+};
+
+static const gint cog_rgb_to_ycbcr_matrix_8bit_hdtv[] = {
+  47, 157, 16, 4096,
+  -26, -87, 112, 32768,
+  112, -102, -10, 32768,
+};
+
+static const gint cog_rgb_to_ycbcr_matrix_8bit_sdtv[] = {
+  66, 129, 25, 4096,
+  -38, -74, 112, 32768,
+  112, -94, -18, 32768,
+};
+
+static const gint cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit[] = {
+  256, -30, -53, 10600,
+  0, 261, 29, -4367,
+  0, 19, 262, -3289,
+};
+
+static const gint cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit[] = {
+  256, 25, 49, -9536,
+  0, 253, -28, 3958,
+  0, -19, 252, 2918,
+};
+
+static const gint cog_identity_matrix_8bit[] = {
+  256, 0, 0, 0,
+  0, 256, 0, 0,
+  0, 0, 256, 0,
+};
+
+#define APPLY_MATRIX(m,o,v1,v2,v3) ((m[o*4] * v1 + m[o*4+1] * v2 + m[o*4+2] * v3 + m[o*4+3]) >> 8)
+
+static void
+fill_ayuv (GstVideoBoxFill fill_type, guint b_alpha,
+    GstVideoFrame * frame, gboolean sdtv)
 {
-  VIDEO_BOX_FILL_BLACK,
-  VIDEO_BOX_FILL_GREEN,
-  VIDEO_BOX_FILL_BLUE,
-  VIDEO_BOX_FILL_LAST
+  guint32 empty_pixel;
+  guint8 *dest;
+  gint width, height;
+  gint stride;
+
+  width = GST_VIDEO_FRAME_WIDTH (frame);
+  height = GST_VIDEO_FRAME_HEIGHT (frame);
+
+  b_alpha = CLAMP (b_alpha, 0, 255);
+
+  if (sdtv)
+    empty_pixel = GUINT32_FROM_BE ((b_alpha << 24) |
+        (yuv_sdtv_colors_Y[fill_type] << 16) |
+        (yuv_sdtv_colors_U[fill_type] << 8) | yuv_sdtv_colors_V[fill_type]);
+  else
+    empty_pixel = GUINT32_FROM_BE ((b_alpha << 24) |
+        (yuv_hdtv_colors_Y[fill_type] << 16) |
+        (yuv_hdtv_colors_U[fill_type] << 8) | yuv_hdtv_colors_V[fill_type]);
+
+  dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
+  stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
+
+  if (G_LIKELY (stride == 4 * width))
+    video_box_orc_splat_u32 ((guint32 *) dest, empty_pixel, width * height);
+  else if (height) {
+    for (; height; --height) {
+      video_box_orc_splat_u32 ((guint32 *) dest, empty_pixel, width);
+      dest += stride;
+    }
+  }
 }
-GstVideoBoxFill;
 
-struct _GstVideoBox
+static void
+copy_ayuv_ayuv (guint i_alpha, GstVideoFrame * dest_frame,
+    gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src_frame,
+    gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
 {
-  GstBaseTransform element;
+  gint i, j;
+  gint src_stride;
+  gint dest_stride;
+  guint8 *dest, *src;
+
+  src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (src_frame, 0);
+  dest_stride = GST_VIDEO_FRAME_PLANE_STRIDE (dest_frame, 0);
+
+  src = GST_VIDEO_FRAME_PLANE_DATA (src_frame, 0);
+  dest = GST_VIDEO_FRAME_PLANE_DATA (dest_frame, 0);
+
+  dest = dest + dest_y * dest_stride + dest_x * 4;
+  src = src + src_y * src_stride + src_x * 4;
+
+  w *= 4;
+
+  if (dest_sdtv != src_sdtv) {
+    gint matrix[12];
+    gint y, u, v;
+
+    memcpy (matrix,
+        dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
+        cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
+
+    for (i = 0; i < h; i++) {
+      for (j = 0; j < w; j += 4) {
+        /* ORC FIXME */
+        dest[j] = (src[j] * i_alpha) >> 8;
+        y = src[j + 1];
+        u = src[j + 2];
+        v = src[j + 3];
+        dest[j + 1] = APPLY_MATRIX (matrix, 0, y, u, v);
+        dest[j + 2] = APPLY_MATRIX (matrix, 1, y, u, v);
+        dest[j + 3] = APPLY_MATRIX (matrix, 2, y, u, v);
+      }
+      dest += dest_stride;
+      src += src_stride;
+    }
+  } else {
+    for (i = 0; i < h; i++) {
+      for (j = 0; j < w; j += 4) {
+        /* ORC FIXME */
+        dest[j] = (src[j] * i_alpha) >> 8;
+        dest[j + 1] = src[j + 1];
+        dest[j + 2] = src[j + 2];
+        dest[j + 3] = src[j + 3];
+      }
+      dest += dest_stride;
+      src += src_stride;
+    }
+  }
+}
+
+static void
+copy_ayuv_i420 (guint i_alpha, GstVideoFrame * dest_frame,
+    gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src_frame,
+    gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
+{
+  gint i, j;
+  guint8 *destY, *destY2, *destU, *destV;
+  gint dest_strideY, dest_strideU, dest_strideV;
+  const guint8 *src2;
+  gint src_stride;
+  gint y_idx, uv_idx;
+  gint y1, y2, y3, y4;
+  gint u1, u2, u3, u4;
+  gint v1, v2, v3, v4;
+  gint matrix[12];
+  guint8 *src;
+  gint dest_height, src_height, dest_width;
+
+  dest_height = GST_VIDEO_FRAME_HEIGHT (dest_frame);
+  dest_width = GST_VIDEO_FRAME_WIDTH (dest_frame);
+  src_height = GST_VIDEO_FRAME_HEIGHT (src_frame);
+
+  dest_strideY = GST_VIDEO_FRAME_COMP_STRIDE (dest_frame, 0);
+  dest_strideU = GST_VIDEO_FRAME_COMP_STRIDE (dest_frame, 1);
+  dest_strideV = GST_VIDEO_FRAME_COMP_STRIDE (dest_frame, 2);
+
+  src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (src_frame, 0);
+
+  destY = GST_VIDEO_FRAME_COMP_DATA (dest_frame, 0);
+  destU = GST_VIDEO_FRAME_COMP_DATA (dest_frame, 1);
+  destV = GST_VIDEO_FRAME_COMP_DATA (dest_frame, 2);
+
+  destY = destY + dest_y * dest_strideY + dest_x;
+  destY2 = (dest_y < dest_height) ? destY + dest_strideY : destY;
+  destU = destU + (dest_y / 2) * dest_strideU + dest_x / 2;
+  destV = destV + (dest_y / 2) * dest_strideV + dest_x / 2;
+
+  src = GST_VIDEO_FRAME_PLANE_DATA (src_frame, 0);
+  src = src + src_y * src_stride + src_x * 4;
+  src2 = (src_y < src_height) ? src + src_stride : src;
+
+  h = dest_y + h;
+  w = dest_x + w;
+
+  if (src_sdtv != dest_sdtv)
+    memcpy (matrix,
+        dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
+        cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
+  else
+    memcpy (matrix, cog_identity_matrix_8bit, 12 * sizeof (gint));
+
+  /* 1. Handle the first destination scanline specially if it
+   *    doesn't start at the macro pixel boundary, i.e. blend
+   *    with the background! */
+  if (dest_y % 2 == 1) {
+    /* 1.1. Handle the first destination pixel if it doesn't
+     *      start at the macro pixel boundary, i.e. blend with
+     *      the background! */
+    if (dest_x % 2 == 1) {
+      y1 = src[4 * 0 + 1];
+      u1 = src[4 * 0 + 2];
+      v1 = src[4 * 0 + 3];
+
+      destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[0] =
+          CLAMP ((3 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
+          255);
+      destV[0] =
+          CLAMP ((3 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4, 0,
+          255);
+
+      j = dest_x + 1;
+      y_idx = uv_idx = 1;
+    } else {
+      j = dest_x;
+      y_idx = uv_idx = 0;
+    }
 
-  /* caps */
-  guint32 in_fourcc;
-  gint in_width, in_height;
-  guint32 out_fourcc;
-  gint out_width, out_height;
+    /* 1.2. Copy all macro pixels from the source to the destination
+     *      but blend with the background because we're only filling
+     *      the lower part of the macro pixels. */
+    for (; j < w - 1; j += 2) {
+      y1 = src[4 * y_idx + 1];
+      y2 = src[4 * y_idx + 4 + 1];
+
+      u1 = src[4 * y_idx + 2];
+      u2 = src[4 * y_idx + 4 + 2];
+
+      v1 = src[4 * y_idx + 3];
+      v2 = src[4 * y_idx + 4 + 3];
+
+      destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destY[y_idx + 1] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
+      destU[uv_idx] = CLAMP (
+          (2 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
+      destV[uv_idx] = CLAMP (
+          (2 * destV[uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
+
+      y_idx += 2;
+      uv_idx++;
+    }
 
-  gint box_left, box_right, box_top, box_bottom;
+    /* 1.3. Now copy the last pixel if one exists and blend it
+     *      with the background because we only fill part of
+     *      the macro pixel. In case this is the last pixel of
+     *      the destination we will a larger part. */
+    if (j == w - 1 && j == dest_width - 1) {
+      y1 = src[4 * y_idx + 1];
+      u1 = src[4 * y_idx + 2];
+      v1 = src[4 * y_idx + 3];
+
+      destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[uv_idx] = CLAMP (
+          (destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
+      destV[uv_idx] = CLAMP (
+          (destV[uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 2, 0, 255);
+    } else if (j == w - 1) {
+      y1 = src[4 * y_idx + 1];
+      u1 = src[4 * y_idx + 2];
+      v1 = src[4 * y_idx + 3];
+
+      destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[uv_idx] = CLAMP (
+          (3 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
+          255);
+      destV[uv_idx] =
+          CLAMP ((3 * destV[uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4,
+          0, 255);
+    }
 
-  gint border_left, border_right, border_top, border_bottom;
-  gint crop_left, crop_right, crop_top, crop_bottom;
+    destY += dest_strideY;
+    destY2 += dest_strideY;
+    destU += dest_strideU;
+    destV += dest_strideV;
+    src += src_stride;
+    src2 += src_stride;
+    i = dest_y + 1;
+  } else {
+    i = dest_y;
+  }
 
-  gdouble alpha;
-  gdouble border_alpha;
+  /* 2. Copy all macro pixel scanlines, the destination scanline
+   *    now starts at macro pixel boundary. */
+  for (; i < h - 1; i += 2) {
+    /* 2.1. Handle the first destination pixel if it doesn't
+     *      start at the macro pixel boundary, i.e. blend with
+     *      the background! */
+    if (dest_x % 2 == 1) {
+      y1 = src[4 * 0 + 1];
+      y2 = src2[4 * 0 + 1];
+      u1 = src[4 * 0 + 2];
+      u2 = src2[4 * 0 + 2];
+      v1 = src[4 * 0 + 3];
+      v2 = src2[4 * 0 + 3];
+
+      destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destY2[0] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
+      destU[0] = CLAMP (
+          (2 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
+      destV[0] = CLAMP (
+          (2 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
+      j = dest_x + 1;
+      y_idx = uv_idx = 1;
+    } else {
+      j = dest_x;
+      y_idx = uv_idx = 0;
+    }
 
-  GstVideoBoxFill fill_type;
-};
+    /* 2.2. Copy all macro pixels from the source to the destination.
+     *      All pixels now start at macro pixel boundary, i.e. no
+     *      blending with the background is necessary. */
+    for (; j < w - 1; j += 2) {
+      y1 = src[4 * y_idx + 1];
+      y2 = src[4 * y_idx + 4 + 1];
+      y3 = src2[4 * y_idx + 1];
+      y4 = src2[4 * y_idx + 4 + 1];
+
+      u1 = src[4 * y_idx + 2];
+      u2 = src[4 * y_idx + 4 + 2];
+      u3 = src2[4 * y_idx + 2];
+      u4 = src2[4 * y_idx + 4 + 2];
+
+      v1 = src[4 * y_idx + 3];
+      v2 = src[4 * y_idx + 4 + 3];
+      v3 = src2[4 * y_idx + 3];
+      v4 = src2[4 * y_idx + 4 + 3];
+
+      destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destY[y_idx + 1] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
+      destY2[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y3, u3, v3), 0, 255);
+      destY2[y_idx + 1] = CLAMP (APPLY_MATRIX (matrix, 0, y4, u4, v4), 0, 255);
+
+      destU[uv_idx] = CLAMP (
+          (APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 1, y2,
+                  u2, v2) + APPLY_MATRIX (matrix, 1, y3, u3,
+                  v3) + APPLY_MATRIX (matrix, 1, y4, u4, v4)) / 4, 0, 255);
+      destV[uv_idx] = CLAMP (
+          (APPLY_MATRIX (matrix, 2, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
+                  u2, v2) + APPLY_MATRIX (matrix, 2, y3, u3,
+                  v3) + APPLY_MATRIX (matrix, 2, y4, u4, v4)) / 4, 0, 255);
+
+      y_idx += 2;
+      uv_idx++;
+    }
+
+    /* 2.3. Now copy the last pixel if one exists and blend it
+     *      with the background because we only fill part of
+     *      the macro pixel. In case this is the last pixel of
+     *      the destination we will a larger part. */
+    if (j == w - 1 && j == dest_width - 1) {
+      y1 = src[4 * y_idx + 1];
+      y2 = src2[4 * y_idx + 1];
+
+      u1 = src[4 * y_idx + 2];
+      u2 = src2[4 * y_idx + 2];
+
+      v1 = src[4 * y_idx + 3];
+      v2 = src2[4 * y_idx + 3];
+
+      destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destY2[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
+      destU[uv_idx] = CLAMP (
+          (APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
+                  u2, v2)) / 2, 0, 255);
+      destV[uv_idx] = CLAMP (
+          (APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
+                  u2, v2)) / 2, 0, 255);
+    } else if (j == w - 1) {
+      y1 = src[4 * y_idx + 1];
+      y2 = src2[4 * y_idx + 1];
+
+      u1 = src[4 * y_idx + 2];
+      u2 = src2[4 * y_idx + 2];
+
+      v1 = src[4 * y_idx + 3];
+      v2 = src2[4 * y_idx + 3];
+
+      destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destY2[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
+      destU[uv_idx] = CLAMP (
+          (2 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
+      destV[uv_idx] = CLAMP (
+          (2 * destV[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
+    }
+
+    destY += 2 * dest_strideY;
+    destY2 += 2 * dest_strideY;
+    destU += dest_strideU;
+    destV += dest_strideV;
+    src += 2 * src_stride;
+    src2 += 2 * src_stride;
+  }
+
+  /* 3. Handle the last scanline if one exists. This again
+   *    doesn't start at macro pixel boundary but should
+   *    only fill the upper part of the macro pixels. */
+  if (i == h - 1 && i == dest_height - 1) {
+    /* 3.1. Handle the first destination pixel if it doesn't
+     *      start at the macro pixel boundary, i.e. blend with
+     *      the background! */
+    if (dest_x % 2 == 1) {
+      y1 = src[4 * 0 + 1];
+      u1 = src[4 * 0 + 2];
+      v1 = src[4 * 0 + 3];
+
+      destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[0] =
+          CLAMP ((destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
+      destV[0] =
+          CLAMP ((destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 2, 0, 255);
+
+      j = dest_x + 1;
+      y_idx = uv_idx = 1;
+    } else {
+      j = dest_x;
+      y_idx = uv_idx = 0;
+    }
+
+    /* 3.2. Copy all macro pixels from the source to the destination
+     *      but blend with the background because we're only filling
+     *      the upper part of the macro pixels. */
+    for (; j < w - 1; j += 2) {
+      y1 = src[4 * y_idx + 1];
+      y2 = src[4 * y_idx + 4 + 1];
+
+      u1 = src[4 * y_idx + 2];
+      u2 = src[4 * y_idx + 4 + 2];
+
+      v1 = src[4 * y_idx + 3];
+      v2 = src[4 * y_idx + 4 + 3];
 
-struct _GstVideoBoxClass
+      destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destY[y_idx + 1] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
+
+      destU[uv_idx] = CLAMP (
+          (2 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
+      destV[uv_idx] = CLAMP (
+          (2 * destV[uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
+
+      y_idx += 2;
+      uv_idx++;
+    }
+
+    /* 3.3. Now copy the last pixel if one exists and blend it
+     *      with the background because we only fill part of
+     *      the macro pixel. In case this is the last pixel of
+     *      the destination we will a larger part. */
+    if (j == w - 1 && j == dest_width - 1) {
+      y1 = src[4 * y_idx + 1];
+      u1 = src[4 * y_idx + 2];
+      v1 = src[4 * y_idx + 3];
+
+      destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[uv_idx] = CLAMP (
+          (destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
+      destV[uv_idx] = CLAMP (
+          (destV[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
+    } else if (j == w - 1) {
+      y1 = src[4 * y_idx + 1];
+      u1 = src[4 * y_idx + 2];
+      v1 = src[4 * y_idx + 3];
+
+      destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[uv_idx] = CLAMP (
+          (3 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
+          255);
+      destV[uv_idx] =
+          CLAMP ((3 * destV[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4,
+          0, 255);
+    }
+  } else if (i == h - 1) {
+    /* 3.1. Handle the first destination pixel if it doesn't
+     *      start at the macro pixel boundary, i.e. blend with
+     *      the background! */
+    if (dest_x % 2 == 1) {
+      y1 = src[4 * 0 + 1];
+      u1 = src[4 * 0 + 2];
+      v1 = src[4 * 0 + 3];
+
+      destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[0] =
+          CLAMP ((3 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
+          255);
+      destV[0] =
+          CLAMP ((3 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4, 0,
+          255);
+
+      j = dest_x + 1;
+      y_idx = uv_idx = 1;
+    } else {
+      j = dest_x;
+      y_idx = uv_idx = 0;
+    }
+
+    /* 3.2. Copy all macro pixels from the source to the destination
+     *      but blend with the background because we're only filling
+     *      the upper part of the macro pixels. */
+    for (; j < w - 1; j += 2) {
+      y1 = src[4 * y_idx + 1];
+      y2 = src[4 * y_idx + 4 + 1];
+
+      u1 = src[4 * y_idx + 2];
+      u2 = src[4 * y_idx + 4 + 2];
+
+      v1 = src[4 * y_idx + 3];
+      v2 = src[4 * y_idx + 4 + 3];
+
+      destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destY[y_idx + 1] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
+
+      destU[uv_idx] = CLAMP (
+          (2 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
+      destV[uv_idx] = CLAMP (
+          (2 * destV[uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
+
+      y_idx += 2;
+      uv_idx++;
+    }
+
+    /* 3.3. Now copy the last pixel if one exists and blend it
+     *      with the background because we only fill part of
+     *      the macro pixel. In case this is the last pixel of
+     *      the destination we will a larger part. */
+    if (j == w - 1 && j == dest_width - 1) {
+      y1 = src[4 * y_idx + 1];
+      u1 = src[4 * y_idx + 2];
+      v1 = src[4 * y_idx + 3];
+
+      destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[uv_idx] = CLAMP (
+          (destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
+      destV[uv_idx] = CLAMP (
+          (destV[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
+    } else if (j == w - 1) {
+      y1 = src[4 * y_idx + 1];
+      u1 = src[4 * y_idx + 2];
+      v1 = src[4 * y_idx + 3];
+
+      destY[y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[uv_idx] = CLAMP (
+          (3 * destU[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
+          255);
+      destV[uv_idx] =
+          CLAMP ((3 * destV[uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4,
+          0, 255);
+    }
+  }
+}
+
+static void
+fill_planar_yuv (GstVideoBoxFill fill_type, guint b_alpha,
+    GstVideoFrame * frame, gboolean sdtv)
 {
-  GstBaseTransformClass parent_class;
-};
+  guint8 empty_pixel[3];
+  guint8 *destY, *destU, *destV;
+  gint strideY, strideU, strideV;
+  gint heightY, heightU, heightV;
+  gint widthY, widthU, widthV;
+
+  if (sdtv) {
+    empty_pixel[0] = yuv_sdtv_colors_Y[fill_type];
+    empty_pixel[1] = yuv_sdtv_colors_U[fill_type];
+    empty_pixel[2] = yuv_sdtv_colors_V[fill_type];
+  } else {
+    empty_pixel[0] = yuv_hdtv_colors_Y[fill_type];
+    empty_pixel[1] = yuv_hdtv_colors_U[fill_type];
+    empty_pixel[2] = yuv_hdtv_colors_V[fill_type];
+  }
+
+  strideY = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0);
+  strideU = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1);
+  strideV = GST_VIDEO_FRAME_COMP_STRIDE (frame, 2);
+
+  destY = GST_VIDEO_FRAME_COMP_DATA (frame, 0);
+  destU = GST_VIDEO_FRAME_COMP_DATA (frame, 1);
+  destV = GST_VIDEO_FRAME_COMP_DATA (frame, 2);
+
+  widthY = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0);
+  widthU = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1);
+  widthV = GST_VIDEO_FRAME_COMP_WIDTH (frame, 2);
+
+  heightY = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0);
+  heightU = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 1);
+  heightV = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 2);
+
+  if (strideY == widthY) {
+    memset (destY, empty_pixel[0], strideY * heightY);
+  } else if (heightY) {
+    for (; heightY; --heightY) {
+      memset (destY, empty_pixel[0], widthY);
+      destY += strideY;
+    }
+  }
+  if (strideU == widthU) {
+    memset (destU, empty_pixel[1], strideU * heightU);
+  } else if (heightU) {
+    for (; heightU; --heightU) {
+      memset (destU, empty_pixel[1], widthU);
+      destU += strideU;
+    }
+  }
+  if (strideV == widthV) {
+    memset (destV, empty_pixel[2], strideV * heightV);
+  } else if (heightV) {
+    for (; heightV; --heightV) {
+      memset (destV, empty_pixel[2], widthV);
+      destV += strideV;
+    }
+  }
+}
+
+static void
+copy_y444_y444 (guint i_alpha, GstVideoFrame * dest,
+    gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src,
+    gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
+{
+  gint i, j;
+  guint8 *destY, *destU, *destV;
+  const guint8 *srcY, *srcU, *srcV;
+  gint dest_strideY, dest_strideU, dest_strideV;
+  gint src_strideY, src_strideU, src_strideV;
+
+  dest_strideY = GST_VIDEO_FRAME_COMP_STRIDE (dest, 0);
+  dest_strideU = GST_VIDEO_FRAME_COMP_STRIDE (dest, 1);
+  dest_strideV = GST_VIDEO_FRAME_COMP_STRIDE (dest, 2);
+
+  src_strideY = GST_VIDEO_FRAME_COMP_STRIDE (src, 0);
+  src_strideU = GST_VIDEO_FRAME_COMP_STRIDE (src, 1);
+  src_strideV = GST_VIDEO_FRAME_COMP_STRIDE (src, 2);
+
+  destY = GST_VIDEO_FRAME_COMP_DATA (dest, 0);
+  destU = GST_VIDEO_FRAME_COMP_DATA (dest, 1);
+  destV = GST_VIDEO_FRAME_COMP_DATA (dest, 2);
+
+  srcY = GST_VIDEO_FRAME_COMP_DATA (src, 0);
+  srcU = GST_VIDEO_FRAME_COMP_DATA (src, 1);
+  srcV = GST_VIDEO_FRAME_COMP_DATA (src, 2);
+
+  destY = destY + dest_y * dest_strideY + dest_x;
+  destU = destU + dest_y * dest_strideU + dest_x;
+  destV = destV + dest_y * dest_strideV + dest_x;
+
+  srcY = srcY + src_y * src_strideY + src_x;
+  srcU = srcU + src_y * src_strideU + src_x;
+  srcV = srcV + src_y * src_strideV + src_x;
+
+  if (src_sdtv != dest_sdtv) {
+    gint matrix[12];
+    gint y, u, v;
+
+    memcpy (matrix,
+        dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
+        cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
+
+    for (i = 0; i < h; i++) {
+      for (j = 0; j < w; j++) {
+        y = APPLY_MATRIX (matrix, 0, srcY[j], srcU[j], srcV[j]);
+        u = APPLY_MATRIX (matrix, 1, srcY[j], srcU[j], srcV[j]);
+        v = APPLY_MATRIX (matrix, 2, srcY[j], srcU[j], srcV[j]);
+
+        destY[j] = y;
+        destU[j] = u;
+        destV[j] = v;
+      }
+      destY += dest_strideY;
+      destU += dest_strideU;
+      destV += dest_strideV;
+
+      srcY += src_strideY;
+      srcU += src_strideU;
+      srcV += src_strideV;
+    }
+  } else {
+    for (i = 0; i < h; i++) {
+      memcpy (destY, srcY, w);
+      memcpy (destU, srcU, w);
+      memcpy (destV, srcV, w);
+
+      destY += dest_strideY;
+      destU += dest_strideU;
+      destV += dest_strideV;
+
+      srcY += src_strideY;
+      srcU += src_strideU;
+      srcV += src_strideV;
+    }
+  }
+}
+
+static void
+copy_y42b_y42b (guint i_alpha, GstVideoFrame * dest,
+    gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src,
+    gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
+{
+  gint i, j;
+  guint8 *destY, *destU, *destV;
+  const guint8 *srcY, *srcU, *srcV;
+  gint dest_strideY, dest_strideU, dest_strideV;
+  gint src_strideY, src_strideU, src_strideV;
+  gint src_y_idx, src_uv_idx;
+  gint dest_y_idx, dest_uv_idx;
+  gint matrix[12];
+  gint y1, y2;
+  gint u1, u2;
+  gint v1, v2;
+  gint dest_width;
+
+  dest_width = GST_VIDEO_FRAME_WIDTH (dest);
+
+  dest_strideY = GST_VIDEO_FRAME_COMP_STRIDE (dest, 0);
+  dest_strideU = GST_VIDEO_FRAME_COMP_STRIDE (dest, 1);
+  dest_strideV = GST_VIDEO_FRAME_COMP_STRIDE (dest, 2);
+
+  src_strideY = GST_VIDEO_FRAME_COMP_STRIDE (src, 0);
+  src_strideU = GST_VIDEO_FRAME_COMP_STRIDE (src, 1);
+  src_strideV = GST_VIDEO_FRAME_COMP_STRIDE (src, 2);
+
+  destY = GST_VIDEO_FRAME_COMP_DATA (dest, 0);
+  destU = GST_VIDEO_FRAME_COMP_DATA (dest, 1);
+  destV = GST_VIDEO_FRAME_COMP_DATA (dest, 2);
+
+  srcY = GST_VIDEO_FRAME_COMP_DATA (src, 0);
+  srcU = GST_VIDEO_FRAME_COMP_DATA (src, 1);
+  srcV = GST_VIDEO_FRAME_COMP_DATA (src, 2);
+
+  destY = destY + dest_y * dest_strideY + dest_x;
+  destU = destU + dest_y * dest_strideU + dest_x / 2;
+  destV = destV + dest_y * dest_strideV + dest_x / 2;
+
+  srcY = srcY + src_y * src_strideY + src_x;
+  srcU = srcU + src_y * src_strideU + src_x / 2;
+  srcV = srcV + src_y * src_strideV + src_x / 2;
+
+  h = dest_y + h;
+  w = dest_x + w;
+
+  if (src_sdtv != dest_sdtv)
+    memcpy (matrix,
+        dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
+        cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
+  else
+    memcpy (matrix, cog_identity_matrix_8bit, 12 * sizeof (gint));
+
+  /* 1. Copy all macro pixel scanlines, the destination scanline
+   *    now starts at macro pixel boundary. */
+  for (i = dest_y; i < h; i++) {
+    /* 1.1. Handle the first destination pixel if it doesn't
+     *      start at the macro pixel boundary, i.e. blend with
+     *      the background! */
+    if (dest_x % 2 == 1) {
+      y1 = srcY[0];
+      u1 = srcU[0];
+      v1 = srcV[0];
+
+      destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[0] = CLAMP (
+          (destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
+      destV[0] = CLAMP (
+          (destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 2, 0, 255);
+      j = dest_x + 1;
+      src_y_idx = dest_y_idx = dest_uv_idx = 1;
+      src_uv_idx = (src_x % 2) + 1;
+    } else {
+      j = dest_x;
+      src_y_idx = dest_y_idx = dest_uv_idx = 0;
+      src_uv_idx = (src_x % 2);
+    }
+
+    /* 1.2. Copy all macro pixels from the source to the destination.
+     *      All pixels now start at macro pixel boundary, i.e. no
+     *      blending with the background is necessary. */
+    for (; j < w - 1; j += 2) {
+      y1 = srcY[src_y_idx];
+      y2 = srcY[src_y_idx + 1];
+
+      u1 = srcU[src_uv_idx / 2];
+      v1 = srcV[src_uv_idx / 2];
+      src_uv_idx++;
+      u2 = srcU[src_uv_idx / 2];
+      v2 = srcV[src_uv_idx / 2];
+      src_uv_idx++;
+
+      destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destY[dest_y_idx + 1] =
+          CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
+
+      destU[dest_uv_idx] = CLAMP (
+          (APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 1, y2,
+                  u2, v2)) / 2, 0, 255);
+      destV[dest_uv_idx] = CLAMP (
+          (APPLY_MATRIX (matrix, 2, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
+                  u2, v2)) / 2, 0, 255);
+
+      dest_y_idx += 2;
+      src_y_idx += 2;
+      dest_uv_idx++;
+    }
+
+    /* 1.3. Now copy the last pixel if one exists and blend it
+     *      with the background because we only fill part of
+     *      the macro pixel. In case this is the last pixel of
+     *      the destination we will a larger part. */
+    if (j == w - 1 && j == dest_width - 1) {
+      y1 = srcY[src_y_idx];
+      u1 = srcU[src_uv_idx / 2];
+      v1 = srcV[src_uv_idx / 2];
+
+      destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
+      destV[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
+    } else if (j == w - 1) {
+      y1 = srcY[src_y_idx];
+      u1 = srcU[src_uv_idx / 2];
+      v1 = srcV[src_uv_idx / 2];
+
+      destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[dest_uv_idx] = CLAMP (
+          (destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1)) / 2, 0, 255);
+      destV[dest_uv_idx] = CLAMP (
+          (destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1)) / 2, 0, 255);
+    }
+
+    destY += dest_strideY;
+    destU += dest_strideU;
+    destV += dest_strideV;
+    srcY += src_strideY;
+
+    srcU += src_strideU;
+    srcV += src_strideV;
+  }
+}
+
+static void
+copy_y41b_y41b (guint i_alpha, GstVideoFrame * dest,
+    gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src,
+    gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
+{
+  gint i, j;
+  guint8 *destY, *destU, *destV;
+  const guint8 *srcY, *srcU, *srcV;
+  gint dest_strideY, dest_strideU, dest_strideV;
+  gint src_strideY, src_strideU, src_strideV;
+  gint src_y_idx, src_uv_idx;
+  gint dest_y_idx, dest_uv_idx;
+  gint matrix[12];
+  gint y1, y2, y3, y4;
+  gint u1, u2, u3, u4;
+  gint v1, v2, v3, v4;
+  gint dest_width;
+
+  dest_width = GST_VIDEO_FRAME_WIDTH (dest);
+
+  dest_strideY = GST_VIDEO_FRAME_COMP_STRIDE (dest, 0);
+  dest_strideU = GST_VIDEO_FRAME_COMP_STRIDE (dest, 1);
+  dest_strideV = GST_VIDEO_FRAME_COMP_STRIDE (dest, 2);
+
+  src_strideY = GST_VIDEO_FRAME_COMP_STRIDE (src, 0);
+  src_strideU = GST_VIDEO_FRAME_COMP_STRIDE (src, 1);
+  src_strideV = GST_VIDEO_FRAME_COMP_STRIDE (src, 2);
+
+  destY = GST_VIDEO_FRAME_COMP_DATA (dest, 0);
+  destU = GST_VIDEO_FRAME_COMP_DATA (dest, 1);
+  destV = GST_VIDEO_FRAME_COMP_DATA (dest, 2);
+
+  srcY = GST_VIDEO_FRAME_COMP_DATA (src, 0);
+  srcU = GST_VIDEO_FRAME_COMP_DATA (src, 1);
+  srcV = GST_VIDEO_FRAME_COMP_DATA (src, 2);
+
+  destY = destY + dest_y * dest_strideY + dest_x;
+  destU = destU + dest_y * dest_strideU + dest_x / 4;
+  destV = destV + dest_y * dest_strideV + dest_x / 4;
+
+  srcY = srcY + src_y * src_strideY + src_x;
+  srcU = srcU + src_y * src_strideU + src_x / 4;
+  srcV = srcV + src_y * src_strideV + src_x / 4;
+
+  h = dest_y + h;
+  w = dest_x + w;
+
+  if (src_sdtv != dest_sdtv)
+    memcpy (matrix,
+        dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
+        cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
+  else
+    memcpy (matrix, cog_identity_matrix_8bit, 12 * sizeof (gint));
+
+  /* 1. Copy all macro pixel scanlines, the destination scanline
+   *    now starts at macro pixel boundary. */
+  for (i = dest_y; i < h; i++) {
+    /* 1.1. Handle the first destination pixel if it doesn't
+     *      start at the macro pixel boundary, i.e. blend with
+     *      the background! */
+    if (dest_x % 4 == 1) {
+      y1 = srcY[0];
+      y2 = srcY[1];
+      y3 = srcY[2];
+      u1 = srcU[0];
+      v1 = srcV[0];
+
+      destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destY[1] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u1, v1), 0, 255);
+      destY[2] = CLAMP (APPLY_MATRIX (matrix, 0, y3, u1, v1), 0, 255);
+
+      destU[0] = CLAMP (
+          (destU[0] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 1, y2, u1,
+                  v1) + APPLY_MATRIX (matrix, 1, y3, u1, v1)) / 4, 0, 255);
+      destV[0] =
+          CLAMP ((destV[0] + APPLY_MATRIX (matrix, 2, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 2, y2, u1,
+                  v1) + APPLY_MATRIX (matrix, 2, y3, u1, v1)) / 4, 0, 255);
+
+      j = dest_x + 3;
+      src_y_idx = dest_y_idx = 3;
+      dest_uv_idx = 1;
+      src_uv_idx = (src_x % 4) + 3;
+    } else if (dest_x % 4 == 2) {
+      y1 = srcY[0];
+      y2 = srcY[1];
+      u1 = srcU[0];
+      v1 = srcV[0];
+
+      destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destY[1] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u1, v1), 0, 255);
+
+      destU[0] = CLAMP (
+          (2 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 1, y2, u1, v1)) / 4, 0, 255);
+      destV[0] =
+          CLAMP ((2 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 2, y2, u1, v1)) / 4, 0, 255);
+
+      j = dest_x + 2;
+      src_y_idx = dest_y_idx = 2;
+      dest_uv_idx = 1;
+      src_uv_idx = (src_x % 4) + 2;
+    } else if (dest_x % 4 == 3) {
+      y1 = srcY[0];
+      u1 = srcU[0];
+      v1 = srcV[0];
+
+      destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+
+      destU[0] = CLAMP (
+          (3 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0, 255);
+      destV[0] = CLAMP (
+          (3 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4, 0, 255);
+
+      j = dest_x + 1;
+      src_y_idx = dest_y_idx = 1;
+      dest_uv_idx = 1;
+      src_uv_idx = (src_x % 4) + 1;
+    } else {
+      j = dest_x;
+      src_y_idx = dest_y_idx = dest_uv_idx = 0;
+      src_uv_idx = (src_x % 4);
+    }
+
+    /* 1.2. Copy all macro pixels from the source to the destination.
+     *      All pixels now start at macro pixel boundary, i.e. no
+     *      blending with the background is necessary. */
+    for (; j < w - 3; j += 4) {
+      y1 = srcY[src_y_idx];
+      y2 = srcY[src_y_idx + 1];
+      y3 = srcY[src_y_idx + 2];
+      y4 = srcY[src_y_idx + 3];
+
+      u1 = srcU[src_uv_idx / 4];
+      v1 = srcV[src_uv_idx / 4];
+      src_uv_idx++;
+      u2 = srcU[src_uv_idx / 4];
+      v2 = srcV[src_uv_idx / 4];
+      src_uv_idx++;
+      u3 = srcU[src_uv_idx / 4];
+      v3 = srcV[src_uv_idx / 4];
+      src_uv_idx++;
+      u4 = srcU[src_uv_idx / 4];
+      v4 = srcV[src_uv_idx / 4];
+      src_uv_idx++;
+
+      destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destY[dest_y_idx + 1] =
+          CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
+      destY[dest_y_idx + 2] =
+          CLAMP (APPLY_MATRIX (matrix, 0, y3, u3, v3), 0, 255);
+      destY[dest_y_idx + 3] =
+          CLAMP (APPLY_MATRIX (matrix, 0, y4, u4, v4), 0, 255);
+
+      destU[dest_uv_idx] = CLAMP (
+          (APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 1, y2,
+                  u2, v2) + APPLY_MATRIX (matrix, 1, y3, u3,
+                  v3) + APPLY_MATRIX (matrix, 1, y4, u4, v4)) / 4, 0, 255);
+      destV[dest_uv_idx] =
+          CLAMP ((APPLY_MATRIX (matrix, 2, y1, u1, v1) + APPLY_MATRIX (matrix,
+                  2, y2, u2, v2) + APPLY_MATRIX (matrix, 2, y3, u3,
+                  v3) + APPLY_MATRIX (matrix, 2, y4, u4, v4)) / 4, 0, 255);
+
+      dest_y_idx += 4;
+      src_y_idx += 4;
+      dest_uv_idx++;
+    }
+
+    /* 1.3. Now copy the last pixel if one exists and blend it
+     *      with the background because we only fill part of
+     *      the macro pixel. In case this is the last pixel of
+     *      the destination we will a larger part. */
+    if (j == w - 1 && j == dest_width - 1) {
+      y1 = srcY[src_y_idx];
+      u1 = srcU[src_uv_idx / 4];
+      v1 = srcV[src_uv_idx / 4];
+
+      destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
+      destV[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
+    } else if (j == w - 1) {
+      y1 = srcY[src_y_idx];
+      u1 = srcU[src_uv_idx / 4];
+      v1 = srcV[src_uv_idx / 4];
+
+      destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[dest_uv_idx] = CLAMP (
+          (destU[dest_uv_idx] + 3 * APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1)) / 4, 0, 255);
+      destV[dest_uv_idx] = CLAMP (
+          (destV[dest_uv_idx] + 3 * APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1)) / 4, 0, 255);
+    } else if (j == w - 2 && j == dest_width - 2) {
+      y1 = srcY[src_y_idx];
+      y2 = srcY[src_y_idx + 1];
+      u1 = srcU[src_uv_idx / 4];
+      v1 = srcV[src_uv_idx / 4];
+
+      destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destY[dest_y_idx + 1] =
+          CLAMP (APPLY_MATRIX (matrix, 0, y2, u1, v1), 0, 255);
+      destU[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
+      destV[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
+    } else if (j == w - 2) {
+      y1 = srcY[src_y_idx];
+      y2 = srcY[src_y_idx + 1];
+      u1 = srcU[src_uv_idx / 4];
+      v1 = srcV[src_uv_idx / 4];
+
+      destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destY[dest_y_idx + 1] =
+          CLAMP (APPLY_MATRIX (matrix, 0, y2, u1, v1), 0, 255);
+      destU[dest_uv_idx] =
+          CLAMP ((destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1)) / 2, 0, 255);
+      destV[dest_uv_idx] =
+          CLAMP ((destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1)) / 2, 0, 255);
+    } else if (j == w - 3 && j == dest_width - 3) {
+      y1 = srcY[src_y_idx];
+      y2 = srcY[src_y_idx + 1];
+      y3 = srcY[src_y_idx + 2];
+      u1 = srcU[src_uv_idx / 4];
+      v1 = srcV[src_uv_idx / 4];
+
+      destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destY[dest_y_idx + 1] =
+          CLAMP (APPLY_MATRIX (matrix, 0, y2, u1, v1), 0, 255);
+      destY[dest_y_idx + 2] =
+          CLAMP (APPLY_MATRIX (matrix, 0, y3, u1, v1), 0, 255);
+      destU[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
+      destV[dest_uv_idx] = CLAMP (APPLY_MATRIX (matrix, 1, y1, u1, v1), 0, 255);
+    } else if (j == w - 3) {
+      y1 = srcY[src_y_idx];
+      y2 = srcY[src_y_idx + 1];
+      y3 = srcY[src_y_idx + 2];
+      u1 = srcU[src_uv_idx / 4];
+      v1 = srcV[src_uv_idx / 4];
+
+      destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destY[dest_y_idx + 1] =
+          CLAMP (APPLY_MATRIX (matrix, 0, y2, u1, v1), 0, 255);
+      destY[dest_y_idx + 2] =
+          CLAMP (APPLY_MATRIX (matrix, 0, y3, u1, v1), 0, 255);
+      destU[dest_uv_idx] =
+          CLAMP ((3 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1)) / 4, 0, 255);
+      destV[dest_uv_idx] =
+          CLAMP ((3 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1)) / 4, 0, 255);
+    }
+
+    destY += dest_strideY;
+    destU += dest_strideU;
+    destV += dest_strideV;
+    srcY += src_strideY;
+    srcU += src_strideU;
+    srcV += src_strideV;
+  }
+}
+
+static void
+copy_i420_i420 (guint i_alpha, GstVideoFrame * dest,
+    gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src,
+    gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
+{
+  gint i, j;
+  guint8 *destY, *destU, *destV;
+  const guint8 *srcY, *srcU, *srcV;
+  guint8 *destY2;
+  const guint8 *srcY2, *srcU2, *srcV2;
+  gint dest_strideY, dest_strideU, dest_strideV;
+  gint src_strideY, src_strideU, src_strideV;
+  gint src_y_idx, src_uv_idx;
+  gint dest_y_idx, dest_uv_idx;
+  gint matrix[12];
+  gint y1, y2, y3, y4;
+  gint u1, u2, u3, u4;
+  gint v1, v2, v3, v4;
+  gint dest_width, dest_height;
+
+  dest_width = GST_VIDEO_FRAME_WIDTH (dest);
+  dest_height = GST_VIDEO_FRAME_HEIGHT (dest);
+
+  dest_strideY = GST_VIDEO_FRAME_COMP_STRIDE (dest, 0);
+  dest_strideU = GST_VIDEO_FRAME_COMP_STRIDE (dest, 1);
+  dest_strideV = GST_VIDEO_FRAME_COMP_STRIDE (dest, 2);
+
+  src_strideY = GST_VIDEO_FRAME_COMP_STRIDE (src, 0);
+  src_strideU = GST_VIDEO_FRAME_COMP_STRIDE (src, 1);
+  src_strideV = GST_VIDEO_FRAME_COMP_STRIDE (src, 2);
+
+  destY = GST_VIDEO_FRAME_COMP_DATA (dest, 0);
+  destU = GST_VIDEO_FRAME_COMP_DATA (dest, 1);
+  destV = GST_VIDEO_FRAME_COMP_DATA (dest, 2);
+
+  srcY = GST_VIDEO_FRAME_COMP_DATA (src, 0);
+  srcU = GST_VIDEO_FRAME_COMP_DATA (src, 1);
+  srcV = GST_VIDEO_FRAME_COMP_DATA (src, 2);
+
+  destY = destY + dest_y * dest_strideY + dest_x;
+  destU = destU + (dest_y / 2) * dest_strideU + dest_x / 2;
+  destV = destV + (dest_y / 2) * dest_strideV + dest_x / 2;
+
+  srcY = srcY + src_y * src_strideY + src_x;
+  srcU = srcU + (src_y / 2) * src_strideU + src_x / 2;
+  srcV = srcV + (src_y / 2) * src_strideV + src_x / 2;
+
+  destY2 = destY + dest_strideY;
+  srcY2 = srcY + src_strideY;
+
+  h = dest_y + h;
+  w = dest_x + w;
+
+  if (src_sdtv != dest_sdtv)
+    memcpy (matrix,
+        dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
+        cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
+  else
+    memcpy (matrix, cog_identity_matrix_8bit, 12 * sizeof (gint));
+
+  /* 1. Handle the first destination scanline specially if it
+   *    doesn't start at the macro pixel boundary, i.e. blend
+   *    with the background! */
+  if (dest_y % 2 == 1) {
+    /* 1.1. Handle the first destination pixel if it doesn't
+     *      start at the macro pixel boundary, i.e. blend with
+     *      the background! */
+    if (dest_x % 2 == 1) {
+      y1 = srcY[0];
+      u1 = srcU[0];
+      v1 = srcV[0];
+
+      destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[0] =
+          CLAMP ((3 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
+          255);
+      destV[0] =
+          CLAMP ((3 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4, 0,
+          255);
+
+      j = dest_x + 1;
+      src_y_idx = dest_y_idx = dest_uv_idx = 1;
+      src_uv_idx = (src_x % 2) + 1;
+    } else {
+      j = dest_x;
+      src_y_idx = dest_y_idx = dest_uv_idx = 0;
+      src_uv_idx = (src_x % 2);
+    }
+
+    /* 1.2. Copy all macro pixels from the source to the destination
+     *      but blend with the background because we're only filling
+     *      the lower part of the macro pixels. */
+    for (; j < w - 1; j += 2) {
+      y1 = srcY[src_y_idx];
+      y2 = srcY[src_y_idx + 1];
+
+      u1 = srcU[src_uv_idx / 2];
+      v1 = srcV[src_uv_idx / 2];
+      src_uv_idx++;
+      u2 = srcU[src_uv_idx / 2];
+      v2 = srcV[src_uv_idx / 2];
+      src_uv_idx++;
+
+      destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destY[dest_y_idx + 1] =
+          CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
+      destU[dest_uv_idx] =
+          CLAMP ((2 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
+      destV[dest_uv_idx] =
+          CLAMP ((2 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
+
+      dest_y_idx += 2;
+      src_y_idx += 2;
+      dest_uv_idx++;
+    }
+
+    /* 1.3. Now copy the last pixel if one exists and blend it
+     *      with the background because we only fill part of
+     *      the macro pixel. In case this is the last pixel of
+     *      the destination we will a larger part. */
+    if (j == w - 1 && j == dest_width - 1) {
+      y1 = srcY[src_y_idx];
+      u1 = srcU[src_uv_idx / 2];
+      v1 = srcV[src_uv_idx / 2];
+
+      destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[dest_uv_idx] = CLAMP (
+          (destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0,
+          255);
+      destV[dest_uv_idx] =
+          CLAMP ((destV[dest_uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
+                  v1)) / 2, 0, 255);
+    } else if (j == w - 1) {
+      y1 = srcY[src_y_idx];
+      u1 = srcU[src_uv_idx / 2];
+      v1 = srcV[src_uv_idx / 2];
+
+      destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[dest_uv_idx] = CLAMP (
+          (3 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4,
+          0, 255);
+      destV[dest_uv_idx] =
+          CLAMP ((3 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
+                  v1)) / 4, 0, 255);
+    }
+
+    destY += dest_strideY;
+    destY2 += dest_strideY;
+    destU += dest_strideU;
+    destV += dest_strideV;
+    srcY += src_strideY;
+    srcY2 += src_strideY;
+    src_y++;
+    if (src_y % 2 == 0) {
+      srcU += src_strideU;
+      srcV += src_strideV;
+    }
+    i = dest_y + 1;
+  } else {
+    i = dest_y;
+  }
+
+  /* 2. Copy all macro pixel scanlines, the destination scanline
+   *    now starts at macro pixel boundary. */
+  for (; i < h - 1; i += 2) {
+    /* 2.1. Handle the first destination pixel if it doesn't
+     *      start at the macro pixel boundary, i.e. blend with
+     *      the background! */
+
+    srcU2 = srcU;
+    srcV2 = srcV;
+    if (src_y % 2 == 1) {
+      srcU2 += src_strideU;
+      srcV2 += src_strideV;
+    }
+
+    if (dest_x % 2 == 1) {
+      y1 = srcY[0];
+      y2 = srcY2[0];
+      u1 = srcU[0];
+      v1 = srcV[0];
+      u2 = srcU2[0];
+      v2 = srcV2[0];
+
+      destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destY2[0] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
+      destU[0] = CLAMP (
+          (2 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
+      destV[0] = CLAMP (
+          (2 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
+      j = dest_x + 1;
+      src_y_idx = dest_y_idx = dest_uv_idx = 1;
+      src_uv_idx = (src_x % 2) + 1;
+    } else {
+      j = dest_x;
+      src_y_idx = dest_y_idx = dest_uv_idx = 0;
+      src_uv_idx = (src_x % 2);
+    }
+
+    /* 2.2. Copy all macro pixels from the source to the destination.
+     *      All pixels now start at macro pixel boundary, i.e. no
+     *      blending with the background is necessary. */
+    for (; j < w - 1; j += 2) {
+      y1 = srcY[src_y_idx];
+      y2 = srcY[src_y_idx + 1];
+      y3 = srcY2[src_y_idx];
+      y4 = srcY2[src_y_idx + 1];
+
+      u1 = srcU[src_uv_idx / 2];
+      u3 = srcU2[src_uv_idx / 2];
+      v1 = srcV[src_uv_idx / 2];
+      v3 = srcV2[src_uv_idx / 2];
+      src_uv_idx++;
+      u2 = srcU[src_uv_idx / 2];
+      u4 = srcU2[src_uv_idx / 2];
+      v2 = srcV[src_uv_idx / 2];
+      v4 = srcV2[src_uv_idx / 2];
+      src_uv_idx++;
+
+      destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destY[dest_y_idx + 1] =
+          CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
+      destY2[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y3, u3, v3), 0, 255);
+      destY2[dest_y_idx + 1] =
+          CLAMP (APPLY_MATRIX (matrix, 0, y4, u4, v4), 0, 255);
+
+      destU[dest_uv_idx] = CLAMP (
+          (APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 1, y2,
+                  u2, v2) + APPLY_MATRIX (matrix, 1, y3, u3,
+                  v3) + APPLY_MATRIX (matrix, 1, y4, u4, v4)) / 4, 0, 255);
+      destV[dest_uv_idx] = CLAMP (
+          (APPLY_MATRIX (matrix, 2, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
+                  u2, v2) + APPLY_MATRIX (matrix, 2, y3, u3,
+                  v3) + APPLY_MATRIX (matrix, 2, y4, u4, v4)) / 4, 0, 255);
+
+      dest_y_idx += 2;
+      src_y_idx += 2;
+      dest_uv_idx++;
+    }
+
+    /* 2.3. Now copy the last pixel if one exists and blend it
+     *      with the background because we only fill part of
+     *      the macro pixel. In case this is the last pixel of
+     *      the destination we will a larger part. */
+    if (j == w - 1 && j == dest_width - 1) {
+      y1 = srcY[src_y_idx];
+      y2 = srcY2[src_y_idx];
+
+      u1 = srcU[src_uv_idx / 2];
+      u2 = srcU2[src_uv_idx / 2];
+
+      v1 = srcV[src_uv_idx / 2];
+      v2 = srcV2[src_uv_idx / 2];
+
+      destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destY2[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
+      destU[dest_uv_idx] = CLAMP (
+          (APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
+                  u2, v2)) / 2, 0, 255);
+      destV[dest_uv_idx] = CLAMP (
+          (APPLY_MATRIX (matrix, 1, y1, u1, v1) + APPLY_MATRIX (matrix, 2, y2,
+                  u2, v2)) / 2, 0, 255);
+    } else if (j == w - 1) {
+      y1 = srcY[src_y_idx];
+      y2 = srcY2[src_y_idx];
+
+      u1 = srcU[src_uv_idx / 2];
+      u2 = srcU2[src_uv_idx / 2];
+
+      v1 = srcV[src_uv_idx / 2];
+      v2 = srcV2[src_uv_idx / 2];
+
+      destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destY2[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
+      destU[dest_uv_idx] = CLAMP (
+          (2 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
+      destV[dest_uv_idx] = CLAMP (
+          (2 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
+    }
+
+    destY += 2 * dest_strideY;
+    destY2 += 2 * dest_strideY;
+    destU += dest_strideU;
+    destV += dest_strideV;
+    srcY += 2 * src_strideY;
+    srcY2 += 2 * src_strideY;
+
+    src_y += 2;
+    srcU += src_strideU;
+    srcV += src_strideV;
+  }
+
+  /* 3. Handle the last scanline if one exists. This again
+   *    doesn't start at macro pixel boundary but should
+   *    only fill the upper part of the macro pixels. */
+  if (i == h - 1 && i == dest_height - 1) {
+    /* 3.1. Handle the first destination pixel if it doesn't
+     *      start at the macro pixel boundary, i.e. blend with
+     *      the background! */
+    if (dest_x % 2 == 1) {
+      y1 = srcY[0];
+      u1 = srcU[0];
+      v1 = srcV[0];
+
+      destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[0] =
+          CLAMP ((destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0, 255);
+      destV[0] =
+          CLAMP ((destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 2, 0, 255);
+
+      j = dest_x + 1;
+      src_y_idx = dest_y_idx = dest_uv_idx = 1;
+      src_uv_idx = (src_x % 2) + 1;
+    } else {
+      j = dest_x;
+      src_y_idx = dest_y_idx = dest_uv_idx = 0;
+      src_uv_idx = (src_x % 2);
+    }
+
+    /* 3.2. Copy all macro pixels from the source to the destination
+     *      but blend with the background because we're only filling
+     *      the upper part of the macro pixels. */
+    for (; j < w - 1; j += 2) {
+      y1 = srcY[src_y_idx];
+      y2 = srcY[src_y_idx + 1];
+
+      u1 = srcU[src_uv_idx / 2];
+      v1 = srcV[src_uv_idx / 2];
+      src_uv_idx++;
+      u2 = srcU[src_uv_idx / 2];
+      v2 = srcV[src_uv_idx / 2];
+      src_uv_idx++;
+
+      destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destY[dest_y_idx + 1] =
+          CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
+
+      destU[dest_uv_idx] = CLAMP (
+          (2 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
+      destV[dest_uv_idx] = CLAMP (
+          (2 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
+
+      dest_y_idx += 2;
+      src_y_idx += 2;
+      dest_uv_idx++;
+    }
+
+    /* 3.3. Now copy the last pixel if one exists and blend it
+     *      with the background because we only fill part of
+     *      the macro pixel. In case this is the last pixel of
+     *      the destination we will a larger part. */
+    if (j == w - 1 && j == dest_width - 1) {
+      y1 = srcY[src_y_idx];
+      u1 = srcU[src_uv_idx / 2];
+      v1 = srcV[src_uv_idx / 2];
+
+      destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[dest_uv_idx] = CLAMP (
+          (destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0,
+          255);
+      destV[dest_uv_idx] =
+          CLAMP ((destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1)) / 2, 0, 255);
+    } else if (j == w - 1) {
+      y1 = srcY[src_y_idx];
+      u1 = srcU[src_uv_idx / 2];
+      v1 = srcV[src_uv_idx / 2];
+
+      destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[dest_uv_idx] = CLAMP (
+          (3 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4,
+          0, 255);
+      destV[dest_uv_idx] =
+          CLAMP ((3 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1)) / 4, 0, 255);
+    }
+  } else if (i == h - 1) {
+    /* 3.1. Handle the first destination pixel if it doesn't
+     *      start at the macro pixel boundary, i.e. blend with
+     *      the background! */
+    if (dest_x % 2 == 1) {
+      y1 = srcY[0];
+      u1 = srcU[0];
+      v1 = srcV[0];
+
+      destY[0] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[0] =
+          CLAMP ((3 * destU[0] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4, 0,
+          255);
+      destV[0] =
+          CLAMP ((3 * destV[0] + APPLY_MATRIX (matrix, 2, y1, u1, v1)) / 4, 0,
+          255);
+
+      j = dest_x + 1;
+      src_y_idx = dest_y_idx = dest_uv_idx = 1;
+      src_uv_idx = (src_x % 2) + 1;
+    } else {
+      j = dest_x;
+      src_y_idx = dest_y_idx = dest_uv_idx = 0;
+      src_uv_idx = (src_x % 2);
+    }
+
+    /* 3.2. Copy all macro pixels from the source to the destination
+     *      but blend with the background because we're only filling
+     *      the upper part of the macro pixels. */
+    for (; j < w - 1; j += 2) {
+      y1 = srcY[src_y_idx];
+      y2 = srcY[src_y_idx + 1];
+
+      u1 = srcU[src_uv_idx / 2];
+      v1 = srcV[src_uv_idx / 2];
+      src_uv_idx++;
+      u2 = srcU[src_uv_idx / 2];
+      v2 = srcV[src_uv_idx / 2];
+      src_uv_idx++;
+
+      destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destY[dest_y_idx + 1] =
+          CLAMP (APPLY_MATRIX (matrix, 0, y2, u2, v2), 0, 255);
+
+      destU[dest_uv_idx] = CLAMP (
+          (2 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 1, y2, u2, v2)) / 4, 0, 255);
+      destV[dest_uv_idx] = CLAMP (
+          (2 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 2, y1, u1,
+                  v1) + APPLY_MATRIX (matrix, 2, y2, u2, v2)) / 4, 0, 255);
+
+      dest_y_idx += 2;
+      src_y_idx += 2;
+      dest_uv_idx++;
+    }
+
+    /* 3.3. Now copy the last pixel if one exists and blend it
+     *      with the background because we only fill part of
+     *      the macro pixel. In case this is the last pixel of
+     *      the destination we will a larger part. */
+    if (j == w - 1 && j == dest_width - 1) {
+      y1 = srcY[src_y_idx];
+      u1 = srcU[src_uv_idx / 2];
+      v1 = srcV[src_uv_idx / 2];
+
+      destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[dest_uv_idx] = CLAMP (
+          (destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 2, 0,
+          255);
+      destV[dest_uv_idx] =
+          CLAMP ((destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1)) / 2, 0, 255);
+    } else if (j == w - 1) {
+      y1 = srcY[src_y_idx];
+      u1 = srcU[src_uv_idx / 2];
+      v1 = srcV[src_uv_idx / 2];
+
+      destY[dest_y_idx] = CLAMP (APPLY_MATRIX (matrix, 0, y1, u1, v1), 0, 255);
+      destU[dest_uv_idx] = CLAMP (
+          (3 * destU[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1, v1)) / 4,
+          0, 255);
+      destV[dest_uv_idx] =
+          CLAMP ((3 * destV[dest_uv_idx] + APPLY_MATRIX (matrix, 1, y1, u1,
+                  v1)) / 4, 0, 255);
+    }
+  }
+}
+
+static void
+copy_i420_ayuv (guint i_alpha, GstVideoFrame * dest_frame,
+    gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src_frame,
+    gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
+{
+  const guint8 *srcY, *srcU, *srcV;
+  gint src_strideY, src_strideU, src_strideV;
+  gint dest_stride;
+  guint8 *dest;
+
+  src_strideY = GST_VIDEO_FRAME_COMP_STRIDE (src_frame, 0);
+  src_strideU = GST_VIDEO_FRAME_COMP_STRIDE (src_frame, 1);
+  src_strideV = GST_VIDEO_FRAME_COMP_STRIDE (src_frame, 2);
+
+  srcY = GST_VIDEO_FRAME_COMP_DATA (src_frame, 0);
+  srcU = GST_VIDEO_FRAME_COMP_DATA (src_frame, 1);
+  srcV = GST_VIDEO_FRAME_COMP_DATA (src_frame, 2);
+
+  dest_stride = GST_VIDEO_FRAME_PLANE_STRIDE (dest_frame, 0);
+
+  dest = GST_VIDEO_FRAME_PLANE_DATA (dest_frame, 0);
+  dest = dest + dest_y * dest_stride + dest_x * 4;
+
+  srcY = srcY + src_y * src_strideY + src_x;
+  srcU = srcU + (src_y / 2) * src_strideU + src_x / 2;
+  srcV = srcV + (src_y / 2) * src_strideV + src_x / 2;
+
+  i_alpha = CLAMP (i_alpha, 0, 255);
+
+  if (src_sdtv != dest_sdtv) {
+    gint i, j, uv_idx;
+    gint y, u, v;
+    gint y1, u1, v1;
+    gint matrix[12];
+
+    memcpy (matrix,
+        dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
+        cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
+
+    for (i = 0; i < h; i++) {
+      for (j = 0, uv_idx = src_x % 2; j < w; j++, uv_idx++) {
+        y = srcY[j];
+        u = srcU[uv_idx / 2];
+        v = srcV[uv_idx / 2];
+
+        y1 = APPLY_MATRIX (matrix, 0, y, u, v);
+        u1 = APPLY_MATRIX (matrix, 1, y, u, v);
+        v1 = APPLY_MATRIX (matrix, 2, y, u, v);
+
+        dest[4 * j + 0] = i_alpha;
+        dest[4 * j + 1] = y1;
+        dest[4 * j + 2] = u1;
+        dest[4 * j + 3] = v1;
+      }
+      dest += dest_stride;
+
+      src_y++;
+      srcY += src_strideY;
+      if (src_y % 2 == 0) {
+        srcU += src_strideU;
+        srcV += src_strideV;
+      }
+    }
+  } else {
+    gint i, j, uv_idx;
+    gint y, u, v;
+
+    for (i = 0; i < h; i++) {
+      for (j = 0, uv_idx = src_x % 2; j < w; j++, uv_idx++) {
+        y = srcY[j];
+        u = srcU[uv_idx / 2];
+        v = srcV[uv_idx / 2];
+
+        dest[4 * j + 0] = i_alpha;
+        dest[4 * j + 1] = y;
+        dest[4 * j + 2] = u;
+        dest[4 * j + 3] = v;
+      }
+      dest += dest_stride;
+
+      src_y++;
+      srcY += src_strideY;
+      if (src_y % 2 == 0) {
+        srcU += src_strideU;
+        srcV += src_strideV;
+      }
+    }
+  }
+}
+
+static void
+fill_rgb32 (GstVideoBoxFill fill_type, guint b_alpha,
+    GstVideoFrame * frame, gboolean sdtv)
+{
+  guint32 empty_pixel;
+  gint p[4];
+  guint8 *dest;
+  guint stride;
+  gint width, height;
+
+  width = GST_VIDEO_FRAME_WIDTH (frame);
+  height = GST_VIDEO_FRAME_HEIGHT (frame);
+
+  dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
+  stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
+
+  p[0] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 3);
+  p[1] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 0);
+  p[2] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 1);
+  p[3] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 2);
+
+  b_alpha = CLAMP (b_alpha, 0, 255);
+
+  if (GST_VIDEO_FRAME_N_COMPONENTS (frame) == 4) {
+    empty_pixel = GUINT32_FROM_LE ((b_alpha << (p[0] * 8)) |
+        (rgb_colors_R[fill_type] << (p[1] * 8)) |
+        (rgb_colors_G[fill_type] << (p[2] * 8)) |
+        (rgb_colors_B[fill_type] << (p[3] * 8)));
+  } else {
+    empty_pixel = GUINT32_FROM_LE (
+        (rgb_colors_R[fill_type] << (p[1] * 8)) |
+        (rgb_colors_G[fill_type] << (p[2] * 8)) |
+        (rgb_colors_B[fill_type] << (p[3] * 8)));
+  }
+
+  if (stride == width * 4) {
+    video_box_orc_splat_u32 ((guint32 *) dest, empty_pixel, width * height);
+  } else if (height) {
+    for (; height; --height) {
+      video_box_orc_splat_u32 ((guint32 *) dest, empty_pixel, width);
+      dest += stride;
+    }
+  }
+}
+
+static void
+fill_rgb24 (GstVideoBoxFill fill_type, guint b_alpha,
+    GstVideoFrame * frame, gboolean sdtv)
+{
+  gint dest_stride;
+  gint p[4];
+  gint i, j;
+  guint8 *dest;
+  gint width, height;
+
+  width = GST_VIDEO_FRAME_WIDTH (frame);
+  height = GST_VIDEO_FRAME_HEIGHT (frame);
+
+  dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
+  dest_stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
+
+  p[1] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 0);
+  p[2] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 1);
+  p[3] = GST_VIDEO_FRAME_COMP_OFFSET (frame, 2);
+
+  for (i = 0; i < height; i++) {
+    for (j = 0; j < width; j++) {
+      dest[3 * j + p[1]] = rgb_colors_R[fill_type];
+      dest[3 * j + p[2]] = rgb_colors_G[fill_type];
+      dest[3 * j + p[3]] = rgb_colors_B[fill_type];
+    }
+    dest += dest_stride;
+  }
+}
+
+static void
+copy_rgb32 (guint i_alpha, GstVideoFrame * dest_frame,
+    gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src_frame,
+    gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
+{
+  gint i, j;
+  gint src_stride, dest_stride;
+  gboolean in_alpha, out_alpha;
+  gint in_bpp, out_bpp;
+  gint p_out[4];
+  gint p_in[4];
+  gboolean packed_out;
+  gboolean packed_in;
+  guint8 *src, *dest;
+
+  src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (src_frame, 0);
+  dest_stride = GST_VIDEO_FRAME_PLANE_STRIDE (dest_frame, 0);
+  in_bpp = GST_VIDEO_FRAME_COMP_PSTRIDE (src_frame, 0);
+  out_bpp = GST_VIDEO_FRAME_COMP_PSTRIDE (dest_frame, 0);
+  packed_in = (in_bpp < 4);
+  packed_out = (out_bpp < 4);
+
+  out_alpha = GST_VIDEO_INFO_HAS_ALPHA (&dest_frame->info);
+  p_out[0] = GST_VIDEO_FRAME_COMP_OFFSET (dest_frame, 3);
+  p_out[1] = GST_VIDEO_FRAME_COMP_OFFSET (dest_frame, 0);
+  p_out[2] = GST_VIDEO_FRAME_COMP_OFFSET (dest_frame, 1);
+  p_out[3] = GST_VIDEO_FRAME_COMP_OFFSET (dest_frame, 2);
+
+  in_alpha = GST_VIDEO_INFO_HAS_ALPHA (&src_frame->info);
+  p_in[0] = GST_VIDEO_FRAME_COMP_OFFSET (src_frame, 3);
+  p_in[1] = GST_VIDEO_FRAME_COMP_OFFSET (src_frame, 0);
+  p_in[2] = GST_VIDEO_FRAME_COMP_OFFSET (src_frame, 1);
+  p_in[3] = GST_VIDEO_FRAME_COMP_OFFSET (src_frame, 2);
+
+  dest = GST_VIDEO_FRAME_PLANE_DATA (dest_frame, 0);
+  dest = dest + dest_y * dest_stride + dest_x * out_bpp;
+  src = GST_VIDEO_FRAME_PLANE_DATA (src_frame, 0);
+  src = src + src_y * src_stride + src_x * in_bpp;
+
+  if (in_alpha && out_alpha) {
+    w *= 4;
+    for (i = 0; i < h; i++) {
+      for (j = 0; j < w; j += 4) {
+        dest[j + p_out[0]] = (src[j + p_in[0]] * i_alpha) >> 8;
+        dest[j + p_out[1]] = src[j + p_in[1]];
+        dest[j + p_out[2]] = src[j + p_in[2]];
+        dest[j + p_out[3]] = src[j + p_in[3]];
+      }
+      dest += dest_stride;
+      src += src_stride;
+    }
+  } else if (out_alpha && !packed_in) {
+    w *= 4;
+    i_alpha = CLAMP (i_alpha, 0, 255);
+
+    for (i = 0; i < h; i++) {
+      for (j = 0; j < w; j += 4) {
+        dest[j + p_out[0]] = i_alpha;
+        dest[j + p_out[1]] = src[j + p_in[1]];
+        dest[j + p_out[2]] = src[j + p_in[2]];
+        dest[j + p_out[3]] = src[j + p_in[3]];
+      }
+      dest += dest_stride;
+      src += src_stride;
+    }
+  } else if (out_alpha && packed_in) {
+    i_alpha = CLAMP (i_alpha, 0, 255);
+
+    for (i = 0; i < h; i++) {
+      for (j = 0; j < w; j++) {
+        dest[4 * j + p_out[0]] = i_alpha;
+        dest[4 * j + p_out[1]] = src[in_bpp * j + p_in[1]];
+        dest[4 * j + p_out[2]] = src[in_bpp * j + p_in[2]];
+        dest[4 * j + p_out[3]] = src[in_bpp * j + p_in[3]];
+      }
+      dest += dest_stride;
+      src += src_stride;
+    }
+  } else if (!packed_out && !packed_in) {
+    w *= 4;
+    for (i = 0; i < h; i++) {
+      for (j = 0; j < w; j += 4) {
+        dest[j + p_out[1]] = src[j + p_in[1]];
+        dest[j + p_out[2]] = src[j + p_in[2]];
+        dest[j + p_out[3]] = src[j + p_in[3]];
+      }
+      dest += dest_stride;
+      src += src_stride;
+    }
+  } else {
+    for (i = 0; i < h; i++) {
+      for (j = 0; j < w; j++) {
+        dest[out_bpp * j + p_out[1]] = src[in_bpp * j + p_in[1]];
+        dest[out_bpp * j + p_out[2]] = src[in_bpp * j + p_in[2]];
+        dest[out_bpp * j + p_out[3]] = src[in_bpp * j + p_in[3]];
+      }
+      dest += dest_stride;
+      src += src_stride;
+    }
+  }
+}
+
+static void
+copy_rgb32_ayuv (guint i_alpha, GstVideoFrame * dest_frame,
+    gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src_frame,
+    gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
+{
+  gint i, j;
+  gint src_stride, dest_stride;
+  gboolean in_alpha;
+  gint in_bpp;
+  gint p_in[4];
+  gboolean packed_in;
+  gint matrix[12];
+  gint a;
+  gint y, u, v;
+  gint r, g, b;
+  guint8 *dest, *src;
+
+  dest_stride = GST_VIDEO_FRAME_PLANE_STRIDE (dest_frame, 0);
+  src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (src_frame, 0);
+  in_bpp = GST_VIDEO_FRAME_COMP_PSTRIDE (src_frame, 0);
+  packed_in = (in_bpp < 4);
+
+  in_alpha = GST_VIDEO_INFO_HAS_ALPHA (&src_frame->info);
+  p_in[0] = GST_VIDEO_FRAME_COMP_OFFSET (src_frame, 3);
+  p_in[1] = GST_VIDEO_FRAME_COMP_OFFSET (src_frame, 0);
+  p_in[2] = GST_VIDEO_FRAME_COMP_OFFSET (src_frame, 1);
+  p_in[3] = GST_VIDEO_FRAME_COMP_OFFSET (src_frame, 2);
+
+  memcpy (matrix,
+      (dest_sdtv) ? cog_rgb_to_ycbcr_matrix_8bit_sdtv :
+      cog_rgb_to_ycbcr_matrix_8bit_hdtv, 12 * sizeof (gint));
+
+  dest = GST_VIDEO_FRAME_PLANE_DATA (dest_frame, 0);
+  dest = dest + dest_y * dest_stride + dest_x * 4;
+  src = GST_VIDEO_FRAME_PLANE_DATA (src_frame, 0);
+  src = src + src_y * src_stride + src_x * in_bpp;
+
+  if (in_alpha) {
+    w *= 4;
+    for (i = 0; i < h; i++) {
+      for (j = 0; j < w; j += 4) {
+        a = (src[j + p_in[0]] * i_alpha) >> 8;
+        r = src[j + p_in[1]];
+        g = src[j + p_in[2]];
+        b = src[j + p_in[3]];
+
+        y = APPLY_MATRIX (matrix, 0, r, g, b);
+        u = APPLY_MATRIX (matrix, 1, r, g, b);
+        v = APPLY_MATRIX (matrix, 2, r, g, b);
+
+        dest[j + 0] = a;
+        dest[j + 1] = CLAMP (y, 0, 255);
+        dest[j + 2] = CLAMP (u, 0, 255);
+        dest[j + 3] = CLAMP (v, 0, 255);
+      }
+      dest += dest_stride;
+      src += src_stride;
+    }
+  } else if (!packed_in) {
+    w *= 4;
+    i_alpha = CLAMP (i_alpha, 0, 255);
+
+    for (i = 0; i < h; i++) {
+      for (j = 0; j < w; j += 4) {
+        a = i_alpha;
+        r = src[j + p_in[1]];
+        g = src[j + p_in[2]];
+        b = src[j + p_in[3]];
+
+        y = APPLY_MATRIX (matrix, 0, r, g, b);
+        u = APPLY_MATRIX (matrix, 1, r, g, b);
+        v = APPLY_MATRIX (matrix, 2, r, g, b);
+
+        dest[j + 0] = a;
+        dest[j + 1] = CLAMP (y, 0, 255);
+        dest[j + 2] = CLAMP (u, 0, 255);
+        dest[j + 3] = CLAMP (v, 0, 255);
+      }
+      dest += dest_stride;
+      src += src_stride;
+    }
+  } else {
+    i_alpha = CLAMP (i_alpha, 0, 255);
+
+    for (i = 0; i < h; i++) {
+      for (j = 0; j < w; j++) {
+        a = i_alpha;
+        r = src[in_bpp * j + p_in[1]];
+        g = src[in_bpp * j + p_in[2]];
+        b = src[in_bpp * j + p_in[3]];
+
+        y = APPLY_MATRIX (matrix, 0, r, g, b);
+        u = APPLY_MATRIX (matrix, 1, r, g, b);
+        v = APPLY_MATRIX (matrix, 2, r, g, b);
+
+        dest[4 * j + 0] = a;
+        dest[4 * j + 1] = CLAMP (y, 0, 255);
+        dest[4 * j + 2] = CLAMP (u, 0, 255);
+        dest[4 * j + 3] = CLAMP (v, 0, 255);
+      }
+      dest += dest_stride;
+      src += src_stride;
+    }
+  }
+}
+
+static void
+copy_ayuv_rgb32 (guint i_alpha, GstVideoFrame * dest_frame,
+    gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src_frame,
+    gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
+{
+  gint i, j;
+  gint src_stride, dest_stride;
+  gboolean out_alpha;
+  gint out_bpp;
+  gint p_out[4];
+  gboolean packed_out;
+  gint matrix[12];
+  gint a;
+  gint y, u, v;
+  gint r, g, b;
+  guint8 *src, *dest;
+
+  src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (src_frame, 0);
+  dest_stride = GST_VIDEO_FRAME_PLANE_STRIDE (dest_frame, 0);
+  out_bpp = GST_VIDEO_FRAME_COMP_PSTRIDE (dest_frame, 0);
+  packed_out = (out_bpp < 4);
+
+  out_alpha = GST_VIDEO_INFO_HAS_ALPHA (&dest_frame->info);
+  p_out[0] = GST_VIDEO_FRAME_COMP_OFFSET (dest_frame, 3);
+  p_out[1] = GST_VIDEO_FRAME_COMP_OFFSET (dest_frame, 0);
+  p_out[2] = GST_VIDEO_FRAME_COMP_OFFSET (dest_frame, 1);
+  p_out[3] = GST_VIDEO_FRAME_COMP_OFFSET (dest_frame, 2);
+
+  memcpy (matrix,
+      (src_sdtv) ? cog_ycbcr_to_rgb_matrix_8bit_sdtv :
+      cog_ycbcr_to_rgb_matrix_8bit_hdtv, 12 * sizeof (gint));
+
+  dest = GST_VIDEO_FRAME_PLANE_DATA (dest_frame, 0);
+  dest = dest + dest_y * dest_stride + dest_x * out_bpp;
+  src = GST_VIDEO_FRAME_PLANE_DATA (src_frame, 0);
+  src = src + src_y * src_stride + src_x * 4;
+
+  if (out_alpha) {
+    w *= 4;
+    for (i = 0; i < h; i++) {
+      for (j = 0; j < w; j += 4) {
+        a = (src[j + 0] * i_alpha) >> 8;
+        y = src[j + 1];
+        u = src[j + 2];
+        v = src[j + 3];
+
+        r = APPLY_MATRIX (matrix, 0, y, u, v);
+        g = APPLY_MATRIX (matrix, 1, y, u, v);
+        b = APPLY_MATRIX (matrix, 2, y, u, v);
+
+        dest[j + p_out[0]] = a;
+        dest[j + p_out[1]] = CLAMP (r, 0, 255);
+        dest[j + p_out[2]] = CLAMP (g, 0, 255);
+        dest[j + p_out[3]] = CLAMP (b, 0, 255);
+      }
+      dest += dest_stride;
+      src += src_stride;
+    }
+  } else if (!packed_out) {
+    w *= 4;
+    for (i = 0; i < h; i++) {
+      for (j = 0; j < w; j += 4) {
+        y = src[j + 1];
+        u = src[j + 2];
+        v = src[j + 3];
+
+        r = APPLY_MATRIX (matrix, 0, y, u, v);
+        g = APPLY_MATRIX (matrix, 1, y, u, v);
+        b = APPLY_MATRIX (matrix, 2, y, u, v);
+
+        dest[j + p_out[1]] = CLAMP (r, 0, 255);
+        dest[j + p_out[2]] = CLAMP (g, 0, 255);
+        dest[j + p_out[3]] = CLAMP (b, 0, 255);
+      }
+      dest += dest_stride;
+      src += src_stride;
+    }
+  } else {
+    for (i = 0; i < h; i++) {
+      for (j = 0; j < w; j++) {
+        y = src[4 * j + 1];
+        u = src[4 * j + 2];
+        v = src[4 * j + 3];
+
+        r = APPLY_MATRIX (matrix, 0, y, u, v);
+        g = APPLY_MATRIX (matrix, 1, y, u, v);
+        b = APPLY_MATRIX (matrix, 2, y, u, v);
+
+        dest[out_bpp * j + p_out[1]] = CLAMP (r, 0, 255);
+        dest[out_bpp * j + p_out[2]] = CLAMP (g, 0, 255);
+        dest[out_bpp * j + p_out[3]] = CLAMP (b, 0, 255);
+      }
+      dest += dest_stride;
+      src += src_stride;
+    }
+  }
+}
+
+static void
+fill_gray (GstVideoBoxFill fill_type, guint b_alpha,
+    GstVideoFrame * frame, gboolean sdtv)
+{
+  gint i, j;
+  gint dest_stride;
+  guint8 *dest;
+  gint width, height;
+  GstVideoFormat format;
+
+  format = GST_VIDEO_FRAME_FORMAT (frame);
+
+  width = GST_VIDEO_FRAME_WIDTH (frame);
+  height = GST_VIDEO_FRAME_HEIGHT (frame);
 
-/* elementfactory information */
-static const GstElementDetails gst_video_box_details =
-GST_ELEMENT_DETAILS ("Video box filter",
-    "Filter/Effect/Video",
-    "Resizes a video by adding borders or cropping",
-    "Wim Taymans <wim@fluendo.com>");
+  dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
+  dest_stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
 
+  if (format == GST_VIDEO_FORMAT_GRAY8) {
+    guint8 val = yuv_sdtv_colors_Y[fill_type];
+
+    for (i = 0; i < height; i++) {
+      memset (dest, val, width);
+      dest += dest_stride;
+    }
+  } else {
+    guint16 val = yuv_sdtv_colors_Y[fill_type] << 8;
+
+    if (format == GST_VIDEO_FORMAT_GRAY16_BE) {
+      for (i = 0; i < height; i++) {
+        for (j = 0; j < width; j++) {
+          GST_WRITE_UINT16_BE (dest + 2 * j, val);
+        }
+        dest += dest_stride;
+      }
+    } else {
+      for (i = 0; i < height; i++) {
+        for (j = 0; j < width; j++) {
+          GST_WRITE_UINT16_LE (dest + 2 * j, val);
+        }
+        dest += dest_stride;
+      }
+    }
+  }
+}
+
+static void
+copy_packed_simple (guint i_alpha, GstVideoFrame * dest_frame,
+    gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src_frame,
+    gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
+{
+  gint i;
+  gint src_stride, dest_stride;
+  gint pixel_stride, row_size;
+  guint8 *src, *dest;
+
+  src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (src_frame, 0);
+  dest_stride = GST_VIDEO_FRAME_PLANE_STRIDE (dest_frame, 0);
+  pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (dest_frame, 0);
+  row_size = w * pixel_stride;
+
+  dest = GST_VIDEO_FRAME_PLANE_DATA (dest_frame, 0);
+  dest = dest + dest_y * dest_stride + dest_x * pixel_stride;
+  src = GST_VIDEO_FRAME_PLANE_DATA (src_frame, 0);
+  src = src + src_y * src_stride + src_x * pixel_stride;
+
+  for (i = 0; i < h; i++) {
+    memcpy (dest, src, row_size);
+    dest += dest_stride;
+    src += src_stride;
+  }
+}
+
+static void
+fill_yuy2 (GstVideoBoxFill fill_type, guint b_alpha,
+    GstVideoFrame * frame, gboolean sdtv)
+{
+  guint8 y, u, v;
+  gint i, j;
+  gint stride;
+  gint width, height;
+  guint8 *dest;
+  GstVideoFormat format;
+
+  format = GST_VIDEO_FRAME_FORMAT (frame);
+
+  width = GST_VIDEO_FRAME_WIDTH (frame);
+  height = GST_VIDEO_FRAME_HEIGHT (frame);
+
+  dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
+  stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
+
+  y = (sdtv) ? yuv_sdtv_colors_Y[fill_type] : yuv_hdtv_colors_Y[fill_type];
+  u = (sdtv) ? yuv_sdtv_colors_U[fill_type] : yuv_hdtv_colors_U[fill_type];
+  v = (sdtv) ? yuv_sdtv_colors_V[fill_type] : yuv_hdtv_colors_V[fill_type];
+
+  width = width + (width % 2);
+
+  if (format == GST_VIDEO_FORMAT_YUY2) {
+    for (i = 0; i < height; i++) {
+      for (j = 0; j < width; j += 2) {
+        dest[j * 2 + 0] = y;
+        dest[j * 2 + 1] = u;
+        dest[j * 2 + 2] = y;
+        dest[j * 2 + 3] = v;
+      }
+
+      dest += stride;
+    }
+  } else if (format == GST_VIDEO_FORMAT_YVYU) {
+    for (i = 0; i < height; i++) {
+      for (j = 0; j < width; j += 2) {
+        dest[j * 2 + 0] = y;
+        dest[j * 2 + 1] = v;
+        dest[j * 2 + 2] = y;
+        dest[j * 2 + 3] = u;
+      }
+
+      dest += stride;
+    }
+  } else {
+    for (i = 0; i < height; i++) {
+      for (j = 0; j < width; j += 2) {
+        dest[j * 2 + 0] = u;
+        dest[j * 2 + 1] = y;
+        dest[j * 2 + 2] = v;
+        dest[j * 2 + 3] = y;
+      }
+
+      dest += stride;
+    }
+  }
+}
+
+static void
+copy_yuy2_yuy2 (guint i_alpha, GstVideoFrame * dest_frame,
+    gboolean dest_sdtv, gint dest_x, gint dest_y, GstVideoFrame * src_frame,
+    gboolean src_sdtv, gint src_x, gint src_y, gint w, gint h)
+{
+  gint i, j;
+  gint src_stride, dest_stride;
+  guint8 *src, *dest;
+  GstVideoFormat src_format;
+
+  src_format = GST_VIDEO_FRAME_FORMAT (src_frame);
+
+  src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (src_frame, 0);
+  dest_stride = GST_VIDEO_FRAME_PLANE_STRIDE (dest_frame, 0);
+
+  dest_x = (dest_x & ~1);
+  src_x = (src_x & ~1);
+
+  w = w + (w % 2);
+
+  dest = GST_VIDEO_FRAME_PLANE_DATA (dest_frame, 0);
+  dest = dest + dest_y * dest_stride + dest_x * 2;
+  src = GST_VIDEO_FRAME_PLANE_DATA (src_frame, 0);
+  src = src + src_y * src_stride + src_x * 2;
+
+  if (src_sdtv != dest_sdtv) {
+    gint y1, u1, v1;
+    gint y2, u2, v2;
+    gint matrix[12];
+
+    memcpy (matrix,
+        dest_sdtv ? cog_ycbcr_hdtv_to_ycbcr_sdtv_matrix_8bit :
+        cog_ycbcr_sdtv_to_ycbcr_hdtv_matrix_8bit, 12 * sizeof (gint));
+
+    if (src_format == GST_VIDEO_FORMAT_YUY2) {
+      for (i = 0; i < h; i++) {
+        for (j = 0; j < w; j += 2) {
+          y1 = src[j * 2 + 0];
+          y2 = src[j * 2 + 2];
+          u1 = u2 = src[j * 2 + 1];
+          v1 = v2 = src[j * 2 + 3];
+
+          dest[j * 2 + 0] = APPLY_MATRIX (matrix, 0, y1, u1, v1);
+          dest[j * 2 + 1] = APPLY_MATRIX (matrix, 1, y1, u1, v1);
+          dest[j * 2 + 2] = APPLY_MATRIX (matrix, 0, y1, u2, v2);
+          dest[j * 2 + 3] = APPLY_MATRIX (matrix, 2, y2, u2, v2);
+        }
+        dest += dest_stride;
+        src += src_stride;
+      }
+    } else if (src_format == GST_VIDEO_FORMAT_YVYU) {
+      for (i = 0; i < h; i++) {
+        for (j = 0; j < w; j += 2) {
+          y1 = src[j * 2 + 0];
+          y2 = src[j * 2 + 2];
+          v1 = v2 = src[j * 2 + 1];
+          u1 = u2 = src[j * 2 + 3];
+
+          dest[j * 2 + 0] = APPLY_MATRIX (matrix, 0, y1, u1, v1);
+          dest[j * 2 + 1] = APPLY_MATRIX (matrix, 2, y1, u1, v1);
+          dest[j * 2 + 2] = APPLY_MATRIX (matrix, 0, y1, u2, v2);
+          dest[j * 2 + 3] = APPLY_MATRIX (matrix, 1, y2, u2, v2);
+        }
+        dest += dest_stride;
+        src += src_stride;
+      }
+    } else {
+      for (i = 0; i < h; i++) {
+        for (j = 0; j < w; j += 2) {
+          u1 = u2 = src[j * 2 + 0];
+          v1 = v2 = src[j * 2 + 2];
+          y1 = src[j * 2 + 1];
+          y2 = src[j * 2 + 3];
+
+          dest[j * 2 + 1] = APPLY_MATRIX (matrix, 0, y1, u1, v1);
+          dest[j * 2 + 0] = APPLY_MATRIX (matrix, 1, y1, u1, v1);
+          dest[j * 2 + 3] = APPLY_MATRIX (matrix, 0, y1, u2, v2);
+          dest[j * 2 + 2] = APPLY_MATRIX (matrix, 2, y2, u2, v2);
+        }
+        dest += dest_stride;
+        src += src_stride;
+      }
+    }
+  } else {
+    for (i = 0; i < h; i++) {
+      memcpy (dest, src, w * 2);
+      dest += dest_stride;
+      src += src_stride;
+    }
+  }
+}
 
 #define DEFAULT_LEFT      0
 #define DEFAULT_RIGHT     0
@@ -122,44 +2381,51 @@ enum
   PROP_BOTTOM,
   PROP_FILL_TYPE,
   PROP_ALPHA,
-  PROP_BORDER_ALPHA
+  PROP_BORDER_ALPHA,
+  PROP_AUTOCROP
       /* FILL ME */
 };
 
 static GstStaticPadTemplate gst_video_box_src_template =
-    GST_STATIC_PAD_TEMPLATE ("src",
+GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
     GST_PAD_ALWAYS,
-    GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";"
-        GST_VIDEO_CAPS_YUV ("I420"))
+    GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ AYUV, "
+            "ARGB, BGRA, ABGR, RGBA, xRGB, BGRx, xBGR, RGBx, RGB, BGR, "
+            "Y444, Y42B, YUY2, YVYU, UYVY, I420, YV12, Y41B, "
+            "GRAY8, GRAY16_BE, GRAY16_LE } "))
     );
 
 static GstStaticPadTemplate gst_video_box_sink_template =
-    GST_STATIC_PAD_TEMPLATE ("sink",
+GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
     GST_PAD_ALWAYS,
-    GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";"
-        GST_VIDEO_CAPS_YUV ("I420"))
+    GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ AYUV, "
+            "ARGB, BGRA, ABGR, RGBA, xRGB, BGRx, xBGR, RGBx, RGB, BGR, "
+            "Y444, Y42B, YUY2, YVYU, UYVY, I420, YV12, Y41B, "
+            "GRAY8, GRAY16_BE, GRAY16_LE } "))
     );
 
-
-GST_BOILERPLATE (GstVideoBox, gst_video_box, GstBaseTransform,
-    GST_TYPE_BASE_TRANSFORM);
+#define gst_video_box_parent_class parent_class
+G_DEFINE_TYPE (GstVideoBox, gst_video_box, GST_TYPE_VIDEO_FILTER);
 
 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 gboolean gst_video_box_recalc_transform (GstVideoBox * video_box);
 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);
+    GstPadDirection direction, GstCaps * from, GstCaps * filter);
+static void gst_video_box_before_transform (GstBaseTransform * trans,
+    GstBuffer * in);
+static gboolean gst_video_box_src_event (GstBaseTransform * trans,
+    GstEvent * event);
 
+static gboolean gst_video_box_set_info (GstVideoFilter * vfilter, GstCaps * in,
+    GstVideoInfo * in_info, GstCaps * out, GstVideoInfo * out_info);
+static GstFlowReturn gst_video_box_transform_frame (GstVideoFilter * vfilter,
+    GstVideoFrame * in_frame, GstVideoFrame * out_frame);
 
 #define GST_TYPE_VIDEO_BOX_FILL (gst_video_box_fill_get_type())
 static GType
@@ -168,8 +2434,11 @@ gst_video_box_fill_get_type (void)
   static GType video_box_fill_type = 0;
   static const GEnumValue video_box_fill[] = {
     {VIDEO_BOX_FILL_BLACK, "Black", "black"},
-    {VIDEO_BOX_FILL_GREEN, "Colorkey green", "green"},
-    {VIDEO_BOX_FILL_BLUE, "Colorkey blue", "blue"},
+    {VIDEO_BOX_FILL_GREEN, "Green", "green"},
+    {VIDEO_BOX_FILL_BLUE, "Blue", "blue"},
+    {VIDEO_BOX_FILL_RED, "Red", "red"},
+    {VIDEO_BOX_FILL_YELLOW, "Yellow", "yellow"},
+    {VIDEO_BOX_FILL_WHITE, "White", "white"},
     {0, NULL, NULL},
   };
 
@@ -180,72 +2449,93 @@ gst_video_box_fill_get_type (void)
   return video_box_fill_type;
 }
 
-
 static void
-gst_video_box_base_init (gpointer g_class)
+gst_video_box_finalize (GObject * object)
 {
-  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+  GstVideoBox *video_box = GST_VIDEO_BOX (object);
 
-  gst_element_class_set_details (element_class, &gst_video_box_details);
+  g_mutex_clear (&video_box->mutex);
 
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&gst_video_box_sink_template));
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&gst_video_box_src_template));
+  G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
 static void
 gst_video_box_class_init (GstVideoBoxClass * klass)
 {
-  GObjectClass *gobject_class;
-  GstBaseTransformClass *trans_class;
-
-  gobject_class = (GObjectClass *) klass;
-  trans_class = (GstBaseTransformClass *) klass;
+  GObjectClass *gobject_class = (GObjectClass *) klass;
+  GstElementClass *element_class = (GstElementClass *) (klass);
+  GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
+  GstVideoFilterClass *vfilter_class = (GstVideoFilterClass *) klass;
 
   gobject_class->set_property = gst_video_box_set_property;
   gobject_class->get_property = gst_video_box_get_property;
+  gobject_class->finalize = gst_video_box_finalize;
 
   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_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
   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));
+          DEFAULT_LEFT,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
   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));
+          DEFAULT_RIGHT,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
   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));
+          DEFAULT_TOP,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
   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));
+          DEFAULT_BOTTOM,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
   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));
+          DEFAULT_ALPHA,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BORDER_ALPHA,
-      g_param_spec_double ("border_alpha", "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));
-
-  trans_class->transform = GST_DEBUG_FUNCPTR (gst_video_box_transform);
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
+  /**
+   * GstVideoBox:autocrop:
+   *
+   * If set to %TRUE videobox will automatically crop/pad the input
+   * video to be centered in the output.
+   */
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_AUTOCROP,
+      g_param_spec_boolean ("autocrop", "Auto crop",
+          "Auto crop", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  trans_class->before_transform =
+      GST_DEBUG_FUNCPTR (gst_video_box_before_transform);
   trans_class->transform_caps =
       GST_DEBUG_FUNCPTR (gst_video_box_transform_caps);
-  trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_video_box_set_caps);
-  trans_class->get_unit_size = GST_DEBUG_FUNCPTR (gst_video_box_get_unit_size);
+  trans_class->src_event = GST_DEBUG_FUNCPTR (gst_video_box_src_event);
 
-  GST_DEBUG_CATEGORY_INIT (videobox_debug, "videobox", 0,
-      "Resizes a video by adding borders or cropping");
+  vfilter_class->set_info = GST_DEBUG_FUNCPTR (gst_video_box_set_info);
+  vfilter_class->transform_frame =
+      GST_DEBUG_FUNCPTR (gst_video_box_transform_frame);
+
+  gst_element_class_set_static_metadata (element_class, "Video box filter",
+      "Filter/Effect/Video",
+      "Resizes a video by adding borders or cropping",
+      "Wim Taymans <wim@fluendo.com>");
+
+  gst_element_class_add_static_pad_template (element_class,
+      &gst_video_box_sink_template);
+  gst_element_class_add_static_pad_template (element_class,
+      &gst_video_box_src_template);
 }
 
 static void
-gst_video_box_init (GstVideoBox * video_box, GstVideoBoxClass * g_class)
+gst_video_box_init (GstVideoBox * video_box)
 {
   video_box->box_right = DEFAULT_RIGHT;
   video_box->box_left = DEFAULT_LEFT;
@@ -258,6 +2548,9 @@ gst_video_box_init (GstVideoBox * video_box, GstVideoBoxClass * g_class)
   video_box->fill_type = DEFAULT_FILL_TYPE;
   video_box->alpha = DEFAULT_ALPHA;
   video_box->border_alpha = DEFAULT_BORDER_ALPHA;
+  video_box->autocrop = FALSE;
+
+  g_mutex_init (&video_box->mutex);
 }
 
 static void
@@ -266,7 +2559,7 @@ gst_video_box_set_property (GObject * object, guint prop_id,
 {
   GstVideoBox *video_box = GST_VIDEO_BOX (object);
 
-  GST_BASE_TRANSFORM_LOCK (GST_BASE_TRANSFORM_CAST (video_box));
+  g_mutex_lock (&video_box->mutex);
   switch (prop_id) {
     case PROP_LEFT:
       video_box->box_left = g_value_get_int (value);
@@ -317,13 +2610,74 @@ gst_video_box_set_property (GObject * object, guint prop_id,
     case PROP_BORDER_ALPHA:
       video_box->border_alpha = g_value_get_double (value);
       break;
+    case PROP_AUTOCROP:
+      video_box->autocrop = g_value_get_boolean (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
   }
+  gst_video_box_recalc_transform (video_box);
+
   GST_DEBUG_OBJECT (video_box, "Calling reconfigure");
-  gst_base_transform_reconfigure (GST_BASE_TRANSFORM (video_box));
-  GST_BASE_TRANSFORM_UNLOCK (GST_BASE_TRANSFORM_CAST (video_box));
+  gst_base_transform_reconfigure_src (GST_BASE_TRANSFORM_CAST (video_box));
+
+  g_mutex_unlock (&video_box->mutex);
+}
+
+static void
+gst_video_box_autocrop (GstVideoBox * video_box)
+{
+  gint crop_w = video_box->in_width - video_box->out_width;
+  gint crop_h = video_box->in_height - video_box->out_height;
+
+  video_box->box_left = crop_w / 2;
+  if (video_box->box_left < 0) {
+    video_box->border_left = -video_box->box_left;
+    video_box->crop_left = 0;
+  } else {
+    video_box->border_left = 0;
+    video_box->crop_left = video_box->box_left;
+  }
+
+  /* Round down/up for odd width differences */
+  if (crop_w < 0)
+    crop_w -= 1;
+  else
+    crop_w += 1;
+
+  video_box->box_right = crop_w / 2;
+  if (video_box->box_right < 0) {
+    video_box->border_right = -video_box->box_right;
+    video_box->crop_right = 0;
+  } else {
+    video_box->border_right = 0;
+    video_box->crop_right = video_box->box_right;
+  }
+
+  video_box->box_top = crop_h / 2;
+  if (video_box->box_top < 0) {
+    video_box->border_top = -video_box->box_top;
+    video_box->crop_top = 0;
+  } else {
+    video_box->border_top = 0;
+    video_box->crop_top = video_box->box_top;
+  }
+
+  /* Round down/up for odd height differences */
+  if (crop_h < 0)
+    crop_h -= 1;
+  else
+    crop_h += 1;
+  video_box->box_bottom = crop_h / 2;
+
+  if (video_box->box_bottom < 0) {
+    video_box->border_bottom = -video_box->box_bottom;
+    video_box->crop_bottom = 0;
+  } else {
+    video_box->border_bottom = 0;
+    video_box->crop_bottom = video_box->box_bottom;
+  }
 }
 
 static void
@@ -354,376 +2708,532 @@ gst_video_box_get_property (GObject * object, guint prop_id, GValue * value,
     case PROP_BORDER_ALPHA:
       g_value_set_double (value, video_box->border_alpha);
       break;
+    case PROP_AUTOCROP:
+      g_value_set_boolean (value, video_box->autocrop);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
   }
 }
 
+static inline gint
+gst_video_box_transform_dimension (gint val, gint delta)
+{
+  gint64 new_val = (gint64) val + (gint64) delta;
+
+  new_val = CLAMP (new_val, 1, G_MAXINT);
+
+  return (gint) new_val;
+}
+
+static gboolean
+gst_video_box_transform_dimension_value (const GValue * src_val,
+    gint delta, GValue * dest_val)
+{
+  gboolean ret = TRUE;
+
+  g_value_init (dest_val, G_VALUE_TYPE (src_val));
+
+  if (G_VALUE_HOLDS_INT (src_val)) {
+    gint ival = g_value_get_int (src_val);
+
+    ival = gst_video_box_transform_dimension (ival, delta);
+    g_value_set_int (dest_val, ival);
+  } else if (GST_VALUE_HOLDS_INT_RANGE (src_val)) {
+    gint min = gst_value_get_int_range_min (src_val);
+    gint max = gst_value_get_int_range_max (src_val);
+
+    min = gst_video_box_transform_dimension (min, delta);
+    max = gst_video_box_transform_dimension (max, delta);
+    if (min >= max) {
+      ret = FALSE;
+      g_value_unset (dest_val);
+    } else {
+      gst_value_set_int_range (dest_val, min, max);
+    }
+  } else if (GST_VALUE_HOLDS_LIST (src_val)) {
+    gint i;
+
+    for (i = 0; i < gst_value_list_get_size (src_val); ++i) {
+      const GValue *list_val;
+      GValue newval = { 0, };
+
+      list_val = gst_value_list_get_value (src_val, i);
+      if (gst_video_box_transform_dimension_value (list_val, delta, &newval))
+        gst_value_list_append_value (dest_val, &newval);
+      g_value_unset (&newval);
+    }
+
+    if (gst_value_list_get_size (dest_val) == 0) {
+      g_value_unset (dest_val);
+      ret = FALSE;
+    }
+  } else {
+    g_value_unset (dest_val);
+    ret = FALSE;
+  }
+
+  return ret;
+}
+
 static GstCaps *
 gst_video_box_transform_caps (GstBaseTransform * trans,
-    GstPadDirection direction, GstCaps * from)
+    GstPadDirection direction, GstCaps * from, GstCaps * filter)
 {
-  GstVideoBox *video_box;
+  GstVideoBox *video_box = GST_VIDEO_BOX (trans);
   GstCaps *to, *ret;
-  const GstCaps *templ;
+  GstCaps *templ;
   GstStructure *structure;
   GstPad *other;
-  gint width, height;
-
-  video_box = GST_VIDEO_BOX (trans);
+  gint i, j;
 
-  to = gst_caps_copy (from);
-  structure = gst_caps_get_structure (to, 0);
+  to = gst_caps_new_empty ();
+  for (i = 0; i < gst_caps_get_size (from); i++) {
+    const GValue *fval, *lval;
+    GValue list = { 0, };
+    GValue val = { 0, };
+    gboolean seen_yuv = FALSE, seen_rgb = FALSE;
+    const gchar *str;
 
-  /* get rid of format */
-  gst_structure_remove_field (structure, "format");
+    structure = gst_structure_copy (gst_caps_get_structure (from, i));
 
-  /* calculate width and height */
-  if (gst_structure_get_int (structure, "width", &width)) {
-    if (direction == GST_PAD_SINK) {
-      width -= video_box->box_left;
-      width -= video_box->box_right;
+    /* Transform width/height */
+    if (video_box->autocrop) {
+      gst_structure_remove_field (structure, "width");
+      gst_structure_remove_field (structure, "height");
     } else {
-      width += video_box->box_left;
-      width += video_box->box_right;
+      gint dw = 0, dh = 0;
+      const GValue *v;
+      GValue w_val = { 0, };
+      GValue h_val = { 0, };
+
+      /* calculate width and height */
+      if (direction == GST_PAD_SINK) {
+        dw -= video_box->box_left;
+        dw -= video_box->box_right;
+      } else {
+        dw += video_box->box_left;
+        dw += video_box->box_right;
+      }
+
+      if (direction == GST_PAD_SINK) {
+        dh -= video_box->box_top;
+        dh -= video_box->box_bottom;
+      } else {
+        dh += video_box->box_top;
+        dh += video_box->box_bottom;
+      }
+
+      v = gst_structure_get_value (structure, "width");
+      if (!gst_video_box_transform_dimension_value (v, dw, &w_val)) {
+        GST_WARNING_OBJECT (video_box,
+            "could not tranform width value with dw=%d" ", caps structure=%"
+            GST_PTR_FORMAT, dw, structure);
+        goto bail;
+      }
+      gst_structure_set_value (structure, "width", &w_val);
+
+      v = gst_structure_get_value (structure, "height");
+      if (!gst_video_box_transform_dimension_value (v, dh, &h_val)) {
+        g_value_unset (&w_val);
+        GST_WARNING_OBJECT (video_box,
+            "could not tranform height value with dh=%d" ", caps structure=%"
+            GST_PTR_FORMAT, dh, structure);
+        goto bail;
+      }
+      gst_structure_set_value (structure, "height", &h_val);
+      g_value_unset (&w_val);
+      g_value_unset (&h_val);
+    }
+
+    /* Supported conversions:
+     * I420->AYUV
+     * I420->YV12
+     * YV12->AYUV
+     * YV12->I420
+     * AYUV->I420
+     * AYUV->YV12
+     * AYUV->xRGB (24bpp, 32bpp, incl. alpha)
+     * xRGB->xRGB (24bpp, 32bpp, from/to all variants, incl. alpha)
+     * xRGB->AYUV (24bpp, 32bpp, incl. alpha)
+     *
+     * Passthrough only for everything else.
+     */
+    fval = gst_structure_get_value (structure, "format");
+    if (fval && GST_VALUE_HOLDS_LIST (fval)) {
+      for (j = 0; j < gst_value_list_get_size (fval); j++) {
+        lval = gst_value_list_get_value (fval, j);
+        if ((str = g_value_get_string (lval))) {
+          if (strcmp (str, "AYUV") == 0) {
+            seen_yuv = TRUE;
+            seen_rgb = TRUE;
+            break;
+          } else if (strstr (str, "RGB") || strstr (str, "BGR")) {
+            seen_rgb = TRUE;
+          } else if (strcmp (str, "I420") == 0 || strcmp (str, "YV12") == 0) {
+            seen_yuv = TRUE;
+          }
+        }
+      }
+    } else if (fval && G_VALUE_HOLDS_STRING (fval)) {
+      if ((str = g_value_get_string (fval))) {
+        if (strcmp (str, "AYUV") == 0) {
+          seen_yuv = TRUE;
+          seen_rgb = TRUE;
+        } else if (strstr (str, "RGB") || strstr (str, "BGR")) {
+          seen_rgb = TRUE;
+        } else if (strcmp (str, "I420") == 0 || strcmp (str, "YV12") == 0) {
+          seen_yuv = TRUE;
+        }
+      }
     }
-    if (width <= 0)
-      width = 1;
 
-    GST_DEBUG_OBJECT (trans, "New caps width: %d", width);
-    gst_structure_set (structure, "width", G_TYPE_INT, width, NULL);
-  }
-
-  if (gst_structure_get_int (structure, "height", &height)) {
-    if (direction == GST_PAD_SINK) {
-      height -= video_box->box_top;
-      height -= video_box->box_bottom;
-    } else {
-      height += video_box->box_top;
-      height += video_box->box_bottom;
+    if (seen_yuv || seen_rgb) {
+      g_value_init (&list, GST_TYPE_LIST);
+
+      g_value_init (&val, G_TYPE_STRING);
+      g_value_set_string (&val, "AYUV");
+      gst_value_list_append_value (&list, &val);
+      g_value_unset (&val);
+
+      if (seen_yuv) {
+        g_value_init (&val, G_TYPE_STRING);
+        g_value_set_string (&val, "I420");
+        gst_value_list_append_value (&list, &val);
+        g_value_reset (&val);
+        g_value_set_string (&val, "YV12");
+        gst_value_list_append_value (&list, &val);
+        g_value_unset (&val);
+      }
+      if (seen_rgb) {
+        g_value_init (&val, G_TYPE_STRING);
+        g_value_set_string (&val, "RGBx");
+        gst_value_list_append_value (&list, &val);
+        g_value_reset (&val);
+        g_value_set_string (&val, "BGRx");
+        gst_value_list_append_value (&list, &val);
+        g_value_reset (&val);
+        g_value_set_string (&val, "xRGB");
+        gst_value_list_append_value (&list, &val);
+        g_value_reset (&val);
+        g_value_set_string (&val, "xBGR");
+        gst_value_list_append_value (&list, &val);
+        g_value_reset (&val);
+        g_value_set_string (&val, "RGBA");
+        gst_value_list_append_value (&list, &val);
+        g_value_reset (&val);
+        g_value_set_string (&val, "BGRA");
+        gst_value_list_append_value (&list, &val);
+        g_value_reset (&val);
+        g_value_set_string (&val, "ARGB");
+        gst_value_list_append_value (&list, &val);
+        g_value_reset (&val);
+        g_value_set_string (&val, "ABGR");
+        gst_value_list_append_value (&list, &val);
+        g_value_reset (&val);
+        g_value_set_string (&val, "RGB");
+        gst_value_list_append_value (&list, &val);
+        g_value_reset (&val);
+        g_value_set_string (&val, "BGR");
+        gst_value_list_append_value (&list, &val);
+        g_value_unset (&val);
+      }
+      gst_value_list_merge (&val, fval, &list);
+      gst_structure_set_value (structure, "format", &val);
+      g_value_unset (&val);
+      g_value_unset (&list);
     }
 
-    if (height <= 0)
-      height = 1;
+    gst_structure_remove_field (structure, "colorimetry");
+    gst_structure_remove_field (structure, "chroma-site");
 
-    GST_DEBUG_OBJECT (trans, "New caps height: %d", height);
-    gst_structure_set (structure, "height", G_TYPE_INT, height, NULL);
+    gst_caps_append_structure (to, structure);
   }
 
   /* filter against set allowed caps on the pad */
   other = (direction == GST_PAD_SINK) ? trans->srcpad : trans->sinkpad;
-
   templ = gst_pad_get_pad_template_caps (other);
   ret = gst_caps_intersect (to, templ);
   gst_caps_unref (to);
+  gst_caps_unref (templ);
 
   GST_DEBUG_OBJECT (video_box, "direction %d, transformed %" GST_PTR_FORMAT
       " to %" GST_PTR_FORMAT, direction, from, ret);
 
+  if (ret && filter) {
+    GstCaps *intersection;
+
+    GST_DEBUG_OBJECT (video_box, "Using filter caps %" GST_PTR_FORMAT, filter);
+    intersection =
+        gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
+    gst_caps_unref (ret);
+    ret = intersection;
+    GST_DEBUG_OBJECT (video_box, "Intersection %" GST_PTR_FORMAT, ret);
+  }
+
   return ret;
+
+  /* ERRORS */
+bail:
+  {
+    gst_structure_free (structure);
+    gst_caps_unref (to);
+    to = gst_caps_new_empty ();
+    return to;
+  }
 }
 
 static gboolean
-gst_video_box_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
+gst_video_box_recalc_transform (GstVideoBox * video_box)
 {
-  GstVideoBox *video_box;
-  GstStructure *structure;
-  gboolean ret;
-
-  video_box = GST_VIDEO_BOX (trans);
+  gboolean res = TRUE;
 
-  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);
-  ret &= gst_structure_get_fourcc (structure, "format", &video_box->in_fourcc);
-
-  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", &video_box->out_fourcc);
-
-  /* something wrong getting the caps */
-  if (!ret)
-    goto no_caps;
-
-  GST_DEBUG_OBJECT (trans, "Input w: %d h: %d", video_box->in_width,
-      video_box->in_height);
-  GST_DEBUG_OBJECT (trans, "Output w: %d h: %d", video_box->out_width,
-      video_box->out_height);
-
-  /* if we have the same format in and out and we don't need to perform and
-   * cropping at all, we can just operate in passthorugh mode */
-  if (video_box->in_fourcc == video_box->out_fourcc &&
+  /* if we have the same format in and out and we don't need to perform any
+   * cropping at all, we can just operate in passthrough mode */
+  if (video_box->in_format == video_box->out_format &&
       video_box->box_left == 0 && video_box->box_right == 0 &&
-      video_box->box_top == 0 && video_box->box_bottom == 0) {
+      video_box->box_top == 0 && video_box->box_bottom == 0 &&
+      video_box->in_sdtv == video_box->out_sdtv) {
 
     GST_LOG_OBJECT (video_box, "we are using passthrough");
-    gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (video_box), TRUE);
+    gst_base_transform_set_passthrough (GST_BASE_TRANSFORM_CAST (video_box),
+        TRUE);
   } else {
     GST_LOG_OBJECT (video_box, "we are not using passthrough");
-    gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (video_box), FALSE);
-  }
-
-  return ret;
-
-  /* ERRORS */
-no_caps:
-  {
-    GST_DEBUG_OBJECT (video_box, "Could not get all caps fields");
-    return FALSE;
+    gst_base_transform_set_passthrough (GST_BASE_TRANSFORM_CAST (video_box),
+        FALSE);
   }
+  return res;
 }
 
-/* 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_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)
+gst_video_box_select_processing_functions (GstVideoBox * video_box)
 {
-
-  GstVideoBox *video_box;
-  GstStructure *structure = NULL;
-  guint32 fourcc;
-  gint width, height;
-
-  g_assert (size);
-
-  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;
+  switch (video_box->out_format) {
+    case GST_VIDEO_FORMAT_AYUV:
+      video_box->fill = fill_ayuv;
+      switch (video_box->in_format) {
+        case GST_VIDEO_FORMAT_AYUV:
+          video_box->copy = copy_ayuv_ayuv;
+          break;
+        case GST_VIDEO_FORMAT_I420:
+        case GST_VIDEO_FORMAT_YV12:
+          video_box->copy = copy_i420_ayuv;
+          break;
+        case GST_VIDEO_FORMAT_ARGB:
+        case GST_VIDEO_FORMAT_ABGR:
+        case GST_VIDEO_FORMAT_RGBA:
+        case GST_VIDEO_FORMAT_BGRA:
+        case GST_VIDEO_FORMAT_xRGB:
+        case GST_VIDEO_FORMAT_xBGR:
+        case GST_VIDEO_FORMAT_RGBx:
+        case GST_VIDEO_FORMAT_BGRx:
+        case GST_VIDEO_FORMAT_RGB:
+        case GST_VIDEO_FORMAT_BGR:
+          video_box->copy = copy_rgb32_ayuv;
+          break;
+        default:
+          break;
+      }
+      break;
+    case GST_VIDEO_FORMAT_I420:
+    case GST_VIDEO_FORMAT_YV12:
+      video_box->fill = fill_planar_yuv;
+      switch (video_box->in_format) {
+        case GST_VIDEO_FORMAT_AYUV:
+          video_box->copy = copy_ayuv_i420;
+          break;
+        case GST_VIDEO_FORMAT_I420:
+        case GST_VIDEO_FORMAT_YV12:
+          video_box->copy = copy_i420_i420;
+          break;
+        default:
+          break;
+      }
+      break;
+    case GST_VIDEO_FORMAT_ARGB:
+    case GST_VIDEO_FORMAT_ABGR:
+    case GST_VIDEO_FORMAT_RGBA:
+    case GST_VIDEO_FORMAT_BGRA:
+    case GST_VIDEO_FORMAT_xRGB:
+    case GST_VIDEO_FORMAT_xBGR:
+    case GST_VIDEO_FORMAT_RGBx:
+    case GST_VIDEO_FORMAT_BGRx:
+    case GST_VIDEO_FORMAT_RGB:
+    case GST_VIDEO_FORMAT_BGR:
+      video_box->fill = (video_box->out_format == GST_VIDEO_FORMAT_BGR
+          || video_box->out_format ==
+          GST_VIDEO_FORMAT_RGB) ? fill_rgb24 : fill_rgb32;
+      switch (video_box->in_format) {
+        case GST_VIDEO_FORMAT_ARGB:
+        case GST_VIDEO_FORMAT_ABGR:
+        case GST_VIDEO_FORMAT_RGBA:
+        case GST_VIDEO_FORMAT_BGRA:
+        case GST_VIDEO_FORMAT_xRGB:
+        case GST_VIDEO_FORMAT_xBGR:
+        case GST_VIDEO_FORMAT_RGBx:
+        case GST_VIDEO_FORMAT_BGRx:
+        case GST_VIDEO_FORMAT_RGB:
+        case GST_VIDEO_FORMAT_BGR:
+          video_box->copy = copy_rgb32;
+          break;
+        case GST_VIDEO_FORMAT_AYUV:
+          video_box->copy = copy_ayuv_rgb32;
+        default:
+          break;
+      }
+      break;
+    case GST_VIDEO_FORMAT_GRAY8:
+    case GST_VIDEO_FORMAT_GRAY16_BE:
+    case GST_VIDEO_FORMAT_GRAY16_LE:
+      video_box->fill = fill_gray;
+      switch (video_box->in_format) {
+        case GST_VIDEO_FORMAT_GRAY8:
+        case GST_VIDEO_FORMAT_GRAY16_BE:
+        case GST_VIDEO_FORMAT_GRAY16_LE:
+          video_box->copy = copy_packed_simple;
+          break;
+        default:
+          break;
+      }
+      break;
+    case GST_VIDEO_FORMAT_YUY2:
+    case GST_VIDEO_FORMAT_YVYU:
+    case GST_VIDEO_FORMAT_UYVY:
+      video_box->fill = fill_yuy2;
+      switch (video_box->in_format) {
+        case GST_VIDEO_FORMAT_YUY2:
+        case GST_VIDEO_FORMAT_YVYU:
+        case GST_VIDEO_FORMAT_UYVY:
+          video_box->copy = copy_yuy2_yuy2;
+          break;
+        default:
+          break;
+      }
       break;
-    case GST_MAKE_FOURCC ('I', '4', '2', '0'):
-      *size = GST_VIDEO_I420_SIZE (width, height);
+    case GST_VIDEO_FORMAT_Y444:
+    case GST_VIDEO_FORMAT_Y42B:
+    case GST_VIDEO_FORMAT_Y41B:
+      video_box->fill = fill_planar_yuv;
+      switch (video_box->in_format) {
+        case GST_VIDEO_FORMAT_Y444:
+          video_box->copy = copy_y444_y444;
+          break;
+        case GST_VIDEO_FORMAT_Y42B:
+          video_box->copy = copy_y42b_y42b;
+          break;
+        case GST_VIDEO_FORMAT_Y41B:
+          video_box->copy = copy_y41b_y41b;
+          break;
+        default:
+          break;
+      }
       break;
     default:
-      return FALSE;
       break;
   }
 
-  GST_LOG_OBJECT (video_box, "Returning from _unit_size %d", *size);
-
-  return TRUE;
-}
-
-static const guint8 yuv_colors_Y[VIDEO_BOX_FILL_LAST] = { 16, 150, 29 };
-static const guint8 yuv_colors_U[VIDEO_BOX_FILL_LAST] = { 128, 46, 255 };
-static const guint8 yuv_colors_V[VIDEO_BOX_FILL_LAST] = { 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_apply_alpha (guint8 * dest, guint8 alpha)
-{
-  if (dest[0] != 0)
-    dest[0] = alpha;
+  return video_box->fill != NULL && video_box->copy != NULL;
 }
 
-static void
-gst_video_box_ayuv_ayuv (GstVideoBox * video_box, guint8 * src, guint8 * dest)
+static gboolean
+gst_video_box_set_info (GstVideoFilter * vfilter, GstCaps * in,
+    GstVideoInfo * in_info, GstCaps * out, GstVideoInfo * out_info)
 {
-  gint dblen = video_box->out_height * video_box->out_width;
-  guint32 *destb = (guint32 *) dest;
-  guint32 *srcb = (guint32 *) src;
-  guint8 b_alpha = (guint8) (video_box->border_alpha * 255);
-  guint8 i_alpha = (guint8) (video_box->alpha * 255);
-  gint br, bl, bt, bb, crop_w, crop_h;
-  gint i;
-  guint32 *loc = destb;
-  guint32 empty_pixel;
-
-  GST_LOG ("Processing AYUV -> AYUV data");
-
-  crop_h = 0;
-  crop_w = 0;
-  empty_pixel = GUINT32_FROM_BE ((b_alpha << 24) |
-      (yuv_colors_Y[video_box->fill_type] << 16) |
-      (yuv_colors_U[video_box->fill_type] << 8) |
-      yuv_colors_V[video_box->fill_type]);
-
-  br = video_box->box_right;
-  bl = video_box->box_left;
-  bt = video_box->box_top;
-  bb = video_box->box_bottom;
-
-  if (br >= 0 && bl >= 0) {
-    crop_w = video_box->in_width - (br + bl);
-  } else if (br >= 0 && bl < 0) {
-    crop_w = video_box->in_width - (br);
-  } else if (br < 0 && bl >= 0) {
-    crop_w = video_box->in_width - (bl);
-  } else if (br < 0 && bl < 0) {
-    crop_w = video_box->in_width;
-  }
-
-  if (bb >= 0 && bt >= 0) {
-    crop_h = video_box->in_height - (bb + bt);
-  } else if (bb >= 0 && bt < 0) {
-    crop_h = video_box->in_height - (bb);
-  } else if (bb < 0 && bt >= 0) {
-    crop_h = video_box->in_height - (bt);
-  } else if (bb < 0 && bt < 0) {
-    crop_h = video_box->in_height;
-  }
-
-  GST_DEBUG ("Borders are: L:%d, R:%d, T:%d, B:%d", bl, br, bt, bb);
-  GST_DEBUG ("Alpha value is: %d", i_alpha);
-
-  if (crop_h <= 0 || crop_w <= 0) {
-
-    oil_splat_u32_ns (destb, &empty_pixel, dblen);
+  GstVideoBox *video_box = GST_VIDEO_BOX (vfilter);
+  gboolean ret;
 
-  } else {
+  g_mutex_lock (&video_box->mutex);
 
-    guint32 *src_loc = srcb;
+  video_box->in_format = GST_VIDEO_INFO_FORMAT (in_info);
+  video_box->in_width = GST_VIDEO_INFO_WIDTH (in_info);
+  video_box->in_height = GST_VIDEO_INFO_HEIGHT (in_info);
 
-    /* Top border */
-    if (bt < 0) {
-      oil_splat_u32_ns (loc, &empty_pixel, (-bt) * video_box->out_width);
-      loc = loc + ((-bt) * video_box->out_width);
-    } else {
-      src_loc = src_loc + (bt * video_box->in_width);
-    }
+  video_box->out_format = GST_VIDEO_INFO_FORMAT (out_info);
+  video_box->out_width = GST_VIDEO_INFO_WIDTH (out_info);
+  video_box->out_height = GST_VIDEO_INFO_HEIGHT (out_info);
 
-    if (bl >= 0)
-      src_loc += bl;
+  video_box->in_sdtv =
+      in_info->colorimetry.matrix == GST_VIDEO_COLOR_MATRIX_BT601;
+  video_box->out_sdtv =
+      out_info->colorimetry.matrix == GST_VIDEO_COLOR_MATRIX_BT601;
 
-    for (i = 0; i < crop_h; i++) {
-      gint j;
+  GST_DEBUG_OBJECT (video_box, "Input w: %d h: %d", video_box->in_width,
+      video_box->in_height);
+  GST_DEBUG_OBJECT (video_box, "Output w: %d h: %d", video_box->out_width,
+      video_box->out_height);
 
-      /* Left border */
-      if (bl < 0) {
-        oil_splat_u32_ns (loc, &empty_pixel, -bl);
-        loc += (-bl);
-      }
+  if (video_box->autocrop)
+    gst_video_box_autocrop (video_box);
 
-      /* Cropped area */
-      oil_copy_u8 ((guint8 *) loc, (guint8 *) src_loc, crop_w * 4);
+  /* recalc the transformation strategy */
+  ret = gst_video_box_recalc_transform (video_box);
 
-      for (j = 0; j < crop_w; j++)
-        gst_video_box_apply_alpha ((guint8 *) & loc[j], i_alpha);
+  if (ret)
+    ret = gst_video_box_select_processing_functions (video_box);
+  g_mutex_unlock (&video_box->mutex);
 
-      src_loc += video_box->in_width;
-      loc += crop_w;
+  return ret;
+}
 
-      /* Right border */
-      if (br < 0) {
-        oil_splat_u32_ns (loc, &empty_pixel, -br);
-        loc += (-br);
+static gboolean
+gst_video_box_src_event (GstBaseTransform * trans, GstEvent * event)
+{
+  GstVideoBox *video_box = GST_VIDEO_BOX (trans);
+  GstStructure *new_structure;
+  const GstStructure *structure;
+  const gchar *event_name;
+  gdouble pointer_x;
+  gdouble pointer_y;
+
+  GST_OBJECT_LOCK (video_box);
+  if (GST_EVENT_TYPE (event) == GST_EVENT_NAVIGATION &&
+      (video_box->box_left != 0 || video_box->box_top != 0)) {
+    structure = gst_event_get_structure (event);
+    event_name = gst_structure_get_string (structure, "event");
+
+    if (event_name &&
+        (strcmp (event_name, "mouse-move") == 0 ||
+            strcmp (event_name, "mouse-button-press") == 0 ||
+            strcmp (event_name, "mouse-button-release") == 0)) {
+      if (gst_structure_get_double (structure, "pointer_x", &pointer_x) &&
+          gst_structure_get_double (structure, "pointer_y", &pointer_y)) {
+        gdouble new_pointer_x, new_pointer_y;
+        GstEvent *new_event;
+
+        new_pointer_x = pointer_x + video_box->box_left;
+        new_pointer_y = pointer_y + video_box->box_top;
+
+        new_structure = gst_structure_copy (structure);
+        gst_structure_set (new_structure,
+            "pointer_x", G_TYPE_DOUBLE, (gdouble) (new_pointer_x),
+            "pointer_y", G_TYPE_DOUBLE, (gdouble) (new_pointer_y), NULL);
+
+        new_event = gst_event_new_navigation (new_structure);
+        gst_event_unref (event);
+        event = new_event;
+      } else {
+        GST_WARNING_OBJECT (video_box, "Failed to read navigation event");
       }
     }
-
-    /* Bottom border */
-    if (bb < 0) {
-      oil_splat_u32_ns (loc, &empty_pixel, (-bb) * video_box->out_width);
-    }
   }
+  GST_OBJECT_UNLOCK (video_box);
 
-  GST_LOG ("image created");
-
-}
-
-static gpointer
-gst_video_box_clear (gpointer dest, gint size)
-{
-  guint8 nil = 255;
-
-  oil_splat_u8_ns (dest, &nil, size);
-
-  return dest;
-}
-
-static gint
-UVfloor (gint j)
-{
-  return floor (((float) j) / 2);
-}
-
-static gint
-UVceil (gint j)
-{
-  return ceil (((float) j) / 2);
+  return GST_BASE_TRANSFORM_CLASS (parent_class)->src_event (trans, event);
 }
 
 static void
-gst_video_box_ayuv_i420 (GstVideoBox * video_box, guint8 * src, guint8 * dest)
+gst_video_box_process (GstVideoBox * video_box, GstVideoFrame * in,
+    GstVideoFrame * out)
 {
-  gint br, bl, bt, bb, crop_w, crop_h, rest;
-  gint Ysize, Usize, Vsize;
-  guint8 *Ydest, *Udest, *Vdest;
-  guint8 *Utemp, *Vtemp;
-  guint32 empty_px_values[3];
-  gint i, j;
-  guint Ywidth, Uwidth, Vwidth;
-
-  GST_LOG ("AYUV to I420 conversion");
+  guint b_alpha = CLAMP (video_box->border_alpha * 256, 0, 255);
+  guint i_alpha = CLAMP (video_box->alpha * 256, 0, 255);
+  GstVideoBoxFill fill_type = video_box->fill_type;
+  gint br, bl, bt, bb, crop_w, crop_h;
 
   crop_h = 0;
   crop_w = 0;
-  rest = 0;
-
-  empty_px_values[0] = yuv_colors_Y[video_box->fill_type];
-  empty_px_values[1] = yuv_colors_U[video_box->fill_type];
-  empty_px_values[2] = yuv_colors_V[video_box->fill_type];
-
-  Ywidth = GST_VIDEO_I420_Y_ROWSTRIDE (video_box->out_width);
-  Uwidth = GST_VIDEO_I420_U_ROWSTRIDE (video_box->out_width);
-  Vwidth = GST_VIDEO_I420_V_ROWSTRIDE (video_box->out_width);
-
-  Ydest = dest + GST_VIDEO_I420_Y_OFFSET (video_box->out_width,
-      video_box->out_height);
-  Udest = Ydest + GST_VIDEO_I420_U_OFFSET (video_box->out_width,
-      video_box->out_height);
-  Vdest = Ydest + GST_VIDEO_I420_V_OFFSET (video_box->out_width,
-      video_box->out_height);
-
-  Ysize = Ywidth * video_box->out_height;
-  Usize = Uwidth * UVceil (video_box->out_height);
-  Vsize = Vwidth * UVceil (video_box->out_height);
 
   br = video_box->box_right;
   bl = video_box->box_left;
@@ -731,16 +3241,12 @@ gst_video_box_ayuv_i420 (GstVideoBox * video_box, guint8 * src, guint8 * dest)
   bb = video_box->box_bottom;
 
   if (br >= 0 && bl >= 0) {
-    rest = Ywidth - video_box->out_width;
-    crop_w = video_box->in_width - (bl + br);
+    crop_w = video_box->in_width - (br + bl);
   } else if (br >= 0 && bl < 0) {
-    rest = Ywidth - video_box->out_width;
     crop_w = video_box->in_width - (br);
   } else if (br < 0 && bl >= 0) {
-    rest = Ywidth - video_box->out_width;
     crop_w = video_box->in_width - (bl);
   } else if (br < 0 && bl < 0) {
-    rest = Ywidth - video_box->out_width;
     crop_w = video_box->in_width;
   }
 
@@ -754,426 +3260,81 @@ gst_video_box_ayuv_i420 (GstVideoBox * video_box, guint8 * src, guint8 * dest)
     crop_h = video_box->in_height;
   }
 
-  Utemp = g_malloc0 (Uwidth);
-  Vtemp = g_malloc0 (Vwidth);
-
-  GST_LOG ("Borders are: L:%d, R:%d, T:%d, B:%d", bl, br, bt, bb);
-
-  GST_LOG ("Starting conversion");
-
-  if (crop_h <= 0 || crop_w <= 0) {
-
-    oil_splat_u8_ns (Ydest, (guint8 *) & empty_px_values[0], Ysize);
-    oil_splat_u8_ns (Udest, (guint8 *) & empty_px_values[1], Usize);
-    oil_splat_u8_ns (Vdest, (guint8 *) & empty_px_values[2], Vsize);
+  GST_DEBUG_OBJECT (video_box, "Borders are: L:%d, R:%d, T:%d, B:%d", bl, br,
+      bt, bb);
+  GST_DEBUG_OBJECT (video_box, "Alpha value is: %u (frame) %u (border)",
+      i_alpha, b_alpha);
 
+  if (crop_h < 0 || crop_w < 0) {
+    video_box->fill (fill_type, b_alpha, out, video_box->out_sdtv);
+  } else if (bb == 0 && bt == 0 && br == 0 && bl == 0) {
+    video_box->copy (i_alpha, out, video_box->out_sdtv, 0, 0, in,
+        video_box->in_sdtv, 0, 0, crop_w, crop_h);
   } else {
+    gint src_x = 0, src_y = 0;
+    gint dest_x = 0, dest_y = 0;
 
-    gboolean sumbuff = FALSE;
-    guint32 *src_loc1;
-    gint a = 0;
-
-    src_loc1 = (guint32 *) src;
+    /* Fill everything if a border should be added somewhere */
+    if (bt < 0 || bb < 0 || br < 0 || bl < 0)
+      video_box->fill (fill_type, b_alpha, out, video_box->out_sdtv);
 
+    /* Top border */
     if (bt < 0) {
-      oil_splat_u8_ns (Ydest, (guint8 *) & empty_px_values[0], (-bt) * Ywidth);
-
-      oil_splat_u8_ns (Udest, (guint8 *) & empty_px_values[1],
-          (UVfloor (-bt) * Uwidth) + 7);
-      oil_splat_u8_ns (Vdest, (guint8 *) & empty_px_values[2],
-          UVfloor (-bt) * Vwidth);
-
-      if ((-bt) % 2 > 0) {
-        oil_splat_u8_ns (Utemp, (guint8 *) & empty_px_values[1], Uwidth);
-        oil_splat_u8_ns (Vtemp, (guint8 *) & empty_px_values[2], Vwidth);
-        sumbuff = TRUE;
-      }
-
-      Ydest += ((-bt) * Ywidth);
-      Udest += (UVfloor (-bt) * Uwidth);
-      Vdest += (UVfloor (-bt) * Vwidth);
-
+      dest_y += -bt;
     } else {
-      src_loc1 = src_loc1 + (bt * video_box->in_width);
-    }
-
-    if (bl >= 0)
-      src_loc1 += bl;
-
-    GST_LOG ("Cropped area");
-    GST_LOG ("Ydest value: %p Ywidth: %u", Ydest, Ywidth);
-    GST_LOG ("Udest value: %p Uwidth: %u", Udest, Uwidth);
-    GST_LOG ("Vdest value: %p Vwidth: %u", Vdest, Vwidth);
-    GST_LOG ("Rest: %d", rest);
-    for (i = 0; i < crop_h; i++) {
-
-      a = 0;
-      if (sumbuff) {
-        /* left border */
-        if (bl < 0) {
-          oil_splat_u8_ns (Ydest, (guint8 *) & empty_px_values[0], -bl);
-
-          for (j = 0; j < -bl; j++) {
-            Utemp[UVfloor (j)] = (Utemp[UVfloor (j)] + empty_px_values[1]) / 2;
-            Vtemp[UVfloor (j)] = (Vtemp[UVfloor (j)] + empty_px_values[2]) / 2;
-          }
-          Ydest += -bl;
-          a = -bl;
-        }
-
-        for (j = 0; j < crop_w; j++) {
-          /* check ARCH */
-          Ydest[j] = ((guint8 *) & src_loc1[j])[1];
-          Utemp[UVfloor (a + j)] =
-              (Utemp[UVfloor (a + j)] + ((guint8 *) & src_loc1[j])[2]) / 2;
-          Vtemp[UVfloor (a + j)] =
-              (Vtemp[UVfloor (a + j)] + ((guint8 *) & src_loc1[j])[3]) / 2;
-        }
-        Ydest += crop_w;
-
-        /* right border */
-        if (br < 0) {
-          oil_splat_u8_ns (Ydest, (guint8 *) & empty_px_values[0], -br);
-          for (j = 0; j < -br; j++) {
-            Utemp[UVfloor (a + crop_w + j)] =
-                (Utemp[UVfloor (a + crop_w + j)] + empty_px_values[1]) / 2;
-            Vtemp[UVfloor (a + crop_w + j)] =
-                (Vtemp[UVfloor (a + crop_w + j)] + empty_px_values[2]) / 2;
-          }
-          Ydest += -br;
-        }
-        oil_copy_u8 (Udest, Utemp, Uwidth);
-        oil_copy_u8 (Vdest, Vtemp, Vwidth);
-        Udest += Uwidth;
-        Vdest += Vwidth;
-        Ydest += rest;
-        gst_video_box_clear (Utemp, Uwidth);
-        gst_video_box_clear (Vtemp, Vwidth);
-        src_loc1 += video_box->in_width;
-        sumbuff = FALSE;
-
-      } else {
-
-        /* left border */
-        a = 0;
-        if (bl < 0) {
-          oil_splat_u8_ns (Ydest, (guint8 *) & empty_px_values[0], -bl);
-          oil_splat_u8_ns (Vtemp, (guint8 *) & empty_px_values[1],
-              UVceil (-bl));
-          oil_splat_u8_ns (Utemp, (guint8 *) & empty_px_values[2],
-              UVceil (-bl));
-          Ydest += -bl;
-          a = -bl;
-        }
-
-        for (j = 0; j < crop_w; j++) {
-
-          /* check ARCH */
-          Ydest[j] = ((guint8 *) & src_loc1[j])[1];
-
-          if ((a + j) % 2 > 0) {
-            Utemp[UVfloor (a + j)] =
-                (Utemp[UVfloor (a + j)] + ((guint8 *) & src_loc1[j])[2]) / 2;
-            Vtemp[UVfloor (a + j)] =
-                (Vtemp[UVfloor (a + j)] + ((guint8 *) & src_loc1[j])[3]) / 2;
-          } else {
-            Utemp[UVfloor (a + j)] = ((guint8 *) & src_loc1[j])[2] / 2;
-            Vtemp[UVfloor (a + j)] = ((guint8 *) & src_loc1[j])[3] / 2;
-          }
-        }
-        Ydest += crop_w;
-
-        /* right border */
-        if (br < 0) {
-          j = 0;
-          if ((a + crop_w) % 2 > 0) {
-            Utemp[UVfloor (a + crop_w)] =
-                (Utemp[UVfloor (a + crop_w)] + empty_px_values[1]) / 2;
-            Vtemp[UVfloor (a + crop_w)] =
-                (Vtemp[UVfloor (a + crop_w)] + empty_px_values[2]) / 2;
-            a++;
-            j = -1;
-          }
-
-          oil_splat_u8_ns (Ydest, (guint8 *) & empty_px_values[0], -br);
-          oil_splat_u8_ns (&Utemp[UVfloor (a + crop_w)],
-              (guint8 *) & empty_px_values[1], UVceil ((-br) + j));
-          oil_splat_u8_ns (&Vtemp[UVfloor (a + crop_w)],
-              (guint8 *) & empty_px_values[2], UVceil ((-br) + j));
-          Ydest += -br;
-        }
-        Ydest += rest;
-        src_loc1 += video_box->in_width;
-        sumbuff = TRUE;
-
-      }
-    }
-
-    /* bottom border */
-    if (bb < 0) {
-      oil_splat_u8_ns (Ydest, (guint8 *) & empty_px_values[0], (-bb) * Ywidth);
-      if (sumbuff) {
-        for (i = 0; i < Uwidth; i++) {
-          Utemp[i] = (Utemp[i] + empty_px_values[1]) / 2;
-        }
-        for (i = 0; i < Vwidth; i++) {
-          Vtemp[i] = (Vtemp[i] + empty_px_values[2]) / 2;
-        }
-
-        oil_copy_u8 (Udest, Utemp, Uwidth);
-        oil_copy_u8 (Vdest, Vtemp, Vwidth);
-        Udest += Uwidth;
-        Vdest += Vwidth;
-        sumbuff = FALSE;
-      }
-      oil_splat_u8_ns (Udest, (guint8 *) & empty_px_values[1],
-          (UVfloor ((-bb))) * Uwidth);
-      oil_splat_u8_ns (Vdest, (guint8 *) & empty_px_values[2],
-          (UVfloor ((-bb))) * Vwidth);
+      src_y += bt;
     }
-    if (sumbuff) {
-      oil_copy_u8 (Udest, Utemp, Uwidth);
-      oil_copy_u8 (Vdest, Vtemp, Vwidth);
-    }
-  }
-
-  GST_LOG ("image created");
-  g_free (Utemp);
-  g_free (Vtemp);
-}
 
-static void
-gst_video_box_i420_ayuv (GstVideoBox * video_box, guint8 * src, guint8 * dest)
-{
-  guint8 *srcY, *srcU, *srcV;
-  gint crop_width, crop_width2, crop_height;
-  gint out_width;
-  gint src_stridey, src_strideu, src_stridev;
-  gint br, bl, bt, bb;
-  gint colorY, colorU, colorV;
-  gint i, j;
-  guint8 b_alpha = (guint8) (video_box->border_alpha * 255);
-  guint8 i_alpha = (guint8) (video_box->alpha * 255);
-  guint32 *destp;
-  guint32 *destb = (guint32 *) dest;
-  guint32 ayuv;
-
-  br = video_box->border_right;
-  bl = video_box->border_left;
-  bt = video_box->border_top;
-  bb = video_box->border_bottom;
-
-  out_width = video_box->out_width;
-
-  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;
-  crop_width -= (video_box->crop_left + video_box->crop_right);
-  crop_width2 = crop_width / 2;
-  crop_height = video_box->in_height;
-  crop_height -= (video_box->crop_top + video_box->crop_bottom);
-
-  srcY =
-      src + GST_VIDEO_I420_Y_OFFSET (video_box->in_width, video_box->in_height);
-  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_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_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];
-  colorV = yuv_colors_V[video_box->fill_type];
-
-  ayuv =
-      GUINT32_FROM_BE ((b_alpha << 24) | (colorY << 16) | (colorU << 8) |
-      colorV);
-
-  /* top border */
-  if (bt) {
-    size_t nb_pixels = bt * out_width;
-
-    oil_splat_u32_ns (destb, &ayuv, nb_pixels);
-    destb += nb_pixels;
-  }
-  for (i = 0; i < crop_height; i++) {
-    destp = destb;
-    /* left border */
-    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++;
-      *dest++ = *srcY++;
-      *dest++ = *srcU;
-      *dest++ = *srcV;
-      dest++;
-      *dest++ = *srcY++;
-      *dest++ = *srcU++;
-      *dest++ = *srcV++;
-    }
-    if (i % 2 == 0) {
-      srcU -= crop_width2;
-      srcV -= crop_width2;
+    /* Left border */
+    if (bl < 0) {
+      dest_x += -bl;
     } else {
-      srcU += src_strideu - crop_width2;
-      srcV += src_stridev - crop_width2;
+      src_x += bl;
     }
-    srcY += src_stridey - (crop_width2 * 2);
 
-    destp = (guint32 *) dest;
-    /* right border */
-    if (br) {
-      oil_splat_u32_ns (destp, &ayuv, br);
-    }
-    destb += out_width;
+    /* Frame */
+    video_box->copy (i_alpha, out, video_box->out_sdtv, dest_x, dest_y,
+        in, video_box->in_sdtv, src_x, src_y, crop_w, crop_h);
   }
-  /* bottom border */
-  if (bb) {
-    size_t nb_pixels = bb * out_width;
 
-    oil_splat_u32_ns (destb, &ayuv, nb_pixels);
-  }
+  GST_LOG_OBJECT (video_box, "image created");
 }
 
-
 static void
-gst_video_box_i420_i420 (GstVideoBox * video_box, guint8 * src, guint8 * dest)
+gst_video_box_before_transform (GstBaseTransform * trans, GstBuffer * in)
 {
-  guint8 *srcY, *srcU, *srcV;
-  guint8 *destY, *destU, *destV;
-  gint crop_width, crop_height;
-  gint out_width, out_height;
-  gint src_width, src_height;
-  gint src_stride, dest_stride;
-  gint br, bl, bt, bb;
-
-  br = video_box->border_right;
-  bl = video_box->border_left;
-  bt = video_box->border_top;
-  bb = video_box->border_bottom;
-
-  out_width = video_box->out_width;
-  out_height = video_box->out_height;
-
-  src_width = video_box->in_width;
-  src_height = video_box->in_height;
+  GstVideoBox *video_box = GST_VIDEO_BOX (trans);
+  GstClockTime timestamp, stream_time;
 
-  crop_width = src_width - (video_box->crop_left + video_box->crop_right);
-  crop_height = src_height - (video_box->crop_top + video_box->crop_bottom);
+  timestamp = GST_BUFFER_TIMESTAMP (in);
+  stream_time =
+      gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, timestamp);
 
-  /* Y plane */
-  src_stride = GST_VIDEO_I420_Y_ROWSTRIDE (src_width);
-  dest_stride = GST_VIDEO_I420_Y_ROWSTRIDE (out_width);
+  GST_DEBUG_OBJECT (video_box, "sync to %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (timestamp));
 
-  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;
-
-  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]);
-
-  br /= 2;
-  bb /= 2;
-  bl /= 2;
-  bt /= 2;
-
-  /* we need to round up to make sure we draw all the U and V lines */
-  crop_width = (crop_width + 1) / 2;
-  crop_height = (crop_height + 1) / 2;
-
-  /* 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);
-
-  srcU = src + GST_VIDEO_I420_U_OFFSET (src_width, src_height);
-  srcU += src_stride * (video_box->crop_top / 2) + (video_box->crop_left / 2);
-
-  gst_video_box_copy_plane_i420 (video_box, srcU, destU, br, bl, bt, bb,
-      crop_width, crop_height, src_stride, out_width / 2, dest_stride,
-      yuv_colors_U[video_box->fill_type]);
-
-  /* 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, bl, bt, bb,
-      crop_width, crop_height, src_stride, out_width / 2, dest_stride,
-      yuv_colors_V[video_box->fill_type]);
+  if (GST_CLOCK_TIME_IS_VALID (stream_time))
+    gst_object_sync_values (GST_OBJECT (video_box), stream_time);
 }
 
 static GstFlowReturn
-gst_video_box_transform (GstBaseTransform * trans, GstBuffer * in,
-    GstBuffer * out)
+gst_video_box_transform_frame (GstVideoFilter * vfilter,
+    GstVideoFrame * in_frame, GstVideoFrame * out_frame)
 {
-  GstVideoBox *video_box;
-  guint8 *indata, *outdata;
+  GstVideoBox *video_box = GST_VIDEO_BOX (vfilter);
 
-  video_box = GST_VIDEO_BOX (trans);
-
-  indata = GST_BUFFER_DATA (in);
-  outdata = GST_BUFFER_DATA (out);
-
-  switch (video_box->in_fourcc) {
-    case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
-      switch (video_box->out_fourcc) {
-        case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
-          gst_video_box_ayuv_ayuv (video_box, indata, outdata);
-          break;
-        case GST_MAKE_FOURCC ('I', '4', '2', '0'):
-          gst_video_box_ayuv_i420 (video_box, indata, outdata);
-          break;
-        default:
-          goto invalid_format;
-      }
-      break;
-    case GST_MAKE_FOURCC ('I', '4', '2', '0'):
-      switch (video_box->out_fourcc) {
-        case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
-          gst_video_box_i420_ayuv (video_box, indata, outdata);
-          break;
-        case GST_MAKE_FOURCC ('I', '4', '2', '0'):
-          gst_video_box_i420_i420 (video_box, indata, outdata);
-          break;
-        default:
-          goto invalid_format;
-      }
-      break;
-    default:
-      goto invalid_format;
-  }
+  g_mutex_lock (&video_box->mutex);
+  gst_video_box_process (video_box, in_frame, out_frame);
+  g_mutex_unlock (&video_box->mutex);
   return GST_FLOW_OK;
-
-  /* ERRORS */
-invalid_format:
-  {
-    return GST_FLOW_ERROR;
-  }
 }
 
 /* FIXME: 0.11 merge with videocrop plugin */
 static gboolean
 plugin_init (GstPlugin * plugin)
 {
-  oil_init ();
+  GST_DEBUG_CATEGORY_INIT (videobox_debug, "videobox", 0,
+      "Resizes a video by adding borders or cropping");
 
   return gst_element_register (plugin, "videobox", GST_RANK_NONE,
       GST_TYPE_VIDEO_BOX);
@@ -1181,6 +3342,6 @@ plugin_init (GstPlugin * plugin)
 
 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
     GST_VERSION_MINOR,
-    "videobox",
+    videobox,
     "resizes a video by adding borders or cropping",
     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)