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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6241>
This commit is contained in:
Thibault Saunier 2024-02-27 14:00:04 -03:00 committed by GStreamer Marge Bot
parent 1b74f039ab
commit 14d6773aba
9 changed files with 287 additions and 42 deletions

View file

@ -4487,21 +4487,21 @@ composition.</doc>
</field>
<field name="posx" writable="1">
<doc xml:space="preserve" filename="../subprojects/gst-editing-services/ges/ges-frame-composition-meta.h">The desired x position.</doc>
<type name="gint" c:type="gint"/>
<type name="gdouble" c:type="gdouble"/>
</field>
<field name="posy" writable="1">
<doc xml:space="preserve" filename="../subprojects/gst-editing-services/ges/ges-frame-composition-meta.h">The desired y position.</doc>
<type name="gint" c:type="gint"/>
<type name="gdouble" c:type="gdouble"/>
</field>
<field name="height" writable="1">
<doc xml:space="preserve" filename="../subprojects/gst-editing-services/ges/ges-frame-composition-meta.h">The desired height of the video. -1 means that no scaling should be
applied.</doc>
<type name="gint" c:type="gint"/>
<type name="gdouble" c:type="gdouble"/>
</field>
<field name="width" writable="1">
<doc xml:space="preserve" filename="../subprojects/gst-editing-services/ges/ges-frame-composition-meta.h">The desired width of the video. -1 means that no scaling should beapplied
applied.</doc>
<type name="gint" c:type="gint"/>
<type name="gdouble" c:type="gdouble"/>
</field>
<field name="zorder" writable="1">
<doc xml:space="preserve" filename="../subprojects/gst-editing-services/ges/ges-frame-composition-meta.h">The desired z order.</doc>

View file

@ -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;
};

View file

@ -20,6 +20,7 @@
#include "config.h"
#endif
#include <math.h>
#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;

View file

@ -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),

View file

@ -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);

View file

@ -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;

View file

@ -22,6 +22,7 @@
#endif
#include <gst/gst.h>
#include <math.h>
#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);
}

View file

@ -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;
}

View file

@ -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,