ges: Also track children props 'duplicates' in TimelineElement

We used to only track the first one but this was wrong, so we start
tracking all the children properties here, adapting the test which
was already thought for this to be implemented.

At the same time add some flags to determine how children properties
need to be handled adding a mode that means that all duplicated
children props will be set together when the user sets that particular
child property. This is going to be tested in a following commit.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/918>
This commit is contained in:
Thibault Saunier 2020-05-01 11:33:38 -04:00 committed by GStreamer Marge Bot
parent 9b43892f6e
commit 38d60c9f43
6 changed files with 250 additions and 192 deletions

View file

@ -211,7 +211,8 @@ _add_childs_child_property (GESTimelineElement * container_child,
* instance who the property comes from */
gboolean res =
ges_timeline_element_add_child_property_full (GES_TIMELINE_ELEMENT
(container), container_child, property, prop_child);
(container), container_child, property, prop_child,
GES_TIMELINE_ELEMENT_CHILD_PROP_FLAG_INHERIT);
if (!res)
GST_INFO_OBJECT (container, "Could not register the child property '%s' "
"of our child %" GES_FORMAT " for the object %" GST_PTR_FORMAT,
@ -233,10 +234,14 @@ _ges_container_add_child_properties (GESContainer * container,
for (i = 0; i < n_props; i++) {
GParamSpec *property = child_props[i];
GObject *prop_child =
ges_timeline_element_get_child_from_child_property (child, property);
if (prop_child)
_add_childs_child_property (child, prop_child, property, container);
GList *tmp, *children =
ges_timeline_element_get_children_from_child_property (child, property);
for (tmp = children; tmp; tmp = tmp->next)
ges_timeline_element_add_child_property_full (GES_TIMELINE_ELEMENT
(container), child, property, tmp->data,
GES_TIMELINE_ELEMENT_CHILD_PROP_FLAG_INHERIT);
g_list_free (children);
g_param_spec_unref (property);
}
@ -247,37 +252,8 @@ static void
_remove_childs_child_property (GESTimelineElement * container_child,
GObject * prop_child, GParamSpec * property, GESContainer * container)
{
/* NOTE: some children may share the same GParamSpec. Currently, only
* the first such child added will have its children properties
* successfully registered for the container (even though the GObject
* child who the properties belong to will be a different instance). As
* such, we only want to remove the child property if it corresponds to
* the same instance that the parent container has.
* E.g. if we add child1 and child2, that have the same (or some
* overlapping) children properties. And child1 is added before child2,
* then child2's overlapping children properties would not be registered.
* If we remove child2, we do *not* want to removed the child properties
* for child1 because they belong to a GObject instance that we still
* have in our control.
* If we remove child1, we *do* want to remove the child properties for
* child1, even though child2 may overlap with some of them, because we
* are loosing the specific GObject instance that it belongs to!
* We could try and register the ones that match for the other children.
* However, it is probably simpler to change
* ges_timeline_element_add_child_property_full to accept the same
* GParamSpec, for different instances.
*/
GESTimelineElement *element = GES_TIMELINE_ELEMENT (container);
GObject *our_prop_child =
ges_timeline_element_get_child_from_child_property (element, property);
if (our_prop_child == prop_child)
ges_timeline_element_remove_child_property (element, property);
else
GST_INFO_OBJECT (container, "Not removing child property '%s' for child"
" %" GES_FORMAT " because it derives from the object %" GST_PTR_FORMAT
"(%p) rather than the object %" GST_PTR_FORMAT "(%p)", property->name,
GES_ARGS (container_child), prop_child, prop_child, our_prop_child,
our_prop_child);
ges_timeline_element_remove_child_property_full (GES_TIMELINE_ELEMENT
(container), property, prop_child);
}
static void
@ -295,10 +271,13 @@ _ges_container_remove_child_properties (GESContainer * container,
for (i = 0; i < n_props; i++) {
GParamSpec *property = child_props[i];
GObject *prop_child =
ges_timeline_element_get_child_from_child_property (child, property);
if (prop_child)
_remove_childs_child_property (child, prop_child, property, container);
GList *tmp, *children =
ges_timeline_element_get_children_from_child_property (child, property);
for (tmp = children; tmp; tmp = tmp->next)
ges_timeline_element_remove_child_property_full (GES_TIMELINE_ELEMENT
(container), property, tmp->data);
g_list_free (children);
g_param_spec_unref (property);
}

View file

@ -551,18 +551,32 @@ typedef enum
} GESTimelineElementFlags;
G_GNUC_INTERNAL GESTimelineElement * ges_timeline_element_peak_toplevel (GESTimelineElement * self);
typedef enum
{
GES_TIMELINE_ELEMENT_CHILD_PROP_FLAG_NONE = (1 << 0),
/* Inherit flags from the owner child property registration */
GES_TIMELINE_ELEMENT_CHILD_PROP_FLAG_INHERIT = (1 << 1),
/* When setting child property, ensure to set value on every registered instances */
GES_TIMELINE_ELEMENT_CHILD_PROP_FLAG_SET_ON_ALL_INSTANCES = (1 << 2),
} GESTimelineElementChildPropertyFlags;
G_GNUC_INTERNAL GESTimelineElement * ges_timeline_element_get_copied_from (GESTimelineElement *self);
G_GNUC_INTERNAL GESTimelineElementFlags ges_timeline_element_flags (GESTimelineElement *self);
G_GNUC_INTERNAL void ges_timeline_element_set_flags (GESTimelineElement *self, GESTimelineElementFlags flags);
G_GNUC_INTERNAL gboolean ges_timeline_element_add_child_property_full (GESTimelineElement *self,
GESTimelineElement *owner,
GParamSpec *pspec,
GObject *child);
GObject *child,
GESTimelineElementChildPropertyFlags flags);
G_GNUC_INTERNAL GObject * ges_timeline_element_get_child_from_child_property (GESTimelineElement * self,
GParamSpec * pspec);
G_GNUC_INTERNAL GList * ges_timeline_element_get_children_from_child_property (GESTimelineElement * self,
GParamSpec * pspec);
G_GNUC_INTERNAL GParamSpec ** ges_timeline_element_get_children_properties (GESTimelineElement * self,
guint * n_properties);
G_GNUC_INTERNAL gboolean ges_timeline_element_remove_child_property_full (GESTimelineElement * self,
GParamSpec * pspec,
GObject *child);
#define ELEMENT_FLAGS(obj) (ges_timeline_element_flags (GES_TIMELINE_ELEMENT(obj)))
#define ELEMENT_SET_FLAG(obj,flag) (ges_timeline_element_set_flags(GES_TIMELINE_ELEMENT(obj), (ELEMENT_FLAGS(obj) | (flag))))

View file

@ -301,7 +301,7 @@ static void
ges_test_clip_class_init (GESTestClipClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GESClipClass *timobj_class = GES_CLIP_CLASS (klass);
GESClipClass *clip_class = GES_CLIP_CLASS (klass);
object_class->get_property = ges_test_clip_get_property;
object_class->set_property = ges_test_clip_set_property;
@ -347,7 +347,7 @@ ges_test_clip_class_init (GESTestClipClass * klass)
g_param_spec_boolean ("mute", "Mute", "Mute audio track",
FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
timobj_class->create_track_element = ges_test_clip_create_track_element;
clip_class->create_track_element = ges_test_clip_create_track_element;
}
static void

View file

@ -165,19 +165,18 @@ static GParamSpec *properties[PROP_LAST] = { NULL, };
typedef struct
{
GObject *child;
GParamSpec *pspec;
GESTimelineElement *owner;
gulong handler_id;
GESTimelineElement *self;
} ChildPropHandler;
GESTimelineElementChildPropertyFlags flags;
} ChildPropSpec;
struct _GESTimelineElementPrivate
{
gboolean serialize;
/* We keep a link between properties name and elements internally
* The hashtable should look like
* {GParamaSpec ---> child}*/
GHashTable *children_props;
GArray *children_props;
GESTimelineElement *copied_from;
@ -210,6 +209,31 @@ _set_child_property (GESTimelineElement * self G_GNUC_UNUSED, GObject * child,
g_object_set_property (child, pspec->name, value);
}
static ChildPropSpec *
_find_child_prop (GESTimelineElement * self,
GParamSpec * pspec, GObject * child, gint * index)
{
gint i;
for (i = 0; i < self->priv->children_props->len; i++) {
ChildPropSpec *childprop =
&g_array_index (self->priv->children_props, ChildPropSpec, i);
if (child && childprop->child != child) {
continue;
}
if (ges_pspec_hash (childprop->pspec) == ges_pspec_hash (pspec)) {
if (index)
*index = i;
return childprop;
}
}
return NULL;
}
static gboolean
_set_child_property_full (GESTimelineElement * self, GObject * child,
GParamSpec * pspec, const GValue * value, GError ** error)
@ -223,10 +247,9 @@ static gboolean
_lookup_child (GESTimelineElement * self, const gchar * prop_name,
GObject ** child, GParamSpec ** pspec)
{
GHashTableIter iter;
gpointer key, value;
gchar **names, *name, *classename;
gboolean res;
gint i;
classename = NULL;
res = FALSE;
@ -238,22 +261,22 @@ _lookup_child (GESTimelineElement * self, const gchar * prop_name,
} else
name = names[0];
g_hash_table_iter_init (&iter, self->priv->children_props);
while (g_hash_table_iter_next (&iter, &key, &value)) {
if (g_strcmp0 (G_PARAM_SPEC (key)->name, name) == 0) {
ChildPropHandler *handler = (ChildPropHandler *) value;
if (classename == NULL ||
g_strcmp0 (G_OBJECT_TYPE_NAME (G_OBJECT (handler->child)),
classename) == 0 ||
g_strcmp0 (g_type_name (G_PARAM_SPEC (key)->owner_type),
for (i = 0; i < self->priv->children_props->len; i++) {
ChildPropSpec *childprop =
&g_array_index (self->priv->children_props, ChildPropSpec, i);
if (g_strcmp0 (childprop->pspec->name, name) == 0) {
if (classename == NULL
|| g_strcmp0 (G_OBJECT_TYPE_NAME (G_OBJECT (childprop->child)),
classename) == 0
|| g_strcmp0 (g_type_name (childprop->pspec->owner_type),
classename) == 0) {
GST_DEBUG_OBJECT (self, "The %s property from %s has been found", name,
classename);
if (child)
*child = gst_object_ref (handler->child);
*child = gst_object_ref (childprop->child);
if (pspec)
*pspec = g_param_spec_ref (key);
*pspec = g_param_spec_ref (childprop->pspec);
res = TRUE;
break;
}
@ -268,20 +291,17 @@ GParamSpec **
ges_timeline_element_get_children_properties (GESTimelineElement * self,
guint * n_properties)
{
GParamSpec **pspec, *spec;
GHashTableIter iter;
gpointer key, value;
GParamSpec **pspec;
guint i = 0;
*n_properties = g_hash_table_size (self->priv->children_props);
*n_properties = self->priv->children_props->len;
pspec = g_new (GParamSpec *, *n_properties);
g_hash_table_iter_init (&iter, self->priv->children_props);
while (g_hash_table_iter_next (&iter, &key, &value)) {
spec = G_PARAM_SPEC (key);
pspec[i] = g_param_spec_ref (spec);
i++;
for (i = 0; i < *n_properties; i++) {
pspec[i] =
g_param_spec_ref (g_array_index (self->priv->children_props,
ChildPropSpec, i).pspec);
}
return pspec;
@ -370,10 +390,7 @@ ges_timeline_element_dispose (GObject * object)
{
GESTimelineElement *self = GES_TIMELINE_ELEMENT (object);
if (self->priv->children_props) {
g_hash_table_unref (self->priv->children_props);
self->priv->children_props = NULL;
}
g_clear_pointer (&self->priv->children_props, g_array_unref);
g_clear_object (&self->priv->copied_from);
@ -391,12 +408,12 @@ ges_timeline_element_finalize (GObject * self)
}
static void
_child_prop_handler_free (ChildPropHandler * handler)
_child_prop_spec (ChildPropSpec * childprop)
{
g_object_freeze_notify (handler->child);
if (handler->handler_id)
g_signal_handler_disconnect (handler->child, handler->handler_id);
g_object_thaw_notify (handler->child);
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)
@ -420,10 +437,9 @@ ges_timeline_element_init (GESTimelineElement * self)
self->priv->serialize = TRUE;
self->priv->children_props =
g_hash_table_new_full ((GHashFunc) ges_pspec_hash, ges_pspec_equal,
(GDestroyNotify) g_param_spec_unref,
(GDestroyNotify) _child_prop_handler_free);
self->priv->children_props = g_array_new (TRUE, TRUE, sizeof (ChildPropSpec));
g_array_set_clear_func (self->priv->children_props,
(GDestroyNotify) _child_prop_spec);
}
static void
@ -792,52 +808,92 @@ child_prop_changed_cb (GObject * child, GParamSpec * arg,
}
static gboolean
set_child_property_by_pspec (GESTimelineElement * self,
GParamSpec * pspec, const GValue * value, GError ** error)
set_child_property (GESTimelineElement * self, ChildPropSpec * childprop,
const GValue * value, GError **error)
{
GESTimelineElementClass *klass;
GESTimelineElement *setter = self;
ChildPropHandler *handler =
g_hash_table_lookup (self->priv->children_props, pspec);
GESTimelineElementClass *klass;
if (!handler) {
GST_ERROR_OBJECT (self, "The %s property doesn't exist", pspec->name);
return FALSE;
}
if (handler->owner) {
klass = GES_TIMELINE_ELEMENT_GET_CLASS (handler->owner);
setter = handler->owner;
if (childprop->owner) {
klass = GES_TIMELINE_ELEMENT_GET_CLASS (childprop->owner);
setter = childprop->owner;
} else {
klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
}
if (klass->set_child_property_full)
return klass->set_child_property_full (setter, handler->child, pspec,
value, error);
return klass->set_child_property_full (setter, childprop->child,
childprop->pspec, value, error);
g_assert (klass->set_child_property);
klass->set_child_property (setter, handler->child, pspec, (GValue *) value);
klass->set_child_property (setter, childprop->child, childprop->pspec,
(GValue *) value);
return TRUE;
}
static gboolean
set_child_property_by_pspec (GESTimelineElement * self,
GParamSpec * pspec, const GValue * value, GError ** error)
{
gint i;
ChildPropSpec *childprop;
gboolean found = FALSE, res = TRUE;
for (i = 0; i < self->priv->children_props->len; i++) {
childprop = &g_array_index (self->priv->children_props, ChildPropSpec, i);
if (ges_pspec_hash (childprop->pspec) != ges_pspec_hash (pspec))
continue;
found = TRUE;
res &= set_child_property (self, childprop, value, error);
if (!(childprop->flags &
GES_TIMELINE_ELEMENT_CHILD_PROP_FLAG_SET_ON_ALL_INSTANCES))
break;
}
if (!found) {
GST_ERROR_OBJECT (self, "The %s property doesn't exist", pspec->name);
return FALSE;
}
GST_DEBUG_OBJECT (self, "Set child property %s", pspec->name);
return res;
}
gboolean
ges_timeline_element_add_child_property_full (GESTimelineElement * self,
GESTimelineElement * owner, GParamSpec * pspec, GObject * child)
GESTimelineElement * owner, GParamSpec * pspec, GObject * child,
GESTimelineElementChildPropertyFlags flags)
{
gchar *signame;
ChildPropHandler *handler;
ChildPropSpec childprop, *prev;
/* FIXME: allow the same pspec, provided the child is different. This
* is important for containers that may have duplicate children
* If this is changed, _remove_childs_child_property in ges-container.c
* should be changed to reflect this.
* We could hack around this by copying the pspec into a new instance
* of GParamSpec, but there is no such GLib method, and it would break
* the usage of get_..._from_pspec and set_..._from_pspec */
if (g_hash_table_contains (self->priv->children_props, pspec)) {
if (_find_child_prop (self, pspec, child, NULL)) {
GST_INFO_OBJECT (self, "Child property already exists: %s", pspec->name);
return FALSE;
}
prev = _find_child_prop (self, pspec, NULL, NULL);
if (flags & GES_TIMELINE_ELEMENT_CHILD_PROP_FLAG_INHERIT) {
if (!owner) {
g_warning ("Trying to inherit child property flags without an owner");
} else {
ChildPropSpec *owner_prop = _find_child_prop (owner, pspec, NULL, NULL);
g_return_val_if_fail (owner_prop, FALSE);
flags |= owner_prop->flags;
}
}
if (prev && prev->flags != flags) {
GST_ERROR_OBJECT (self,
"Trying to add child property with flags %d but the same"
" property had flags %d - this is not supported", flags, prev->flags);
return FALSE;
}
@ -848,15 +904,16 @@ ges_timeline_element_add_child_property_full (GESTimelineElement * self,
handler = (ChildPropHandler *) g_new0 (ChildPropHandler, 1);
handler->self = self;
if (child == G_OBJECT (self) || child == G_OBJECT (owner))
handler->child = child;
childprop.child = child;
else
handler->child = gst_object_ref (child);
handler->owner = owner;
handler->handler_id =
childprop.child = gst_object_ref (child);
childprop.pspec = g_param_spec_ref (pspec);
childprop.owner = owner;
childprop.flags = flags;
childprop.handler_id =
g_signal_connect (child, signame, G_CALLBACK (child_prop_changed_cb),
self);
g_hash_table_insert (self->priv->children_props, g_param_spec_ref (pspec),
handler);
g_array_append_val (self->priv->children_props, childprop);
g_signal_emit (self, ges_timeline_element_signals[CHILD_PROPERTY_ADDED], 0,
child, pspec);
@ -865,15 +922,22 @@ ges_timeline_element_add_child_property_full (GESTimelineElement * self,
return TRUE;
}
GObject *
ges_timeline_element_get_child_from_child_property (GESTimelineElement * self,
GParamSpec * pspec)
GList *
ges_timeline_element_get_children_from_child_property (GESTimelineElement *
self, GParamSpec * pspec)
{
ChildPropHandler *handler =
g_hash_table_lookup (self->priv->children_props, pspec);
if (handler)
return handler->child;
return NULL;
gint i;
GList *res = NULL;
for (i = 0; i < self->priv->children_props->len; i++) {
ChildPropSpec *childprop =
&g_array_index (self->priv->children_props, ChildPropSpec, i);
if (ges_pspec_hash (childprop->pspec) == ges_pspec_hash (pspec))
res = g_list_append (res, childprop->child);
}
return res;
}
@ -1778,7 +1842,7 @@ ges_timeline_element_add_child_property (GESTimelineElement * self,
g_return_val_if_fail (G_IS_OBJECT (child), FALSE);
return ges_timeline_element_add_child_property_full (self, NULL, pspec,
child);
child, 0);
}
/**
@ -1795,16 +1859,16 @@ void
ges_timeline_element_get_child_property_by_pspec (GESTimelineElement * self,
GParamSpec * pspec, GValue * value)
{
ChildPropHandler *handler;
ChildPropSpec *childprop;
g_return_if_fail (GES_IS_TIMELINE_ELEMENT (self));
g_return_if_fail (G_IS_PARAM_SPEC (pspec));
handler = g_hash_table_lookup (self->priv->children_props, pspec);
if (!handler)
childprop = _find_child_prop (self, pspec, NULL, NULL);
if (!childprop)
goto not_found;
g_object_get_property (G_OBJECT (handler->child), pspec->name, value);
g_object_get_property (G_OBJECT (childprop->child), pspec->name, value);
return;
@ -2232,28 +2296,37 @@ gboolean
ges_timeline_element_remove_child_property (GESTimelineElement * self,
GParamSpec * pspec)
{
gpointer key, value;
GParamSpec *found_pspec;
ChildPropHandler *handler;
return ges_timeline_element_remove_child_property_full (self, pspec, NULL);
}
gboolean
ges_timeline_element_remove_child_property_full (GESTimelineElement * self,
GParamSpec * pspec, GObject * child)
{
gint index;
ChildPropSpec *childprop, handler_copy;
g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
g_return_val_if_fail ((!child || G_IS_OBJECT (child)), FALSE);
if (!g_hash_table_lookup_extended (self->priv->children_props, pspec,
&key, &value)) {
if (!(childprop = _find_child_prop (self, pspec, child, &index))) {
GST_WARNING_OBJECT (self, "No child property with pspec %p (%s) found",
pspec, pspec->name);
return FALSE;
}
g_hash_table_steal (self->priv->children_props, pspec);
found_pspec = G_PARAM_SPEC (key);
handler = (ChildPropHandler *) value;
GST_DEBUG_OBJECT (child, "Removing %s", pspec->name);
handler_copy = *childprop;
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);
g_signal_emit (self, ges_timeline_element_signals[CHILD_PROPERTY_REMOVED], 0,
handler->child, found_pspec);
handler_copy.child, handler_copy.pspec);
g_param_spec_unref (found_pspec);
_child_prop_handler_free (handler);
_child_prop_spec (&handler_copy);
return TRUE;
}

View file

@ -279,9 +279,9 @@ ges_track_element_dispose (GObject * object)
static void
ges_track_element_set_asset (GESExtractable * extractable, GESAsset * asset)
{
gchar *tmp;
GESTrackElementClass *class;
GstElement *nleobject;
gchar *tmp;
GESTrackElement *object = GES_TRACK_ELEMENT (extractable);
if (ges_track_element_get_track_type (object) == GES_TRACK_TYPE_UNKNOWN) {
@ -300,8 +300,7 @@ ges_track_element_set_asset (GESExtractable * extractable, GESAsset * asset)
return;
}
tmp = g_strdup_printf ("%s:%s", G_OBJECT_TYPE_NAME (object),
GST_OBJECT_NAME (nleobject));
tmp = g_strdup_printf ("nleges%s", GES_TIMELINE_ELEMENT_NAME (object));
gst_object_set_name (GST_OBJECT (nleobject), tmp);
g_free (tmp);

View file

@ -635,7 +635,6 @@ static void
project_loaded_cb (GESProject * project, GESTimeline * timeline,
GMainLoop * mainloop)
{
GST_ERROR ("LOADED!");
g_main_loop_quit (mainloop);
}
@ -728,7 +727,7 @@ GST_START_TEST (test_children_properties_contain)
GESTimeline *timeline;
GESLayer *layer;
GESAsset *asset;
GESTimelineElement *c1, *c2, *c3, *g1, *g2;
GESTimelineElement *audioc0, *videoc, *audioc1, *g1, *g2;
GParamSpec **child_props1, **child_props2;
guint num_props1, num_props2;
@ -739,99 +738,92 @@ GST_START_TEST (test_children_properties_contain)
asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
/* choose one audio and one video to give them different properties */
c1 = GES_TIMELINE_ELEMENT (ges_layer_add_asset (layer, asset, 0, 0, 10,
audioc0 = GES_TIMELINE_ELEMENT (ges_layer_add_asset (layer, asset, 0, 0, 10,
GES_TRACK_TYPE_AUDIO));
c2 = GES_TIMELINE_ELEMENT (ges_layer_add_asset (layer, asset, 20, 0, 10,
videoc = GES_TIMELINE_ELEMENT (ges_layer_add_asset (layer, asset, 20, 0, 10,
GES_TRACK_TYPE_VIDEO));
/* but c3 will have the same child properties as c1! */
c3 = GES_TIMELINE_ELEMENT (ges_layer_add_asset (layer, asset, 40, 0, 10,
/* but audioc1 will have the same child properties as audioc0! */
audioc1 = GES_TIMELINE_ELEMENT (ges_layer_add_asset (layer, asset, 40, 0, 10,
GES_TRACK_TYPE_AUDIO));
fail_unless (c1);
fail_unless (c2);
fail_unless (audioc0);
fail_unless (videoc);
g1 = GES_TIMELINE_ELEMENT (ges_group_new ());
g2 = GES_TIMELINE_ELEMENT (ges_group_new ());
/* group should have the same as its children */
fail_unless (ges_container_add (GES_CONTAINER (g1), c1));
fail_unless (ges_container_add (GES_CONTAINER (g1), audioc0));
num_props1 = 0;
child_props1 = append_children_properties (NULL, c1, &num_props1);
child_props1 = append_children_properties (NULL, audioc0, &num_props1);
num_props2 = 0;
child_props2 = append_children_properties (NULL, g1, &num_props2);
/* audioc0 VS g1 */
assert_property_list_match (child_props1, num_props1,
child_props2, num_props2);
/* add next child and gain its children properties as well */
fail_unless (ges_container_add (GES_CONTAINER (g1), c2));
fail_unless (ges_container_add (GES_CONTAINER (g1), videoc));
/* add the child properties of c2 to the existing list for c1 */
child_props1 = append_children_properties (child_props1, c2, &num_props1);
/* add the child properties of videoc to the existing list for audioc0 */
child_props1 = append_children_properties (child_props1, videoc, &num_props1);
free_children_properties (child_props2, num_props2);
num_props2 = 0;
child_props2 = append_children_properties (NULL, g1, &num_props2);
/* audioc0+videoc VS g1 */
assert_property_list_match (child_props1, num_props1,
child_props2, num_props2);
/* FIXME: if c1 and c3 have the same child properties (they use the
* same GParamSpec) then ges_timeline_element_add_child_property_full
* will fail, even though the corresponding GObject child is not the
* same instance */
fail_unless (ges_container_add (GES_CONTAINER (g1), c3));
/* FIXME: regarding the above comment, ideally we would append the
* children properties for c3 to child_props1, so that its children
* properties appear twice in the list:
* child_props1 =
* append_children_properties (child_props1, c3, &num_props1); */
fail_unless (ges_container_add (GES_CONTAINER (g1), audioc1));
child_props1 =
append_children_properties (child_props1, audioc1, &num_props1);
free_children_properties (child_props2, num_props2);
num_props2 = 0;
child_props2 = append_children_properties (NULL, g1, &num_props2);
/* audioc0+videoc+audioc1 VS g1 */
assert_property_list_match (child_props1, num_props1,
child_props2, num_props2);
/* remove c3 */
fail_unless (ges_container_remove (GES_CONTAINER (g1), c3));
/* FIXME: regarding the above comment, ideally we would reset
* child_props1 to only contain the child properties for c1 and c2
* Currently, we at least want to make sure that the child properties
* for c1 remain.
* Currently, if we removed c1 first, all its children properties would
* be removed from g1, and this would *not* automatically register the
* children properties for c3. */
free_children_properties (child_props2, num_props2);
num_props2 = 0;
child_props2 = append_children_properties (NULL, g1, &num_props2);
assert_property_list_match (child_props1, num_props1,
child_props2, num_props2);
/* remove c1 */
fail_unless (ges_container_remove (GES_CONTAINER (g1), c1));
/* remove audioc1 */
fail_unless (ges_container_remove (GES_CONTAINER (g1), audioc1));
free_children_properties (child_props1, num_props1);
num_props1 = 0;
child_props1 = append_children_properties (NULL, c2, &num_props1);
child_props1 = append_children_properties (NULL, audioc0, &num_props1);
child_props1 = append_children_properties (child_props1, videoc, &num_props1);
free_children_properties (child_props2, num_props2);
num_props2 = 0;
child_props2 = append_children_properties (NULL, g1, &num_props2);
/* audioc0+videoc VS g1 */
assert_property_list_match (child_props1, num_props1,
child_props2, num_props2);
/* add g1 and c1 to g2 */
/* remove audioc0 */
fail_unless (ges_container_remove (GES_CONTAINER (g1), audioc0));
free_children_properties (child_props1, num_props1);
num_props1 = 0;
child_props1 = append_children_properties (NULL, videoc, &num_props1);
free_children_properties (child_props2, num_props2);
num_props2 = 0;
child_props2 = append_children_properties (NULL, g1, &num_props2);
/* videoc VS g1 */
assert_property_list_match (child_props1, num_props1,
child_props2, num_props2);
/* add g1 and audioc0 to g2 */
fail_unless (ges_container_add (GES_CONTAINER (g2), g1));
fail_unless (ges_container_add (GES_CONTAINER (g2), c1));
fail_unless (ges_container_add (GES_CONTAINER (g2), audioc0));
free_children_properties (child_props1, num_props1);
num_props1 = 0;
@ -839,9 +831,10 @@ GST_START_TEST (test_children_properties_contain)
free_children_properties (child_props2, num_props2);
num_props2 = 0;
child_props2 = append_children_properties (NULL, c1, &num_props2);
child_props2 = append_children_properties (NULL, audioc0, &num_props2);
child_props2 = append_children_properties (child_props2, g1, &num_props2);
/* g2+audioc0 VS g2 */
assert_property_list_match (child_props1, num_props1,
child_props2, num_props2);