videobox: Add support for autocropping according to the caps
authorStephen Jungels <stephen@jungels.net>
Fri, 14 Aug 2009 11:15:57 +0000 (13:15 +0200)
committerSebastian Dröge <sebastian.droege@collabora.co.uk>
Mon, 31 Aug 2009 06:19:25 +0000 (08:19 +0200)
Fixes bug #582238.

gst/videobox/gstvideobox.c

index 0cec93570d5aaa61b95308f859c50fc441e2e5c1..e58276ac5a631af4c6c2384f1613868edd0269b2 100644 (file)
@@ -35,6 +35,7 @@
  * The videobox plugin has many uses such as doing a mosaic of pictures, 
  * letterboxing video, cutting out pieces of video, picture in picture, etc..
  */
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
@@ -90,6 +91,8 @@ struct _GstVideoBox
   gdouble border_alpha;
 
   GstVideoBoxFill fill_type;
+
+  gboolean autocrop;
 };
 
 struct _GstVideoBoxClass
@@ -122,10 +125,30 @@ enum
   PROP_BOTTOM,
   PROP_FILL_TYPE,
   PROP_ALPHA,
-  PROP_BORDER_ALPHA
+  PROP_BORDER_ALPHA,
+  PROP_AUTOCROP
       /* FILL ME */
 };
 
+/*
+
+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 videotestsrc ! videobox autocrop=true ! \
+"video/x-raw-yuv, width=600, height=400" ! ffmpegcolorspace ! ximagesink
+
+*/
+
+
 static GstStaticPadTemplate gst_video_box_src_template =
     GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
@@ -151,6 +174,7 @@ static void gst_video_box_set_property (GObject * object, guint prop_id,
 static void gst_video_box_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec);
 
+static gboolean 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,
@@ -233,6 +257,9 @@ gst_video_box_class_init (GstVideoBoxClass * klass)
       g_param_spec_double ("border_alpha", "Border Alpha",
           "Alpha value of the border", 0.0, 1.0, DEFAULT_BORDER_ALPHA,
           G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_AUTOCROP,
+      g_param_spec_boolean ("autocrop", "Auto crop",
+          "Auto crop", FALSE, G_PARAM_READWRITE));
 
   trans_class->transform = GST_DEBUG_FUNCPTR (gst_video_box_transform);
   trans_class->transform_caps =
@@ -258,6 +285,7 @@ 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;
 }
 
 static void
@@ -317,12 +345,65 @@ 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_DEBUG_OBJECT (video_box, "Calling reconfigure");
-  gst_base_transform_reconfigure (GST_BASE_TRANSFORM (video_box));
+  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));
+}
+
+static void
+gst_video_box_autocrop (GstVideoBox * video_box)
+{
+  gint crop_w = (video_box->in_width - video_box->out_width) / 2;
+  gint crop_h = (video_box->in_height - video_box->out_height) / 2;
+
+  GST_BASE_TRANSFORM_LOCK (GST_BASE_TRANSFORM_CAST (video_box));
+
+  video_box->box_left = crop_w;
+  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;
+  }
+
+  video_box->box_right = crop_w;
+  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;
+  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;
+  }
+
+  video_box->box_bottom = crop_h;
+  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;
+  }
+
   GST_BASE_TRANSFORM_UNLOCK (GST_BASE_TRANSFORM_CAST (video_box));
 }
 
@@ -354,6 +435,9 @@ 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;
@@ -379,36 +463,45 @@ gst_video_box_transform_caps (GstBaseTransform * trans,
   /* get rid of format */
   gst_structure_remove_field (structure, "format");
 
-  /* 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;
-    } else {
-      width += video_box->box_left;
-      width += video_box->box_right;
-    }
-    if (width <= 0)
-      width = 1;
-
-    GST_DEBUG_OBJECT (trans, "New caps width: %d", width);
-    gst_structure_set (structure, "width", G_TYPE_INT, width, NULL);
+  /* otherwise caps nego will fail: */
+  if (video_box->autocrop) {
+    gst_structure_remove_field (structure, "width");
+    gst_structure_remove_field (structure, "height");
   }
 
-  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 (!video_box->autocrop) {
+
+    /* 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;
+      } else {
+        width += video_box->box_left;
+        width += video_box->box_right;
+      }
+      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 (height <= 0)
-      height = 1;
+    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;
+      }
 
-    GST_DEBUG_OBJECT (trans, "New caps height: %d", height);
-    gst_structure_set (structure, "height", G_TYPE_INT, height, NULL);
+      if (height <= 0)
+        height = 1;
+
+      GST_DEBUG_OBJECT (trans, "New caps height: %d", height);
+      gst_structure_set (structure, "height", G_TYPE_INT, height, NULL);
+    }
   }
 
   /* filter against set allowed caps on the pad */
@@ -424,6 +517,26 @@ gst_video_box_transform_caps (GstBaseTransform * trans,
   return ret;
 }
 
+static gboolean
+video_box_recalc_transform (GstVideoBox * video_box)
+{
+  gboolean res = TRUE;
+
+  /* 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 passthorugh mode */
+  if (video_box->in_fourcc == video_box->out_fourcc &&
+      video_box->box_left == 0 && video_box->box_right == 0 &&
+      video_box->box_top == 0 && video_box->box_bottom == 0) {
+
+    GST_LOG_OBJECT (video_box, "we are using passthrough");
+    gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (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 res;
+}
+
 static gboolean
 gst_video_box_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
 {
@@ -452,18 +565,11 @@ gst_video_box_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
   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 &&
-      video_box->box_left == 0 && video_box->box_right == 0 &&
-      video_box->box_top == 0 && video_box->box_bottom == 0) {
+  if (video_box->autocrop)
+    gst_video_box_autocrop (video_box);
 
-    GST_LOG_OBJECT (video_box, "we are using passthrough");
-    gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (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);
-  }
+  /* recalc the transformation strategy */
+  ret = video_box_recalc_transform (video_box);
 
   return ret;
 
@@ -947,7 +1053,7 @@ 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 out_width, out_height;
   gint src_stridey, src_strideu, src_stridev;
   gint br, bl, bt, bb;
   gint colorY, colorU, colorV;
@@ -964,6 +1070,7 @@ gst_video_box_i420_ayuv (GstVideoBox * video_box, guint8 * src, guint8 * dest)
   bb = video_box->border_bottom;
 
   out_width = video_box->out_width;
+  out_height = video_box->out_height;
 
   src_stridey = GST_VIDEO_I420_Y_ROWSTRIDE (video_box->in_width);
   src_strideu = GST_VIDEO_I420_U_ROWSTRIDE (video_box->in_width);