mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 20:21:24 +00:00
videobox: Add support for autocropping according to the caps
Fixes bug #582238.
This commit is contained in:
parent
041fa82179
commit
041ddd6f8f
1 changed files with 147 additions and 40 deletions
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue