From a342d14ba9392514ba0bfeb2e01330412b042794 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Fri, 1 May 2020 22:56:38 -0400 Subject: [PATCH] ges: Add support for sources reverse playback It introduces a `reverse` property at the `nlesource` level which will basically reverse playback of the clip. We guarantee that outside that clip, the data flow looks like it was playing forward as we add an 'identity' element with `single-segment=true`. This start making use of the newly introduced concept of `CHILD_PROP_FLAG_SET_ON_ALL_INSTANCES` as it is natural that doing `source_clip.set_child_property("reverse", True)` will make the whole clip go reverse, meaning that all the GESSource children properties will be set. To set an individual source "reverse" child property as the desired effect. Fixes: https://gitlab.freedesktop.org/gstreamer/gst-editing-services/-/issues/24 Depends on: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/451 Part-of: --- .../docs/gst_plugins_cache.json | 15 +- .../gst-editing-services/ges/ges-source.c | 31 ++++ .../ges/ges-timeline-element.c | 22 ++- .../plugins/nle/nleghostpad.c | 6 + .../plugins/nle/nlesource.c | 151 ++++++++++++++---- .../plugins/nle/nlesource.h | 2 + .../check_reverse_source.validatetest | 25 +++ .../log-audiosink-sink-expected | 107 +++++++++++++ .../log-audiosrcbin-src-expected | 8 + .../log-videosink-sink-expected | 37 +++++ .../log-videosrcbin-src-expected | 7 + ...t_child_prop_on_all_instances.validatetest | 39 +++++ .../testsuites/ges.testslist | 2 + 13 files changed, 409 insertions(+), 43 deletions(-) create mode 100644 subprojects/gst-integration-testsuites/ges/scenarios/check_reverse_source.validatetest create mode 100644 subprojects/gst-integration-testsuites/ges/scenarios/check_reverse_source/flow-expectations/log-audiosink-sink-expected create mode 100644 subprojects/gst-integration-testsuites/ges/scenarios/check_reverse_source/flow-expectations/log-audiosrcbin-src-expected create mode 100644 subprojects/gst-integration-testsuites/ges/scenarios/check_reverse_source/flow-expectations/log-videosink-sink-expected create mode 100644 subprojects/gst-integration-testsuites/ges/scenarios/check_reverse_source/flow-expectations/log-videosrcbin-src-expected create mode 100644 subprojects/gst-integration-testsuites/ges/scenarios/timelineelement.set_child_prop_on_all_instances.validatetest diff --git a/subprojects/gst-editing-services/docs/gst_plugins_cache.json b/subprojects/gst-editing-services/docs/gst_plugins_cache.json index b3186450e0..ff1713ef93 100644 --- a/subprojects/gst-editing-services/docs/gst_plugins_cache.json +++ b/subprojects/gst-editing-services/docs/gst_plugins_cache.json @@ -265,7 +265,20 @@ "presence": "always" } }, - "properties": {}, + "properties": { + "reverse": { + "blurb": "Whether to playback the source reverse or not", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "false", + "mutable": "null", + "readable": true, + "type": "gboolean", + "writable": true + } + }, "rank": "none", "signals": {} }, diff --git a/subprojects/gst-editing-services/ges/ges-source.c b/subprojects/gst-editing-services/ges/ges-source.c index 9ee6ce8d29..9b65289480 100644 --- a/subprojects/gst-editing-services/ges/ges-source.c +++ b/subprojects/gst-editing-services/ges/ges-source.c @@ -266,6 +266,36 @@ ges_source_get_rendering_smartly (GESSource * source) return source->priv->is_rendering_smartly; } +static GstElement * +ges_source_create_nle_object (GESTrackElement * self) +{ + GParamSpec *pspec; + GstElement *nleobject; + + nleobject = + GES_TRACK_ELEMENT_CLASS (ges_source_parent_class)->create_gnl_object + (self); + + if (!nleobject) + return NULL; + + pspec = + g_object_class_find_property (G_OBJECT_GET_CLASS (nleobject), "reverse"); + g_assert (pspec); + + + if (!ges_timeline_element_add_child_property_full (GES_TIMELINE_ELEMENT + (self), NULL, pspec, G_OBJECT (nleobject), + GES_TIMELINE_ELEMENT_CHILD_PROP_FLAG_SET_ON_ALL_INSTANCES)) + GST_ERROR_OBJECT (self, + "Could not register the child property 'reverse' for %" GST_PTR_FORMAT, + nleobject); + + g_param_spec_unref (pspec); + + return nleobject; +} + static void ges_source_dispose (GObject * object) { @@ -290,6 +320,7 @@ ges_source_class_init (GESSourceClass * klass) track_class->nleobject_factorytype = "nlesource"; track_class->create_element = NULL; + track_class->create_gnl_object = ges_source_create_nle_object; object_class->dispose = ges_source_dispose; GES_TRACK_ELEMENT_CLASS_DEFAULT_HAS_INTERNAL_SOURCE (klass) = TRUE; diff --git a/subprojects/gst-editing-services/ges/ges-timeline-element.c b/subprojects/gst-editing-services/ges/ges-timeline-element.c index 3bb80ae0f3..c5136f58d4 100644 --- a/subprojects/gst-editing-services/ges/ges-timeline-element.c +++ b/subprojects/gst-editing-services/ges/ges-timeline-element.c @@ -408,17 +408,16 @@ ges_timeline_element_finalize (GObject * self) } static void -_child_prop_spec (ChildPropSpec * childprop) +_child_prop_spec_free (ChildPropSpec * childprop) { g_object_freeze_notify (childprop->child); if (childprop->handler_id) g_signal_handler_disconnect (childprop->child, childprop->handler_id); g_object_thaw_notify (childprop->child); - if (handler->child != (GObject *) handler->self && - handler->child != (GObject *) handler->owner) - gst_object_unref (handler->child); - g_free (handler); + if (childprop->child != (GObject *) childprop->self && + childprop->child != (GObject *) childprop->owner) + gst_object_unref (childprop->child); } static gboolean @@ -439,7 +438,7 @@ ges_timeline_element_init (GESTimelineElement * self) self->priv->children_props = g_array_new (TRUE, TRUE, sizeof (ChildPropSpec)); g_array_set_clear_func (self->priv->children_props, - (GDestroyNotify) _child_prop_spec); + (GDestroyNotify) _child_prop_spec_free); } static void @@ -809,7 +808,7 @@ child_prop_changed_cb (GObject * child, GParamSpec * arg, static gboolean set_child_property (GESTimelineElement * self, ChildPropSpec * childprop, - const GValue * value, GError **error) + const GValue * value, GError ** error) { GESTimelineElement *setter = self; GESTimelineElementClass *klass; @@ -823,7 +822,7 @@ set_child_property (GESTimelineElement * self, ChildPropSpec * childprop, if (klass->set_child_property_full) return klass->set_child_property_full (setter, childprop->child, - childprop->pspec, value, error); + childprop->pspec, value, error); g_assert (klass->set_child_property); klass->set_child_property (setter, childprop->child, childprop->pspec, @@ -901,8 +900,7 @@ ges_timeline_element_add_child_property_full (GESTimelineElement * self, child, pspec->name); signame = g_strconcat ("notify::", pspec->name, NULL); - handler = (ChildPropHandler *) g_new0 (ChildPropHandler, 1); - handler->self = self; + childprop.self = self; if (child == G_OBJECT (self) || child == G_OBJECT (owner)) childprop.child = child; else @@ -2321,12 +2319,12 @@ ges_timeline_element_remove_child_property_full (GESTimelineElement * self, g_array_set_clear_func (self->priv->children_props, NULL); g_array_remove_index (self->priv->children_props, index); g_array_set_clear_func (self->priv->children_props, - (GDestroyNotify) _child_prop_spec); + (GDestroyNotify) _child_prop_spec_free); g_signal_emit (self, ges_timeline_element_signals[CHILD_PROPERTY_REMOVED], 0, handler_copy.child, handler_copy.pspec); - _child_prop_spec (&handler_copy); + _child_prop_spec_free (&handler_copy); return TRUE; } diff --git a/subprojects/gst-editing-services/plugins/nle/nleghostpad.c b/subprojects/gst-editing-services/plugins/nle/nleghostpad.c index eeefdc0997..06ccb1e3ce 100644 --- a/subprojects/gst-editing-services/plugins/nle/nleghostpad.c +++ b/subprojects/gst-editing-services/plugins/nle/nleghostpad.c @@ -72,6 +72,12 @@ nle_object_translate_incoming_seek (NleObject * object, GstEvent * event) if (G_UNLIKELY (format != GST_FORMAT_TIME)) goto invalid_format; + + if (NLE_IS_SOURCE (object) && NLE_SOURCE (object)->reverse) { + GST_DEBUG_OBJECT (object, "Reverse playback! %d", seqnum); + rate = -rate; + } + /* convert cur */ ncurtype = GST_SEEK_TYPE_SET; if (G_LIKELY ((curtype == GST_SEEK_TYPE_SET) diff --git a/subprojects/gst-editing-services/plugins/nle/nlesource.c b/subprojects/gst-editing-services/plugins/nle/nlesource.c index db5316ce4f..4c94885e6b 100644 --- a/subprojects/gst-editing-services/plugins/nle/nlesource.c +++ b/subprojects/gst-editing-services/plugins/nle/nlesource.c @@ -38,12 +38,20 @@ GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY); +enum +{ + PROP_0, + PROP_REVERSE, + PROP_LAST, +}; + GST_DEBUG_CATEGORY_STATIC (nlesource); #define GST_CAT_DEFAULT nlesource #define _do_init \ GST_DEBUG_CATEGORY_INIT (nlesource, "nlesource", GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin Source Element"); #define nle_source_parent_class parent_class + struct _NleSourcePrivate { gboolean dispose_has_run; @@ -62,6 +70,10 @@ struct _NleSourcePrivate GstEvent *seek_event; guint32 flush_seqnum; gulong probeid; + + + /* Identity automatically created to handle reverse playback */ + GstElement *identity; }; G_DEFINE_TYPE_WITH_CODE (NleSource, nle_source, NLE_TYPE_OBJECT, @@ -80,6 +92,57 @@ nle_source_control_element_func (NleSource * source, GstElement * element); static GstStateChangeReturn nle_source_change_state (GstElement * element, GstStateChange transition); +static gboolean +nle_source_commit (NleObject * object, gboolean recurse) +{ + NleSource *self = NLE_SOURCE (object); + + if (!NLE_OBJECT_CLASS (parent_class)->commit (object, recurse)) + return FALSE; + + self->reverse = self->pending_reverse; + g_object_set (self->priv->identity, "single-segment", self->reverse, NULL); + + return TRUE; +} + +static void +nle_source_get_property (GObject * object, guint property_id, + GValue * value, GParamSpec * pspec) +{ + NleSource *self = NLE_SOURCE (object); + + GST_OBJECT_LOCK (self); + switch (property_id) { + case PROP_REVERSE: + g_value_set_boolean (value, self->pending_reverse); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } + GST_OBJECT_UNLOCK (self); +} + +static void +nle_source_set_property (GObject * object, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + NleSource *self = NLE_SOURCE (object); + + GST_OBJECT_LOCK (self); + switch (property_id) { + case PROP_REVERSE: + self->pending_reverse = g_value_get_boolean (value); + if (self->pending_reverse != self->reverse) + nle_object_set_commit_needed ((NleObject *) self); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } + GST_OBJECT_UNLOCK (self); +} + + static void nle_source_class_init (NleSourceClass * klass) { @@ -93,6 +156,20 @@ nle_source_class_init (NleSourceClass * klass) gstbin_class = (GstBinClass *) klass; nleobject_class = (NleObjectClass *) klass; + gobject_class->get_property = nle_source_get_property; + gobject_class->set_property = nle_source_set_property; + + /** + * NleSource:reverse: + * @reverse: Whether to playback the source reverse or not + * + * Since: 1.26 + */ + g_object_class_install_property (gobject_class, PROP_REVERSE, + g_param_spec_boolean ("reverse", "Reverse", + "Whether to playback the source reverse or not", FALSE, + G_PARAM_READWRITE)); + gst_element_class_set_static_metadata (gstelement_class, "GNonLin Source", "Filter/Editor", "Manages source elements", @@ -106,6 +183,7 @@ nle_source_class_init (NleSourceClass * klass) klass->control_element = GST_DEBUG_FUNCPTR (nle_source_control_element_func); nleobject_class->prepare = GST_DEBUG_FUNCPTR (nle_source_prepare); + nleobject_class->commit = GST_DEBUG_FUNCPTR (nle_source_commit); gstbin_class->add_element = GST_DEBUG_FUNCPTR (nle_source_add_element); gstbin_class->remove_element = GST_DEBUG_FUNCPTR (nle_source_remove_element); @@ -139,10 +217,19 @@ srcpad_probe_cb (GstPad * pad, GstPadProbeInfo * info, NleSource * source) static void nle_source_init (NleSource * source) { + NleSourcePrivate *priv; + NleObject *nleobject = NLE_OBJECT (source); + GST_OBJECT_FLAG_SET (source, NLE_OBJECT_SOURCE); source->element = NULL; - source->priv = nle_source_get_instance_private (source); - g_mutex_init (&source->priv->seek_lock); + priv = source->priv = nle_source_get_instance_private (source); + priv->identity = gst_element_factory_make ("identity", NULL); + + gst_bin_add (GST_BIN (source), priv->identity); + nle_object_ghost_pad_set_target (nleobject, nleobject->srcpad, + priv->identity->srcpads->data); + + g_mutex_init (&priv->seek_lock); gst_pad_add_probe (NLE_OBJECT_SRC (source), GST_PAD_PROBE_TYPE_EVENT_UPSTREAM, (GstPadProbeCallback) srcpad_probe_cb, @@ -158,6 +245,8 @@ nle_source_dispose (GObject * object) NleObject *nleobject = (NleObject *) object; NleSource *source = (NleSource *) object; NleSourcePrivate *priv = source->priv; + GstElement *tmpidentity; + GST_DEBUG_OBJECT (object, "dispose"); @@ -173,26 +262,18 @@ nle_source_dispose (GObject * object) } GST_OBJECT_UNLOCK (object); - - if (source->element) { - gst_object_unref (source->element); - source->element = NULL; - } + gst_clear_object (&source->element); + tmpidentity = priv->identity; + priv->identity = NULL; + gst_bin_remove (GST_BIN (source), tmpidentity); priv->dispose_has_run = TRUE; - if (priv->ghostedpad) - nle_object_ghost_pad_set_target (nleobject, nleobject->srcpad, NULL); + nle_object_ghost_pad_set_target (nleobject, nleobject->srcpad, NULL); - if (priv->staticpad) { - gst_object_unref (priv->staticpad); - priv->staticpad = NULL; - } + gst_clear_object (&priv->staticpad); g_mutex_lock (&priv->seek_lock); - if (priv->seek_event) { - gst_event_unref (priv->seek_event); - priv->seek_event = NULL; - } + gst_clear_event (&priv->seek_event); g_mutex_unlock (&priv->seek_lock); G_OBJECT_CLASS (parent_class)->dispose (object); @@ -225,12 +306,14 @@ element_pad_added_cb (GstElement * element G_GNUC_UNUSED, GstPad * pad, } gst_caps_unref (srccaps); - priv->ghostedpad = pad; - GST_DEBUG_OBJECT (nleobject, "SET target %" GST_PTR_FORMAT, pad); - nle_object_ghost_pad_set_target (nleobject, nleobject->srcpad, pad); - - GST_DEBUG_OBJECT (source, "Using pad pad %s:%s as a target now!", - GST_DEBUG_PAD_NAME (pad)); + if (gst_pad_link (pad, priv->identity->sinkpads->data) != GST_PAD_LINK_OK) { + GST_ERROR_OBJECT (source, "Could not link pads: %" GST_PTR_FORMAT + " and %" GST_PTR_FORMAT, pad, priv->identity->sinkpads->data); + } else { + GST_DEBUG_OBJECT (source, "Linked pads: %" GST_PTR_FORMAT + " and %" GST_PTR_FORMAT, pad, priv->identity->sinkpads->data); + priv->ghostedpad = pad; + } } static void @@ -238,7 +321,6 @@ element_pad_removed_cb (GstElement * element G_GNUC_UNUSED, GstPad * pad, NleSource * source) { NleSourcePrivate *priv = source->priv; - NleObject *nleobject = (NleObject *) source; GST_DEBUG_OBJECT (source, "pad %s:%s (controlled pad %s:%s)", GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (priv->ghostedpad)); @@ -249,9 +331,9 @@ element_pad_removed_cb (GstElement * element G_GNUC_UNUSED, GstPad * pad, GST_DEBUG_OBJECT (source, "Clearing up ghostpad"); - if (nleobject->srcpad) - nle_object_ghost_pad_set_target (NLE_OBJECT (source), nleobject->srcpad, - NULL); + if (priv->identity && !gst_pad_unlink (pad, priv->identity->sinkpads->data)) + GST_ERROR_OBJECT (source, "Could not unlink pads: %" GST_PTR_FORMAT + " and %" GST_PTR_FORMAT, pad, priv->identity->sinkpads->data); priv->ghostedpad = NULL; } else { GST_DEBUG_OBJECT (source, "The removed pad is NOT our controlled pad"); @@ -343,6 +425,9 @@ nle_source_control_element_func (NleSource * source, GstElement * element) NleSourcePrivate *priv = source->priv; GstPad *pad = NULL; + if (element == priv->identity) + return TRUE; + g_return_val_if_fail (source->element == NULL, FALSE); GST_DEBUG_OBJECT (source, "element: %" GST_PTR_FORMAT ", source->element:%" @@ -353,8 +438,13 @@ nle_source_control_element_func (NleSource * source, GstElement * element) if (get_valid_src_pad (source, source->element, &pad)) { priv->staticpad = pad; - nle_object_ghost_pad_set_target (NLE_OBJECT (source), - NLE_OBJECT_SRC (source), pad); + if (gst_pad_link (pad, priv->identity->sinkpads->data) != GST_PAD_LINK_OK) { + GST_ERROR_OBJECT (source, "Could not link pads: %" GST_PTR_FORMAT + " and %" GST_PTR_FORMAT, pad, priv->identity->sinkpads->data); + } else { + GST_DEBUG_OBJECT (source, "Linked pads: %" GST_PTR_FORMAT + " and %" GST_PTR_FORMAT, pad, priv->identity->sinkpads->data); + } priv->dynamicpads = FALSE; } else { priv->dynamicpads = has_dynamic_srcpads (element); @@ -544,7 +634,8 @@ nle_source_prepare (NleObject * object) stop = object->inpoint + object->duration; g_mutex_lock (&source->priv->seek_lock); - source->priv->seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME, + source->priv->seek_event = gst_event_new_seek (source->reverse ? -1.0 : 1.0, + GST_FORMAT_TIME, GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, start, GST_SEEK_TYPE_SET, stop); g_mutex_unlock (&source->priv->seek_lock); diff --git a/subprojects/gst-editing-services/plugins/nle/nlesource.h b/subprojects/gst-editing-services/plugins/nle/nlesource.h index 6fcb1af4a2..1a6f7169c3 100644 --- a/subprojects/gst-editing-services/plugins/nle/nlesource.h +++ b/subprojects/gst-editing-services/plugins/nle/nlesource.h @@ -45,6 +45,8 @@ typedef struct _NleSourcePrivate NleSourcePrivate; struct _NleSource { NleObject parent; + gboolean reverse; + gboolean pending_reverse; /* controlled source element, acces with gst_bin_[add|remove]_element */ GstElement *element; diff --git a/subprojects/gst-integration-testsuites/ges/scenarios/check_reverse_source.validatetest b/subprojects/gst-integration-testsuites/ges/scenarios/check_reverse_source.validatetest new file mode 100644 index 0000000000..96a4709b8d --- /dev/null +++ b/subprojects/gst-integration-testsuites/ges/scenarios/check_reverse_source.validatetest @@ -0,0 +1,25 @@ +meta, + tool = "ges-launch-$(gst_api_version)", + args = { + --videosink, "fakevideosink name=videosink qos=false", + --audiosink, "fakeaudiosink name=audiosink qos=false", + --video-caps, "video/x-raw,width=1280,height=720,framerate=30/1,format=I420", + --audio-caps, "audio/x-raw,rate=44100,format=S32LE,channels=2,layout=interleaved", + }, + handles-states = true, + ignore-eos = true, + configs = { + # We can't record buffers on sources as we have no timing guarantees + "$(validateflow), pad=videosrcbin:src, ignored-fields=\"stream-start={stream-id, group-id, stream}\"", + "$(validateflow), pad=videosink:sink, record-buffers=true, ignored-fields=\"stream-start={stream-id, group-id, stream}\"", + "$(validateflow), pad=audiosrcbin:src, ignored-fields=\"stream-start={stream-id, group-id, stream}\"", + "$(validateflow), pad=audiosink:sink, record-buffers=true, ignored-fields=\"stream-start={stream-id, group-id, stream}\"", + } + +add-clip, name=c0, asset-id="time-overlay,disable-timecodestamper=true", layer-priority=0, type=GESTestClip, start=0, duration=1.0 +set-child-properties, element-name=c0, pattern=blue, valignment=center, halignment=center, reverse=true +play + +pause, on-message=eos +check-position, expected-position=1000000001 +stop diff --git a/subprojects/gst-integration-testsuites/ges/scenarios/check_reverse_source/flow-expectations/log-audiosink-sink-expected b/subprojects/gst-integration-testsuites/ges/scenarios/check_reverse_source/flow-expectations/log-audiosink-sink-expected new file mode 100644 index 0000000000..1c918cfb9d --- /dev/null +++ b/subprojects/gst-integration-testsuites/ges/scenarios/check_reverse_source/flow-expectations/log-audiosink-sink-expected @@ -0,0 +1,107 @@ +event stream-start: GstEventStreamStart, flags=(GstStreamFlags)GST_STREAM_FLAG_NONE; +event caps: audio/x-raw, channel-mask=(bitmask)0x0000000000000003, channels=(int)2, format=(string)S32LE, layout=(string)interleaved, rate=(int)44100; +event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, stop=0:00:01.000000000, flags=0x01, time=0:00:00.000000000, base=0:00:00.000000000, position=0:00:00.000000000 +buffer: pts=0:00:00.000000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.010000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.020000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.030000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.040000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.050000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.060000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.070000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.080000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.090000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.100000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.110000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.120000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.130000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.140000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.150000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.160000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.170000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.180000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.190000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.200000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.210000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.220000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.230000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.240000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.250000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.260000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.270000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.280000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.290000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.300000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.310000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.320000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.330000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.340000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.350000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.360000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.370000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.380000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.390000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.400000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.410000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.420000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.430000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.440000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.450000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.460000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.470000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.480000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.490000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.500000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.510000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.520000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.530000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.540000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.550000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.560000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.570000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.580000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.590000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.600000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.610000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.620000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.630000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.640000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.650000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.660000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.670000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.680000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.690000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.700000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.710000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.720000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.730000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.740000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.750000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.760000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.770000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.780000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.790000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.800000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.810000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.820000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.830000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.840000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.850000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.860000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.870000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.880000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.890000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.900000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.910000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.920000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.930000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.940000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.950000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.960000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.970000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.980000000, dur=0:00:00.010000000 +buffer: pts=0:00:00.990000000, dur=0:00:00.010000000 +event segment: format=TIME, start=0:00:01.000000000, offset=0:00:00.000000000, stop=0:00:01.000000001, flags=0x01, time=0:00:01.000000000, base=0:00:01.000000000, position=0:00:01.000000000 +buffer: pts=0:00:01.000000000, dur=0:00:00.000000001, flags=gap +event gap: GstEventGap, duration=(guint64)18446744073709551615, timestamp=(guint64)1000022675; +event eos: (no structure) diff --git a/subprojects/gst-integration-testsuites/ges/scenarios/check_reverse_source/flow-expectations/log-audiosrcbin-src-expected b/subprojects/gst-integration-testsuites/ges/scenarios/check_reverse_source/flow-expectations/log-audiosrcbin-src-expected new file mode 100644 index 0000000000..f66b32d7c3 --- /dev/null +++ b/subprojects/gst-integration-testsuites/ges/scenarios/check_reverse_source/flow-expectations/log-audiosrcbin-src-expected @@ -0,0 +1,8 @@ +event stream-start: GstEventStreamStart, flags=(GstStreamFlags)GST_STREAM_FLAG_NONE; +event caps: audio/x-raw, channel-mask=(bitmask)0x0000000000000003, channels=(int)2, format=(string)S32LE, layout=(string)interleaved, rate=(int)44100; +event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, stop=none, time=0:00:00.000000000, base=0:00:00.000000000, position=0:00:00.000000000 +event tag: GstTagList-stream, taglist=(taglist)"taglist\,\ description\=\(string\)\"audiotest\\\ wave\"\;"; +event flush-start: (no structure) +event flush-stop: GstEventFlushStop, reset-time=(boolean)true; +event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, stop=0:00:01.000000000, rate=-1.000000, flags=0x01, time=0:00:00.000000000, base=0:00:00.000000000, position=0:00:01.000000000 +event eos: (no structure) diff --git a/subprojects/gst-integration-testsuites/ges/scenarios/check_reverse_source/flow-expectations/log-videosink-sink-expected b/subprojects/gst-integration-testsuites/ges/scenarios/check_reverse_source/flow-expectations/log-videosink-sink-expected new file mode 100644 index 0000000000..0dbaa790ca --- /dev/null +++ b/subprojects/gst-integration-testsuites/ges/scenarios/check_reverse_source/flow-expectations/log-videosink-sink-expected @@ -0,0 +1,37 @@ +event stream-start: GstEventStreamStart, flags=(GstStreamFlags)GST_STREAM_FLAG_NONE; +event caps: video/x-raw, colorimetry=(string)bt709, format=(string)I420, framerate=(fraction)30/1, height=(int)720, width=(int)1280; +event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, stop=0:00:01.000000000, flags=0x01, time=0:00:00.000000000, base=0:00:00.000000000, position=none +buffer: pts=0:00:00.000000000, dur=0:00:00.033333333, meta=GstVideoMeta +buffer: pts=0:00:00.033333333, dur=0:00:00.033333333, meta=GstVideoMeta +buffer: pts=0:00:00.066666666, dur=0:00:00.033333334, meta=GstVideoMeta +buffer: pts=0:00:00.100000000, dur=0:00:00.033333333, meta=GstVideoMeta +buffer: pts=0:00:00.133333333, dur=0:00:00.033333333, meta=GstVideoMeta +buffer: pts=0:00:00.166666666, dur=0:00:00.033333334, meta=GstVideoMeta +buffer: pts=0:00:00.200000000, dur=0:00:00.033333333, meta=GstVideoMeta +buffer: pts=0:00:00.233333333, dur=0:00:00.033333333, meta=GstVideoMeta +buffer: pts=0:00:00.266666666, dur=0:00:00.033333334, meta=GstVideoMeta +buffer: pts=0:00:00.300000000, dur=0:00:00.033333333, meta=GstVideoMeta +buffer: pts=0:00:00.333333333, dur=0:00:00.033333333, meta=GstVideoMeta +buffer: pts=0:00:00.366666666, dur=0:00:00.033333334, meta=GstVideoMeta +buffer: pts=0:00:00.400000000, dur=0:00:00.033333333, meta=GstVideoMeta +buffer: pts=0:00:00.433333333, dur=0:00:00.033333333, meta=GstVideoMeta +buffer: pts=0:00:00.466666666, dur=0:00:00.033333334, meta=GstVideoMeta +buffer: pts=0:00:00.500000000, dur=0:00:00.033333333, meta=GstVideoMeta +buffer: pts=0:00:00.533333333, dur=0:00:00.033333333, meta=GstVideoMeta +buffer: pts=0:00:00.566666666, dur=0:00:00.033333334, meta=GstVideoMeta +buffer: pts=0:00:00.600000000, dur=0:00:00.033333333, meta=GstVideoMeta +buffer: pts=0:00:00.633333333, dur=0:00:00.033333333, meta=GstVideoMeta +buffer: pts=0:00:00.666666666, dur=0:00:00.033333334, meta=GstVideoMeta +buffer: pts=0:00:00.700000000, dur=0:00:00.033333333, meta=GstVideoMeta +buffer: pts=0:00:00.733333333, dur=0:00:00.033333333, meta=GstVideoMeta +buffer: pts=0:00:00.766666666, dur=0:00:00.033333334, meta=GstVideoMeta +buffer: pts=0:00:00.800000000, dur=0:00:00.033333333, meta=GstVideoMeta +buffer: pts=0:00:00.833333333, dur=0:00:00.033333333, meta=GstVideoMeta +buffer: pts=0:00:00.866666666, dur=0:00:00.033333334, meta=GstVideoMeta +buffer: pts=0:00:00.900000000, dur=0:00:00.033333333, meta=GstVideoMeta +buffer: pts=0:00:00.933333333, dur=0:00:00.033333333, meta=GstVideoMeta +buffer: pts=0:00:00.966666666, dur=0:00:00.033333334, meta=GstVideoMeta +event caps: video/x-raw, colorimetry=(string)bt601, format=(string)I420, framerate=(fraction)30/1, height=(int)720, width=(int)1280; +event segment: format=TIME, start=0:00:01.000000000, offset=0:00:00.000000000, stop=0:00:01.000000001, flags=0x01, time=0:00:01.000000000, base=0:00:01.000000000, position=none +buffer: pts=0:00:01.000000000, dur=0:00:00.000000001, meta=GstVideoMeta +event eos: (no structure) diff --git a/subprojects/gst-integration-testsuites/ges/scenarios/check_reverse_source/flow-expectations/log-videosrcbin-src-expected b/subprojects/gst-integration-testsuites/ges/scenarios/check_reverse_source/flow-expectations/log-videosrcbin-src-expected new file mode 100644 index 0000000000..70fc6bf616 --- /dev/null +++ b/subprojects/gst-integration-testsuites/ges/scenarios/check_reverse_source/flow-expectations/log-videosrcbin-src-expected @@ -0,0 +1,7 @@ +event stream-start: GstEventStreamStart, flags=(GstStreamFlags)GST_STREAM_FLAG_NONE; +event caps: video/x-raw, format=(string)AYUV, framerate=(fraction)30/1, height=(int)720, interlace-mode=(string)progressive, multiview-mode=(string)mono, pixel-aspect-ratio=(fraction)1/1, width=(int)1280; +event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, stop=none, time=0:00:00.000000000, base=0:00:00.000000000, position=0:00:00.000000000 +event flush-start: (no structure) +event flush-stop: GstEventFlushStop, reset-time=(boolean)true; +event segment: format=TIME, start=0:00:00.000000000, offset=0:00:00.000000000, stop=0:00:01.000000000, rate=-1.000000, flags=0x01, time=0:00:00.000000000, base=0:00:00.000000000, position=0:00:01.000000000 +event eos: (no structure) diff --git a/subprojects/gst-integration-testsuites/ges/scenarios/timelineelement.set_child_prop_on_all_instances.validatetest b/subprojects/gst-integration-testsuites/ges/scenarios/timelineelement.set_child_prop_on_all_instances.validatetest new file mode 100644 index 0000000000..5e59808492 --- /dev/null +++ b/subprojects/gst-integration-testsuites/ges/scenarios/timelineelement.set_child_prop_on_all_instances.validatetest @@ -0,0 +1,39 @@ +meta, + tool = "ges-launch-$(gst_api_version)", + handles-states = true + +add-clip, name=c0, asset-id="time-overlay", layer-priority=0, type=GESTestClip, start=0, duration=1.0 +check-child-properties, element-name=videotestsource0, reverse=false +check-child-properties, element-name=audiotestsource0, reverse=false + +set-child-properties, element-name=c0, pattern=blue, valignment=center, halignment=center, reverse=true +check-child-properties, element-name=videotestsource0, reverse=true +check-child-properties, element-name=audiotestsource0, reverse=true + +set-child-properties, element-name=videotestsource0, reverse=false +check-child-properties, element-name=videotestsource0, reverse=false +check-child-properties, element-name=audiotestsource0, reverse=true + + +add-clip, name=c1, asset-id=GESTestClip, layer-priority=0, type=GESTestClip, start=1.0, duration=2.0 +group, containers={c0, c1}, container-name=g0 + +set-child-properties, element-name=g0, reverse=true +check-child-properties, element-name=videotestsource0, reverse=true +check-child-properties, element-name=audiotestsource0, reverse=true +check-child-properties, element-name=videotestsource1, reverse=true +check-child-properties, element-name=audiotestsource1, reverse=true + +set-child-properties, element-name=audiotestsource1, reverse=false +check-child-properties, element-name=videotestsource0, reverse=true +check-child-properties, element-name=audiotestsource0, reverse=true +check-child-properties, element-name=videotestsource1, reverse=true +check-child-properties, element-name=audiotestsource1, reverse=false + +ungroup-container, container-name=g0 +check-child-properties, element-name=videotestsource0, reverse=true +check-child-properties, element-name=audiotestsource0, reverse=true +check-child-properties, element-name=videotestsource1, reverse=true +check-child-properties, element-name=audiotestsource1, reverse=false + +stop diff --git a/subprojects/gst-integration-testsuites/testsuites/ges.testslist b/subprojects/gst-integration-testsuites/testsuites/ges.testslist index 93e7544c42..dcafaf11d2 100644 --- a/subprojects/gst-integration-testsuites/testsuites/ges.testslist +++ b/subprojects/gst-integration-testsuites/testsuites/ges.testslist @@ -762,3 +762,5 @@ ges.test.play_two_nested_back_to_back ges.test.seek_on_stack_change_on_internal_subtimeline ges.test.videoscale_effect ges.test.backward_playback_with_start +ges.test.check_reverse_source +ges.test.timelineelement.set_child_prop_on_all_instances