From a8a7ee6d83a45603dd9c78d2e90f7b8839fcdeb9 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Fri, 20 Jan 2023 08:20:12 -0300 Subject: [PATCH] glvideomixer: Add xalign and yalign properties With the addition of the 'keep-aspect-ratio' sizing policy, content that doesn't fit the target size is downscaled according to its own aspect ratio to fit that target size, and centered. Centering might not always be the desired behaviour, however; consumers of this API might want to align the resulting picture to the left or to the right. To account for any of these cases, add two new properties to the glvideomixer pad: xalign, and yalign. They operate on normalized coordinates (0.0 for start, 1.0 for end), and default to 0.5 which centers content. Part-of: --- .../docs/plugins/gst_plugins_cache.json | 58 +++++- .../gst-plugins-base/ext/gl/gstglvideomixer.c | 171 +++++++++++++++++- 2 files changed, 224 insertions(+), 5 deletions(-) diff --git a/subprojects/gst-plugins-base/docs/plugins/gst_plugins_cache.json b/subprojects/gst-plugins-base/docs/plugins/gst_plugins_cache.json index 70d70f09d3..fd6541e019 100644 --- a/subprojects/gst-plugins-base/docs/plugins/gst_plugins_cache.json +++ b/subprojects/gst-plugins-base/docs/plugins/gst_plugins_cache.json @@ -7657,6 +7657,20 @@ "type": "gint", "writable": true }, + "xalign": { + "blurb": "X alignment of the picture", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": true, + "default": "0", + "max": "1", + "min": "0", + "mutable": "null", + "readable": true, + "type": "gdouble", + "writable": true + }, "xpos": { "blurb": "X Position of the picture", "conditionally-available": false, @@ -7671,6 +7685,20 @@ "type": "gint", "writable": true }, + "yalign": { + "blurb": "Y alignment of the picture", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": true, + "default": "0", + "max": "1", + "min": "0", + "mutable": "null", + "readable": true, + "type": "gdouble", + "writable": true + }, "ypos": { "blurb": "Y Position of the picture", "conditionally-available": false, @@ -7953,6 +7981,20 @@ "type": "gint", "writable": true }, + "xalign": { + "blurb": "X alignment of the picture", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": true, + "default": "0.5", + "max": "1", + "min": "0", + "mutable": "null", + "readable": true, + "type": "gdouble", + "writable": true + }, "xpos": { "blurb": "X Position of the picture", "conditionally-available": false, @@ -7967,6 +8009,20 @@ "type": "gint", "writable": true }, + "yalign": { + "blurb": "Y alignment of the picture", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": true, + "default": "0.5", + "max": "1", + "min": "0", + "mutable": "null", + "readable": true, + "type": "gdouble", + "writable": true + }, "ypos": { "blurb": "Y Position of the picture", "conditionally-available": false, @@ -7993,7 +8049,7 @@ "value": "0" }, { - "desc": "Keep Aspect Ratio: Image is scaled to fit destination rectangle specified by GstGLVideoMixerPad:{xpos, ypos, width, height} with preserved aspect ratio. Resulting image will be centered in the destination rectangle with padding if necessary", + "desc": "Keep Aspect Ratio: Image is scaled to fit destination rectangle specified by GstGLVideoMixerPad:{xpos, ypos, width, height} with preserved aspect ratio. The empty space of the resulting image will be distributed in the destination rectangle according to the GstGLVideoMixerPad:{xalign, yalign} values", "name": "keep-aspect-ratio", "value": "1" } diff --git a/subprojects/gst-plugins-base/ext/gl/gstglvideomixer.c b/subprojects/gst-plugins-base/ext/gl/gstglvideomixer.c index e43268e06c..e3a589d368 100644 --- a/subprojects/gst-plugins-base/ext/gl/gstglvideomixer.c +++ b/subprojects/gst-plugins-base/ext/gl/gstglvideomixer.c @@ -167,8 +167,9 @@ gst_gl_video_mixer_sizing_policy_get_type (void) {GST_GL_VIDEO_MIXER_SIZING_POLICY_KEEP_ASPECT_RATIO, "Keep Aspect Ratio: Image is scaled to fit destination rectangle " "specified by GstGLVideoMixerPad:{xpos, ypos, width, height} " - "with preserved aspect ratio. Resulting image will be centered in " - "the destination rectangle with padding if necessary", + "with preserved aspect ratio. The empty space of the resulting image " + "will be distributed in the destination rectangle according to the " + "GstGLVideoMixerPad:{xalign, yalign} values", "keep-aspect-ratio"}, {0, NULL, NULL}, }; @@ -195,6 +196,8 @@ gst_gl_video_mixer_sizing_policy_get_type (void) #define DEFAULT_PAD_BLEND_FUNCTION_DST_ALPHA GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE_MINUS_SRC_ALPHA #define DEFAULT_PAD_CROP 0 #define DEFAULT_PAD_SIZING_POLICY GST_GL_VIDEO_MIXER_SIZING_POLICY_NONE +#define DEFAULT_PAD_XALIGN 0.5 +#define DEFAULT_PAD_YALIGN 0.5 enum { @@ -221,6 +224,8 @@ enum PROP_INPUT_CROP_TOP, PROP_INPUT_CROP_BOTTOM, PROP_INPUT_SIZING_POLICY, + PROP_INPUT_XALIGN, + PROP_INPUT_YALIGN, }; static void gst_gl_video_mixer_input_get_property (GObject * object, @@ -417,6 +422,54 @@ gst_gl_video_mixer_input_class_init (GstGLVideoMixerInputClass * klass) "Sizing policy to use for image scaling", GST_TYPE_GL_VIDEO_MIXER_SIZING_POLICY, DEFAULT_PAD_SIZING_POLICY, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); + + /** + * GstGLVideoMixerInput:xalign: + * + * Defines the alignment of the input within the available horizontal space, + * relative to #GstGLVideoMixerPad:width and #GstGLVideoMixerPad:height. + * Values range from 0.0 (left) to 1.0 (right). + * + * The image is aligned in the available space as if the pivot point is + * matching the alignment. For example, setting the `xalign` property to 0.0 + * will align the left edge of the image with the left edge of the bounding + * box; 0.5 aligns the horizontal center of the image with the horizontal + * center of the bounding box; 1.0 aligns the right edge of the image with the + * right edge of the bounding box; and so it goes. + * + * This property is only effective when #GstGLVideoMixerInput:sizing-policy + * is set to 'keep-aspect-ratio'. + * + * Since: 1.24 + */ + g_object_class_install_property (gobject_class, PROP_INPUT_XALIGN, + g_param_spec_double ("xalign", "X alignment", + "X alignment of the picture", 0.0, 1.0, DEFAULT_PAD_XALIGN, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); + + /** + * GstGLVideoMixerInput:yalign: + * + * Defines the alignment of the input within the available vertical space, + * relative to #GstGLVideoMixerPad:width and #GstGLVideoMixerPad:height. + * Values range from 0.0 (top) to 1.0 (bottom). + * + * The image is aligned in the available space as if the pivot point is + * matching the alignment. For example, setting the `xalign` property to 0.0 + * will align the left edge of the image with the left edge of the bounding + * box; 0.5 aligns the horizontal center of the image with the horizontal + * center of the bounding box; 1.0 aligns the right edge of the image with the + * right edge of the bounding box; and so it goes. + * + * This property is only effective when #GstGLVideoMixerInput:sizing-policy + * is set to 'keep-aspect-ratio'. + * + * Since: 1.24 + */ + g_object_class_install_property (gobject_class, PROP_INPUT_YALIGN, + g_param_spec_double ("yalign", "Y alignment", + "Y alignment of the picture", 0.0, 1.0, DEFAULT_PAD_YALIGN, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); } static void @@ -468,6 +521,8 @@ _create_video_mixer_input (GstGLMixerBin * self, GstPad * mixer_pad) ADD_BINDING (mixer_pad, input, "blend-constant-color-blue"); ADD_BINDING (mixer_pad, input, "blend-constant-color-alpha"); ADD_BINDING (mixer_pad, input, "sizing-policy"); + ADD_BINDING (mixer_pad, input, "xalign"); + ADD_BINDING (mixer_pad, input, "yalign"); #undef ADD_BINDING input->mixer_pad = mixer_pad; @@ -665,6 +720,7 @@ struct _GstGLVideoMixerPad /* properties */ gint xpos, ypos; gint width, height; + gdouble xalign, yalign; gdouble alpha; GstGLVideoMixerSizingPolicy sizing_policy; @@ -724,6 +780,8 @@ enum PROP_PAD_CROP_TOP, PROP_PAD_CROP_BOTTOM, PROP_PAD_SIZING_POLICY, + PROP_PAD_XALIGN, + PROP_PAD_YALIGN, }; static void @@ -737,6 +795,8 @@ gst_gl_video_mixer_pad_init (GstGLVideoMixerPad * pad) pad->blend_function_dst_rgb = DEFAULT_PAD_BLEND_FUNCTION_DST_RGB; pad->blend_function_dst_alpha = DEFAULT_PAD_BLEND_FUNCTION_DST_ALPHA; pad->sizing_policy = DEFAULT_PAD_SIZING_POLICY; + pad->xalign = DEFAULT_PAD_XALIGN; + pad->yalign = DEFAULT_PAD_YALIGN; memset (pad->m_matrix, 0, sizeof (gfloat) * 4 * 4); pad->m_matrix[0] = 1.0; pad->m_matrix[5] = 1.0; @@ -901,6 +961,54 @@ gst_gl_video_mixer_pad_class_init (GstGLVideoMixerPadClass * klass) "Sizing policy to use for image scaling", GST_TYPE_GL_VIDEO_MIXER_SIZING_POLICY, DEFAULT_PAD_SIZING_POLICY, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); + + /** + * GstGLVideoMixerPad:xalign: + * + * Defines the alignment of the input within the available horizontal space, + * relative to #GstGLVideoMixerPad:width and #GstGLVideoMixerPad:height. + * Values range from 0.0 (left) to 1.0 (right). + * + * The image is aligned in the available space as if the pivot point is + * matching the alignment. For example, setting the `xalign` property to 0.0 + * will align the left edge of the image with the left edge of the bounding + * box; 0.5 aligns the horizontal center of the image with the horizontal + * center of the bounding box; 1.0 aligns the right edge of the image with the + * right edge of the bounding box; and so it goes. + * + * This property is only effective when #GstGLVideoMixerInput:sizing-policy + * is set to 'keep-aspect-ratio'. + * + * Since: 1.24 + */ + g_object_class_install_property (gobject_class, PROP_PAD_XALIGN, + g_param_spec_double ("xalign", "X alignment", + "X alignment of the picture", 0.0, 1.0, DEFAULT_PAD_XALIGN, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); + + /** + * GstGLVideoMixerPad:yalign: + * + * Defines the alignment of the input within the available vertical space, + * relative to #GstGLVideoMixerPad:width and #GstGLVideoMixerPad:height. + * Values range from 0.0 (top) to 1.0 (bottom). + * + * The image is aligned in the available space as if the pivot point is + * matching the alignment. For example, setting the `yalign` property to 0.0 + * will align the top edge of the image with the top edge of the bounding box; + * 0.5 aligns the vertical center of the image with the vertical center of the + * bounding box; 1.0 aligns the bottom edge of the image with the bottom edge + * of the bounding box; and so it goes. + * + * This property is only effective when #GstGLVideoMixerInput:sizing-policy + * is set to 'keep-aspect-ratio'. + * + * Since: 1.24 + */ + g_object_class_install_property (gobject_class, PROP_PAD_YALIGN, + g_param_spec_double ("yalign", "Y alignment", + "Y alignment of the picture", 0.0, 1.0, DEFAULT_PAD_YALIGN, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); } static void @@ -970,6 +1078,12 @@ gst_gl_video_mixer_pad_get_property (GObject * object, guint prop_id, case PROP_PAD_SIZING_POLICY: g_value_set_enum (value, pad->sizing_policy); break; + case PROP_PAD_XALIGN: + g_value_set_double (value, pad->xalign); + break; + case PROP_PAD_YALIGN: + g_value_set_double (value, pad->yalign); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1072,6 +1186,18 @@ gst_gl_video_mixer_pad_set_property (GObject * object, guint prop_id, pad->sizing_policy = val; break; } + case PROP_PAD_XALIGN:{ + gdouble val = g_value_get_double (value); + pad->geometry_change |= !G_APPROX_VALUE (val, pad->xalign, DBL_EPSILON); + pad->xalign = val; + break; + } + case PROP_PAD_YALIGN:{ + gdouble val = g_value_get_double (value); + pad->geometry_change |= !G_APPROX_VALUE (val, pad->yalign, DBL_EPSILON); + pad->yalign = val; + break; + } default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1242,6 +1368,41 @@ gst_gl_video_mixer_propose_allocation (GstAggregator * agg, return TRUE; } +static void +align_rect (const GstVideoRectangle * src, + const GstVideoRectangle * dst, GstVideoRectangle * result, gdouble xalign, + gdouble yalign) +{ + gdouble src_ratio, dst_ratio; + + g_return_if_fail (src->h != 0); + g_return_if_fail (dst->h != 0); + + src_ratio = (gdouble) src->w / src->h; + dst_ratio = (gdouble) dst->w / dst->h; + + if (src_ratio > dst_ratio) { + result->w = dst->w; + result->h = dst->w / src_ratio; + result->x = dst->x; + result->y = dst->y + (dst->h - result->h) * yalign; + } else if (src_ratio < dst_ratio) { + result->w = dst->h * src_ratio; + result->h = dst->h; + result->x = dst->x + (dst->w - result->w) * xalign; + result->y = dst->y; + } else { + result->x = dst->x; + result->y = dst->y; + result->w = dst->w; + result->h = dst->h; + } + + GST_DEBUG ("source is %dx%d dest is %dx%d, result is %dx%d with x,y %dx%d", + src->w, src->h, dst->w, dst->h, + result->w, result->h, result->x, result->y); +} + static void _mixer_pad_get_output_size (GstGLVideoMixer * mix, GstGLVideoMixerPad * mix_pad, gint out_par_n, gint out_par_d, gint * width, @@ -1332,7 +1493,8 @@ _mixer_pad_get_output_size (GstGLVideoMixer * mix, if (from_dar_n != -1 && from_dar_d != -1 && gst_util_fraction_multiply (from_dar_n, from_dar_d, out_par_d, out_par_n, &num, &den)) { - GstVideoRectangle src_rect, dst_rect, rst_rect; + GstVideoRectangle src_rect, dst_rect; + GstVideoRectangle rst_rect = { 0, 0, 0, 0 }; src_rect.h = gst_util_uint64_scale_int (pad_width, den, num); if (src_rect.h == 0) { @@ -1349,7 +1511,8 @@ _mixer_pad_get_output_size (GstGLVideoMixer * mix, dst_rect.h = pad_height; /* Scale rect to be centered in destination rect */ - gst_video_center_rect (&src_rect, &dst_rect, &rst_rect, TRUE); + align_rect (&src_rect, &dst_rect, &rst_rect, mix_pad->xalign, + mix_pad->yalign); GST_LOG_OBJECT (mix_pad, "Re-calculated size %dx%d -> %dx%d (x-offset %d, y-offset %d)",