mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-17 03:35:21 +00:00
timeline, track: Emit commited at the correct moment.
Summary: + [API] GESTrack::commited signal. + [API] ges_track_commit_sync We were emitting commited when timeline_commit was called, which wasn't very helpful. This commit makes it so we emit commited once all the compositions have actually been commited. We also add a synchronous commit method to spare the user the need to connect to the signal and wait, and update the documentation. Reviewers: thiblahute Differential Revision: http://phabricator.freedesktop.org/D83
This commit is contained in:
parent
54190d9dd8
commit
2e26a9e926
4 changed files with 169 additions and 23 deletions
|
@ -364,6 +364,8 @@ ges_timeline_load_from_uri
|
|||
ges_timeline_save_to_uri
|
||||
ges_timeline_enable_update
|
||||
ges_timeline_is_updating
|
||||
ges_timeline_commit
|
||||
ges_timeline_commit_sync
|
||||
<SUBSECTION usage>
|
||||
ges_timeline_get_tracks
|
||||
ges_timeline_get_layer
|
||||
|
|
|
@ -190,7 +190,14 @@ struct _GESTimelinePrivate
|
|||
|
||||
GHashTable *all_elements;
|
||||
|
||||
/* With GST_OBJECT_LOCK */
|
||||
guint expected_async_done;
|
||||
/* With GST_OBJECT_LOCK */
|
||||
guint expected_commited;
|
||||
|
||||
/* For ges_timeline_commit_sync */
|
||||
GMutex commited_lock;
|
||||
GCond commited_cond;
|
||||
};
|
||||
|
||||
/* private structure to contain our track-related information */
|
||||
|
@ -589,6 +596,10 @@ ges_timeline_class_init (GESTimelineClass * klass)
|
|||
/**
|
||||
* GESTimeline::commited:
|
||||
* @timeline: the #GESTimeline
|
||||
*
|
||||
* This signal will be emitted once the changes initiated by #ges_timeline_commit
|
||||
* have been executed in the backend. Use #ges_timeline_commit_sync if you
|
||||
* don't need to do anything in the meantime.
|
||||
*/
|
||||
ges_timeline_signals[COMMITED] =
|
||||
g_signal_new ("commited", G_TYPE_FROM_CLASS (klass),
|
||||
|
@ -610,6 +621,7 @@ ges_timeline_init (GESTimeline * self)
|
|||
self->priv->auto_transition = FALSE;
|
||||
priv->snapping_distance = 0;
|
||||
priv->expected_async_done = 0;
|
||||
priv->expected_commited = 0;
|
||||
|
||||
/* Move context initialization */
|
||||
init_movecontext (&self->priv->movecontext, TRUE);
|
||||
|
@ -639,6 +651,7 @@ ges_timeline_init (GESTimeline * self)
|
|||
G_CALLBACK (select_tracks_for_object_default), NULL);
|
||||
|
||||
g_rec_mutex_init (&priv->dyn_mutex);
|
||||
g_mutex_init (&priv->commited_lock);
|
||||
}
|
||||
|
||||
/* Private methods */
|
||||
|
@ -3024,23 +3037,25 @@ ges_timeline_get_layers (GESTimeline * timeline)
|
|||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* ges_timeline_commit:
|
||||
* @timeline: a #GESTimeline
|
||||
*
|
||||
* Commits all the pending changes of the clips contained in the
|
||||
* @timeline.
|
||||
*
|
||||
* When timing changes happen in a timeline, the changes are not
|
||||
* directly done inside NLE. This method needs to be called so any changes
|
||||
* on a clip contained in the timeline actually happen at the media
|
||||
* processing level.
|
||||
*
|
||||
* Returns: %TRUE if something as been commited %FALSE if nothing needed
|
||||
* to be commited
|
||||
*/
|
||||
gboolean
|
||||
ges_timeline_commit (GESTimeline * timeline)
|
||||
static void
|
||||
track_commited_cb (GESTrack * track, GESTimeline * timeline)
|
||||
{
|
||||
gboolean emit_commited = FALSE;
|
||||
GST_OBJECT_LOCK (timeline);
|
||||
timeline->priv->expected_commited -= 1;
|
||||
if (timeline->priv->expected_commited == 0)
|
||||
emit_commited = TRUE;
|
||||
g_signal_handlers_disconnect_by_func (track, track_commited_cb, timeline);
|
||||
GST_OBJECT_UNLOCK (timeline);
|
||||
|
||||
if (emit_commited) {
|
||||
g_signal_emit (timeline, ges_timeline_signals[COMMITED], 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Must be called with the timeline's DYN_LOCK */
|
||||
static gboolean
|
||||
ges_timeline_commit_unlocked (GESTimeline * timeline)
|
||||
{
|
||||
GList *tmp;
|
||||
gboolean res = TRUE;
|
||||
|
@ -3052,20 +3067,130 @@ ges_timeline_commit (GESTimeline * timeline)
|
|||
NULL, NULL, _find_transition_from_auto_transitions);
|
||||
}
|
||||
|
||||
for (tmp = timeline->tracks; tmp; tmp = tmp->next) {
|
||||
if (!ges_track_commit (GES_TRACK (tmp->data)))
|
||||
res = FALSE;
|
||||
timeline->priv->expected_commited =
|
||||
g_list_length (timeline->priv->priv_tracks);
|
||||
|
||||
if (timeline->priv->expected_commited == 0) {
|
||||
g_signal_emit (timeline, ges_timeline_signals[COMMITED], 0);
|
||||
} else {
|
||||
for (tmp = timeline->tracks; tmp; tmp = tmp->next) {
|
||||
g_signal_connect (tmp->data, "commited", G_CALLBACK (track_commited_cb),
|
||||
timeline);
|
||||
if (!ges_track_commit (GES_TRACK (tmp->data)))
|
||||
res = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure we reset the context */
|
||||
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
||||
|
||||
if (res)
|
||||
g_signal_emit (timeline, ges_timeline_signals[COMMITED], 0);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* ges_timeline_commit:
|
||||
* @timeline: a #GESTimeline
|
||||
*
|
||||
* Commit all the pending changes of the clips contained in the
|
||||
* @timeline.
|
||||
*
|
||||
* When changes happen in a timeline, they are not
|
||||
* directly executed in the non-linear engine. Call this method once you are
|
||||
* done with a set of changes and want it to be executed.
|
||||
*
|
||||
* The GESTimeline::commited signal will be emitted when the (possibly updated)
|
||||
* #GstPipeline is ready to output data again, except if the state of the
|
||||
* timeline was #GST_STATE_READY or #GST_STATE_NULL.
|
||||
*
|
||||
* Note that all the pending changes will automatically be executed when the
|
||||
* timeline goes from #GST_STATE_READY to #GST_STATE_PAUSED, which usually is
|
||||
* triggered by corresponding state changes in a containing #GESPipeline.
|
||||
*
|
||||
* You should not try to change the state of the timeline, seek it or add
|
||||
* tracks to it during a commit operation, that is between a call to this
|
||||
* function and after receiving the GESTimeline::commited signal.
|
||||
*
|
||||
* See #ges_timeline_commit_sync if you don't want to bother with waiting
|
||||
* for the signal.
|
||||
*
|
||||
* Returns: %TRUE if pending changes were commited or %FALSE if nothing needed
|
||||
* to be commited
|
||||
*/
|
||||
gboolean
|
||||
ges_timeline_commit (GESTimeline * timeline)
|
||||
{
|
||||
gboolean ret;
|
||||
|
||||
LOCK_DYN (timeline);
|
||||
ret = ges_timeline_commit_unlocked (timeline);
|
||||
UNLOCK_DYN (timeline);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
commited_cb (GESTimeline * timeline)
|
||||
{
|
||||
g_mutex_lock (&timeline->priv->commited_lock);
|
||||
g_cond_signal (&timeline->priv->commited_cond);
|
||||
g_mutex_unlock (&timeline->priv->commited_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* ges_timeline_commit_sync:
|
||||
* @timeline: a #GESTimeline
|
||||
*
|
||||
* Commit all the pending changes of the #GESClips contained in the
|
||||
* @timeline.
|
||||
*
|
||||
* Will return once the update is complete, that is when the
|
||||
* (possibly updated) #GstPipeline is ready to output data again, or if the
|
||||
* state of the timeline was #GST_STATE_READY or #GST_STATE_NULL.
|
||||
*
|
||||
* This function will wait for any pending state change of the timeline by
|
||||
* calling #gst_element_get_state with a #GST_CLOCK_TIME_NONE timeout, you
|
||||
* should not try to change the state from another thread before this function
|
||||
* has returned.
|
||||
*
|
||||
* See #ges_timeline_commit for more information.
|
||||
*
|
||||
* Returns: %TRUE if pending changes were commited or %FALSE if nothing needed
|
||||
* to be commited
|
||||
*/
|
||||
gboolean
|
||||
ges_timeline_commit_sync (GESTimeline * timeline)
|
||||
{
|
||||
gboolean ret;
|
||||
gboolean wait_for_signal;
|
||||
|
||||
/* Let's make sure our state is stable */
|
||||
gst_element_get_state (GST_ELEMENT (timeline), NULL, NULL,
|
||||
GST_CLOCK_TIME_NONE);
|
||||
|
||||
/* Let's make sure no track gets added between now and the actual commiting */
|
||||
LOCK_DYN (timeline);
|
||||
wait_for_signal = g_list_length (timeline->priv->priv_tracks) > 0
|
||||
&& GST_STATE (timeline) >= GST_STATE_PAUSED;
|
||||
|
||||
if (!wait_for_signal) {
|
||||
ret = ges_timeline_commit_unlocked (timeline);
|
||||
} else {
|
||||
gulong handler_id =
|
||||
g_signal_connect (timeline, "commited", (GCallback) commited_cb, NULL);
|
||||
|
||||
g_mutex_lock (&timeline->priv->commited_lock);
|
||||
|
||||
ret = ges_timeline_commit_unlocked (timeline);
|
||||
g_cond_wait (&timeline->priv->commited_cond,
|
||||
&timeline->priv->commited_lock);
|
||||
g_mutex_unlock (&timeline->priv->commited_lock);
|
||||
g_signal_handler_disconnect (timeline, handler_id);
|
||||
}
|
||||
|
||||
UNLOCK_DYN (timeline);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ges_timeline_get_duration:
|
||||
* @timeline: a #GESTimeline
|
||||
|
|
|
@ -119,6 +119,7 @@ GstPad * ges_timeline_get_pad_for_track (GESTimeline * timeline, GESTrack *track
|
|||
GList *ges_timeline_get_tracks (GESTimeline *timeline);
|
||||
|
||||
gboolean ges_timeline_commit (GESTimeline * timeline);
|
||||
gboolean ges_timeline_commit_sync (GESTimeline * timeline);
|
||||
|
||||
GstClockTime ges_timeline_get_duration (GESTimeline *timeline);
|
||||
|
||||
|
|
|
@ -92,6 +92,7 @@ enum
|
|||
ARG_LAST,
|
||||
TRACK_ELEMENT_ADDED,
|
||||
TRACK_ELEMENT_REMOVED,
|
||||
COMMITED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
|
@ -313,6 +314,13 @@ composition_duration_cb (GstElement * composition,
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
composition_commited_cb (GstElement * composition, gboolean changed,
|
||||
GESTrack * self)
|
||||
{
|
||||
g_signal_emit (self, ges_track_signals[COMMITED], 0);
|
||||
}
|
||||
|
||||
/* Internal */
|
||||
GstElement *
|
||||
ges_track_get_composition (GESTrack * track)
|
||||
|
@ -621,6 +629,14 @@ ges_track_class_init (GESTrackClass * klass)
|
|||
G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
|
||||
G_TYPE_NONE, 1, GES_TYPE_TRACK_ELEMENT);
|
||||
|
||||
/**
|
||||
* GESTrack::commited:
|
||||
* @track: the #GESTrack
|
||||
*/
|
||||
ges_track_signals[COMMITED] =
|
||||
g_signal_new ("commited", G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
|
||||
|
||||
klass->get_mixing_element = NULL;
|
||||
}
|
||||
|
||||
|
@ -643,6 +659,8 @@ ges_track_init (GESTrack * self)
|
|||
|
||||
g_signal_connect (G_OBJECT (self->priv->composition), "notify::duration",
|
||||
G_CALLBACK (composition_duration_cb), self);
|
||||
g_signal_connect (G_OBJECT (self->priv->composition), "commited",
|
||||
G_CALLBACK (composition_commited_cb), self);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue