/tests/check/ges/mixers
/tests/check/ges/timelineedition
/tests/check/ges/timelinegroup
+/tests/check/ges/tempochange
/tests/check/ges/uriclip
/tests/check/integration
/tests/benchmarks/timeline
/tests/check/nle/nlecomposition
/tests/check/nle/nleoperation
/tests/check/nle/nlesource
+/tests/check/nle/tempochange
/tools/ges-launch-1.0
/tests/check/ges/project
/**
* ges_clip_split:
* @clip: the #GESClip to split
- * @position: a #GstClockTime representing the position at which to split
+ * @position: a #GstClockTime representing the timeline position at which to split
*
- * The function modifies @clip, and creates another #GESClip so
- * we have two clips at the end, splitted at the time specified by @position.
- * The newly created clip will be added to the same layer as @clip is in.
- * This implies that @clip must be in a #GESLayer for the operation to
- * be possible.
+ * The function modifies @clip, and creates another #GESClip so we have two
+ * clips at the end, splitted at the time specified by @position, as a position
+ * in the timeline (not in the clip to be split). For example, if
+ * ges_clip_split is called on a 4-second clip playing from 0:01.00 until
+ * 0:05.00, with a split position of 0:02.00, this will result in one clip of 1
+ * second and one clip of 3 seconds, not in two clips of 2 seconds.
+ *
+ * The newly created clip will be added to the same layer as @clip is in. This
+ * implies that @clip must be in a #GESLayer for the operation to be possible.
+ *
+ * This method supports clips playing at a different tempo than one second per
+ * second. For example, splitting a clip with a #GESEffect 'pitch tempo=1.5'
+ * four seconds after it starts, will set the inpoint of the new clip to six
+ * seconds after that of the clip to split. For this, the rate-changing
+ * property must be registered using @ges_effect_class_register_rate_property;
+ * for the 'pitch' plugin, this is already done.
*
* Returns: (transfer none): The newly created #GESClip resulting from the
* splitting
{
GList *tmp;
GESClip *new_object;
- GstClockTime start, inpoint, duration;
+ GstClockTime start, inpoint, duration, old_duration, new_duration;
+ gdouble media_duration_factor;
g_return_val_if_fail (GES_IS_CLIP (clip), NULL);
g_return_val_if_fail (clip->priv->layer, NULL);
GST_DEBUG_OBJECT (new_object, "New 'splitted' clip");
/* Set new timing properties on the Clip */
+ media_duration_factor =
+ ges_timeline_element_get_media_duration_factor (GES_TIMELINE_ELEMENT
+ (clip));
+ new_duration = duration + start - position;
+ old_duration = position - start;
_set_start0 (GES_TIMELINE_ELEMENT (new_object), position);
_set_inpoint0 (GES_TIMELINE_ELEMENT (new_object),
- _INPOINT (clip) + duration - (duration + start - position));
- _set_duration0 (GES_TIMELINE_ELEMENT (new_object),
- duration + start - position);
+ inpoint + old_duration * media_duration_factor);
+ _set_duration0 (GES_TIMELINE_ELEMENT (new_object), new_duration);
/* We do not want the timeline to create again TrackElement-s */
ges_clip_set_moving_from_layer (new_object, TRUE);
/* Set 'new' track element timing propeties */
_set_start0 (GES_TIMELINE_ELEMENT (new_trackelement), position);
_set_inpoint0 (GES_TIMELINE_ELEMENT (new_trackelement),
- inpoint + duration - (duration + start - position));
- _set_duration0 (GES_TIMELINE_ELEMENT (new_trackelement),
- duration + start - position);
+ inpoint + old_duration * media_duration_factor);
+ _set_duration0 (GES_TIMELINE_ELEMENT (new_trackelement), new_duration);
ges_container_add (GES_CONTAINER (new_object),
GES_TIMELINE_ELEMENT (new_trackelement));
position - start + inpoint);
}
- _set_duration0 (GES_TIMELINE_ELEMENT (clip), position - _START (clip));
+ _set_duration0 (GES_TIMELINE_ELEMENT (clip), old_duration);
return new_object;
}
obj_bg_class->create_element = ges_effect_create_element;
+ klass->rate_properties = NULL;
+ ges_effect_class_register_rate_property (klass, "pitch", "tempo");
+ ges_effect_class_register_rate_property (klass, "pitch", "rate");
+
/**
* GESEffect:bin-description:
*
return effect;
}
+
+static int
+property_name_compare (gconstpointer s1, gconstpointer s2)
+{
+ return g_strcmp0 ((const gchar *) s1, (const gchar *) s2);
+}
+
+/**
+ * ges_effect_class_register_rate_property:
+ * @klass: Instance of the GESEffectClass
+ * @element_name: Name of the GstElement that changes the rate
+ * @property_name: Name of the property that changes the rate
+ *
+ * Register an element that can change the rate at which media is playing. The
+ * property type must be float or double, and must be a factor of the rate,
+ * i.e. a value of 2.0 must mean that the media plays twice as fast. For
+ * example, this is true for the properties 'rate' and 'tempo' of the element
+ * 'pitch', which is already registered by default. By registering the element,
+ * timeline duration can be correctly converted into media duration, allowing
+ * the right segment seeks to be sent to the sources.
+ *
+ * A reference to the GESEffectClass can be obtained as follows:
+ * GES_EFFECT_CLASS (g_type_class_ref (GES_TYPE_EFFECT));
+ *
+ * Returns: whether the rate property was succesfully registered. When this
+ * method returns false, a warning is emitted with more information.
+ */
+gboolean
+ges_effect_class_register_rate_property (GESEffectClass * klass,
+ const gchar * element_name, const gchar * property_name)
+{
+ GstElementFactory *element_factory = NULL;
+ GstElement *element = NULL;
+ GParamSpec *pspec = NULL;
+ gchar *full_property_name = NULL;
+ GType param_type;
+ gboolean res = FALSE;
+
+ element_factory = gst_element_factory_find (element_name);
+ if (element_factory == NULL) {
+ GST_WARNING
+ ("Did not add rate property '%s' for element '%s': the element factory could not be found",
+ property_name, element_name);
+ goto fail;
+ }
+
+ element = gst_element_factory_create (element_factory, NULL);
+ if (element == NULL) {
+ GST_WARNING
+ ("Did not add rate property '%s' for element '%s': the element could not be constructed",
+ property_name, element_name);
+ goto fail;
+ }
+
+ pspec =
+ g_object_class_find_property (G_OBJECT_GET_CLASS (element),
+ property_name);
+ if (pspec == NULL) {
+ GST_WARNING
+ ("Did not add rate property '%s' for element '%s': the element did not have the property name specified",
+ property_name, element_name);
+ goto fail;
+ }
+
+ param_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
+ if (param_type != G_TYPE_FLOAT && param_type != G_TYPE_DOUBLE) {
+ GST_WARNING
+ ("Did not add rate property '%s' for element '%s': the property is not of float or double type",
+ property_name, element_name);
+ goto fail;
+ }
+
+ full_property_name = g_strdup_printf ("%s::%s",
+ g_type_name (gst_element_factory_get_element_type (element_factory)),
+ property_name);
+
+ if (g_list_find_custom (klass->rate_properties, full_property_name,
+ property_name_compare) == NULL) {
+ klass->rate_properties =
+ g_list_append (klass->rate_properties, full_property_name);
+ GST_DEBUG ("Added rate property %s", full_property_name);
+ } else {
+ g_free (full_property_name);
+ }
+
+ res = TRUE;
+
+fail:
+ if (element_factory != NULL)
+ gst_object_unref (element_factory);
+ if (element != NULL)
+ gst_object_unref (element);
+ if (pspec != NULL)
+ g_param_spec_unref (pspec);
+
+ return res;
+}
{
/*< private > */
GESBaseEffectClass parent_class;
+
+ GList *rate_properties;
+
/* Padding for API extension */
gpointer _ges_reserved[GES_PADDING];
GESEffect*
ges_effect_new (const gchar * bin_description);
+gboolean
+ges_effect_class_register_rate_property (GESEffectClass *klass, const gchar *element, const gchar *property_name);
+
G_END_DECLS
#endif /* _GES_EFFECT */
G_GNUC_INTERNAL GESTitleSource * ges_title_source_new (void);
G_GNUC_INTERNAL GESVideoTestSource * ges_video_test_source_new (void);
+/****************************************************
+ * GESTimelineElement *
+ ****************************************************/
+G_GNUC_INTERNAL gdouble ges_timeline_element_get_media_duration_factor(GESTimelineElement *self);
+
/******************************
* GESMultiFile internal API *
******************************/
#include "ges-extractable.h"
#include "ges-meta-container.h"
#include "ges-internal.h"
+#include "ges-effect.h"
#include <string.h>
#include <gobject/gvaluecollector.h>
return g_object_ref (res);
}
+
+/* Internal */
+gdouble
+ges_timeline_element_get_media_duration_factor (GESTimelineElement * self)
+{
+ gdouble media_duration_factor;
+ GESEffectClass *class;
+ GList *props;
+
+ media_duration_factor = 1.0;
+
+ class = GES_EFFECT_CLASS (g_type_class_ref (GES_TYPE_EFFECT));
+
+ for (props = class->rate_properties; props != NULL; props = props->next) {
+ GObject *child;
+ GParamSpec *pspec;
+ if (ges_timeline_element_lookup_child (self, props->data, &child, &pspec)) {
+ if (G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_FLOAT) {
+ gfloat rate_change;
+ g_object_get (child, pspec->name, &rate_change, NULL);
+ media_duration_factor *= rate_change;
+ } else if (G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_DOUBLE) {
+ gdouble rate_change;
+ g_object_get (child, pspec->name, &rate_change, NULL);
+ media_duration_factor *= rate_change;
+ } else {
+ GST_WARNING_OBJECT (self,
+ "Rate property %s in child %" GST_PTR_FORMAT
+ " is of unsupported type %s", pspec->name, child,
+ G_VALUE_TYPE_NAME (pspec->value_type));
+ }
+
+ gst_object_unref (child);
+ g_param_spec_unref (pspec);
+
+ GST_DEBUG_OBJECT (self,
+ "Added rate changing property %s, set to value %lf",
+ (const char *) props->data, media_duration_factor);
+ }
+ }
+
+ g_type_class_unref (class);
+ return media_duration_factor;
+}
{
GESTrackElementClass *class;
GstElement *nleobject;
+ gdouble media_duration_factor;
gchar *tmp;
GESTrackElement *object = GES_TRACK_ELEMENT (gobject);
"priority", GES_TIMELINE_ELEMENT_PRIORITY (object),
"active", object->active, NULL);
+ media_duration_factor =
+ ges_timeline_element_get_media_duration_factor (GES_TIMELINE_ELEMENT
+ (object));
+ g_object_set (object->priv->nleobject,
+ "media-duration-factor", media_duration_factor, NULL);
+
G_OBJECT_CLASS (ges_track_element_parent_class)->constructed (gobject);
}
{
NleObject *newobj;
NleObject *newparent;
+ GNode *node_it;
GstPad *srcpad = NULL, *sinkpad = NULL;
GstEvent *translated_seek;
GST_DEBUG_OBJECT (comp, "newobj:%s",
GST_ELEMENT_NAME ((GstElement *) newobj));
+ newobj->recursive_media_duration_factor = 1.0f;
+ for (node_it = node; node_it != NULL; node_it = node_it->parent) {
+ NleObject *object = (NleObject *) node_it->data;
+ newobj->recursive_media_duration_factor *= object->media_duration_factor;
+ }
+
srcpad = NLE_OBJECT_SRC (newobj);
gst_bin_add (GST_BIN (comp->priv->current_bin), GST_ELEMENT (newobj));
PROP_ACTIVE,
PROP_CAPS,
PROP_EXPANDABLE,
+ PROP_MEDIA_DURATION_FACTOR,
PROP_LAST
};
properties[PROP_EXPANDABLE]);
/**
+ * NleObject:media-duration-factor
+ *
+ * Indicates the relative rate caused by this object, in other words, the
+ * relation between the rate of media entering and leaving this object. I.e.
+ * if object pulls data at twice the speed it sends it (e.g. `pitch
+ * tempo=2.0`), this value is set to 2.0.
+ */
+ properties[PROP_MEDIA_DURATION_FACTOR] =
+ g_param_spec_double ("media-duration-factor", "Media duration factor",
+ "The relative rate caused by this object", 0.01, G_MAXDOUBLE,
+ 1.0, G_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class, PROP_MEDIA_DURATION_FACTOR,
+ properties[PROP_MEDIA_DURATION_FACTOR]);
+
+ /**
* NleObject::commit
* @object: a #NleObject
* @recurse: Whether to commit recursiverly into (NleComposition) children of
object->segment_rate = 1.0;
object->segment_start = -1;
object->segment_stop = -1;
+ object->media_duration_factor = 1.0;
+ object->recursive_media_duration_factor = 1.0;
object->srcpad = nle_object_ghost_pad_no_target (object,
"src", GST_PAD_SRC,
if (G_UNLIKELY ((otime >= object->stop))) {
GST_DEBUG_OBJECT (object, "ObjectTime is after stop");
if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (object->inpoint)))
- *mtime = object->inpoint + object->duration;
+ *mtime =
+ object->inpoint +
+ object->duration * object->recursive_media_duration_factor;
else
- *mtime = object->stop - object->start;
+ *mtime =
+ (object->stop -
+ object->start) * object->recursive_media_duration_factor;
return FALSE;
}
if (G_UNLIKELY (object->inpoint == GST_CLOCK_TIME_NONE)) {
/* no time shifting, for live sources ? */
- *mtime = otime - object->start;
+ *mtime = (otime - object->start) * object->recursive_media_duration_factor;
} else {
- *mtime = otime - object->start + object->inpoint;
+ *mtime =
+ (otime - object->start) * object->recursive_media_duration_factor +
+ object->inpoint;
}
GST_DEBUG_OBJECT (object, "Returning MediaTime : %" GST_TIME_FORMAT,
else
GST_OBJECT_FLAG_UNSET (nleobject, NLE_OBJECT_EXPANDABLE);
break;
+ case PROP_MEDIA_DURATION_FACTOR:
+ nleobject->media_duration_factor = g_value_get_double (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
case PROP_EXPANDABLE:
g_value_set_boolean (value, NLE_OBJECT_IS_EXPANDABLE (object));
break;
+ case PROP_MEDIA_DURATION_FACTOR:
+ g_value_set_double (value, nleobject->media_duration_factor);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
gint64 segment_start;
gint64 segment_stop;
+ /* the rate at which this object speeds up or slows down media */
+ gdouble media_duration_factor;
+ /* the rate at which this object and all of its children speed up or slow
+ * down media */
+ gdouble recursive_media_duration_factor;
+
gboolean in_composition;
};
ges/group\
ges/project\
ges/track\
+ ges/tempochange \
nle/simple \
nle/complex \
nle/nleoperation \
- nle/nlecomposition
+ nle/nlecomposition \
+ nle/tempochange
# We do not support sources outside composition
# nle/nlesource
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2016 Sjors Gielen <mixml-ges@sjorsgielen.nl>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "test-utils.h"
+#include <ges/ges.h>
+#include <gst/check/gstcheck.h>
+#include <plugins/nle/nleobject.h>
+
+GST_START_TEST (test_tempochange)
+{
+ GESTimeline *timeline;
+ GESLayer *layer;
+ GESTrack *track_audio;
+ GESEffect *effect;
+ GESTestClip *clip;
+ GESClip *clip2, *clip3;
+ GList *tmp;
+ int found;
+
+ ges_init ();
+
+ timeline = ges_timeline_new ();
+ layer = ges_layer_new ();
+ track_audio = GES_TRACK (ges_audio_track_new ());
+
+ ges_timeline_add_track (timeline, track_audio);
+ ges_timeline_add_layer (timeline, layer);
+
+ /* Add a 9-second clip */
+ clip = ges_test_clip_new ();
+ g_object_set (clip, "duration", 9 * GST_SECOND, NULL);
+ ges_layer_add_clip (layer, (GESClip *) clip);
+
+ /* Split it after 3 seconds */
+ clip2 = ges_clip_split ((GESClip *) clip, 3 * GST_SECOND);
+
+ /* Add pitch effect to play 1.5 times faster */
+ effect = ges_effect_new ("pitch tempo=1.5");
+
+ fail_unless (GES_IS_EFFECT (effect));
+ fail_unless (ges_container_add (GES_CONTAINER (clip2),
+ GES_TIMELINE_ELEMENT (effect)));
+ fail_unless (ges_track_element_get_track (GES_TRACK_ELEMENT (effect)) !=
+ NULL);
+
+ assert_equals_int (GES_TRACK_ELEMENT (effect)->active, TRUE);
+
+ /* Split clip again after 6 seconds (note: this is timeline time) */
+ clip3 = ges_clip_split (clip2, 6 * GST_SECOND);
+
+ // note: start and duration are counted in timeline time, while
+ // inpoint is counted in media time.
+ fail_unless_equals_int64 (_START (clip), 0 * GST_SECOND);
+ fail_unless_equals_int64 (_INPOINT (clip), 0 * GST_SECOND);
+ fail_unless_equals_int64 (_DURATION (clip), 3 * GST_SECOND);
+
+ fail_unless_equals_int64 (_START (clip2), 3 * GST_SECOND);
+ fail_unless_equals_int64 (_INPOINT (clip2), 3 * GST_SECOND);
+ fail_unless_equals_int64 (_DURATION (clip2), 3 * GST_SECOND);
+
+ fail_unless_equals_int64 (_START (clip3), 6 * GST_SECOND);
+ fail_unless_equals_int64 (_INPOINT (clip3), 7.5 * GST_SECOND);
+ fail_unless_equals_int64 (_DURATION (clip3), 3 * GST_SECOND);
+
+#define MDF_OF_CLIP(x) \
+ ((NleObject *) ges_track_element_get_nleobject \
+ (ges_clip_find_track_element (GES_CLIP (x), track_audio, \
+ G_TYPE_NONE)))->media_duration_factor
+
+ fail_unless_equals_float (MDF_OF_CLIP (clip), 1.0);
+ fail_unless_equals_float (MDF_OF_CLIP (clip2), 1.5);
+ fail_unless_equals_float (MDF_OF_CLIP (clip3), 1.5);
+
+ found = 0;
+
+ for (tmp = GES_CONTAINER_CHILDREN (clip2); tmp; tmp = tmp->next) {
+ // A clip may have children other than the effect we added. As such, we
+ // need to find the child which is the pitch effect in order to check its
+ // value.
+ GstElement *nle = ges_track_element_get_nleobject (tmp->data);
+ if (strncmp (GST_OBJECT_NAME (nle), "GESEffect:", 10) == 0) {
+ fail_if (found);
+ found = 1;
+ fail_unless_equals_float (((NleObject *) nle)->media_duration_factor,
+ 1.5);
+ }
+ }
+
+ fail_unless (found);
+
+ found = 0;
+ for (tmp = GES_CONTAINER_CHILDREN (clip3); tmp; tmp = tmp->next) {
+ GstElement *nle = ges_track_element_get_nleobject (tmp->data);
+ if (strncmp (GST_OBJECT_NAME (nle), "GESEffect:", 10) == 0) {
+ fail_if (found);
+ found = 1;
+ fail_unless_equals_float (((NleObject *) nle)->media_duration_factor,
+ 1.5);
+ }
+ }
+
+ fail_unless (found);
+
+ ges_layer_remove_clip (layer, (GESClip *) clip);
+ ges_layer_remove_clip (layer, clip2);
+ ges_layer_remove_clip (layer, clip3);
+
+ gst_object_unref (timeline);
+}
+
+GST_END_TEST;
+
+static Suite *
+ges_suite (void)
+{
+ Suite *s = suite_create ("ges");
+ TCase *tc_chain = tcase_create ("tempochange");
+ GstPluginFeature *pitch = gst_registry_find_feature (gst_registry_get (),
+ "pitch", GST_TYPE_ELEMENT_FACTORY);
+
+ if (pitch) {
+ gst_object_unref (pitch);
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_tempochange);
+ }
+
+ return s;
+}
+
+GST_CHECK_MAIN (ges);
--- /dev/null
+/* GStreamer Editing Services
+ * Copyright (C) 2016 Sjors Gielen <mixml-ges@sjorsgielen.nl>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "common.h"
+#include "plugins/nle/nleobject.h"
+
+GST_START_TEST (test_tempochange)
+{
+ GstElement *pipeline;
+ GstElement *comp, *source1, *def, *sink, *oper;
+ GList *segments = NULL;
+ GstBus *bus;
+ GstMessage *message;
+ gboolean carry_on, ret = FALSE;
+ CollectStructure *collect;
+ GstPad *sinkpad;
+
+ pipeline = gst_pipeline_new ("test_pipeline");
+ comp =
+ gst_element_factory_make_or_warn ("nlecomposition", "test_composition");
+
+ gst_element_set_state (comp, GST_STATE_READY);
+
+ sink = gst_element_factory_make_or_warn ("fakesink", "sink");
+ gst_bin_add_many (GST_BIN (pipeline), comp, sink, NULL);
+
+ gst_element_link (comp, sink);
+
+ /*
+ source1
+ Start : 0s
+ Duration : 2s
+ Priority : 2
+ */
+
+ source1 = audiotest_bin_src ("source1", 0, 2 * GST_SECOND, 2, 2);
+
+ /*
+ def (default source)
+ Priority = G_MAXUINT32
+ */
+ def =
+ audiotest_bin_src ("default", 0 * GST_SECOND, 0 * GST_SECOND, G_MAXUINT32,
+ 1);
+ g_object_set (def, "expandable", TRUE, NULL);
+
+ /* Operation */
+ oper = new_operation ("oper", "identity", 0, 2 * GST_SECOND, 1);
+ fail_if (oper == NULL);
+ ((NleObject *) oper)->media_duration_factor = 2.0;
+
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+ ASSERT_OBJECT_REFCOUNT (def, "default", 1);
+ ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
+
+ /* Add source 1 */
+
+ nle_composition_add (GST_BIN (comp), source1);
+ nle_composition_add (GST_BIN (comp), def);
+ nle_composition_add (GST_BIN (comp), oper);
+ commit_and_wait (comp, &ret);
+ check_start_stop_duration (source1, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+ check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+ check_start_stop_duration (oper, 0, 2 * GST_SECOND, 2 * GST_SECOND);
+
+ /* Define expected segments */
+ segments = g_list_append (segments,
+ segment_new (1.0, GST_FORMAT_TIME, 0 * GST_SECOND, 4.0 * GST_SECOND, 0));
+ collect = g_new0 (CollectStructure, 1);
+ collect->comp = comp;
+ collect->sink = sink;
+
+ collect->expected_segments = segments;
+ collect->keep_expected_segments = FALSE;
+
+ sinkpad = gst_element_get_static_pad (sink, "sink");
+ gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
+ (GstPadProbeCallback) sinkpad_probe, collect, NULL);
+
+ bus = gst_element_get_bus (GST_ELEMENT (pipeline));
+
+ GST_DEBUG ("Setting pipeline to PAUSED");
+ ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE);
+
+ GST_DEBUG ("Let's poll the bus");
+
+ carry_on = TRUE;
+ while (carry_on) {
+ message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
+ if (message) {
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_ASYNC_DONE:
+ {
+ carry_on = FALSE;
+ GST_DEBUG ("Pipeline reached PAUSED, stopping polling");
+ break;
+ }
+ case GST_MESSAGE_EOS:
+ {
+ GST_WARNING ("Saw EOS");
+
+ fail_if (TRUE);
+ }
+ case GST_MESSAGE_ERROR:
+ fail_error_message (message);
+ default:
+ break;
+ }
+ gst_mini_object_unref (GST_MINI_OBJECT (message));
+ }
+ }
+
+ fail_if (((NleObject *) source1)->media_duration_factor != 1.0f);
+ fail_if (((NleObject *) source1)->recursive_media_duration_factor != 2.0f);
+ fail_if (((NleObject *) oper)->media_duration_factor != 2.0f);
+ fail_if (((NleObject *) oper)->recursive_media_duration_factor != 2.0f);
+
+ GST_DEBUG ("Setting pipeline to READY");
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_READY) == GST_STATE_CHANGE_FAILURE);
+
+ fail_if (collect->expected_segments != NULL);
+
+ fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
+ GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
+
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (pipeline, "main pipeline", 1, 2);
+ gst_object_unref (pipeline);
+ ASSERT_OBJECT_REFCOUNT_BETWEEN (bus, "main bus", 1, 2);
+ gst_object_unref (bus);
+
+ g_free (collect);
+}
+
+GST_END_TEST;
+
+static Suite *
+gnonlin_suite (void)
+{
+ Suite *s = suite_create ("nle");
+ TCase *tc_chain = tcase_create ("tempochange");
+
+ ges_init ();
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_tempochange);
+
+ return s;
+}
+
+GST_CHECK_MAIN (gnonlin)