videocrop: Add support for automatic cropping
authorNicolas Dufresne <nicolas.dufresne@collabora.com>
Tue, 6 Nov 2012 14:03:55 +0000 (15:03 +0100)
committerOlivier CrĂȘte <olivier.crete@collabora.com>
Wed, 7 Nov 2012 10:20:24 +0000 (11:20 +0100)
This change enable automatic cropping using -1 set to left, top, right or
bottom property. In the case both side are set to automatic cropping, the
croping will be done equally on both side (in the odd case, right and
bottom cropping will be 1 pixel more).

https://bugzilla.gnome.org/show_bug.cgi?id=687761

gst/videocrop/gstvideocrop.c
gst/videocrop/gstvideocrop.h
tests/check/elements/videocrop.c

index b6430cb..eeac548 100644 (file)
@@ -179,17 +179,21 @@ gst_video_crop_class_init (GstVideoCropClass * klass)
   gobject_class->get_property = gst_video_crop_get_property;
 
   g_object_class_install_property (gobject_class, ARG_LEFT,
-      g_param_spec_int ("left", "Left", "Pixels to crop at left",
-          0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+      g_param_spec_int ("left", "Left",
+          "Pixels to crop at left (-1 to auto-crop)", -1, G_MAXINT, 0,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
   g_object_class_install_property (gobject_class, ARG_RIGHT,
-      g_param_spec_int ("right", "Right", "Pixels to crop at right",
-          0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+      g_param_spec_int ("right", "Right",
+          "Pixels to crop at right (-1 to auto-crop)", -1, G_MAXINT, 0,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
   g_object_class_install_property (gobject_class, ARG_TOP,
-      g_param_spec_int ("top", "Top", "Pixels to crop at top",
-          0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+      g_param_spec_int ("top", "Top",
+          "Pixels to crop at top (-1 to auto-crop)", -1, G_MAXINT, 0,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
   g_object_class_install_property (gobject_class, ARG_BOTTOM,
-      g_param_spec_int ("bottom", "Bottom", "Pixels to crop at bottom",
-          0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+      g_param_spec_int ("bottom", "Bottom",
+          "Pixels to crop at bottom (-1 to auto-crop)", -1, G_MAXINT, 0,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   gst_element_class_add_pad_template (element_class,
       gst_static_pad_template_get (&sink_template));
@@ -406,33 +410,69 @@ gst_video_crop_transform_dimension (gint val, gint delta)
 
 static gboolean
 gst_video_crop_transform_dimension_value (const GValue * src_val,
-    gint delta, GValue * dest_val)
+    gint delta, GValue * dest_val, GstPadDirection direction, gboolean dynamic)
 {
   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_crop_transform_dimension (ival, delta);
-    g_value_set_int (dest_val, ival);
+
+    if (dynamic) {
+      if (direction == GST_PAD_SRC) {
+        if (ival == G_MAXINT) {
+          g_value_init (dest_val, G_TYPE_INT);
+          g_value_set_int (dest_val, ival);
+        } else {
+          g_value_init (dest_val, GST_TYPE_INT_RANGE);
+          gst_value_set_int_range (dest_val, ival, G_MAXINT);
+        }
+      } else {
+        if (ival == 1) {
+          g_value_init (dest_val, G_TYPE_INT);
+          g_value_set_int (dest_val, ival);
+        } else {
+          g_value_init (dest_val, GST_TYPE_INT_RANGE);
+          gst_value_set_int_range (dest_val, 1, ival);
+        }
+      }
+    } else {
+      g_value_init (dest_val, G_TYPE_INT);
+      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_crop_transform_dimension (min, delta);
     max = gst_video_crop_transform_dimension (max, delta);
-    gst_value_set_int_range (dest_val, min, max);
+
+    if (dynamic) {
+      if (direction == GST_PAD_SRC)
+        max = G_MAXINT;
+      else
+        min = 1;
+    }
+
+    if (min == max) {
+      g_value_init (dest_val, G_TYPE_INT);
+      g_value_set_int (dest_val, min);
+    } else {
+      g_value_init (dest_val, GST_TYPE_INT_RANGE);
+      gst_value_set_int_range (dest_val, min, max);
+    }
   } else if (GST_VALUE_HOLDS_LIST (src_val)) {
     gint i;
 
+    g_value_init (dest_val, GST_TYPE_LIST);
+
     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_crop_transform_dimension_value (list_val, delta, &newval))
+      if (gst_video_crop_transform_dimension_value (list_val, delta, &newval,
+              direction, dynamic))
         gst_value_list_append_value (dest_val, &newval);
       g_value_unset (&newval);
     }
@@ -442,7 +482,6 @@ gst_video_crop_transform_dimension_value (const GValue * src_val,
       ret = FALSE;
     }
   } else {
-    g_value_unset (dest_val);
     ret = FALSE;
   }
 
@@ -456,21 +495,30 @@ gst_video_crop_transform_caps (GstBaseTransform * trans,
 {
   GstVideoCrop *vcrop;
   GstCaps *other_caps;
-  gint dy, dx, i;
+  gint dy, dx, i, left, right, bottom, top;
+  gboolean w_dynamic, h_dynamic;
 
   vcrop = GST_VIDEO_CROP (trans);
 
   GST_OBJECT_LOCK (vcrop);
 
   GST_LOG_OBJECT (vcrop, "l=%d,r=%d,b=%d,t=%d",
-      vcrop->crop_left, vcrop->crop_right, vcrop->crop_bottom, vcrop->crop_top);
+      vcrop->prop_left, vcrop->prop_right, vcrop->prop_bottom, vcrop->prop_top);
+
+  w_dynamic = (vcrop->prop_left == -1 || vcrop->prop_right == -1);
+  h_dynamic = (vcrop->prop_top == -1 || vcrop->prop_bottom == -1);
+
+  left = (vcrop->prop_left == -1) ? 0 : vcrop->prop_left;
+  right = (vcrop->prop_right == -1) ? 0 : vcrop->prop_right;
+  bottom = (vcrop->prop_bottom == -1) ? 0 : vcrop->prop_bottom;
+  top = (vcrop->prop_top == -1) ? 0 : vcrop->prop_top;
 
   if (direction == GST_PAD_SRC) {
-    dx = vcrop->crop_left + vcrop->crop_right;
-    dy = vcrop->crop_top + vcrop->crop_bottom;
+    dx = left + right;
+    dy = top + bottom;
   } else {
-    dx = 0 - (vcrop->crop_left + vcrop->crop_right);
-    dy = 0 - (vcrop->crop_top + vcrop->crop_bottom);
+    dx = 0 - (left + right);
+    dy = 0 - (top + bottom);
   }
   GST_OBJECT_UNLOCK (vcrop);
 
@@ -487,14 +535,16 @@ gst_video_crop_transform_caps (GstBaseTransform * trans,
     structure = gst_caps_get_structure (caps, i);
 
     v = gst_structure_get_value (structure, "width");
-    if (!gst_video_crop_transform_dimension_value (v, dx, &w_val)) {
+    if (!gst_video_crop_transform_dimension_value (v, dx, &w_val, direction,
+            w_dynamic)) {
       GST_WARNING_OBJECT (vcrop, "could not tranform width value with dx=%d"
           ", caps structure=%" GST_PTR_FORMAT, dx, structure);
       continue;
     }
 
     v = gst_structure_get_value (structure, "height");
-    if (!gst_video_crop_transform_dimension_value (v, dy, &h_val)) {
+    if (!gst_video_crop_transform_dimension_value (v, dy, &h_val, direction,
+            h_dynamic)) {
       g_value_unset (&w_val);
       GST_WARNING_OBJECT (vcrop, "could not tranform height value with dy=%d"
           ", caps structure=%" GST_PTR_FORMAT, dy, structure);
@@ -526,6 +576,41 @@ gst_video_crop_set_info (GstVideoFilter * vfilter, GstCaps * in,
     GstVideoInfo * in_info, GstCaps * out, GstVideoInfo * out_info)
 {
   GstVideoCrop *crop = GST_VIDEO_CROP (vfilter);
+  int dx, dy;
+
+  crop->crop_left = crop->prop_left;
+  crop->crop_right = crop->prop_right;
+  crop->crop_top = crop->prop_top;
+  crop->crop_bottom = crop->prop_bottom;
+
+  dx = GST_VIDEO_INFO_WIDTH (in_info) - GST_VIDEO_INFO_WIDTH (out_info);
+  dy = GST_VIDEO_INFO_HEIGHT (in_info) - GST_VIDEO_INFO_HEIGHT (out_info);
+
+  if (crop->prop_left == -1 && crop->prop_right == -1) {
+    crop->crop_left = dx / 2;
+    crop->crop_right = dx / 2 + (dx & 1);
+  } else if (crop->prop_left == -1) {
+    if (G_UNLIKELY (crop->prop_right > dx))
+      goto cropping_too_much;
+    crop->crop_left = dx - crop->prop_right;
+  } else if (crop->prop_right == -1) {
+    if (G_UNLIKELY (crop->prop_left > dx))
+      goto cropping_too_much;
+    crop->crop_right = dx - crop->prop_left;
+  }
+
+  if (crop->prop_top == -1 && crop->prop_bottom == -1) {
+    crop->crop_top = dy / 2;
+    crop->crop_bottom = dy / 2 + (dy & 1);
+  } else if (crop->prop_top == -1) {
+    if (G_UNLIKELY (crop->prop_bottom > dy))
+      goto cropping_too_much;
+    crop->crop_top = dy - crop->prop_bottom;
+  } else if (crop->prop_bottom == -1) {
+    if (G_UNLIKELY (crop->prop_top > dy))
+      goto cropping_too_much;
+    crop->crop_bottom = dy - crop->prop_top;
+  }
 
   if (G_UNLIKELY ((crop->crop_left + crop->crop_right) >=
           GST_VIDEO_INFO_WIDTH (in_info)
@@ -582,12 +667,12 @@ gst_video_crop_set_info (GstVideoFilter * vfilter, GstCaps * in,
   /* ERROR */
 cropping_too_much:
   {
-    GST_DEBUG_OBJECT (crop, "we are cropping too much");
+    GST_WARNING_OBJECT (crop, "we are cropping too much");
     return FALSE;
   }
 unknown_format:
   {
-    GST_DEBUG_OBJECT (crop, "Unsupported format");
+    GST_WARNING_OBJECT (crop, "Unsupported format");
     return FALSE;
   }
 }
@@ -607,16 +692,16 @@ gst_video_crop_set_property (GObject * object, guint prop_id,
   GST_OBJECT_LOCK (video_crop);
   switch (prop_id) {
     case ARG_LEFT:
-      video_crop->crop_left = g_value_get_int (value);
+      video_crop->prop_left = g_value_get_int (value);
       break;
     case ARG_RIGHT:
-      video_crop->crop_right = g_value_get_int (value);
+      video_crop->prop_right = g_value_get_int (value);
       break;
     case ARG_TOP:
-      video_crop->crop_top = g_value_get_int (value);
+      video_crop->prop_top = g_value_get_int (value);
       break;
     case ARG_BOTTOM:
-      video_crop->crop_bottom = g_value_get_int (value);
+      video_crop->prop_bottom = g_value_get_int (value);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -642,16 +727,16 @@ gst_video_crop_get_property (GObject * object, guint prop_id, GValue * value,
   GST_OBJECT_LOCK (video_crop);
   switch (prop_id) {
     case ARG_LEFT:
-      g_value_set_int (value, video_crop->crop_left);
+      g_value_set_int (value, video_crop->prop_left);
       break;
     case ARG_RIGHT:
-      g_value_set_int (value, video_crop->crop_right);
+      g_value_set_int (value, video_crop->prop_right);
       break;
     case ARG_TOP:
-      g_value_set_int (value, video_crop->crop_top);
+      g_value_set_int (value, video_crop->prop_top);
       break;
     case ARG_BOTTOM:
-      g_value_set_int (value, video_crop->crop_bottom);
+      g_value_set_int (value, video_crop->prop_bottom);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
index 53ec92a..346e9bc 100644 (file)
@@ -51,6 +51,11 @@ struct _GstVideoCrop
   GstVideoFilter parent;
 
   /*< private >*/
+  gint prop_left;
+  gint prop_right;
+  gint prop_top;
+  gint prop_bottom;
+
   gint crop_left;
   gint crop_right;
   gint crop_top;
index 3e574c4..d7bfdfa 100644 (file)
@@ -160,6 +160,7 @@ typedef struct
   GstElement *src;
   GstElement *filter;
   GstElement *crop;
+  GstElement *filter2;
   GstElement *sink;
   GstBuffer *last_buf;
   GstCaps *last_caps;
@@ -190,12 +191,16 @@ videocrop_test_cropping_init_context (GstVideoCropTestContext * ctx)
   fail_unless (ctx->filter != NULL, "Failed to create capsfilter element");
   ctx->crop = gst_element_factory_make ("videocrop", "crop");
   fail_unless (ctx->crop != NULL, "Failed to create videocrop element");
+  ctx->filter2 = gst_element_factory_make ("capsfilter", "filter2");
+  fail_unless (ctx->filter2 != NULL,
+      "Failed to create second capsfilter element");
   ctx->sink = gst_element_factory_make ("fakesink", "sink");
   fail_unless (ctx->sink != NULL, "Failed to create fakesink element");
 
   gst_bin_add_many (GST_BIN (ctx->pipeline), ctx->src, ctx->filter,
-      ctx->crop, ctx->sink, NULL);
-  gst_element_link_many (ctx->src, ctx->filter, ctx->crop, ctx->sink, NULL);
+      ctx->crop, ctx->filter2, ctx->sink, NULL);
+  gst_element_link_many (ctx->src, ctx->filter, ctx->crop, ctx->filter2,
+      ctx->sink, NULL);
 
   /* set pattern to 'red' - for our purposes it doesn't matter anyway */
   g_object_set (ctx->src, "pattern", 4, NULL);
@@ -225,13 +230,15 @@ typedef void (*GstVideoCropTestBufferFunc) (GstBuffer * buffer, GstCaps * caps);
 
 static void
 videocrop_test_cropping (GstVideoCropTestContext * ctx, GstCaps * in_caps,
-    gint left, gint right, gint top, gint bottom,
+    GstCaps * out_caps, gint left, gint right, gint top, gint bottom,
     GstVideoCropTestBufferFunc func)
 {
-  GST_LOG ("lrtb = %03u %03u %03u %03u, caps = %" GST_PTR_FORMAT, left, right,
-      top, bottom, in_caps);
+  GST_LOG ("lrtb = %03u %03u %03u %03u, in_caps = %" GST_PTR_FORMAT
+      ", out_caps = %" GST_PTR_FORMAT, left, right, top, bottom, in_caps,
+      out_caps);
 
   g_object_set (ctx->filter, "caps", in_caps, NULL);
+  g_object_set (ctx->filter2, "caps", out_caps, NULL);
 
   g_object_set (ctx->crop, "left", left, "right", right, "top", top,
       "bottom", bottom, NULL);
@@ -345,11 +352,12 @@ GST_START_TEST (test_crop_to_1x1)
     gst_structure_set (s, "width", G_TYPE_INT, 160,
         "height", G_TYPE_INT, 160, NULL);
 
-    videocrop_test_cropping (&ctx, caps, 159, 0, 159, 0, check_1x1_buffer);
+    videocrop_test_cropping (&ctx, caps, NULL, 159, 0, 159, 0,
+        check_1x1_buffer);
     /* commented out because they don't really add anything useful check-wise:
-       videocrop_test_cropping (&ctx, caps, 0, 159, 0, 159, check_1x1_buffer);
-       videocrop_test_cropping (&ctx, caps, 159, 0, 0, 159, check_1x1_buffer);
-       videocrop_test_cropping (&ctx, caps, 0, 159, 159, 0, check_1x1_buffer);
+       videocrop_test_cropping (&ctx, caps, NULL, 0, 159, 0, 159, check_1x1_buffer);
+       videocrop_test_cropping (&ctx, caps, NULL, 159, 0, 0, 159, check_1x1_buffer);
+       videocrop_test_cropping (&ctx, caps, NULL, 0, 159, 159, 0, check_1x1_buffer);
      */
     gst_caps_unref (caps);
   }
@@ -397,7 +405,7 @@ GST_START_TEST (test_cropping)
     GST_INFO ("testing format: %" GST_PTR_FORMAT, caps);
 
     for (i = 0; i < G_N_ELEMENTS (sizes_to_try); ++i) {
-      GstCaps *in_caps;
+      GstCaps *in_caps, *out_caps;
 
       GST_INFO (" - %d x %d", sizes_to_try[i].width, sizes_to_try[i].height);
 
@@ -405,29 +413,51 @@ GST_START_TEST (test_cropping)
           "height", G_TYPE_INT, sizes_to_try[i].height, NULL);
       in_caps = gst_caps_copy (caps);
 
-      videocrop_test_cropping (&ctx, in_caps, 0, 0, 0, 0, NULL);
-      videocrop_test_cropping (&ctx, in_caps, 1, 0, 0, 0, NULL);
-      videocrop_test_cropping (&ctx, in_caps, 0, 1, 0, 0, NULL);
-      videocrop_test_cropping (&ctx, in_caps, 0, 0, 1, 0, NULL);
-      videocrop_test_cropping (&ctx, in_caps, 0, 0, 0, 1, NULL);
-      videocrop_test_cropping (&ctx, in_caps, 63, 0, 0, 0, NULL);
-      videocrop_test_cropping (&ctx, in_caps, 0, 63, 0, 0, NULL);
-      videocrop_test_cropping (&ctx, in_caps, 0, 0, 63, 0, NULL);
-      videocrop_test_cropping (&ctx, in_caps, 0, 0, 0, 63, NULL);
-      videocrop_test_cropping (&ctx, in_caps, 63, 0, 0, 1, NULL);
-      videocrop_test_cropping (&ctx, in_caps, 0, 63, 1, 0, NULL);
-      videocrop_test_cropping (&ctx, in_caps, 0, 1, 63, 0, NULL);
-      videocrop_test_cropping (&ctx, in_caps, 1, 0, 0, 63, NULL);
-      videocrop_test_cropping (&ctx, in_caps, 0, 0, 0, 0, NULL);
-      videocrop_test_cropping (&ctx, in_caps, 32, 0, 0, 128, NULL);
-      videocrop_test_cropping (&ctx, in_caps, 0, 32, 128, 0, NULL);
-      videocrop_test_cropping (&ctx, in_caps, 0, 128, 32, 0, NULL);
-      videocrop_test_cropping (&ctx, in_caps, 128, 0, 0, 32, NULL);
-      videocrop_test_cropping (&ctx, in_caps, 1, 1, 1, 1, NULL);
-      videocrop_test_cropping (&ctx, in_caps, 63, 63, 63, 63, NULL);
-      videocrop_test_cropping (&ctx, in_caps, 64, 64, 64, 64, NULL);
+      gst_structure_set (s, "width", G_TYPE_INT, 1, "height", G_TYPE_INT, 1,
+          NULL);
+      out_caps = gst_caps_copy (caps);
+
+      videocrop_test_cropping (&ctx, in_caps, NULL, 0, 0, 0, 0, NULL);
+      videocrop_test_cropping (&ctx, in_caps, NULL, 1, 0, 0, 0, NULL);
+      videocrop_test_cropping (&ctx, in_caps, NULL, 0, 1, 0, 0, NULL);
+      videocrop_test_cropping (&ctx, in_caps, NULL, 0, 0, 1, 0, NULL);
+      videocrop_test_cropping (&ctx, in_caps, NULL, 0, 0, 0, 1, NULL);
+      videocrop_test_cropping (&ctx, in_caps, NULL, 63, 0, 0, 0, NULL);
+      videocrop_test_cropping (&ctx, in_caps, NULL, 0, 63, 0, 0, NULL);
+      videocrop_test_cropping (&ctx, in_caps, NULL, 0, 0, 63, 0, NULL);
+      videocrop_test_cropping (&ctx, in_caps, NULL, 0, 0, 0, 63, NULL);
+      videocrop_test_cropping (&ctx, in_caps, NULL, 63, 0, 0, 1, NULL);
+      videocrop_test_cropping (&ctx, in_caps, NULL, 0, 63, 1, 0, NULL);
+      videocrop_test_cropping (&ctx, in_caps, NULL, 0, 1, 63, 0, NULL);
+      videocrop_test_cropping (&ctx, in_caps, NULL, 1, 0, 0, 63, NULL);
+      videocrop_test_cropping (&ctx, in_caps, NULL, 0, 0, 0, 0, NULL);
+      videocrop_test_cropping (&ctx, in_caps, NULL, 32, 0, 0, 128, NULL);
+      videocrop_test_cropping (&ctx, in_caps, NULL, 0, 32, 128, 0, NULL);
+      videocrop_test_cropping (&ctx, in_caps, NULL, 0, 128, 32, 0, NULL);
+      videocrop_test_cropping (&ctx, in_caps, NULL, 128, 0, 0, 32, NULL);
+      videocrop_test_cropping (&ctx, in_caps, NULL, 1, 1, 1, 1, NULL);
+      videocrop_test_cropping (&ctx, in_caps, NULL, 63, 63, 63, 63, NULL);
+      videocrop_test_cropping (&ctx, in_caps, NULL, 64, 64, 64, 64, NULL);
+
+      /* Dynamic cropping */
+      videocrop_test_cropping (&ctx, in_caps, out_caps, -1, -1, -1, -1, NULL);
+      videocrop_test_cropping (&ctx, in_caps, out_caps, 0, -1, -1, -1, NULL);
+      videocrop_test_cropping (&ctx, in_caps, out_caps, -1, 0, -1, -1, NULL);
+      videocrop_test_cropping (&ctx, in_caps, out_caps, -1, -1, 0, -1, NULL);
+      videocrop_test_cropping (&ctx, in_caps, out_caps, -1, -1, -1, 0, NULL);
+      videocrop_test_cropping (&ctx, in_caps, out_caps, 10, -1, 10, -1, NULL);
+      videocrop_test_cropping (&ctx, in_caps, out_caps, -1, 10, -1, 10, NULL);
+      videocrop_test_cropping (&ctx, in_caps, out_caps,
+          sizes_to_try[i].width - 1, -1, -1, -1, NULL);
+      videocrop_test_cropping (&ctx, in_caps, out_caps, -1,
+          sizes_to_try[i].width - 1, -1, -1, NULL);
+      videocrop_test_cropping (&ctx, in_caps, out_caps, -1, -1,
+          sizes_to_try[i].height - 1, -1, NULL);
+      videocrop_test_cropping (&ctx, in_caps, out_caps, -1, -1, -1,
+          sizes_to_try[i].height - 1, NULL);
 
       gst_caps_unref (in_caps);
+      gst_caps_unref (out_caps);
     }
 
     gst_caps_unref (caps);