From 14d6773aba3083fb7ef2930bd0128776a7edcc0d Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Tue, 27 Feb 2024 14:00:04 -0300 Subject: [PATCH] ges: framepositioner: Expose positioning properties as doubles Making it possible to properly handle compositors that have those properties as doubles and handle antialiasing. Internally we were handling those values as doubles in framepositioner, so expose new properties so user can set values as doubles also. This changes the GESFramePositionMeta API but we are still on time for 1.24 Part-of: --- girs/GES-1.0.gir | 8 +- .../ges/ges-frame-composition-meta.h | 8 +- .../ges/ges-smart-video-mixer.c | 52 ++++- .../ges/ges-track-element.c | 11 +- .../gst-editing-services/ges/ges-utils.c | 41 ++++ .../ges/ges-video-source.c | 4 +- .../gst-editing-services/ges/gesvideoscale.c | 4 +- .../ges/gstframepositioner.c | 200 ++++++++++++++++-- .../ges/gstframepositioner.h | 1 + 9 files changed, 287 insertions(+), 42 deletions(-) diff --git a/girs/GES-1.0.gir b/girs/GES-1.0.gir index 0dfba058db..f1c5dcabc4 100644 --- a/girs/GES-1.0.gir +++ b/girs/GES-1.0.gir @@ -4487,21 +4487,21 @@ composition. The desired x position. - + The desired y position. - + The desired height of the video. -1 means that no scaling should be applied. - + The desired width of the video. -1 means that no scaling should beapplied applied. - + The desired z order. diff --git a/subprojects/gst-editing-services/ges/ges-frame-composition-meta.h b/subprojects/gst-editing-services/ges/ges-frame-composition-meta.h index 82f7b55c89..581d36a097 100644 --- a/subprojects/gst-editing-services/ges/ges-frame-composition-meta.h +++ b/subprojects/gst-editing-services/ges/ges-frame-composition-meta.h @@ -61,10 +61,10 @@ struct _GESFrameCompositionMeta { GstMeta meta; gdouble alpha; - gint posx; - gint posy; - gint height; - gint width; + gdouble posx; + gdouble posy; + gdouble height; + gdouble width; guint zorder; gint operator; }; diff --git a/subprojects/gst-editing-services/ges/ges-smart-video-mixer.c b/subprojects/gst-editing-services/ges/ges-smart-video-mixer.c index 3a8b9d608c..70acced461 100644 --- a/subprojects/gst-editing-services/ges/ges-smart-video-mixer.c +++ b/subprojects/gst-editing-services/ges/ges-smart-video-mixer.c @@ -20,6 +20,7 @@ #include "config.h" #endif +#include #include "gstframepositioner.h" #include "ges-frame-composition-meta.h" #include "ges-types.h" @@ -38,6 +39,11 @@ struct _GESSmartMixerPad gdouble alpha; GstSegment segment; + + GParamSpec *width_pspec; + GParamSpec *height_pspec; + GParamSpec *xpos_pspec; + GParamSpec *ypos_pspec; }; struct _GESSmartMixerPadClass @@ -53,6 +59,18 @@ enum G_DEFINE_TYPE (GESSmartMixerPad, ges_smart_mixer_pad, GST_TYPE_GHOST_PAD); +static void +ges_smart_mixer_notify_wrapped_pad (GESSmartMixerPad * self, + GstPad * real_mixer_pad) +{ + GObjectClass *klass = G_OBJECT_GET_CLASS (real_mixer_pad); + + self->width_pspec = g_object_class_find_property (klass, "width"); + self->height_pspec = g_object_class_find_property (klass, "height"); + self->xpos_pspec = g_object_class_find_property (klass, "xpos"); + self->ypos_pspec = g_object_class_find_property (klass, "ypos"); +} + static void ges_smart_mixer_pad_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) @@ -240,13 +258,35 @@ set_pad_properties_from_composition_meta (GstPad * mixer_pad, g_object_set (mixer_pad, "alpha", meta->alpha * transalpha, NULL); } - g_object_set (mixer_pad, "xpos", meta->posx, "ypos", meta->posy, NULL); + if (G_PARAM_SPEC_VALUE_TYPE (ghost->xpos_pspec) == G_TYPE_INT) { + g_object_set (mixer_pad, "xpos", (gint) round (meta->posx), "ypos", + (gint) round (meta->posy), NULL); + } else if (G_PARAM_SPEC_VALUE_TYPE (ghost->xpos_pspec) == G_TYPE_FLOAT) { + g_object_set (mixer_pad, "xpos", (gfloat) meta->posx, "ypos", + (gfloat) meta->posy, NULL); + } else { + g_object_set (mixer_pad, "xpos", meta->posx, "ypos", meta->posy, NULL); + } - if (meta->width >= 0) - g_object_set (mixer_pad, "width", meta->width, NULL); + if (meta->width >= 0) { + if (G_PARAM_SPEC_VALUE_TYPE (ghost->width_pspec) == G_TYPE_INT) { + g_object_set (mixer_pad, "width", (gint) round (meta->width), NULL); + } else if (G_PARAM_SPEC_VALUE_TYPE (ghost->width_pspec) == G_TYPE_FLOAT) { + g_object_set (mixer_pad, "width", (gfloat) meta->width, NULL); + } else { + g_object_set (mixer_pad, "width", meta->width, NULL); + } + } - if (meta->height >= 0) - g_object_set (mixer_pad, "height", meta->height, NULL); + if (meta->height >= 0) { + if (G_PARAM_SPEC_VALUE_TYPE (ghost->height_pspec) == G_TYPE_INT) { + g_object_set (mixer_pad, "height", (gint) round (meta->height), NULL); + } else if (G_PARAM_SPEC_VALUE_TYPE (ghost->height_pspec) == G_TYPE_FLOAT) { + g_object_set (mixer_pad, "height", (gfloat) meta->height, NULL); + } else { + g_object_set (mixer_pad, "height", meta->height, NULL); + } + } if (self->ABI.abi.has_operator) g_object_set (mixer_pad, "operator", meta->operator, NULL); @@ -294,6 +334,8 @@ _request_new_pad (GstElement * element, GstPadTemplate * templ, "direction", GST_PAD_DIRECTION (infos->mixer_pad), NULL); infos->ghostpad = ghost; gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (ghost), infos->mixer_pad); + ges_smart_mixer_notify_wrapped_pad (GES_SMART_MIXER_PAD (ghost), + infos->real_mixer_pad); gst_pad_set_active (ghost, TRUE); if (!gst_element_add_pad (GST_ELEMENT (self), ghost)) goto could_not_add; diff --git a/subprojects/gst-editing-services/ges/ges-track-element.c b/subprojects/gst-editing-services/ges/ges-track-element.c index bbcc14caa4..60c0691974 100644 --- a/subprojects/gst-editing-services/ges/ges-track-element.c +++ b/subprojects/gst-editing-services/ges/ges-track-element.c @@ -56,6 +56,7 @@ #endif #include "ges-internal.h" +#include "gstframepositioner.h" #include "ges-extractable.h" #include "ges-track-element.h" #include "ges-clip.h" @@ -1913,10 +1914,12 @@ ges_track_element_set_control_source (GESTrackElement * object, goto done; } - /* First remove existing binding */ - if (ges_track_element_remove_control_binding (object, property_name)) - GST_LOG_OBJECT (object, "Removed old binding for property %s", - property_name); + if (GST_IS_FRAME_POSITIONNER (element)) { + if (!gst_frame_positioner_check_can_add_binding (GST_FRAME_POSITIONNER + (element), property_name)) { + goto done; + } + } if (direct_absolute) binding = gst_direct_control_binding_new_absolute (GST_OBJECT (element), diff --git a/subprojects/gst-editing-services/ges/ges-utils.c b/subprojects/gst-editing-services/ges/ges-utils.c index efc0c000e7..8e6218d165 100644 --- a/subprojects/gst-editing-services/ges/ges-utils.c +++ b/subprojects/gst-editing-services/ges/ges-utils.c @@ -192,6 +192,47 @@ find_compositor (GstPluginFeature * feature, gpointer udata) (loaded_feature)), GST_TYPE_AGGREGATOR); } + if (res) { + const gchar *needed_props[] = { "width", "height", "xpos", "ypos" }; + GObjectClass *klass = + g_type_class_ref (gst_element_factory_get_element_type + (GST_ELEMENT_FACTORY (loaded_feature))); + GstPadTemplate *templ = + gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass), + "sink_%u"); + + g_type_class_unref (klass); + if (!templ) { + GST_INFO_OBJECT (loaded_feature, "No sink template found, ignoring"); + res = FALSE; + goto done; + } + + GType pad_type; + g_object_get (templ, "gtype", &pad_type, NULL); + klass = g_type_class_ref (pad_type); + for (gint i = 0; i < G_N_ELEMENTS (needed_props); i++) { + GParamSpec *pspec; + + if (!(pspec = g_object_class_find_property (klass, needed_props[i]))) { + GST_INFO_OBJECT (loaded_feature, "No property %s found, ignoring", + needed_props[i]); + res = FALSE; + break; + } + + if (pspec->value_type != G_TYPE_INT && pspec->value_type != G_TYPE_FLOAT + && pspec->value_type != G_TYPE_DOUBLE) { + GST_INFO_OBJECT (loaded_feature, + "Property %s is not of type int or float, or double, ignoring", + needed_props[i]); + res = FALSE; + break; + } + } + g_type_class_unref (klass); + } + done: gst_clear_object (&elem); gst_object_unref (loaded_feature); diff --git a/subprojects/gst-editing-services/ges/ges-video-source.c b/subprojects/gst-editing-services/ges/ges-video-source.c index 9bae5f123b..aeb92df688 100644 --- a/subprojects/gst-editing-services/ges/ges-video-source.c +++ b/subprojects/gst-editing-services/ges/ges-video-source.c @@ -115,7 +115,9 @@ ges_video_source_create_filters (GESVideoSource * self, GPtrArray * elements, GESTrackElement *trksrc = GES_TRACK_ELEMENT (self); GstElement *positioner, *videoflip, *capsfilter, *videorate; const gchar *positioner_props[] - = { "alpha", "posx", "posy", "width", "height", "operator", NULL }; + = { "alpha", "posx", "fposx", "posy", "fposy", "width", "fwidth", + "height", "fheight", "operator", NULL + }; const gchar *videoflip_props[] = { "video-direction", NULL }; gchar *ename = NULL; diff --git a/subprojects/gst-editing-services/ges/gesvideoscale.c b/subprojects/gst-editing-services/ges/gesvideoscale.c index 3ae0810c30..29cba3d94d 100644 --- a/subprojects/gst-editing-services/ges/gesvideoscale.c +++ b/subprojects/gst-editing-services/ges/gesvideoscale.c @@ -22,6 +22,7 @@ #endif #include +#include #include "ges-frame-composition-meta.h" @@ -96,7 +97,8 @@ chain (GstPad * pad, GESVideoScale * self, GstBuffer * buffer) if (meta->height != self->height || meta->width != self->width) { GST_OBJECT_UNLOCK (self); - set_dimension (self, meta->width, meta->height); + set_dimension (self, (gint) round (meta->width), + (gint) round (meta->height)); } else { GST_OBJECT_UNLOCK (self); } diff --git a/subprojects/gst-editing-services/ges/gstframepositioner.c b/subprojects/gst-editing-services/ges/gstframepositioner.c index 16356204f2..e7d416922c 100644 --- a/subprojects/gst-editing-services/ges/gstframepositioner.c +++ b/subprojects/gst-editing-services/ges/gstframepositioner.c @@ -48,11 +48,20 @@ enum { PROP_0, PROP_ALPHA, + PROP_POSX, + PROP_FPOSX, + PROP_POSY, - PROP_ZORDER, + PROP_FPOSY, + PROP_WIDTH, + PROP_FWIDTH, + PROP_HEIGHT, + PROP_FHEIGHT, + + PROP_ZORDER, PROP_OPERATOR, PROP_LAST, }; @@ -143,9 +152,16 @@ is_user_positionned (GstFramePositioner * self) gint i; GParamSpec *positioning_props[] = { properties[PROP_WIDTH], + properties[PROP_FWIDTH], + properties[PROP_HEIGHT], + properties[PROP_FHEIGHT], + properties[PROP_POSX], + properties[PROP_FPOSX], + properties[PROP_POSY], + properties[PROP_FPOSY], }; if (self->user_positioned) @@ -219,13 +235,22 @@ reposition_properties (GstFramePositioner * pos, gint old_track_width, gint old_track_height) { gint i; + /* *INDENT-OFF* */ RepositionPropertyData props_data[] = { + {&pos->width, old_track_width, pos->track_width, properties[PROP_FWIDTH]}, {&pos->width, old_track_width, pos->track_width, properties[PROP_WIDTH]}, - {&pos->height, old_track_height, pos->track_height, - properties[PROP_HEIGHT]}, + + {&pos->height, old_track_height, pos->track_height, properties[PROP_FHEIGHT]}, + {&pos->height, old_track_height, pos->track_height, properties[PROP_HEIGHT]}, + + {&pos->posx, old_track_width, pos->track_width, properties[PROP_FPOSX]}, {&pos->posx, old_track_width, pos->track_width, properties[PROP_POSX]}, + + {&pos->posy, old_track_height, pos->track_height, properties[PROP_FPOSY]}, {&pos->posy, old_track_height, pos->track_height, properties[PROP_POSY]}, }; + /* *INDENT-ON* */ + for (i = 0; i < G_N_ELEMENTS (props_data); i++) { GList *values, *tmp; @@ -236,8 +261,10 @@ reposition_properties (GstFramePositioner * pos, gint old_track_width, GstControlBinding *binding = gst_object_get_control_binding (GST_OBJECT (pos), d.pspec->name); - *(d.value) = - *(d.value) * (gdouble) d.track_value / (gdouble) d.old_track_value; + if (G_PARAM_SPEC_VALUE_TYPE (d.pspec) == G_TYPE_FLOAT) { + *(d.value) = + *(d.value) * (gdouble) d.track_value / (gdouble) d.old_track_value; + } if (!binding) continue; @@ -523,9 +550,19 @@ gst_frame_positioner_class_init (GstFramePositionerClass * klass) * The desired x position for the stream. */ properties[PROP_POSX] = - g_param_spec_int ("posx", "posx", "x position of the stream", MIN_PIXELS, - MAX_PIXELS, 0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE); + g_param_spec_int ("posx", "posx", "x position of the stream", + MIN_PIXELS, MAX_PIXELS, 0, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_LAX_VALIDATION); + /** + * gstframepositioner:fposx: + * + * The desired x position for the stream. + */ + properties[PROP_FPOSX] = + g_param_spec_float ("fposx", "fposx", "x position of the stream in float", + MIN_PIXELS, MAX_PIXELS, 0, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_LAX_VALIDATION); /** * gstframepositioner:posy: @@ -533,8 +570,20 @@ gst_frame_positioner_class_init (GstFramePositionerClass * klass) * The desired y position for the stream. */ properties[PROP_POSY] = - g_param_spec_int ("posy", "posy", "y position of the stream", MIN_PIXELS, - MAX_PIXELS, 0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE); + g_param_spec_int ("posy", "posy", "y position of the stream", + MIN_PIXELS, MAX_PIXELS, 0, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_LAX_VALIDATION); + + + /** + * gstframepositioner:fposy: + * + * The desired y position for the stream. + */ + properties[PROP_FPOSY] = + g_param_spec_float ("fposy", "fposy", "y position of the stream in float", + MIN_PIXELS, MAX_PIXELS, 0, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_LAX_VALIDATION); /** * gstframepositioner:zorder: @@ -552,8 +601,20 @@ gst_frame_positioner_class_init (GstFramePositionerClass * klass) * Set to 0 if size is not mandatory, will be set to width of the current track. */ properties[PROP_WIDTH] = - g_param_spec_int ("width", "width", "width of the source", 0, MAX_PIXELS, - 0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE); + g_param_spec_int ("width", "width", "width of the source", 0, + MAX_PIXELS, 0, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_LAX_VALIDATION); + + /** + * gesframepositioner:fwidth: + * + * The desired width for that source. + * Set to 0 if size is not mandatory, will be set to width of the current track. + */ + properties[PROP_FWIDTH] = + g_param_spec_float ("fwidth", "fwidth", "width of the source in float", 0, + MAX_PIXELS, 0, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_LAX_VALIDATION); /** * gesframepositioner:height: @@ -563,7 +624,19 @@ gst_frame_positioner_class_init (GstFramePositionerClass * klass) */ properties[PROP_HEIGHT] = g_param_spec_int ("height", "height", "height of the source", 0, - MAX_PIXELS, 0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE); + MAX_PIXELS, 0, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_LAX_VALIDATION); + + /** + * gesframepositioner:fheight: + * + * The desired height for that source. + * Set to 0 if size is not mandatory, will be set to height of the current track. + */ + properties[PROP_FHEIGHT] = + g_param_spec_float ("fheight", "fheight", "height of the source in float", + 0, MAX_PIXELS, 0, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_LAX_VALIDATION); /** * gesframepositioner:operator: @@ -635,10 +708,18 @@ gst_frame_positioner_set_property (GObject * object, guint property_id, framepositioner->posx = g_value_get_int (value); framepositioner->user_positioned = TRUE; break; + case PROP_FPOSX: + framepositioner->posx = g_value_get_float (value); + framepositioner->user_positioned = TRUE; + break; case PROP_POSY: framepositioner->posy = g_value_get_int (value); framepositioner->user_positioned = TRUE; break; + case PROP_FPOSY: + framepositioner->posy = g_value_get_float (value); + framepositioner->user_positioned = TRUE; + break; case PROP_ZORDER: framepositioner->zorder = g_value_get_uint (value); break; @@ -648,12 +729,24 @@ gst_frame_positioner_set_property (GObject * object, guint property_id, gst_frame_positioner_update_properties (framepositioner, track_mixing, 0, 0); break; + case PROP_FWIDTH: + framepositioner->user_positioned = TRUE; + framepositioner->width = g_value_get_float (value); + gst_frame_positioner_update_properties (framepositioner, track_mixing, + 0, 0); + break; case PROP_HEIGHT: framepositioner->user_positioned = TRUE; framepositioner->height = g_value_get_int (value); gst_frame_positioner_update_properties (framepositioner, track_mixing, 0, 0); break; + case PROP_FHEIGHT: + framepositioner->user_positioned = TRUE; + framepositioner->height = g_value_get_float (value); + gst_frame_positioner_update_properties (framepositioner, track_mixing, + 0, 0); + break; case PROP_OPERATOR: framepositioner->operator = g_value_get_enum (value); gst_frame_positioner_update_properties (framepositioner, track_mixing, @@ -671,7 +764,7 @@ gst_frame_positioner_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec) { GstFramePositioner *pos = GST_FRAME_POSITIONNER (object); - gint real_width, real_height; + gdouble real_width, real_height; switch (property_id) { case PROP_ALPHA: @@ -680,9 +773,15 @@ gst_frame_positioner_get_property (GObject * object, guint property_id, case PROP_POSX: g_value_set_int (value, round (pos->posx)); break; + case PROP_FPOSX: + g_value_set_float (value, pos->posx); + break; case PROP_POSY: g_value_set_int (value, round (pos->posy)); break; + case PROP_FPOSY: + g_value_set_float (value, pos->posy); + break; case PROP_ZORDER: g_value_set_uint (value, pos->zorder); break; @@ -690,18 +789,32 @@ gst_frame_positioner_get_property (GObject * object, guint property_id, if (pos->scale_in_compositor) { g_value_set_int (value, round (pos->width)); } else { - real_width = - pos->width > 0 ? round (pos->width) : round (pos->track_width); - g_value_set_int (value, real_width); + real_width = pos->width > 0 ? pos->width : pos->track_width; + g_value_set_int (value, round (real_width)); + } + break; + case PROP_FWIDTH: + if (pos->scale_in_compositor) { + g_value_set_float (value, pos->width); + } else { + real_width = pos->width > 0 ? pos->width : pos->track_width; + g_value_set_float (value, real_width); } break; case PROP_HEIGHT: if (pos->scale_in_compositor) { g_value_set_int (value, round (pos->height)); } else { - real_height = - pos->height > 0 ? round (pos->height) : round (pos->track_height); - g_value_set_int (value, real_height); + real_height = pos->height > 0 ? pos->height : pos->track_height; + g_value_set_int (value, round (real_height)); + } + break; + case PROP_FHEIGHT: + if (pos->scale_in_compositor) { + g_value_set_float (value, pos->height); + } else { + real_height = pos->height > 0 ? pos->height : pos->track_height; + g_value_set_float (value, real_height); } break; case PROP_OPERATOR: @@ -728,13 +841,54 @@ gst_frame_positioner_transform_ip (GstBaseTransform * trans, GstBuffer * buf) GST_OBJECT_LOCK (framepositioner); meta->alpha = framepositioner->alpha; - meta->posx = round (framepositioner->posx); - meta->posy = round (framepositioner->posy); - meta->width = round (framepositioner->width); - meta->height = round (framepositioner->height); + meta->posx = framepositioner->posx; + meta->posy = framepositioner->posy; + meta->width = framepositioner->width; + meta->height = framepositioner->height; meta->zorder = framepositioner->zorder; meta->operator = framepositioner->operator; GST_OBJECT_UNLOCK (framepositioner); return GST_FLOW_OK; } + +gboolean +gst_frame_positioner_check_can_add_binding (GstFramePositioner * self, + const gchar * property_name) +{ + gint i = 0; + const gchar *checked_prop = NULL; + const gchar *props[][2] = { + {"posx", "fposx"}, + {"posy", "fposy"}, + {"width", "fwidth"}, + {"height", "fheight"}, + }; + + + for (i = 0; i < G_N_ELEMENTS (props); i++) { + if (!g_strcmp0 (property_name, props[i][0])) { + checked_prop = props[i][1]; + break; + } else if (!g_strcmp0 (property_name, props[i][1])) { + checked_prop = props[i][0]; + break; + } + } + + if (!checked_prop) + return TRUE; + + GstControlBinding *b = + gst_object_get_control_binding (GST_OBJECT (self), checked_prop); + if (b) { + gst_object_unref (b); + GST_WARNING_OBJECT (self, + "Can't add control binding for %s as %s already has one", property_name, + checked_prop); + + return FALSE; + } + + return TRUE; +} diff --git a/subprojects/gst-editing-services/ges/gstframepositioner.h b/subprojects/gst-editing-services/ges/gstframepositioner.h index d7ce135b79..7ed985a8b4 100644 --- a/subprojects/gst-editing-services/ges/gstframepositioner.h +++ b/subprojects/gst-editing-services/ges/gstframepositioner.h @@ -72,6 +72,7 @@ struct _GstFramePositionerClass GstBaseTransformClass base_framepositioner_class; }; +G_GNUC_INTERNAL gboolean gst_frame_positioner_check_can_add_binding (GstFramePositioner *self, const gchar *property_name); G_GNUC_INTERNAL GType gst_compositor_operator_get_type_and_default_value (int *default_operator_value); G_GNUC_INTERNAL void ges_frame_positioner_set_source_and_filter (GstFramePositioner *pos, GESTrackElement *trksrc,