videocrop: Add support for automatic cropping

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
This commit is contained in:
Nicolas Dufresne 2012-11-06 15:03:55 +01:00 committed by Olivier Crête
parent 8c44361bca
commit 1ad8ebac44
3 changed files with 185 additions and 65 deletions

View 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);

View 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;

View 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);