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,