framepositioner: Reposition source when the user positioned them
authorThibault Saunier <tsaunier@igalia.com>
Sat, 22 Feb 2020 17:23:45 +0000 (14:23 -0300)
committerThibault Saunier <tsaunier@igalia.com>
Wed, 26 Feb 2020 16:39:29 +0000 (13:39 -0300)
Keeping the same proportion in the size and position and only if
the aspect ratio is conserved.

ges/gstframepositioner.c
tests/check/meson.build
tests/check/scenarios/check_video_track_restriction_scale.scenario
tests/check/scenarios/check_video_track_restriction_scale_with_keyframes.scenario [new file with mode: 0644]

index f8533720061976fc799ee953af81d9c5c489a41c..a8a005548947a574923771ee865ca9facad62628 100644 (file)
@@ -21,6 +21,7 @@
 #include "config.h"
 #endif
 
+#include <math.h>
 #include <gst/gst.h>
 #include <gst/video/video.h>
 
@@ -55,9 +56,12 @@ enum
   PROP_POSY,
   PROP_ZORDER,
   PROP_WIDTH,
-  PROP_HEIGHT
+  PROP_HEIGHT,
+  PROP_LAST,
 };
 
+static GParamSpec *properties[PROP_LAST];
+
 static GstStaticPadTemplate gst_frame_positioner_src_template =
 GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
@@ -81,25 +85,46 @@ _weak_notify_cb (GstFramePositioner * pos, GObject * old)
   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
 auto_position (GstFramePositioner * self)
 {
   gint scaled_width = -1, scaled_height = -1, x, y;
 
-  if (self->user_positioned) {
-    GST_DEBUG_OBJECT (self, "Was positioned by the user, not touching anymore");
+  if (is_user_positionned (self)) {
+    GST_DEBUG_OBJECT (self, "Was positioned by the user, not auto positioning");
     return FALSE;
   }
 
   if (!self->natural_width || !self->natural_height)
     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 &&
       self->track_height == self->natural_height)
     return TRUE;
@@ -131,6 +156,80 @@ auto_position (GstFramePositioner * self)
   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
 gst_frame_positioner_update_properties (GstFramePositioner * pos,
     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,
         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 &&
-        old_track_height && pos->height == old_track_height &&
-        pos->track_height && pos->track_width &&
-        ((float) old_track_width / (float) old_track_height) ==
-        ((float) pos->track_width / (float) pos->track_height)) {
+  if (!old_track_height || !old_track_height) {
+    GST_DEBUG_OBJECT (pos, "No old track size, can not properly reposition");
+    goto done;
+  }
 
-      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);
+  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;
+  }
 
-      pos->width = pos->track_width;
-      pos->height = pos->track_height;
-    }
+  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");
 
   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.
    */
-  g_object_class_install_property (gobject_class, PROP_ALPHA,
-      g_param_spec_double ("alpha", "alpha", "alpha of the stream",
-          0.0, 1.0, 1.0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
+  properties[PROP_ALPHA] =
+      g_param_spec_double ("alpha", "alpha", "alpha of the stream", 0.0, 1.0,
+      1.0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE);
 
   /**
    * gstframepositioner:posx:
    *
    * The desired x position for the stream.
    */
-  g_object_class_install_property (gobject_class, PROP_POSX,
-      g_param_spec_int ("posx", "posx", "x position of the stream",
-          MIN_PIXELS, MAX_PIXELS, 0,
-          G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
+  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);
 
 
   /**
@@ -356,19 +470,18 @@ gst_frame_positioner_class_init (GstFramePositionerClass * klass)
    *
    * The desired y position for the stream.
    */
-  g_object_class_install_property (gobject_class, PROP_POSY,
-      g_param_spec_int ("posy", "posy", "y position of the stream",
-          MIN_PIXELS, MAX_PIXELS, 0,
-          G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
+  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);
 
   /**
    * gstframepositioner:zorder:
    *
    * The desired z order for the stream.
    */
-  g_object_class_install_property (gobject_class, PROP_ZORDER,
-      g_param_spec_uint ("zorder", "zorder", "z order of the stream",
-          0, G_MAXUINT, 0, G_PARAM_READWRITE));
+  properties[PROP_ZORDER] =
+      g_param_spec_uint ("zorder", "zorder", "z order of the stream", 0,
+      G_MAXUINT, 0, G_PARAM_READWRITE);
 
   /**
    * gesframepositioner:width:
@@ -376,9 +489,9 @@ gst_frame_positioner_class_init (GstFramePositionerClass * klass)
    * The desired width for that source.
    * 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,
-      g_param_spec_int ("width", "width", "width of the source",
-          0, MAX_PIXELS, 0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
+  properties[PROP_WIDTH] =
+      g_param_spec_int ("width", "width", "width of the source", 0, MAX_PIXELS,
+      0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE);
 
   /**
    * gesframepositioner:height:
@@ -386,9 +499,11 @@ gst_frame_positioner_class_init (GstFramePositionerClass * klass)
    * The desired height for that source.
    * 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,
-      g_param_spec_int ("height", "height", "height of the source",
-          0, MAX_PIXELS, 0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
+  properties[PROP_HEIGHT] =
+      g_param_spec_int ("height", "height", "height of the source", 0,
+      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),
       "frame positioner", "Metadata",
@@ -471,8 +586,6 @@ gst_frame_positioner_get_property (GObject * object, guint property_id,
   GstFramePositioner *pos = GST_FRAME_POSITIONNER (object);
   gint real_width, real_height;
 
-  GST_DEBUG_OBJECT (pos, "get_property");
-
   switch (property_id) {
     case PROP_ALPHA:
       g_value_set_double (value, pos->alpha);
index 20248f332cae69ee7034ee31db5940c71913511f..cf0ba74c61681b183f8952a6789f1e8751deb939 100644 (file)
@@ -72,7 +72,7 @@ foreach t : ges_tests
 endforeach
 
 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
     scenario_file = join_paths(meson.current_source_dir(), 'scenarios', scenario + '.scenario')
index 3ffc1c938c1d9ccc635219b7e5366ceb57c1125e..9be26092c33589340893f0dcc3a851d21a65c84e 100644 (file)
@@ -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"
 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
\ No newline at end of file
diff --git a/tests/check/scenarios/check_video_track_restriction_scale_with_keyframes.scenario b/tests/check/scenarios/check_video_track_restriction_scale_with_keyframes.scenario
new file mode 100644 (file)
index 0000000..0c0b95e
--- /dev/null
@@ -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;
\ No newline at end of file