* @title: GESBaseEffect
* @short_description: adds an effect to a stream in a GESSourceClip or a
* GESLayer
+ *
+ * A #GESBaseEffect is some operation that applies an effect to the data
+ * it receives.
+ *
+ * ## Time Effects
+ *
+ * Some operations will change the timing of the stream data they receive
+ * in some way. In particular, the #GstElement that they wrap could alter
+ * the times of the segment they receive in a #GST_EVENT_SEGMENT event,
+ * or the times of a seek they receive in a #GST_EVENT_SEEK event. Such
+ * operations would be considered time effects since they translate the
+ * times they receive on their source to different times at their sink,
+ * and vis versa. This introduces two sets of time coordinates for the
+ * event: (internal) sink coordinates and (internal) source coordinates,
+ * where segment times are translated from the sink coordinates to the
+ * source coordinates, and seek times are translated from the source
+ * coordinates to the sink coordinates.
+ *
+ * If you use such an effect in GES, you will need to inform GES of the
+ * properties that control the timing with
+ * ges_base_effect_register_time_property(), and the effect's timing
+ * behaviour using ges_base_effect_set_time_translation_funcs().
+ *
+ * Note that a time effect should not have its
+ * #GESTrackElement:has-internal-source set to %TRUE.
+ *
+ * In addition, note that GES only *fully* supports time effects whose
+ * mapping from the source to sink coordinates (those applied to seeks)
+ * obeys:
+ *
+ * + Maps the time `0` to `0`. So initial time-shifting effects are
+ * excluded.
+ * + Is monotonically increasing. So reversing effects, and effects that
+ * jump backwards in the stream are excluded.
+ * + Can handle a reasonable #GstClockTime, relative to the project. So
+ * this would exclude a time effect with an extremely large speed-up
+ * that would cause the converted #GstClockTime seeks to overflow.
+ * + Is 'continuously reversible'. This essentially means that for every
+ * time in the sink coordinates, we can, to 'good enough' accuracy,
+ * calculate the corresponding time in the source coordinates. Moreover,
+ * this should correspond to how segment times are translated from
+ * sink to source.
+ * + Only depends on the registered time properties, rather than the
+ * state of the #GstElement or the data it receives. This would exclude,
+ * say, an effect that would speedup if there is more red in the image
+ * it receives.
+ *
+ * Note that a constant-rate-change effect that is not extremely fast or
+ * slow would satisfy these conditions. For such effects, you may wish to
+ * use ges_effect_class_register_rate_property().
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#include "ges-track-element.h"
#include "ges-base-effect.h"
+typedef struct _TimePropertyData
+{
+ gchar *property_name;
+ GObject *child;
+ GParamSpec *pspec;
+} TimePropertyData;
+
+static void
+_time_property_data_free (gpointer data_p)
+{
+ TimePropertyData *data = data_p;
+ g_free (data->property_name);
+ gst_object_unref (data->child);
+ g_param_spec_unref (data->pspec);
+ g_free (data);
+}
+
struct _GESBaseEffectPrivate
{
- void *nothing;
+ GList *time_properties;
+ GESBaseEffectTimeTranslationFunc source_to_sink;
+ GESBaseEffectTimeTranslationFunc sink_to_source;
+ gpointer translation_data;
+ GDestroyNotify destroy_translation_data;
};
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GESBaseEffect, ges_base_effect,
GES_TYPE_OPERATION);
+static gboolean
+ges_base_effect_set_child_property_full (GESTimelineElement * element,
+ GObject * child, GParamSpec * pspec, const GValue * value, GError ** error)
+{
+ GESClip *parent = GES_IS_CLIP (element->parent) ?
+ GES_CLIP (element->parent) : NULL;
+
+ if (parent && !ges_clip_can_set_time_property_of_child (parent,
+ GES_TRACK_ELEMENT (element), child, pspec, value, error)) {
+ GST_INFO_OBJECT (element, "Cannot set time property '%s::%s' "
+ "because the parent clip %" GES_FORMAT " would not allow it",
+ G_OBJECT_TYPE_NAME (child), pspec->name, GES_ARGS (parent));
+ return FALSE;
+ }
+
+ return
+ GES_TIMELINE_ELEMENT_CLASS
+ (ges_base_effect_parent_class)->set_child_property_full (element, child,
+ pspec, value, error);
+}
+
+static void
+ges_base_effect_dispose (GObject * object)
+{
+ GESBaseEffectPrivate *priv = GES_BASE_EFFECT (object)->priv;
+
+ g_list_free_full (priv->time_properties, _time_property_data_free);
+ priv->time_properties = NULL;
+ if (priv->destroy_translation_data)
+ priv->destroy_translation_data (priv->translation_data);
+ priv->destroy_translation_data = NULL;
+ priv->source_to_sink = NULL;
+ priv->sink_to_source = NULL;
+
+ G_OBJECT_CLASS (ges_base_effect_parent_class)->dispose (object);
+}
+
static void
ges_base_effect_class_init (GESBaseEffectClass * klass)
{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GESTimelineElementClass *element_class = GES_TIMELINE_ELEMENT_CLASS (klass);
+
+ object_class->dispose = ges_base_effect_dispose;
+ element_class->set_child_property_full =
+ ges_base_effect_set_child_property_full;
}
static void
{
self->priv = ges_base_effect_get_instance_private (self);
}
+
+static void
+_child_property_removed (GESTimelineElement * element, GObject * child,
+ GParamSpec * pspec, gpointer user_data)
+{
+ GList *tmp;
+ GESBaseEffectPrivate *priv = GES_BASE_EFFECT (element)->priv;
+
+ for (tmp = priv->time_properties; tmp; tmp = tmp->next) {
+ TimePropertyData *data = tmp->data;
+ if (data->child == child && data->pspec == pspec) {
+ priv->time_properties = g_list_remove (priv->time_properties, data);
+ _time_property_data_free (data);
+ return;
+ }
+ }
+}
+
+/**
+ * ges_base_effect_register_time_property:
+ * @effect: A #GESBaseEffect
+ * @child_property_name: The name of the child property to register as
+ * a time property
+ *
+ * Register a child property of the effect as a property that, when set,
+ * can change the timing of its input data. The child property should be
+ * specified as in ges_timeline_element_lookup_child().
+ *
+ * You should also set the corresponding time translation using
+ * ges_base_effect_set_time_translation_funcs().
+ *
+ * Note that @effect must not be part of a clip, nor can it have
+ * #GESTrackElement:has-internal-source set to %TRUE.
+ *
+ * Returns: %TRUE if the child property was found and newly registered.
+ */
+gboolean
+ges_base_effect_register_time_property (GESBaseEffect * effect,
+ const gchar * child_property_name)
+{
+ GESTimelineElement *element;
+ GESTrackElement *el;
+ GParamSpec *pspec;
+ GObject *child;
+ GList *tmp;
+ TimePropertyData *data;
+
+ g_return_val_if_fail (GES_IS_BASE_EFFECT (effect), FALSE);
+ el = GES_TRACK_ELEMENT (effect);
+ element = GES_TIMELINE_ELEMENT (el);
+
+ g_return_val_if_fail (element->parent == NULL, FALSE);
+ g_return_val_if_fail (ges_track_element_has_internal_source (el) == FALSE,
+ FALSE);
+
+ if (!ges_timeline_element_lookup_child (element, child_property_name,
+ &child, &pspec))
+ return FALSE;
+
+ for (tmp = effect->priv->time_properties; tmp; tmp = tmp->next) {
+ data = tmp->data;
+ if (data->child == child && data->pspec == pspec) {
+ GST_WARNING_OBJECT (effect, "Already registered the time effect for %s",
+ child_property_name);
+ g_object_unref (child);
+ g_param_spec_unref (pspec);
+ return FALSE;
+ }
+ }
+
+ ges_track_element_set_has_internal_source_is_forbidden (el);
+
+ data = g_new0 (TimePropertyData, 1);
+ data->child = child;
+ data->pspec = pspec;
+ data->property_name = g_strdup (child_property_name);
+
+ effect->priv->time_properties =
+ g_list_prepend (effect->priv->time_properties, data);
+
+ g_signal_handlers_disconnect_by_func (effect, _child_property_removed, NULL);
+ g_signal_connect (effect, "child-property-removed",
+ G_CALLBACK (_child_property_removed), NULL);
+
+ return TRUE;
+}
+
+/**
+ * ges_base_effect_set_time_translation_funcs:
+ * @effect: A #GESBaseEffect
+ * @source_to_sink_func: (nullable) (scope notified): The function to use
+ * for querying how a time is translated from the source coordinates to
+ * the sink coordinates of @effect
+ * @sink_to_source_func: (nullable) (scope notified): The function to use
+ * for querying how a time is translated from the sink coordinates to the
+ * source coordinates of @effect
+ * @user_data: (closure): Data to pass to both @source_to_sink_func and
+ * @sink_to_source_func
+ * @destroy: (destroy user_data) (nullable): Method to call to destroy
+ * @user_data, or %NULL
+ *
+ * Set the time translation query functions for the time effect. If an
+ * effect is a time effect, it will have two sets of coordinates: one
+ * at its sink and one at its source. The given functions should be able
+ * to translate between these two sets of coordinates. More specifically,
+ * @source_to_sink_func should *emulate* how the corresponding #GstElement
+ * would translate the #GstSegment @time field, and @sink_to_source_func
+ * should emulate how the corresponding #GstElement would translate the
+ * seek query @start and @stop values, as used in gst_element_seek(). As
+ * such, @sink_to_source_func should act as an approximate reverse of
+ * @source_to_sink_func.
+ *
+ * Note, these functions will be passed a table of time properties, as
+ * registered in ges_base_effect_register_time_property(), and their
+ * values. The functions should emulate what the translation *would* be
+ * *if* the time properties were set to the given values. They should not
+ * use the currently set values.
+ *
+ * Note that @effect must not be part of a clip, nor can it have
+ * #GESTrackElement:has-internal-source set to %TRUE.
+ *
+ * Returns: %TRUE if the translation functions were set.
+ */
+gboolean
+ges_base_effect_set_time_translation_funcs (GESBaseEffect * effect,
+ GESBaseEffectTimeTranslationFunc source_to_sink_func,
+ GESBaseEffectTimeTranslationFunc sink_to_source_func,
+ gpointer user_data, GDestroyNotify destroy)
+{
+ GESTimelineElement *element;
+ GESTrackElement *el;
+ GESBaseEffectPrivate *priv;
+
+ g_return_val_if_fail (GES_IS_BASE_EFFECT (effect), FALSE);
+
+ element = GES_TIMELINE_ELEMENT (effect);
+ el = GES_TRACK_ELEMENT (element);
+
+ g_return_val_if_fail (element->parent == NULL, FALSE);
+ g_return_val_if_fail (ges_track_element_has_internal_source (el) == FALSE,
+ FALSE);
+
+ ges_track_element_set_has_internal_source_is_forbidden (el);
+
+ priv = effect->priv;
+ if (priv->destroy_translation_data)
+ priv->destroy_translation_data (priv->translation_data);
+
+ priv->translation_data = user_data;
+ priv->destroy_translation_data = destroy;
+ priv->source_to_sink = source_to_sink_func;
+ priv->sink_to_source = sink_to_source_func;
+
+ return TRUE;
+}
+
+/**
+ * ges_base_effect_is_time_effect:
+ * @effect: A #GESBaseEffect
+ *
+ * Get whether the effect is considered a time effect or not. An effect
+ * with registered time properties or set translation functions is
+ * considered a time effect.
+ *
+ * Returns: %TRUE if @effect is considered a time effect.
+ */
+gboolean
+ges_base_effect_is_time_effect (GESBaseEffect * effect)
+{
+ GESBaseEffectPrivate *priv;
+ g_return_val_if_fail (GES_IS_BASE_EFFECT (effect), FALSE);
+
+ priv = effect->priv;
+ if (priv->time_properties || priv->source_to_sink || priv->sink_to_source)
+ return TRUE;
+ return FALSE;
+}
+
+gchar *
+ges_base_effect_get_time_property_name (GESBaseEffect * effect,
+ GObject * child, GParamSpec * pspec)
+{
+ GList *tmp;
+ for (tmp = effect->priv->time_properties; tmp; tmp = tmp->next) {
+ TimePropertyData *data = tmp->data;
+ if (data->pspec == pspec && data->child == child)
+ return g_strdup (data->property_name);
+ }
+ return NULL;
+}
+
+static void
+_gvalue_free (gpointer data)
+{
+ GValue *val = data;
+ g_value_unset (val);
+ g_free (val);
+}
+
+GHashTable *
+ges_base_effect_get_time_property_values (GESBaseEffect * effect)
+{
+ GList *tmp;
+ GHashTable *ret =
+ g_hash_table_new_full (g_str_hash, g_str_equal, g_free, _gvalue_free);
+
+ for (tmp = effect->priv->time_properties; tmp; tmp = tmp->next) {
+ TimePropertyData *data = tmp->data;
+ GValue *value = g_new0 (GValue, 1);
+
+ /* FIXME: once we move to GLib 2.60, g_object_get_property() will
+ * automatically initialize the type */
+ g_value_init (value, data->pspec->value_type);
+ g_object_get_property (data->child, data->pspec->name, value);
+
+ g_hash_table_insert (ret, g_strdup (data->property_name), value);
+ }
+
+ return ret;
+}
+
+GstClockTime
+ges_base_effect_translate_source_to_sink_time (GESBaseEffect * effect,
+ GstClockTime time, GHashTable * time_property_values)
+{
+ GESBaseEffectPrivate *priv = effect->priv;
+
+ if (!GST_CLOCK_TIME_IS_VALID (time))
+ return GST_CLOCK_TIME_NONE;
+
+ if (priv->source_to_sink)
+ return priv->source_to_sink (effect, time, time_property_values,
+ priv->translation_data);
+
+ if (time_property_values && g_hash_table_size (time_property_values))
+ GST_ERROR_OBJECT (effect, "The time effect is missing its source to "
+ "sink translation function");
+ return time;
+}
+
+GstClockTime
+ges_base_effect_translate_sink_to_source_time (GESBaseEffect * effect,
+ GstClockTime time, GHashTable * time_property_values)
+{
+ GESBaseEffectPrivate *priv = effect->priv;
+
+ if (!GST_CLOCK_TIME_IS_VALID (time))
+ return GST_CLOCK_TIME_NONE;
+
+ if (priv->sink_to_source)
+ return effect->priv->sink_to_source (effect, time, time_property_values,
+ priv->translation_data);
+
+ if (time_property_values && g_hash_table_size (time_property_values))
+ GST_ERROR_OBJECT (effect, "The time effect is missing its sink to "
+ "source translation function");
+ return time;
+}
GST_END_TEST;
+#define _assert_set_rate(element, prop_name, rate, val) \
+{ \
+ GError *error = NULL; \
+ if (G_VALUE_TYPE (&val) == G_TYPE_DOUBLE) \
+ g_value_set_double (&val, rate); \
+ else if (G_VALUE_TYPE (&val) == G_TYPE_FLOAT) \
+ g_value_set_float (&val, rate); \
+ \
+ fail_unless (ges_timeline_element_set_child_property_full ( \
+ GES_TIMELINE_ELEMENT (element), prop_name, &val, &error)); \
+ fail_if (error); \
+ g_value_reset (&val); \
+}
+
+#define _assert_fail_set_rate(element, prop_name, rate, val, code) \
+{ \
+ GError * error = NULL; \
+ if (G_VALUE_TYPE (&val) == G_TYPE_DOUBLE) \
+ g_value_set_double (&val, rate); \
+ else if (G_VALUE_TYPE (&val) == G_TYPE_FLOAT) \
+ g_value_set_float (&val, rate); \
+ \
+ fail_if (ges_timeline_element_set_child_property_full ( \
+ GES_TIMELINE_ELEMENT (element), prop_name, &val, &error)); \
+ assert_GESError (error, code); \
+ g_value_reset (&val); \
+}
+
+#define _assert_rate_equal(element, prop_name, rate, val) \
+{ \
+ gdouble found = -1.0; \
+ fail_unless (ges_timeline_element_get_child_property ( \
+ GES_TIMELINE_ELEMENT (element), prop_name, &val)); \
+ \
+ if (G_VALUE_TYPE (&val) == G_TYPE_DOUBLE) \
+ found = g_value_get_double (&val); \
+ else if (G_VALUE_TYPE (&val) == G_TYPE_FLOAT) \
+ found = g_value_get_float (&val); \
+ \
+ fail_unless (found == rate, "found %s: %g != expected: %g", found, \
+ prop_name, rate); \
+ g_value_reset (&val); \
+}
+
+GST_START_TEST (test_rate_effects_duration_limit)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESClip *clip;
+ GESTrackElement *source0, *source1;
+ GESTrackElement *overlay0, *overlay1, *videorate, *pitch;
+ GESTrack *track0, *track1;
+ gint limit_notify_count = 0;
+ GValue fval = G_VALUE_INIT;
+ GValue dval = G_VALUE_INIT;
+
+ ges_init ();
+
+ g_value_init (&fval, G_TYPE_FLOAT);
+ g_value_init (&dval, G_TYPE_DOUBLE);
+
+ timeline = ges_timeline_new ();
+ track0 = GES_TRACK (ges_video_track_new ());
+ track1 = GES_TRACK (ges_audio_track_new ());
+
+ fail_unless (ges_timeline_add_track (timeline, track0));
+ fail_unless (ges_timeline_add_track (timeline, track1));
+
+ layer = ges_timeline_append_layer (timeline);
+
+ /* place a dummy clip at the start of the layer */
+ clip = GES_CLIP (ges_test_clip_new ());
+ assert_set_start (clip, 0);
+ assert_set_duration (clip, 26);
+
+ fail_unless (ges_layer_add_clip (layer, clip));
+
+ /* the clip we will be editing overlaps first clip by 16 at its start */
+ clip = GES_CLIP (ges_test_clip_new ());
+
+ g_signal_connect (clip, "notify::duration-limit", G_CALLBACK (_count_cb),
+ &limit_notify_count);
+
+ assert_set_start (clip, 10);
+ assert_set_duration (clip, 64);
+
+ fail_unless (ges_layer_add_clip (layer, clip));
+
+ source0 =
+ ges_clip_find_track_element (clip, track0, GES_TYPE_VIDEO_TEST_SOURCE);
+ source1 =
+ ges_clip_find_track_element (clip, track1, GES_TYPE_AUDIO_TEST_SOURCE);
+
+ fail_unless (source0);
+ fail_unless (source1);
+
+ gst_object_unref (source0);
+ gst_object_unref (source1);
+
+ assert_equals_int (limit_notify_count, 0);
+ _assert_duration_limit (clip, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 0, 64, GST_CLOCK_TIME_NONE);
+
+ assert_set_inpoint (clip, 13);
+
+ assert_equals_int (limit_notify_count, 0);
+ _assert_duration_limit (clip, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 64, GST_CLOCK_TIME_NONE);
+
+ assert_set_max_duration (clip, 77);
+
+ assert_equals_int (limit_notify_count, 1);
+ _assert_duration_limit (clip, 64);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 64, 77);
+
+ /* add effects */
+ overlay0 = GES_TRACK_ELEMENT (ges_effect_new ("textoverlay"));
+ ges_track_element_set_has_internal_source (overlay0, TRUE);
+
+ videorate = GES_TRACK_ELEMENT (ges_effect_new ("videorate"));
+ fail_unless (ges_base_effect_is_time_effect (GES_BASE_EFFECT (videorate)));
+
+ overlay1 = GES_TRACK_ELEMENT (ges_effect_new ("textoverlay"));
+ ges_track_element_set_has_internal_source (overlay1, TRUE);
+
+ pitch = GES_TRACK_ELEMENT (ges_effect_new ("pitch"));
+ fail_unless (ges_base_effect_is_time_effect (GES_BASE_EFFECT (pitch)));
+
+ /* add overlay1 at highest priority */
+ _assert_add (clip, overlay1);
+
+ assert_equals_int (limit_notify_count, 1);
+ _assert_duration_limit (clip, 64);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 0, 64, GST_CLOCK_TIME_NONE);
+
+ _assert_set_rate (videorate, "rate", 4.0, dval);
+ _assert_rate_equal (videorate, "rate", 4.0, dval);
+ fail_unless (ges_track_add_element (track0, videorate));
+
+ /* cannot add videorate as it would cause the duration-limit to drop
+ * to 16, causing a full overlap */
+ /* track keeps alive */
+ fail_if (ges_container_add (GES_CONTAINER (clip),
+ GES_TIMELINE_ELEMENT (videorate)));
+
+ /* setting to 1.0 makes it work again */
+ _assert_set_rate (videorate, "rate", 1.0, dval);
+ _assert_add (clip, videorate);
+
+ assert_equals_int (limit_notify_count, 1);
+ _assert_duration_limit (clip, 64);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 1.0, dval);
+
+ /* add second overlay at lower priority */
+ _assert_add (clip, overlay0);
+
+ assert_equals_int (limit_notify_count, 1);
+ _assert_duration_limit (clip, 64);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 1.0, dval);
+
+ /* also add a pitch element in another track */
+ _assert_add (clip, pitch);
+ _assert_set_rate (pitch, "rate", 1.0, fval);
+ _assert_set_rate (pitch, "tempo", 1.0, fval);
+
+ assert_equals_int (limit_notify_count, 1);
+ _assert_duration_limit (clip, 64);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (pitch, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 1.0, dval);
+ _assert_rate_equal (pitch, "rate", 1.0, fval);
+ _assert_rate_equal (pitch, "tempo", 1.0, fval);
+
+ fail_unless (ges_track_element_get_track (overlay0) == track0);
+ fail_unless (ges_track_element_get_track (videorate) == track0);
+ fail_unless (ges_track_element_get_track (overlay1) == track0);
+ fail_unless (ges_track_element_get_track (pitch) == track1);
+
+ /* flow in track0:
+ * source0 -> overlay0 -> videorate -> overlay1 -> timeline output
+ *
+ * flow in track1:
+ * source1 -> pitch -> timeline output
+ */
+
+ /* cannot set the rates to 4.0 since this would cause a full overlap */
+ _assert_fail_set_rate (videorate, "rate", 4.0, dval,
+ GES_ERROR_INVALID_OVERLAP_IN_TRACK);
+ _assert_fail_set_rate (pitch, "rate", 4.0, fval,
+ GES_ERROR_INVALID_OVERLAP_IN_TRACK);
+ _assert_fail_set_rate (pitch, "tempo", 4.0, fval,
+ GES_ERROR_INVALID_OVERLAP_IN_TRACK);
+
+ assert_equals_int (limit_notify_count, 1);
+ _assert_duration_limit (clip, 64);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (pitch, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 1.0, dval);
+ _assert_rate_equal (pitch, "rate", 1.0, fval);
+ _assert_rate_equal (pitch, "tempo", 1.0, fval);
+
+ /* limit overlay0 */
+ assert_set_max_duration (overlay0, 91);
+
+ assert_equals_int (limit_notify_count, 1);
+ _assert_duration_limit (clip, 64);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 0, 64, 91);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (pitch, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 1.0, dval);
+ _assert_rate_equal (pitch, "rate", 1.0, fval);
+ _assert_rate_equal (pitch, "tempo", 1.0, fval);
+
+ assert_set_inpoint (overlay0, 59);
+
+ assert_equals_int (limit_notify_count, 2);
+ _assert_duration_limit (clip, 32);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 32, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 32, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 32, 77);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 59, 32, 91);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (pitch, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 1.0, dval);
+ _assert_rate_equal (pitch, "rate", 1.0, fval);
+ _assert_rate_equal (pitch, "tempo", 1.0, fval);
+
+ /* can set pitch rate to 2.0, but not videorate rate because videorate
+ * shares a track with overlay0 */
+
+ _assert_set_rate (pitch, "rate", 2.0, fval);
+ assert_equals_int (limit_notify_count, 2);
+ _assert_fail_set_rate (videorate, "rate", 2.0, dval,
+ GES_ERROR_INVALID_OVERLAP_IN_TRACK);
+ assert_equals_int (limit_notify_count, 2);
+ /* can't set tempo to 2.0 since overall effect would bring duration
+ * limit too low */
+ _assert_fail_set_rate (pitch, "tempo", 2.0, fval,
+ GES_ERROR_INVALID_OVERLAP_IN_TRACK);
+
+ assert_equals_int (limit_notify_count, 2);
+ _assert_duration_limit (clip, 32);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 32, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 32, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 32, 77);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 59, 32, 91);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (pitch, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 1.0, dval);
+ _assert_rate_equal (pitch, "rate", 2.0, fval);
+ _assert_rate_equal (pitch, "tempo", 1.0, fval);
+
+ /* cannot set in-point of clip because pitch would cause limit to go
+ * to 16 */
+ assert_fail_set_inpoint (clip, 45);
+ /* same for max-duration of source1 */
+ assert_fail_set_max_duration (source1, 45);
+
+ assert_equals_int (limit_notify_count, 2);
+ _assert_duration_limit (clip, 32);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 32, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 32, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 32, 77);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 59, 32, 91);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (pitch, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 1.0, dval);
+ _assert_rate_equal (pitch, "rate", 2.0, fval);
+ _assert_rate_equal (pitch, "tempo", 1.0, fval);
+
+ /* can set rate to 0.5 */
+ _assert_set_rate (videorate, "rate", 0.5, dval);
+
+ /* no change yet, since pitch rate is still 2.0 */
+ assert_equals_int (limit_notify_count, 2);
+ _assert_duration_limit (clip, 32);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 32, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 32, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 32, 77);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 59, 32, 91);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (pitch, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 0.5, dval);
+ _assert_rate_equal (pitch, "rate", 2.0, fval);
+ _assert_rate_equal (pitch, "tempo", 1.0, fval);
+
+ _assert_set_rate (pitch, "rate", 0.5, fval);
+
+ assert_equals_int (limit_notify_count, 3);
+ /* duration-limit is 64 because overlay0 only has 32 nanoseconds of
+ * content, stretched to 64 by videorate */
+ _assert_duration_limit (clip, 64);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 32, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 32, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 32, 77);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 59, 32, 91);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (pitch, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 0.5, dval);
+ _assert_rate_equal (pitch, "rate", 0.5, fval);
+ _assert_rate_equal (pitch, "tempo", 1.0, fval);
+
+ /* setting the max-duration of the sources does not change the limit
+ * since the limit on overlay0 is fine.
+ * Note that pitch handles the unlimited duration (GST_CLOCK_TIME_NONE)
+ * without any problems */
+ assert_set_max_duration (clip, GST_CLOCK_TIME_NONE);
+
+ assert_equals_int (limit_notify_count, 3);
+ _assert_duration_limit (clip, 64);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 32, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 32, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 32, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 59, 32, 91);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (pitch, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 0.5, dval);
+ _assert_rate_equal (pitch, "rate", 0.5, fval);
+ _assert_rate_equal (pitch, "tempo", 1.0, fval);
+
+ assert_set_max_duration (clip, 77);
+
+ assert_equals_int (limit_notify_count, 3);
+ _assert_duration_limit (clip, 64);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 32, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 32, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 32, 77);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 59, 32, 91);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (pitch, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 0.5, dval);
+ _assert_rate_equal (pitch, "rate", 0.5, fval);
+ _assert_rate_equal (pitch, "tempo", 1.0, fval);
+
+ /* limit overlay1. It should not be changes by the videorate element
+ * since it acts at a lower priority
+ * first make it last longer, so no change in duration-limit */
+
+ assert_set_max_duration (overlay1, 81);
+
+ assert_equals_int (limit_notify_count, 3);
+ _assert_duration_limit (clip, 64);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 32, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 32, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 32, 77);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 59, 32, 91);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 0, 32, 81);
+ CHECK_OBJECT_PROPS_MAX (pitch, 10, 0, 32, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 0.5, dval);
+ _assert_rate_equal (pitch, "rate", 0.5, fval);
+ _assert_rate_equal (pitch, "tempo", 1.0, fval);
+
+ /* now make it shorter */
+ assert_set_inpoint (overlay1, 51);
+
+ assert_equals_int (limit_notify_count, 4);
+ _assert_duration_limit (clip, 30);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 30, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 30, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 30, 77);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 59, 30, 91);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 30, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 51, 30, 81);
+ CHECK_OBJECT_PROPS_MAX (pitch, 10, 0, 30, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 0.5, dval);
+ _assert_rate_equal (pitch, "rate", 0.5, fval);
+ _assert_rate_equal (pitch, "tempo", 1.0, fval);
+
+ /* remove the overlay0 limit */
+ assert_set_max_duration (overlay0, GST_CLOCK_TIME_NONE);
+
+ /* no change because of overlay1 */
+ assert_equals_int (limit_notify_count, 4);
+ _assert_duration_limit (clip, 30);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 30, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 30, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 30, 77);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 59, 30, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 30, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 51, 30, 81);
+ CHECK_OBJECT_PROPS_MAX (pitch, 10, 0, 30, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 0.5, dval);
+ _assert_rate_equal (pitch, "rate", 0.5, fval);
+ _assert_rate_equal (pitch, "tempo", 1.0, fval);
+
+ assert_set_max_duration (overlay1, GST_CLOCK_TIME_NONE);
+
+ assert_equals_int (limit_notify_count, 5);
+ _assert_duration_limit (clip, 128);
+ /* can set up to the limit */
+ assert_set_duration (clip, 128);
+
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 128, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 128, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 128, 77);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 59, 128, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 128, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 51, 128, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (pitch, 10, 0, 128, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 0.5, dval);
+ _assert_rate_equal (pitch, "rate", 0.5, fval);
+ _assert_rate_equal (pitch, "tempo", 1.0, fval);
+
+ /* tempo contributes the same factor as rate */
+
+ _assert_set_rate (pitch, "tempo", 2.0, fval);
+
+ assert_equals_int (limit_notify_count, 6);
+ _assert_duration_limit (clip, 64);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 59, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 51, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (pitch, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 0.5, dval);
+ _assert_rate_equal (pitch, "rate", 0.5, fval);
+ _assert_rate_equal (pitch, "tempo", 2.0, fval);
+
+ _assert_set_rate (videorate, "rate", 0.1, dval);
+ assert_equals_int (limit_notify_count, 6);
+ _assert_set_rate (pitch, "tempo", 0.5, dval);
+
+ assert_equals_int (limit_notify_count, 7);
+ _assert_duration_limit (clip, 256);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 59, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 51, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (pitch, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 0.1, dval);
+ _assert_rate_equal (pitch, "rate", 0.5, fval);
+ _assert_rate_equal (pitch, "tempo", 0.5, fval);
+
+ _assert_set_rate (pitch, "tempo", 1.0, dval);
+ assert_equals_int (limit_notify_count, 8);
+ _assert_set_rate (videorate, "rate", 0.5, dval);
+
+ assert_equals_int (limit_notify_count, 8);
+ _assert_duration_limit (clip, 128);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 59, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 51, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (pitch, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 0.5, dval);
+ _assert_rate_equal (pitch, "rate", 0.5, fval);
+ _assert_rate_equal (pitch, "tempo", 1.0, fval);
+
+ /* make videorate in-active */
+ fail_unless (ges_track_element_set_active (videorate, FALSE));
+
+ assert_equals_int (limit_notify_count, 9);
+ _assert_duration_limit (clip, 64);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 59, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 51, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (pitch, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 0.5, dval);
+ _assert_rate_equal (pitch, "rate", 0.5, fval);
+ _assert_rate_equal (pitch, "tempo", 1.0, fval);
+
+ fail_unless (ges_track_element_set_active (videorate, TRUE));
+
+ assert_equals_int (limit_notify_count, 10);
+ _assert_duration_limit (clip, 128);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 59, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 51, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (pitch, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 0.5, dval);
+ _assert_rate_equal (pitch, "rate", 0.5, fval);
+ _assert_rate_equal (pitch, "tempo", 1.0, fval);
+
+ /* removing pitch, same effect as making inactive */
+ _assert_remove (clip, pitch);
+
+ assert_equals_int (limit_notify_count, 11);
+ _assert_duration_limit (clip, 64);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 59, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 51, 64, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 0.5, dval);
+
+ /* no max-duration will give unlimited limit */
+ assert_set_max_duration (source1, GST_CLOCK_TIME_NONE);
+
+ assert_equals_int (limit_notify_count, 12);
+ _assert_duration_limit (clip, 128);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 64, 77);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 59, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 51, 64, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 0.5, dval);
+
+ assert_set_max_duration (source0, GST_CLOCK_TIME_NONE);
+
+ assert_equals_int (limit_notify_count, 13);
+ _assert_duration_limit (clip, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (clip, 10, 13, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (source0, 10, 13, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (source1, 10, 13, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay0, 10, 59, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (videorate, 10, 0, 64, GST_CLOCK_TIME_NONE);
+ CHECK_OBJECT_PROPS_MAX (overlay1, 10, 51, 64, GST_CLOCK_TIME_NONE);
+ _assert_rate_equal (videorate, "rate", 0.5, dval);
+
+ gst_object_unref (timeline);
+
+ g_value_unset (&fval);
+ g_value_unset (&dval);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
+
GST_START_TEST (test_children_properties_contain)
{
GESTimeline *timeline;
tcase_add_test (tc_chain, test_children_max_duration);
tcase_add_test (tc_chain, test_duration_limit);
tcase_add_test (tc_chain, test_can_set_duration_limit);
+ tcase_add_test (tc_chain, test_rate_effects_duration_limit);
tcase_add_test (tc_chain, test_children_properties_contain);
tcase_add_test (tc_chain, test_children_properties_change);
tcase_add_test (tc_chain, test_copy_paste_children_properties);