summaryrefslogtreecommitdiff
path: root/gnl
diff options
context:
space:
mode:
authorThibault Saunier <thibault.saunier@collabora.com>2013-06-07 18:58:24 -0400
committerThibault Saunier <thibault.saunier@collabora.com>2013-06-23 16:56:35 -0400
commitc99cc0ef17d5040105958bfc6085fe298cba2096 (patch)
tree0b317a359852b0b477c253da08b0b7f95bdfac41 /gnl
parentd66b29d55d7506e0c0fed84439876f7e56c60127 (diff)
gnl: First implementation of the commit based API
Currently, the only safe way to modify a composition, or an object, is to pause the pipeline first. We think that this limitation eliminates many use cases for GNonLin. Few video editors let you create a draft of your video by applying multi-cam technic. Also people could want to eventually consider doing live video production, which obviously require being able to change the composition in playing state (and increase compositon duration as we go). This commit is a first step making GNL fully commit base. Objects, Operations and Compositon keep a copy of modified values, and only apply those changes when a commit is executed. This potentially allows making the changes (or group of changes) fully 'atomic' without having to pause the pipeline. Added API: ---------- GnlObject::commit action signal Removed API: ------------ GnlComposition:update property + Fix the tests https://bugzilla.gnome.org/show_bug.cgi?id=701287
Diffstat (limited to 'gnl')
-rw-r--r--gnl/gnlcomposition.c326
-rw-r--r--gnl/gnlobject.c187
-rw-r--r--gnl/gnlobject.h31
3 files changed, 298 insertions, 246 deletions
diff --git a/gnl/gnlcomposition.c b/gnl/gnlcomposition.c
index ada5c44..d8e1fa0 100644
--- a/gnl/gnlcomposition.c
+++ b/gnl/gnlcomposition.c
@@ -62,6 +62,12 @@ enum
GNLOBJECT_PROP_LAST
};
+enum
+{
+ COMMIT_SIGNAL,
+ LAST_SIGNAL
+};
+
typedef struct _GnlCompositionEntry GnlCompositionEntry;
struct _GnlCompositionPrivate
@@ -80,14 +86,6 @@ struct _GnlCompositionPrivate
GHashTable *objects_hash;
GMutex objects_lock;
- /* Update properties
- * can_update: If True, updates should be taken into account immediatly, else
- * they should be postponed until it is set to True again.
- * update_required: Set to True if an update is required when the
- * can_update property just above is set back to True. */
- gboolean can_update;
- gboolean update_required;
-
/*
thread-safe Seek handling.
flushing_lock : mutex to access flushing and pending_idle
@@ -150,6 +148,8 @@ struct _GnlCompositionPrivate
gboolean running;
};
+static guint _signals[LAST_SIGNAL] = { 0 };
+
static GParamSpec *gnlobject_properties[GNLOBJECT_PROP_LAST];
#define OBJECT_IN_ACTIVE_SEGMENT(comp,element) \
@@ -185,12 +185,14 @@ static gint objects_start_compare (GnlObject * a, GnlObject * b);
static gint objects_stop_compare (GnlObject * a, GnlObject * b);
static GstClockTime get_current_position (GnlComposition * comp);
-static void gnl_composition_set_update (GnlComposition * comp, gboolean update);
static gboolean update_pipeline (GnlComposition * comp,
GstClockTime currenttime, gboolean initial, gboolean change_state,
gboolean modify);
static void no_more_pads_object_cb (GstElement * element,
GnlComposition * comp);
+static gboolean gnl_composition_commit_func (GnlObject * object,
+ gboolean recurse);
+static void update_start_stop_duration (GnlComposition * comp);
/* COMP_REAL_START: actual position to start current playback at. */
@@ -257,12 +259,6 @@ struct _GnlCompositionEntry
{
GnlObject *object;
- /* handler ids for property notifications */
- gulong starthandler;
- gulong stophandler;
- gulong priorityhandler;
- gulong activehandler;
-
/* handler id for 'no-more-pads' signal */
gulong nomorepadshandler;
gulong padaddedhandler;
@@ -278,10 +274,12 @@ gnl_composition_class_init (GnlCompositionClass * klass)
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstBinClass *gstbin_class;
+ GnlObjectClass *gnlobject_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gstbin_class = (GstBinClass *) klass;
+ gnlobject_class = (GnlObjectClass *) klass;
g_type_class_add_private (klass, sizeof (GnlCompositionPrivate));
@@ -307,26 +305,6 @@ gnl_composition_class_init (GnlCompositionClass * klass)
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&gnl_composition_src_template));
- /**
- * GnlComposition:update:
- *
- * If %TRUE, then all modifications to objects within the composition will
- * cause a internal pipeline update (if required).
- * If %FALSE, then only the composition's start/duration/stop properties
- * will be updated, and the internal pipeline will only be updated when the
- * property is set back to %TRUE.
- *
- * It is recommended to temporarily set this property to %FALSE before doing
- * more than one modification in the composition (like adding/moving/removing
- * several objects at once) in order to speed up the process, and then setting
- * back the property to %TRUE when done.
- */
-
- g_object_class_install_property (gobject_class, PROP_UPDATE,
- g_param_spec_boolean ("update", "Update",
- "Update the internal pipeline on every modification", TRUE,
- G_PARAM_READWRITE));
-
/* Get the paramspec of the GnlObject klass so we can do
* fast notifies */
gnlobject_properties[GNLOBJECT_PROP_START] =
@@ -335,18 +313,32 @@ gnl_composition_class_init (GnlCompositionClass * klass)
g_object_class_find_property (gobject_class, "stop");
gnlobject_properties[GNLOBJECT_PROP_DURATION] =
g_object_class_find_property (gobject_class, "duration");
+
+ /**
+ * GnlComposition::commit
+ * @comp: a #GnlComposition
+ * @recurse: Whether to commit recursiverly into (GnlComposition) children of
+ * @object. This is used in case we have composition inside
+ * a gnlsource composition, telling it to commit the included
+ * composition state.
+ *
+ * Action signal to commit all the pending changes of the composition and
+ * its children timing properties
+ *
+ * Returns: %TRUE if changes have been commited, %FALSE if nothing had to
+ * be commited
+ */
+ _signals[COMMIT_SIGNAL] = g_signal_new ("commit", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (GnlObjectClass, commit_signal_handler), NULL, NULL, NULL,
+ G_TYPE_BOOLEAN, 1, G_TYPE_BOOLEAN);
+
+ gnlobject_class->commit = gnl_composition_commit_func;
}
static void
hash_value_destroy (GnlCompositionEntry * entry)
{
- if (entry->starthandler)
- g_signal_handler_disconnect (entry->object, entry->starthandler);
- if (entry->stophandler)
- g_signal_handler_disconnect (entry->object, entry->stophandler);
- if (entry->priorityhandler)
- g_signal_handler_disconnect (entry->object, entry->priorityhandler);
- g_signal_handler_disconnect (entry->object, entry->activehandler);
g_signal_handler_disconnect (entry->object, entry->padremovedhandler);
g_signal_handler_disconnect (entry->object, entry->padaddedhandler);
@@ -361,6 +353,7 @@ gnl_composition_init (GnlComposition * comp)
GnlCompositionPrivate *priv;
GST_OBJECT_FLAG_SET (comp, GNL_OBJECT_SOURCE);
+ GST_OBJECT_FLAG_SET (comp, GNL_OBJECT_COMPOSITION);
priv = G_TYPE_INSTANCE_GET_PRIVATE (comp, GNL_TYPE_COMPOSITION,
GnlCompositionPrivate);
@@ -368,9 +361,6 @@ gnl_composition_init (GnlComposition * comp)
priv->objects_start = NULL;
priv->objects_stop = NULL;
- priv->can_update = TRUE;
- priv->update_required = FALSE;
-
g_mutex_init (&priv->flushing_lock);
priv->flushing = FALSE;
@@ -401,9 +391,6 @@ gnl_composition_dispose (GObject * object)
priv->dispose_has_run = TRUE;
- priv->can_update = TRUE;
- priv->update_required = FALSE;
-
if (priv->ghostpad)
gnl_composition_remove_ghostpad (comp);
@@ -454,12 +441,7 @@ static void
gnl_composition_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
- GnlComposition *comp = (GnlComposition *) object;
-
switch (prop_id) {
- case PROP_UPDATE:
- gnl_composition_set_update (comp, g_value_get_boolean (value));
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -470,12 +452,7 @@ static void
gnl_composition_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
- GnlComposition *comp = (GnlComposition *) object;
-
switch (prop_id) {
- case PROP_UPDATE:
- g_value_set_boolean (value, comp->priv->can_update);
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -637,7 +614,6 @@ gnl_composition_reset (GnlComposition * comp)
priv->reset_time = FALSE;
- priv->update_required = FALSE;
priv->send_stream_start = TRUE;
GST_DEBUG_OBJECT (comp, "Composition now resetted");
@@ -842,37 +818,61 @@ have_to_update_pipeline (GnlComposition * comp)
return FALSE;
}
-static void
-gnl_composition_set_update (GnlComposition * comp, gboolean update)
+/* OBJECTS LOCK must be taken when calling this ! */
+static gboolean
+update_pipeline_at_current_position (GnlComposition * comp)
{
- GnlCompositionPrivate *priv = comp->priv;
+ GstClockTime curpos;
- if (G_UNLIKELY (update == priv->can_update))
- return;
+ /* Get current position */
+ if ((curpos = get_current_position (comp)) == GST_CLOCK_TIME_NONE) {
+ if (GST_CLOCK_TIME_IS_VALID (comp->priv->segment_start))
+ curpos = comp->priv->segment->start = comp->priv->segment_start;
+ else
+ curpos = 0;
+ }
- GST_DEBUG_OBJECT (comp, "update:%d [currently %d], update_required:%d",
- update, priv->can_update, priv->update_required);
- COMP_OBJECTS_LOCK (comp);
+ update_start_stop_duration (comp);
- priv->can_update = update;
+ return update_pipeline (comp, curpos, TRUE, TRUE, TRUE);
+}
- if (update && priv->update_required) {
- GstClockTime curpos;
+static gboolean
+gnl_composition_commit_func (GnlObject * object, gboolean recurse)
+{
+ GList *tmp;
+ gboolean commited = FALSE;
+ GnlComposition *comp = GNL_COMPOSITION (object);
+ GnlCompositionPrivate *priv = comp->priv;
- /* Get current position */
- if ((curpos = get_current_position (comp)) == GST_CLOCK_TIME_NONE) {
- if (GST_CLOCK_TIME_IS_VALID (priv->segment_start))
- curpos = priv->segment->start = priv->segment_start;
- else
- curpos = 0;
- }
- COMP_OBJECTS_UNLOCK (comp);
+ GST_DEBUG_OBJECT (object, "Commiting state");
+ COMP_OBJECTS_LOCK (comp);
+ for (tmp = priv->objects_start; tmp; tmp = tmp->next) {
+ if (gnl_object_commit (tmp->data, recurse))
+ commited = TRUE;
+ }
- /* update pipeline to that position */
- update_pipeline (comp, curpos, TRUE, TRUE, TRUE);
- } else
+ GST_DEBUG_OBJECT (object, "Linking up commit vmethod");
+ if (commited == FALSE &&
+ (GNL_OBJECT_CLASS (parent_class)->commit (object, recurse) == FALSE)) {
COMP_OBJECTS_UNLOCK (comp);
+ GST_DEBUG_OBJECT (object, "Nothing to commit, leaving");
+ return FALSE;
+ }
+ COMP_OBJECTS_UNLOCK (comp);
+
+ /* The topology of the composition might have changed, update the lists */
+ priv->objects_start = g_list_sort
+ (priv->objects_start, (GCompareFunc) objects_start_compare);
+ priv->objects_stop = g_list_sort
+ (priv->objects_stop, (GCompareFunc) objects_stop_compare);
+
+ /* And update the pipeline at current position if needed */
+ update_pipeline_at_current_position (comp);
+
+ GST_DEBUG_OBJECT (object, "Done commiting");
+ return TRUE;
}
/*
@@ -1960,13 +1960,13 @@ update_start_stop_duration (GnlComposition * comp)
GST_LOG ("no objects, resetting everything to 0");
if (cobj->start) {
- cobj->start = 0;
+ cobj->start = cobj->pending_start = 0;
g_object_notify_by_pspec (G_OBJECT (cobj),
gnlobject_properties[GNLOBJECT_PROP_START]);
}
if (cobj->duration) {
- cobj->duration = 0;
+ cobj->pending_duration = cobj->duration = 0;
g_object_notify_by_pspec (G_OBJECT (cobj),
gnlobject_properties[GNLOBJECT_PROP_DURATION]);
signal_duration_change (comp);
@@ -1987,7 +1987,7 @@ update_start_stop_duration (GnlComposition * comp)
"Setting start to 0 because we have a default object");
if (cobj->start != 0) {
- cobj->start = 0;
+ cobj->pending_start = cobj->start = 0;
g_object_notify_by_pspec (G_OBJECT (cobj),
gnlobject_properties[GNLOBJECT_PROP_START]);
}
@@ -2000,7 +2000,7 @@ update_start_stop_duration (GnlComposition * comp)
if (obj->start != cobj->start) {
GST_LOG_OBJECT (obj, "setting start from %s to %" GST_TIME_FORMAT,
GST_OBJECT_NAME (obj), GST_TIME_ARGS (obj->start));
- cobj->start = obj->start;
+ cobj->pending_start = cobj->start = obj->start;
g_object_notify_by_pspec (G_OBJECT (cobj),
gnlobject_properties[GNLOBJECT_PROP_START]);
}
@@ -2016,8 +2016,10 @@ update_start_stop_duration (GnlComposition * comp)
if (priv->expandables) {
GList *tmp;
+ GST_INFO_OBJECT (comp, "RE-setting all expandables duration and commit");
for (tmp = priv->expandables; tmp; tmp = tmp->next) {
g_object_set (tmp->data, "duration", obj->stop, NULL);
+ gnl_object_commit (GNL_OBJECT (tmp->data), FALSE);
}
}
@@ -2028,7 +2030,7 @@ update_start_stop_duration (GnlComposition * comp)
}
if ((cobj->stop - cobj->start) != cobj->duration) {
- cobj->duration = cobj->stop - cobj->start;
+ cobj->pending_duration = cobj->duration = cobj->stop - cobj->start;
g_object_notify_by_pspec (G_OBJECT (cobj),
gnlobject_properties[GNLOBJECT_PROP_DURATION]);
signal_duration_change (comp);
@@ -2582,13 +2584,6 @@ update_pipeline (GnlComposition * comp, GstClockTime currenttime,
COMP_OBJECTS_LOCK (comp);
- if (G_UNLIKELY (!priv->can_update)) {
- COMP_OBJECTS_UNLOCK (comp);
- return TRUE;
- }
-
- update_start_stop_duration (comp);
-
if (GST_CLOCK_TIME_IS_VALID (currenttime)) {
GstState state = GST_STATE (comp);
GstState nextstate =
@@ -2781,67 +2776,6 @@ update_pipeline (GnlComposition * comp, GstClockTime currenttime,
*/
static void
-object_start_stop_priority_changed (GnlObject * object,
- GParamSpec * arg G_GNUC_UNUSED, GnlComposition * comp)
-{
- GnlCompositionPrivate *priv = comp->priv;
-
- GST_DEBUG_OBJECT (object,
- "start/stop/priority changed (%" GST_TIME_FORMAT "/%" GST_TIME_FORMAT
- "/%d), evaluating pipeline update", GST_TIME_ARGS (object->start),
- GST_TIME_ARGS (object->stop), object->priority);
-
- /* The topology of the ocmposition might have changed, update the lists */
- priv->objects_start = g_list_sort
- (priv->objects_start, (GCompareFunc) objects_start_compare);
- priv->objects_stop = g_list_sort
- (priv->objects_stop, (GCompareFunc) objects_stop_compare);
-
- if (!priv->can_update) {
- priv->update_required = TRUE;
- update_start_stop_duration (comp);
- return;
- }
-
- /* Update pipeline if needed */
- if (priv->current &&
- (OBJECT_IN_ACTIVE_SEGMENT (comp, object) ||
- g_node_find (priv->current, G_IN_ORDER, G_TRAVERSE_ALL, object))) {
- GstClockTime curpos = get_current_position (comp);
-
- if (curpos == GST_CLOCK_TIME_NONE)
- curpos = priv->segment->start = priv->segment_start;
-
- update_pipeline (comp, curpos, TRUE, TRUE, TRUE);
- } else
- update_start_stop_duration (comp);
-}
-
-static void
-object_active_changed (GnlObject * object, GParamSpec * arg G_GNUC_UNUSED,
- GnlComposition * comp)
-{
- GnlCompositionPrivate *priv = comp->priv;
-
- GST_DEBUG_OBJECT (object,
- "active flag changed (%d), evaluating pipeline update", object->active);
-
- if (!priv->can_update) {
- priv->update_required = TRUE;
- return;
- }
-
- if (priv->current && OBJECT_IN_ACTIVE_SEGMENT (comp, object)) {
- GstClockTime curpos = get_current_position (comp);
-
- if (curpos == GST_CLOCK_TIME_NONE)
- curpos = priv->segment->start = priv->segment_start;
- update_pipeline (comp, curpos, TRUE, TRUE, TRUE);
- } else
- update_start_stop_duration (comp);
-}
-
-static void
object_pad_removed (GnlObject * object, GstPad * pad, GnlComposition * comp)
{
GnlCompositionPrivate *priv = comp->priv;
@@ -2888,12 +2822,10 @@ object_pad_added (GnlObject * object G_GNUC_UNUSED, GstPad * pad,
static gboolean
gnl_composition_add_object (GstBin * bin, GstElement * element)
{
- GnlComposition *comp = (GnlComposition *) bin;
+ gboolean ret;
GnlCompositionEntry *entry;
- GstClockTime curpos = GST_CLOCK_TIME_NONE;
+ GnlComposition *comp = (GnlComposition *) bin;
GnlCompositionPrivate *priv = comp->priv;
- gboolean ret;
- gboolean update_required;
/* we only accept GnlObject */
g_return_val_if_fail (GNL_IS_OBJECT (element), FALSE);
@@ -2920,6 +2852,8 @@ gnl_composition_add_object (GstBin * bin, GstElement * element)
/* Call parent class ::add_element() */
ret = GST_BIN_CLASS (parent_class)->add_element (bin, element);
+ gnl_object_set_commit_needed (GNL_OBJECT (comp));
+
if (!ret) {
GST_WARNING_OBJECT (bin, "couldn't add element");
goto chiringuito;
@@ -2933,27 +2867,18 @@ gnl_composition_add_object (GstBin * bin, GstElement * element)
entry = g_slice_new0 (GnlCompositionEntry);
entry->object = (GnlObject *) element;
- if (G_LIKELY ((GNL_OBJECT_PRIORITY (element) != G_MAXUINT32) &&
- !GNL_OBJECT_IS_EXPANDABLE (element))) {
+ if (G_LIKELY ((GNL_OBJECT_PRIORITY (element) == G_MAXUINT32) ||
+ GNL_OBJECT_IS_EXPANDABLE (element))) {
/* Only react on non-default objects properties */
- entry->starthandler = g_signal_connect (G_OBJECT (element),
- "notify::start", G_CALLBACK (object_start_stop_priority_changed), comp);
- entry->stophandler =
- g_signal_connect (G_OBJECT (element), "notify::stop",
- G_CALLBACK (object_start_stop_priority_changed), comp);
- entry->priorityhandler =
- g_signal_connect (G_OBJECT (element), "notify::priority",
- G_CALLBACK (object_start_stop_priority_changed), comp);
- } else {
- /* We set the default source start/stop values to 0 and composition-stop */
g_object_set (element,
"start", (GstClockTime) 0,
"inpoint", (GstClockTime) 0,
"duration", (GstClockTimeDiff) GNL_OBJECT_STOP (comp), NULL);
+
+ GST_INFO_OBJECT (element, "Used as expandable, commiting now");
+ gnl_object_commit (GNL_OBJECT (element), FALSE);
}
- entry->activehandler = g_signal_connect (G_OBJECT (element),
- "notify::active", G_CALLBACK (object_active_changed), comp);
entry->padremovedhandler = g_signal_connect (G_OBJECT (element),
"pad-removed", G_CALLBACK (object_pad_removed), comp);
entry->padaddedhandler = g_signal_connect (G_OBJECT (element),
@@ -2971,7 +2896,7 @@ gnl_composition_add_object (GstBin * bin, GstElement * element)
GNL_OBJECT_IS_EXPANDABLE (element)) {
/* It doesn't get added to objects_start and objects_stop. */
priv->expandables = g_list_prepend (priv->expandables, element);
- goto check_update;
+ goto beach;
}
/* add it sorted to the objects list */
@@ -2989,41 +2914,11 @@ gnl_composition_add_object (GstBin * bin, GstElement * element)
priv->objects_stop = g_list_insert_sorted
(priv->objects_stop, element, (GCompareFunc) objects_stop_compare);
- if (priv->objects_stop)
- GST_LOG_OBJECT (comp,
- "Head of objects_stop is now %s [%" GST_TIME_FORMAT "--%"
- GST_TIME_FORMAT "]", GST_OBJECT_NAME (priv->objects_stop->data),
- GST_TIME_ARGS (GNL_OBJECT_START (priv->objects_stop->data)),
- GST_TIME_ARGS (GNL_OBJECT_STOP (priv->objects_stop->data)));
-
- GST_DEBUG_OBJECT (comp,
- "segment_start:%" GST_TIME_FORMAT " segment_stop:%" GST_TIME_FORMAT,
- GST_TIME_ARGS (priv->segment_start), GST_TIME_ARGS (priv->segment_stop));
-
-check_update:
- update_required = OBJECT_IN_ACTIVE_SEGMENT (comp, element) ||
- (!priv->current) ||
- (GNL_OBJECT_PRIORITY (element) == G_MAXUINT32) ||
- GNL_OBJECT_IS_EXPANDABLE (element);
-
- /* We only need the current position if we're going to update */
- if (update_required && priv->can_update)
- if ((curpos = get_current_position (comp)) == GST_CLOCK_TIME_NONE)
- curpos = priv->segment_start;
+ /* Now the object is ready to be commited and then used */
+beach:
COMP_OBJECTS_UNLOCK (comp);
- /* If we added within currently configured segment OR the pipeline was *
- * previously empty, THEN update pipeline */
- if (G_LIKELY (update_required && priv->can_update))
- update_pipeline (comp, curpos, TRUE, TRUE, TRUE);
- else {
- if (!priv->can_update)
- priv->update_required |= update_required;
- update_start_stop_duration (comp);
- }
-
-beach:
gst_object_unref (element);
return ret;
@@ -3041,7 +2936,6 @@ gnl_composition_remove_object (GstBin * bin, GstElement * element)
{
GnlComposition *comp = (GnlComposition *) bin;
GnlCompositionPrivate *priv = comp->priv;
- GstClockTime curpos = GST_CLOCK_TIME_NONE;
gboolean ret = FALSE;
gboolean update_required;
GnlCompositionEntry *entry;
@@ -3082,25 +2976,15 @@ gnl_composition_remove_object (GstBin * bin, GstElement * element)
update_required = OBJECT_IN_ACTIVE_SEGMENT (comp, element) ||
(GNL_OBJECT_PRIORITY (element) == G_MAXUINT32) ||
GNL_OBJECT_IS_EXPANDABLE (element);
- if (update_required && priv->can_update) {
- curpos = get_current_position (comp);
- if (G_UNLIKELY (curpos == GST_CLOCK_TIME_NONE))
- curpos = priv->segment_start;
- }
COMP_OBJECTS_UNLOCK (comp);
- /* If we removed within currently configured segment, or it was the default source, *
- * update pipeline */
- if (G_LIKELY (update_required))
- update_pipeline (comp, curpos, TRUE, TRUE, TRUE);
- else {
- if (!priv->can_update)
- priv->update_required |= update_required;
- update_start_stop_duration (comp);
+ if (G_LIKELY (update_required)) {
+ /* And update the pipeline at current position if needed */
+ update_pipeline_at_current_position (comp);
}
ret = GST_BIN_CLASS (parent_class)->remove_element (bin, element);
- GST_LOG_OBJECT (element, "Done removing from the composition");
+ GST_LOG_OBJECT (element, "Done removing from the composition, now updating");
/* unblock source pad */
if (probeid && (srcpad = get_src_pad (element))) {
@@ -3108,6 +2992,8 @@ gnl_composition_remove_object (GstBin * bin, GstElement * element)
gst_object_unref (srcpad);
}
+ /* Make it possible to reuse the same object later */
+ gnl_object_reset (GNL_OBJECT (element));
gst_object_unref (element);
out:
return ret;
diff --git a/gnl/gnlobject.c b/gnl/gnlobject.c
index d86a467..60b4a10 100644
--- a/gnl/gnlobject.c
+++ b/gnl/gnlobject.c
@@ -46,6 +46,35 @@ GST_DEBUG_CATEGORY_STATIC (gnlobject_debug);
GST_DEBUG_CATEGORY_INIT (gnlobject_debug, "gnlobject", GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin Object base class");
#define gnl_object_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GnlObject, gnl_object, GST_TYPE_BIN, _do_init);
+
+/****************************************************
+ * Helper macros *
+ ****************************************************/
+#define CHECK_AND_SET(PROPERTY, property, prop_str, print_format) \
+{ \
+GstObject *parent = gst_object_get_parent (GST_OBJECT (object)); \
+if (parent == NULL && !GNL_OBJECT_IS_COMPOSITION (object)) { \
+ GST_INFO_OBJECT (object, "Not in a composition yet, " \
+ "not commiting" prop_str); \
+} else if (object->pending_##property != object->property) { \
+ object->property = object->pending_##property; \
+ GST_DEBUG_OBJECT(object, "Setting " prop_str " to %" \
+ print_format, object->property); \
+} else \
+ GST_DEBUG_OBJECT(object, "Nothing to do for " prop_str); \
+if (parent) \
+ gst_object_unref (parent); \
+}
+
+#define SET_PENDING_VALUE(property, property_str, type, print_format) \
+gnlobject->pending_##property = g_value_get_##type (value); \
+if (gnlobject->property != gnlobject->pending_##property) { \
+ GST_DEBUG_OBJECT(object, "Setting pending " property_str " to %" \
+ print_format, gnlobject->pending_##property); \
+ gnl_object_set_commit_needed (gnlobject); \
+} else \
+ GST_DEBUG_OBJECT(object, "Pending " property_str " did not change");
+
enum
{
PROP_0,
@@ -62,7 +91,6 @@ enum
static GParamSpec *properties[PROP_LAST];
-
static void gnl_object_dispose (GObject * object);
static void gnl_object_set_property (GObject * object, guint prop_id,
@@ -75,6 +103,7 @@ static GstStateChangeReturn gnl_object_change_state (GstElement * element,
static gboolean gnl_object_prepare_func (GnlObject * object);
static gboolean gnl_object_cleanup_func (GnlObject * object);
+static gboolean gnl_object_commit_func (GnlObject * object, gboolean recurse);
static GstStateChangeReturn gnl_object_prepare (GnlObject * object);
@@ -97,6 +126,9 @@ gnl_object_class_init (GnlObjectClass * klass)
gnlobject_class->prepare = GST_DEBUG_FUNCPTR (gnl_object_prepare_func);
gnlobject_class->cleanup = GST_DEBUG_FUNCPTR (gnl_object_cleanup_func);
+ gnlobject_class->commit_signal_handler =
+ GST_DEBUG_FUNCPTR (gnl_object_commit);
+ gnlobject_class->commit = GST_DEBUG_FUNCPTR (gnl_object_commit_func);
/**
* GnlObject:start
@@ -213,13 +245,13 @@ gnl_object_class_init (GnlObjectClass * klass)
static void
gnl_object_init (GnlObject * object)
{
- object->start = 0;
- object->duration = 0;
+ object->start = object->pending_start = 0;
+ object->duration = object->pending_duration = 0;
object->stop = 0;
- object->inpoint = GST_CLOCK_TIME_NONE;
- object->priority = 0;
- object->active = TRUE;
+ object->inpoint = object->pending_inpoint = GST_CLOCK_TIME_NONE;
+ object->priority = object->pending_priority = 0;
+ object->active = object->pending_active = TRUE;
object->caps = gst_caps_new_any ();
@@ -395,21 +427,55 @@ gnl_object_set_caps (GnlObject * object, const GstCaps * caps)
object->caps = gst_caps_copy (caps);
}
-static void
-update_values (GnlObject * object)
+static inline void
+_update_stop (GnlObject * gnlobject)
{
/* check if start/duration has changed */
- if ((object->start + object->duration) != object->stop) {
- object->stop = object->start + object->duration;
- GST_LOG_OBJECT (object,
+
+ if ((gnlobject->pending_start + gnlobject->pending_duration) !=
+ gnlobject->stop) {
+ gnlobject->stop = gnlobject->pending_start + gnlobject->pending_duration;
+
+ GST_LOG_OBJECT (gnlobject,
"Updating stop value : %" GST_TIME_FORMAT " [start:%" GST_TIME_FORMAT
- ", duration:%" GST_TIME_FORMAT "]", GST_TIME_ARGS (object->stop),
- GST_TIME_ARGS (object->start), GST_TIME_ARGS (object->duration));
- g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_STOP]);
+ ", duration:%" GST_TIME_FORMAT "]", GST_TIME_ARGS (gnlobject->stop),
+ GST_TIME_ARGS (gnlobject->pending_start),
+ GST_TIME_ARGS (gnlobject->pending_duration));
+ g_object_notify_by_pspec (G_OBJECT (gnlobject), properties[PROP_STOP]);
}
}
static void
+update_values (GnlObject * object)
+{
+ CHECK_AND_SET (START, start, "start", G_GUINT64_FORMAT);
+ CHECK_AND_SET (INPOINT, inpoint, "inpoint", G_GUINT64_FORMAT);
+ CHECK_AND_SET (DURATION, duration, "duration", G_GINT64_FORMAT);
+ CHECK_AND_SET (PRIORITY, priority, "priority", G_GUINT32_FORMAT);
+ CHECK_AND_SET (ACTIVE, active, "active", G_GUINT32_FORMAT);
+
+ _update_stop (object);
+}
+
+static gboolean
+gnl_object_commit_func (GnlObject * object, gboolean recurse)
+{
+ GST_INFO_OBJECT (object, "Commiting object changed");
+
+ if (object->commit_needed == FALSE) {
+ GST_INFO_OBJECT (object, "No changes to commit");
+
+ return FALSE;
+ }
+
+ update_values (object);
+
+ GST_INFO_OBJECT (object, "Done commiting");
+
+ return TRUE;
+}
+
+static void
gnl_object_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
@@ -417,23 +483,29 @@ gnl_object_set_property (GObject * object, guint prop_id,
g_return_if_fail (GNL_IS_OBJECT (object));
+ GST_OBJECT_LOCK (object);
switch (prop_id) {
case PROP_START:
- gnlobject->start = g_value_get_uint64 (value);
- update_values (gnlobject);
+ SET_PENDING_VALUE (start, "start", uint64, G_GUINT64_FORMAT);
break;
case PROP_DURATION:
- gnlobject->duration = g_value_get_int64 (value);
- update_values (gnlobject);
+ SET_PENDING_VALUE (duration, "duration", int64, G_GINT64_FORMAT);
break;
case PROP_INPOINT:
- gnlobject->inpoint = g_value_get_uint64 (value);
+ SET_PENDING_VALUE (inpoint, "inpoint", uint64, G_GUINT64_FORMAT);
break;
case PROP_PRIORITY:
- gnlobject->priority = g_value_get_uint (value);
+ if (g_value_get_uint (value) == G_MAXUINT32) {
+ GST_DEBUG_OBJECT (object, "Setting priority to G_MAXUINT32, which means"
+ "'Default object', commiting right now");
+ /* This is not the cleanast thing to do but we anyway plan to remove
+ * that behaviour soon. */
+ gnlobject->pending_priority = gnlobject->priority = G_MAXUINT32;
+ } else
+ SET_PENDING_VALUE (priority, "priority", uint, G_GUINT32_FORMAT);
break;
case PROP_ACTIVE:
- gnlobject->active = g_value_get_boolean (value);
+ SET_PENDING_VALUE (active, "active", boolean, G_GUINT32_FORMAT);
break;
case PROP_CAPS:
gnl_object_set_caps (gnlobject, gst_value_get_caps (value));
@@ -448,6 +520,9 @@ gnl_object_set_property (GObject * object, guint prop_id,
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
+ GST_OBJECT_UNLOCK (object);
+
+ _update_stop (gnlobject);
}
static void
@@ -458,22 +533,22 @@ gnl_object_get_property (GObject * object, guint prop_id,
switch (prop_id) {
case PROP_START:
- g_value_set_uint64 (value, gnlobject->start);
+ g_value_set_uint64 (value, gnlobject->pending_start);
break;
case PROP_DURATION:
- g_value_set_int64 (value, gnlobject->duration);
+ g_value_set_int64 (value, gnlobject->pending_duration);
break;
case PROP_STOP:
g_value_set_uint64 (value, gnlobject->stop);
break;
case PROP_INPOINT:
- g_value_set_uint64 (value, gnlobject->inpoint);
+ g_value_set_uint64 (value, gnlobject->pending_inpoint);
break;
case PROP_PRIORITY:
- g_value_set_uint (value, gnlobject->priority);
+ g_value_set_uint (value, gnlobject->pending_priority);
break;
case PROP_ACTIVE:
- g_value_set_boolean (value, gnlobject->active);
+ g_value_set_boolean (value, gnlobject->pending_active);
break;
case PROP_CAPS:
gst_value_set_caps (value, gnlobject->caps);
@@ -493,6 +568,24 @@ gnl_object_change_state (GstElement * element, GstStateChange transition)
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ {
+ GstObject *parent = gst_object_get_parent (GST_OBJECT (element));
+
+ /* Going to READY and if we are not in a composition, we need to make
+ * sure that the object positioning state is properly commited */
+ if (parent) {
+ if (!GNL_OBJECT_IS_COMPOSITION (parent) &&
+ !GNL_OBJECT_IS_COMPOSITION (GNL_OBJECT (element))) {
+ GST_DEBUG ("Adding gnlobject to something that is not a composition,"
+ " commiting ourself");
+ gnl_object_commit (GNL_OBJECT (element), FALSE);
+ }
+
+ gst_object_unref (parent);
+ }
+ }
+ break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
if (gnl_object_prepare (GNL_OBJECT (element)) == GST_STATE_CHANGE_FAILURE) {
ret = GST_STATE_CHANGE_FAILURE;
@@ -525,3 +618,45 @@ gnl_object_change_state (GstElement * element, GstStateChange transition)
beach:
return ret;
}
+
+void
+gnl_object_set_commit_needed (GnlObject * object)
+{
+ if (G_UNLIKELY (object->commiting)) {
+ GST_WARNING_OBJECT (object,
+ "Trying to set 'commit-needed' while commiting");
+
+ return;
+ }
+
+ GST_DEBUG_OBJECT (object, "Setting 'commit_needed'");
+ object->commit_needed = TRUE;
+}
+
+gboolean
+gnl_object_commit (GnlObject * object, gboolean recurse)
+{
+ gboolean ret;
+
+ GST_DEBUG_OBJECT (object, "Commiting object state");
+
+ object->commiting = TRUE;
+ ret = GNL_OBJECT_GET_CLASS (object)->commit (object, recurse);
+ object->commiting = FALSE;
+
+ return ret;
+
+}
+
+void
+gnl_object_reset (GnlObject * object)
+{
+ GST_INFO_OBJECT (object, "Resetting child timing values to default");
+
+ object->start = 0;
+ object->duration = 0;
+ object->stop = 0;
+ object->inpoint = GST_CLOCK_TIME_NONE;
+ object->priority = 0;
+ object->active = TRUE;
+}
diff --git a/gnl/gnlobject.h b/gnl/gnlobject.h
index 72f7cdd..4ba7dd8 100644
--- a/gnl/gnlobject.h
+++ b/gnl/gnlobject.h
@@ -56,6 +56,7 @@ typedef enum
GNL_OBJECT_SOURCE = (GST_BIN_FLAG_LAST << 0),
GNL_OBJECT_OPERATION = (GST_BIN_FLAG_LAST << 1),
GNL_OBJECT_EXPANDABLE = (GST_BIN_FLAG_LAST << 2),
+ GNL_OBJECT_COMPOSITION = (GST_BIN_FLAG_LAST << 3),
/* padding */
GNL_OBJECT_LAST_FLAG = (GST_BIN_FLAG_LAST << 5)
} GnlObjectFlags;
@@ -67,6 +68,8 @@ typedef enum
(GST_OBJECT_FLAG_IS_SET(obj, GNL_OBJECT_OPERATION))
#define GNL_OBJECT_IS_EXPANDABLE(obj) \
(GST_OBJECT_FLAG_IS_SET(obj, GNL_OBJECT_EXPANDABLE))
+#define GNL_OBJECT_IS_COMPOSITION(obj) \
+ (GST_OBJECT_FLAG_IS_SET(obj, GNL_OBJECT_COMPOSITION))
/* For internal usage only */
#define GNL_OBJECT_START(obj) (GNL_OBJECT_CAST (obj)->start)
@@ -75,6 +78,8 @@ typedef enum
#define GNL_OBJECT_INPOINT(obj) (GNL_OBJECT_CAST (obj)->inpoint)
#define GNL_OBJECT_PRIORITY(obj) (GNL_OBJECT_CAST (obj)->priority)
+#define GNL_OBJECT_IS_COMMITING(obj) (GNL_OBJECT_CAST (obj)->commiting)
+
struct _GnlObject
{
GstBin parent;
@@ -84,6 +89,20 @@ struct _GnlObject
GstClockTime inpoint;
GstClockTimeDiff duration;
+ /* Pending time positionning
+ * Should be == GST_CLOCK_TIME_NONE when nothing to do
+ */
+ GstClockTime pending_start;
+ GstClockTime pending_inpoint;
+ GstClockTimeDiff pending_duration;
+ guint32 pending_priority;
+ gboolean pending_active;
+
+ gboolean commit_needed;
+ gboolean commiting; /* Set to TRUE during the commiting time only */
+
+ gboolean expandable;
+
/* read-only */
GstClockTime stop;
@@ -107,9 +126,13 @@ struct _GnlObjectClass
{
GstBinClass parent_class;
+ /* Signal method handler */
+ gboolean (*commit_signal_handler) (GnlObject * object, gboolean recurse);
+
/* virtual methods for subclasses */
gboolean (*prepare) (GnlObject * object);
gboolean (*cleanup) (GnlObject * object);
+ gboolean (*commit) (GnlObject * object, gboolean recurse);
};
GType gnl_object_get_type (void);
@@ -125,5 +148,13 @@ gnl_media_to_object_time (GnlObject * object, GstClockTime mtime,
void
gnl_object_set_caps (GnlObject * object, const GstCaps * caps);
+void
+gnl_object_set_commit_needed (GnlObject *object);
+
+gboolean
+gnl_object_commit (GnlObject *object, gboolean recurse);
+
+void
+gnl_object_reset (GnlObject *object);
G_END_DECLS
#endif /* __GNL_OBJECT_H__ */