mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-23 10:11:08 +00:00
framepositioner: Reposition source when the user positioned them
Keeping the same proportion in the size and position and only if the aspect ratio is conserved.
This commit is contained in:
parent
f55033223f
commit
9cf986c71c
4 changed files with 197 additions and 47 deletions
|
@ -21,6 +21,7 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <gst/video/video.h>
|
#include <gst/video/video.h>
|
||||||
|
|
||||||
|
@ -55,9 +56,12 @@ enum
|
||||||
PROP_POSY,
|
PROP_POSY,
|
||||||
PROP_ZORDER,
|
PROP_ZORDER,
|
||||||
PROP_WIDTH,
|
PROP_WIDTH,
|
||||||
PROP_HEIGHT
|
PROP_HEIGHT,
|
||||||
|
PROP_LAST,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static GParamSpec *properties[PROP_LAST];
|
||||||
|
|
||||||
static GstStaticPadTemplate gst_frame_positioner_src_template =
|
static GstStaticPadTemplate gst_frame_positioner_src_template =
|
||||||
GST_STATIC_PAD_TEMPLATE ("src",
|
GST_STATIC_PAD_TEMPLATE ("src",
|
||||||
GST_PAD_SRC,
|
GST_PAD_SRC,
|
||||||
|
@ -81,25 +85,46 @@ _weak_notify_cb (GstFramePositioner * pos, GObject * old)
|
||||||
pos->current_track = NULL;
|
pos->current_track = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
is_user_positionned (GstFramePositioner * self)
|
||||||
|
{
|
||||||
|
gint i;
|
||||||
|
GParamSpec *positioning_props[] = {
|
||||||
|
properties[PROP_WIDTH],
|
||||||
|
properties[PROP_HEIGHT],
|
||||||
|
properties[PROP_POSX],
|
||||||
|
properties[PROP_POSY],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (self->user_positioned)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
for (i = 0; i < G_N_ELEMENTS (positioning_props); i++) {
|
||||||
|
GstControlBinding *b = gst_object_get_control_binding (GST_OBJECT (self),
|
||||||
|
positioning_props[i]->name);
|
||||||
|
|
||||||
|
if (b) {
|
||||||
|
gst_object_unref (b);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
auto_position (GstFramePositioner * self)
|
auto_position (GstFramePositioner * self)
|
||||||
{
|
{
|
||||||
gint scaled_width = -1, scaled_height = -1, x, y;
|
gint scaled_width = -1, scaled_height = -1, x, y;
|
||||||
|
|
||||||
if (self->user_positioned) {
|
if (is_user_positionned (self)) {
|
||||||
GST_DEBUG_OBJECT (self, "Was positioned by the user, not touching anymore");
|
GST_DEBUG_OBJECT (self, "Was positioned by the user, not auto positioning");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!self->natural_width || !self->natural_height)
|
if (!self->natural_width || !self->natural_height)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (!self->track_width || !self->track_height) {
|
|
||||||
GST_INFO_OBJECT (self, "Track doesn't have a proper size, not "
|
|
||||||
"positioning the source");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self->track_width == self->natural_width &&
|
if (self->track_width == self->natural_width &&
|
||||||
self->track_height == self->natural_height)
|
self->track_height == self->natural_height)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
@ -131,6 +156,80 @@ auto_position (GstFramePositioner * self)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
gint *value;
|
||||||
|
gint old_track_value;
|
||||||
|
gint track_value;
|
||||||
|
GParamSpec *pspec;
|
||||||
|
} RepositionPropertyData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
reposition_properties (GstFramePositioner * pos, gint old_track_width,
|
||||||
|
gint old_track_height)
|
||||||
|
{
|
||||||
|
gint i;
|
||||||
|
RepositionPropertyData props_data[] = {
|
||||||
|
{&pos->width, old_track_width, pos->track_width, properties[PROP_WIDTH]},
|
||||||
|
{&pos->height, old_track_height, pos->track_height,
|
||||||
|
properties[PROP_HEIGHT]},
|
||||||
|
{&pos->posx, old_track_width, pos->track_width, properties[PROP_POSX]},
|
||||||
|
{&pos->posy, old_track_height, pos->track_height, properties[PROP_POSY]},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (i = 0; i < G_N_ELEMENTS (props_data); i++) {
|
||||||
|
GList *values, *tmp;
|
||||||
|
gboolean absolute;
|
||||||
|
GstTimedValueControlSource *source = NULL;
|
||||||
|
|
||||||
|
RepositionPropertyData d = props_data[i];
|
||||||
|
GstControlBinding *binding =
|
||||||
|
gst_object_get_control_binding (GST_OBJECT (pos), d.pspec->name);
|
||||||
|
|
||||||
|
*(d.value) = gst_util_uint64_scale_int (*(d.value), d.track_value,
|
||||||
|
d.old_track_value);
|
||||||
|
|
||||||
|
if (!binding)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!GST_IS_DIRECT_CONTROL_BINDING (binding)) {
|
||||||
|
GST_FIXME_OBJECT (pos, "Implement support for control binding type: %s",
|
||||||
|
G_OBJECT_TYPE_NAME (binding));
|
||||||
|
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_get (binding, "control_source", &source, NULL);
|
||||||
|
if (!GST_IS_TIMED_VALUE_CONTROL_SOURCE (source)) {
|
||||||
|
GST_FIXME_OBJECT (pos, "Implement support for control source type: %s",
|
||||||
|
G_OBJECT_TYPE_NAME (source));
|
||||||
|
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
|
values =
|
||||||
|
gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
|
||||||
|
(source));
|
||||||
|
|
||||||
|
if (!values)
|
||||||
|
goto next;
|
||||||
|
|
||||||
|
g_object_get (binding, "absolute", &absolute, NULL);
|
||||||
|
for (tmp = values; tmp; tmp = tmp->next) {
|
||||||
|
GstTimedValue *value = tmp->data;
|
||||||
|
|
||||||
|
gst_timed_value_control_source_set (source, value->timestamp,
|
||||||
|
value->value * d.track_value / d.old_track_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_list_free (values);
|
||||||
|
|
||||||
|
next:
|
||||||
|
gst_clear_object (&source);
|
||||||
|
gst_object_unref (binding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_frame_positioner_update_properties (GstFramePositioner * pos,
|
gst_frame_positioner_update_properties (GstFramePositioner * pos,
|
||||||
gboolean track_mixing, gint old_track_width, gint old_track_height)
|
gboolean track_mixing, gint old_track_width, gint old_track_height)
|
||||||
|
@ -157,24 +256,40 @@ gst_frame_positioner_update_properties (GstFramePositioner * pos,
|
||||||
gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
|
gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
|
||||||
pos->par_n, pos->par_d, NULL);
|
pos->par_n, pos->par_d, NULL);
|
||||||
|
|
||||||
if (!auto_position (pos)) {
|
if (!pos->track_width || !pos->track_height) {
|
||||||
|
GST_INFO_OBJECT (pos, "Track doesn't have a proper size, not "
|
||||||
|
"positioning the source");
|
||||||
|
goto done;
|
||||||
|
} else if (auto_position (pos))
|
||||||
|
goto done;
|
||||||
|
|
||||||
if (old_track_width && pos->width == old_track_width &&
|
if (!old_track_height || !old_track_height) {
|
||||||
old_track_height && pos->height == old_track_height &&
|
GST_DEBUG_OBJECT (pos, "No old track size, can not properly reposition");
|
||||||
pos->track_height && pos->track_width &&
|
goto done;
|
||||||
((float) old_track_width / (float) old_track_height) ==
|
|
||||||
((float) pos->track_width / (float) pos->track_height)) {
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (pos,
|
|
||||||
"Following track size width old_track: %d -- pos: %d"
|
|
||||||
" || height, old_track %d -- pos: %d", old_track_width, pos->width,
|
|
||||||
old_track_height, pos->height);
|
|
||||||
|
|
||||||
pos->width = pos->track_width;
|
|
||||||
pos->height = pos->track_height;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((!pos->natural_width || !pos->natural_height) &&
|
||||||
|
(!pos->width || !pos->height)) {
|
||||||
|
GST_DEBUG_OBJECT (pos, "No natural aspect ratio and no user set "
|
||||||
|
" image size, can't not reposition.");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gst_util_fraction_compare (old_track_width, old_track_height,
|
||||||
|
pos->track_width, pos->track_height)) {
|
||||||
|
GST_INFO_OBJECT (pos, "Not repositioning as track size change didn't"
|
||||||
|
" keep the same aspect ratio (previous %dx%d("
|
||||||
|
"ratio=%f), new: %dx%d(ratio=%f)",
|
||||||
|
old_track_width, old_track_height,
|
||||||
|
(gdouble) old_track_width / (gdouble) old_track_height,
|
||||||
|
pos->track_width, pos->track_height,
|
||||||
|
(gdouble) pos->track_width / (gdouble) pos->track_height);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
reposition_properties (pos, old_track_width, old_track_height);
|
||||||
|
|
||||||
|
done:
|
||||||
GST_DEBUG_OBJECT (caps, "setting caps");
|
GST_DEBUG_OBJECT (caps, "setting caps");
|
||||||
|
|
||||||
g_object_set (pos->capsfilter, "caps", caps, NULL);
|
g_object_set (pos->capsfilter, "caps", caps, NULL);
|
||||||
|
@ -336,19 +451,18 @@ gst_frame_positioner_class_init (GstFramePositionerClass * klass)
|
||||||
*
|
*
|
||||||
* The desired alpha for the stream.
|
* The desired alpha for the stream.
|
||||||
*/
|
*/
|
||||||
g_object_class_install_property (gobject_class, PROP_ALPHA,
|
properties[PROP_ALPHA] =
|
||||||
g_param_spec_double ("alpha", "alpha", "alpha of the stream",
|
g_param_spec_double ("alpha", "alpha", "alpha of the stream", 0.0, 1.0,
|
||||||
0.0, 1.0, 1.0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
|
1.0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gstframepositioner:posx:
|
* gstframepositioner:posx:
|
||||||
*
|
*
|
||||||
* The desired x position for the stream.
|
* The desired x position for the stream.
|
||||||
*/
|
*/
|
||||||
g_object_class_install_property (gobject_class, PROP_POSX,
|
properties[PROP_POSX] =
|
||||||
g_param_spec_int ("posx", "posx", "x position of the stream",
|
g_param_spec_int ("posx", "posx", "x position of the stream", MIN_PIXELS,
|
||||||
MIN_PIXELS, MAX_PIXELS, 0,
|
MAX_PIXELS, 0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE);
|
||||||
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -356,19 +470,18 @@ gst_frame_positioner_class_init (GstFramePositionerClass * klass)
|
||||||
*
|
*
|
||||||
* The desired y position for the stream.
|
* The desired y position for the stream.
|
||||||
*/
|
*/
|
||||||
g_object_class_install_property (gobject_class, PROP_POSY,
|
properties[PROP_POSY] =
|
||||||
g_param_spec_int ("posy", "posy", "y position of the stream",
|
g_param_spec_int ("posy", "posy", "y position of the stream", MIN_PIXELS,
|
||||||
MIN_PIXELS, MAX_PIXELS, 0,
|
MAX_PIXELS, 0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE);
|
||||||
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gstframepositioner:zorder:
|
* gstframepositioner:zorder:
|
||||||
*
|
*
|
||||||
* The desired z order for the stream.
|
* The desired z order for the stream.
|
||||||
*/
|
*/
|
||||||
g_object_class_install_property (gobject_class, PROP_ZORDER,
|
properties[PROP_ZORDER] =
|
||||||
g_param_spec_uint ("zorder", "zorder", "z order of the stream",
|
g_param_spec_uint ("zorder", "zorder", "z order of the stream", 0,
|
||||||
0, G_MAXUINT, 0, G_PARAM_READWRITE));
|
G_MAXUINT, 0, G_PARAM_READWRITE);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gesframepositioner:width:
|
* gesframepositioner:width:
|
||||||
|
@ -376,9 +489,9 @@ gst_frame_positioner_class_init (GstFramePositionerClass * klass)
|
||||||
* The desired width for that source.
|
* The desired width for that source.
|
||||||
* Set to 0 if size is not mandatory, will be set to width of the current track.
|
* Set to 0 if size is not mandatory, will be set to width of the current track.
|
||||||
*/
|
*/
|
||||||
g_object_class_install_property (gobject_class, PROP_WIDTH,
|
properties[PROP_WIDTH] =
|
||||||
g_param_spec_int ("width", "width", "width of the source",
|
g_param_spec_int ("width", "width", "width of the source", 0, MAX_PIXELS,
|
||||||
0, MAX_PIXELS, 0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
|
0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gesframepositioner:height:
|
* gesframepositioner:height:
|
||||||
|
@ -386,9 +499,11 @@ gst_frame_positioner_class_init (GstFramePositionerClass * klass)
|
||||||
* The desired height for that source.
|
* The desired height for that source.
|
||||||
* Set to 0 if size is not mandatory, will be set to height of the current track.
|
* Set to 0 if size is not mandatory, will be set to height of the current track.
|
||||||
*/
|
*/
|
||||||
g_object_class_install_property (gobject_class, PROP_HEIGHT,
|
properties[PROP_HEIGHT] =
|
||||||
g_param_spec_int ("height", "height", "height of the source",
|
g_param_spec_int ("height", "height", "height of the source", 0,
|
||||||
0, MAX_PIXELS, 0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
|
MAX_PIXELS, 0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE);
|
||||||
|
|
||||||
|
g_object_class_install_properties (gobject_class, PROP_LAST, properties);
|
||||||
|
|
||||||
gst_element_class_set_static_metadata (GST_ELEMENT_CLASS (klass),
|
gst_element_class_set_static_metadata (GST_ELEMENT_CLASS (klass),
|
||||||
"frame positioner", "Metadata",
|
"frame positioner", "Metadata",
|
||||||
|
@ -471,8 +586,6 @@ gst_frame_positioner_get_property (GObject * object, guint property_id,
|
||||||
GstFramePositioner *pos = GST_FRAME_POSITIONNER (object);
|
GstFramePositioner *pos = GST_FRAME_POSITIONNER (object);
|
||||||
gint real_width, real_height;
|
gint real_width, real_height;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (pos, "get_property");
|
|
||||||
|
|
||||||
switch (property_id) {
|
switch (property_id) {
|
||||||
case PROP_ALPHA:
|
case PROP_ALPHA:
|
||||||
g_value_set_double (value, pos->alpha);
|
g_value_set_double (value, pos->alpha);
|
||||||
|
|
|
@ -72,7 +72,7 @@ foreach t : ges_tests
|
||||||
endforeach
|
endforeach
|
||||||
|
|
||||||
if gstvalidate_dep.found()
|
if gstvalidate_dep.found()
|
||||||
scenarios = ['check_video_track_restriction_scale']
|
scenarios = ['check_video_track_restriction_scale', 'check_video_track_restriction_scale_with_keyframes']
|
||||||
|
|
||||||
foreach scenario: scenarios
|
foreach scenario: scenarios
|
||||||
scenario_file = join_paths(meson.current_source_dir(), 'scenarios', scenario + '.scenario')
|
scenario_file = join_paths(meson.current_source_dir(), 'scenarios', scenario + '.scenario')
|
||||||
|
|
|
@ -34,4 +34,9 @@ check-child-properties, element-name=clip, width=700, height=600
|
||||||
set-track-restriction-caps, track-type=video, caps="video/x-raw,width=1920,height=1080"
|
set-track-restriction-caps, track-type=video, caps="video/x-raw,width=1920,height=1080"
|
||||||
check-child-properties, element-name=clip, width=700, height=600
|
check-child-properties, element-name=clip, width=700, height=600
|
||||||
|
|
||||||
|
set-child-properties, element-name=clip, width=1280, height=720, posx=320, posy=240
|
||||||
|
check-child-properties, element-name=clip, width=1280, height=720, posx=320, posy=240
|
||||||
|
set-track-restriction-caps, track-type=video, caps="video/x-raw,width=960,height=540"
|
||||||
|
check-child-properties, element-name=clip, width=640, height=360, posx=160, posy=120
|
||||||
|
|
||||||
stop
|
stop
|
|
@ -0,0 +1,32 @@
|
||||||
|
description, handles-states=true, seek=true,
|
||||||
|
ges-options={\
|
||||||
|
--track-types, video,
|
||||||
|
--video-caps, "video/x-raw,width=1280,height=720,framerate=30/1" \
|
||||||
|
}
|
||||||
|
|
||||||
|
add-clip, name=clip, asset-id=GESTestClip, layer-priority=0, type=GESTestClip, start=0.0, duration=1.0, pattern=blue
|
||||||
|
|
||||||
|
set-control-source, element-name=videotestsource0, property-name=width, binding-type=direct-absolute, source-type=interpolation
|
||||||
|
set-control-source, element-name=videotestsource0, property-name=height, binding-type=direct, source-type=interpolation
|
||||||
|
|
||||||
|
|
||||||
|
# Goes from 1280x720 at 0, to 640x360 at 0.5 then back to 1280x720 ar 1.0
|
||||||
|
add-keyframe, element-name=videotestsource0, property-name="width", timestamp=0.0, value=(gint)1280
|
||||||
|
add-keyframe, element-name=videotestsource0, property-name="height", timestamp=0.0, value=0.0072
|
||||||
|
|
||||||
|
add-keyframe, element-name=videotestsource0, property-name="width", timestamp=0.5, value=(gint)640
|
||||||
|
add-keyframe, element-name=videotestsource0, property-name="height", timestamp=0.5, value=0.0036
|
||||||
|
|
||||||
|
add-keyframe, element-name=videotestsource0, property-name="width", timestamp=1.0, value=(gint)1280
|
||||||
|
add-keyframe, element-name=videotestsource0, property-name="height", timestamp=1.0, value=0.0072
|
||||||
|
|
||||||
|
check-child-properties, element-name=videotestsource0, width=1280, height=720, posx=0, posy=0, at-time=0.0
|
||||||
|
check-child-properties, element-name=videotestsource0, width=640, height=360, posx=0, posy=0, at-time=0.5
|
||||||
|
check-child-properties, element-name=videotestsource0, width=1280, height=720, posx=0, posy=0, at-time=1.0
|
||||||
|
|
||||||
|
set-track-restriction-caps, track-type=video, caps="video/x-raw,width=1920,height=1080"
|
||||||
|
check-child-properties, element-name=videotestsource0, width=1920, height=1080, posx=0, posy=0, at-time=0.0
|
||||||
|
check-child-properties, element-name=videotestsource0, width=960, height=540, posx=0, posy=0, at-time=0.5
|
||||||
|
check-child-properties, element-name=videotestsource0, width=1920, height=1080, posx=0, posy=0, at-time=1.0
|
||||||
|
|
||||||
|
stop;
|
Loading…
Reference in a new issue