videobox: Add support for autocropping according to the caps

Fixes bug #582238.
This commit is contained in:
Stephen Jungels 2009-08-14 13:15:57 +02:00 committed by Sebastian Dröge
parent 041fa82179
commit 041ddd6f8f

View file

@ -35,6 +35,7 @@
* The videobox plugin has many uses such as doing a mosaic of pictures, * The videobox plugin has many uses such as doing a mosaic of pictures,
* letterboxing video, cutting out pieces of video, picture in picture, etc.. * letterboxing video, cutting out pieces of video, picture in picture, etc..
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" #include "config.h"
#endif #endif
@ -90,6 +91,8 @@ struct _GstVideoBox
gdouble border_alpha; gdouble border_alpha;
GstVideoBoxFill fill_type; GstVideoBoxFill fill_type;
gboolean autocrop;
}; };
struct _GstVideoBoxClass struct _GstVideoBoxClass
@ -122,10 +125,30 @@ enum
PROP_BOTTOM, PROP_BOTTOM,
PROP_FILL_TYPE, PROP_FILL_TYPE,
PROP_ALPHA, PROP_ALPHA,
PROP_BORDER_ALPHA PROP_BORDER_ALPHA,
PROP_AUTOCROP
/* FILL ME */ /* 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 = static GstStaticPadTemplate gst_video_box_src_template =
GST_STATIC_PAD_TEMPLATE ("src", GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_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, static void gst_video_box_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec); GValue * value, GParamSpec * pspec);
static gboolean video_box_recalc_transform (GstVideoBox * video_box);
static GstCaps *gst_video_box_transform_caps (GstBaseTransform * trans, static GstCaps *gst_video_box_transform_caps (GstBaseTransform * trans,
GstPadDirection direction, GstCaps * from); GstPadDirection direction, GstCaps * from);
static gboolean gst_video_box_set_caps (GstBaseTransform * trans, 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", g_param_spec_double ("border_alpha", "Border Alpha",
"Alpha value of the border", 0.0, 1.0, DEFAULT_BORDER_ALPHA, "Alpha value of the border", 0.0, 1.0, DEFAULT_BORDER_ALPHA,
G_PARAM_READWRITE)); 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 = GST_DEBUG_FUNCPTR (gst_video_box_transform);
trans_class->transform_caps = 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->fill_type = DEFAULT_FILL_TYPE;
video_box->alpha = DEFAULT_ALPHA; video_box->alpha = DEFAULT_ALPHA;
video_box->border_alpha = DEFAULT_BORDER_ALPHA; video_box->border_alpha = DEFAULT_BORDER_ALPHA;
video_box->autocrop = FALSE;
} }
static void static void
@ -317,12 +345,65 @@ gst_video_box_set_property (GObject * object, guint prop_id,
case PROP_BORDER_ALPHA: case PROP_BORDER_ALPHA:
video_box->border_alpha = g_value_get_double (value); video_box->border_alpha = g_value_get_double (value);
break; break;
case PROP_AUTOCROP:
video_box->autocrop = g_value_get_boolean (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
} }
GST_DEBUG_OBJECT (video_box, "Calling reconfigure"); video_box_recalc_transform (video_box);
gst_base_transform_reconfigure (GST_BASE_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)); 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: case PROP_BORDER_ALPHA:
g_value_set_double (value, video_box->border_alpha); g_value_set_double (value, video_box->border_alpha);
break; break;
case PROP_AUTOCROP:
g_value_set_boolean (value, video_box->autocrop);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -379,36 +463,45 @@ gst_video_box_transform_caps (GstBaseTransform * trans,
/* get rid of format */ /* get rid of format */
gst_structure_remove_field (structure, "format"); gst_structure_remove_field (structure, "format");
/* calculate width and height */ /* otherwise caps nego will fail: */
if (gst_structure_get_int (structure, "width", &width)) { if (video_box->autocrop) {
if (direction == GST_PAD_SINK) { gst_structure_remove_field (structure, "width");
width -= video_box->box_left; gst_structure_remove_field (structure, "height");
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 (gst_structure_get_int (structure, "height", &height)) { if (!video_box->autocrop) {
if (direction == GST_PAD_SINK) {
height -= video_box->box_top; /* calculate width and height */
height -= video_box->box_bottom; if (gst_structure_get_int (structure, "width", &width)) {
} else { if (direction == GST_PAD_SINK) {
height += video_box->box_top; width -= video_box->box_left;
height += video_box->box_bottom; 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) if (gst_structure_get_int (structure, "height", &height)) {
height = 1; 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); if (height <= 0)
gst_structure_set (structure, "height", G_TYPE_INT, height, NULL); 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 */ /* filter against set allowed caps on the pad */
@ -424,6 +517,26 @@ gst_video_box_transform_caps (GstBaseTransform * trans,
return ret; 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 static gboolean
gst_video_box_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out) 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, GST_DEBUG_OBJECT (trans, "Output w: %d h: %d", video_box->out_width,
video_box->out_height); video_box->out_height);
/* if we have the same format in and out and we don't need to perform and if (video_box->autocrop)
* cropping at all, we can just operate in passthorugh mode */ gst_video_box_autocrop (video_box);
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"); /* recalc the transformation strategy */
gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (video_box), TRUE); ret = video_box_recalc_transform (video_box);
} else {
GST_LOG_OBJECT (video_box, "we are not using passthrough");
gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (video_box), FALSE);
}
return ret; return ret;
@ -947,7 +1053,7 @@ gst_video_box_i420_ayuv (GstVideoBox * video_box, guint8 * src, guint8 * dest)
{ {
guint8 *srcY, *srcU, *srcV; guint8 *srcY, *srcU, *srcV;
gint crop_width, crop_width2, crop_height; gint crop_width, crop_width2, crop_height;
gint out_width; gint out_width, out_height;
gint src_stridey, src_strideu, src_stridev; gint src_stridey, src_strideu, src_stridev;
gint br, bl, bt, bb; gint br, bl, bt, bb;
gint colorY, colorU, colorV; 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; bb = video_box->border_bottom;
out_width = video_box->out_width; out_width = video_box->out_width;
out_height = video_box->out_height;
src_stridey = GST_VIDEO_I420_Y_ROWSTRIDE (video_box->in_width); src_stridey = GST_VIDEO_I420_Y_ROWSTRIDE (video_box->in_width);
src_strideu = GST_VIDEO_I420_U_ROWSTRIDE (video_box->in_width); src_strideu = GST_VIDEO_I420_U_ROWSTRIDE (video_box->in_width);