diff --git a/ges/ges-pipeline.c b/ges/ges-pipeline.c index d66c5907b7..dd4f1b3ac2 100644 --- a/ges/ges-pipeline.c +++ b/ges/ges-pipeline.c @@ -37,6 +37,10 @@ #include "ges-audio-track.h" #include "ges-video-track.h" +GST_DEBUG_CATEGORY_STATIC (ges_pipeline_debug); +#undef GST_CAT_DEFAULT +#define GST_CAT_DEFAULT ges_pipeline_debug + #define DEFAULT_TIMELINE_MODE GES_PIPELINE_MODE_PREVIEW /* Structure corresponding to a timeline - sink link */ @@ -48,8 +52,6 @@ typedef struct GstPad *srcpad; /* Timeline source pad */ GstPad *playsinkpad; GstPad *encodebinpad; - GstPad *blocked_pad; - gulong probe_id; } OutputChain; @@ -88,6 +90,8 @@ static OutputChain *get_output_chain_for_track (GESPipeline * self, GESTrack * track); static OutputChain *new_output_chain_for_track (GESPipeline * self, GESTrack * track); +static void _link_track (GESPipeline * self, GESTrack * track); +static void _unlink_track (GESPipeline * self, GESTrack * track); /**************************************************** * Video Overlay vmethods implementation * @@ -234,6 +238,9 @@ ges_pipeline_class_init (GESPipelineClass * klass) g_type_class_add_private (klass, sizeof (GESPipelinePrivate)); + GST_DEBUG_CATEGORY_INIT (ges_pipeline_debug, "gespipeline", + GST_DEBUG_FG_YELLOW, "ges pipeline"); + object_class->dispose = ges_pipeline_dispose; object_class->get_property = ges_pipeline_get_property; object_class->set_property = ges_pipeline_set_property; @@ -434,6 +441,39 @@ done: return TRUE; } +static void +_link_tracks (GESPipeline * pipeline) +{ + GList *tmp; + + GST_DEBUG_OBJECT (pipeline, "Linking tracks"); + + if (!pipeline->priv->timeline) { + GST_INFO_OBJECT (pipeline, "Not timeline set yet, doing nothing"); + + return; + } + + for (tmp = pipeline->priv->timeline->tracks; tmp; tmp = tmp->next) + _link_track (pipeline, tmp->data); +} + +static void +_unlink_tracks (GESPipeline * pipeline) +{ + GList *tmp; + + GST_DEBUG_OBJECT (pipeline, "Disconnecting all tracks"); + if (!pipeline->priv->timeline) { + GST_INFO_OBJECT (pipeline, "Not timeline set yet, doing nothing"); + + return; + } + + for (tmp = pipeline->priv->timeline->tracks; tmp; tmp = tmp->next) + _unlink_track (pipeline, tmp->data); +} + static GstStateChangeReturn ges_pipeline_change_state (GstElement * element, GstStateChange transition) { @@ -453,12 +493,13 @@ ges_pipeline_change_state (GstElement * element, GstStateChange transition) if (self->priv->mode & (GES_PIPELINE_MODE_RENDER | GES_PIPELINE_MODE_SMART_RENDER)) GST_DEBUG ("rendering => Updating pipeline caps"); + /* Set caps on all tracks according to profile if present */ if (!ges_pipeline_update_caps (self)) { GST_ERROR_OBJECT (element, "Error setting the caps for rendering"); ret = GST_STATE_CHANGE_FAILURE; goto done; } - /* Set caps on all tracks according to profile if present */ + _link_tracks (self); break; default: break; @@ -468,6 +509,14 @@ ges_pipeline_change_state (GstElement * element, GstStateChange transition) GST_ELEMENT_CLASS (ges_pipeline_parent_class)->change_state (element, transition); + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + _unlink_tracks (self); + break; + default: + break; + } + done: return ret; } @@ -500,25 +549,21 @@ get_output_chain_for_track (GESPipeline * self, GESTrack * track) /* Fetches a compatible pad on the target element which isn't already * linked */ static GstPad * -get_compatible_unlinked_pad (GstElement * element, GstPad * pad) +get_compatible_unlinked_pad (GstElement * element, GESTrack * track) { GstPad *res = NULL; GstIterator *pads; gboolean done = FALSE; - GstCaps *srccaps; + const GstCaps *srccaps; GValue paditem = { 0, }; - if (G_UNLIKELY (pad == NULL)) - goto no_pad; + if (G_UNLIKELY (track == NULL)) + goto no_track; - GST_DEBUG ("element : %s, pad %s:%s", - GST_ELEMENT_NAME (element), GST_DEBUG_PAD_NAME (pad)); + GST_DEBUG_OBJECT (element, " track %" GST_PTR_FORMAT, track); - if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) - pads = gst_element_iterate_sink_pads (element); - else - pads = gst_element_iterate_src_pads (element); - srccaps = gst_pad_query_caps (pad, NULL); + pads = gst_element_iterate_sink_pads (element); + srccaps = ges_track_get_caps (track); GST_DEBUG ("srccaps %" GST_PTR_FORMAT, srccaps); @@ -553,34 +598,27 @@ get_compatible_unlinked_pad (GstElement * element, GstPad * pad) } g_value_reset (&paditem); gst_iterator_free (pads); - gst_caps_unref (srccaps); return res; -no_pad: +no_track: { - GST_ERROR ("No pad to check against"); + GST_ERROR ("No track to check against"); return NULL; } } -static GstPadProbeReturn -pad_blocked (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) -{ - /* no nothing */ - GST_DEBUG_OBJECT (pad, "blocked callback, blocked"); - return GST_PAD_PROBE_OK; -} - static void -pad_added_cb (GstElement * timeline, GstPad * pad, GESPipeline * self) +_link_track (GESPipeline * self, GESTrack * track) { + GstPad *pad; OutputChain *chain; - GESTrack *track; GstPad *sinkpad; GstCaps *caps; + GstPadLinkReturn lret; gboolean reconfigured = FALSE; + pad = ges_timeline_get_pad_for_track (self->priv->timeline, track); caps = gst_pad_query_caps (pad, NULL); GST_DEBUG_OBJECT (self, "new pad %s:%s , caps:%" GST_PTR_FORMAT, @@ -588,8 +626,6 @@ pad_added_cb (GstElement * timeline, GstPad * pad, GESPipeline * self) gst_caps_unref (caps); - track = ges_timeline_get_track_for_pad (self->priv->timeline, pad); - if (G_UNLIKELY (!track)) { GST_WARNING_OBJECT (self, "Couldn't find coresponding track !"); return; @@ -613,15 +649,23 @@ pad_added_cb (GstElement * timeline, GstPad * pad, GESPipeline * self) if (!(chain = get_output_chain_for_track (self, track))) chain = new_output_chain_for_track (self, track); chain->srcpad = pad; + gst_object_unref (pad); /* Adding tee */ - chain->tee = gst_element_factory_make ("tee", NULL); - gst_bin_add (GST_BIN_CAST (self), chain->tee); - gst_element_sync_state_with_parent (chain->tee); + if (!chain->tee) { + chain->tee = gst_element_factory_make ("tee", NULL); + gst_bin_add (GST_BIN_CAST (self), chain->tee); + gst_element_sync_state_with_parent (chain->tee); + } /* Linking pad to tee */ sinkpad = gst_element_get_static_pad (chain->tee, "sink"); - gst_pad_link_full (pad, sinkpad, GST_PAD_LINK_CHECK_NOTHING); + lret = gst_pad_link (pad, sinkpad); + if (lret != GST_PAD_LINK_OK) { + GST_ERROR_OBJECT (self, "Could not link the tee (%i)", lret); + goto error; + } + gst_object_unref (sinkpad); /* Connect playsink */ @@ -658,14 +702,10 @@ pad_added_cb (GstElement * timeline, GstPad * pad, GESPipeline * self) tmppad = gst_element_get_request_pad (chain->tee, "src_%u"); if (G_UNLIKELY (gst_pad_link_full (tmppad, sinkpad, GST_PAD_LINK_CHECK_NOTHING) != GST_PAD_LINK_OK)) { - GST_ERROR_OBJECT (self, "Couldn't link track pad to playsink"); + GST_ERROR_OBJECT (self, "Couldn't link track pad to encodebin"); gst_object_unref (tmppad); goto error; } - chain->blocked_pad = tmppad; - GST_DEBUG_OBJECT (tmppad, "blocking pad"); - chain->probe_id = gst_pad_add_probe (tmppad, - GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM, pad_blocked, NULL, NULL); GST_DEBUG ("Reconfiguring playsink"); @@ -685,7 +725,7 @@ pad_added_cb (GstElement * timeline, GstPad * pad, GESPipeline * self) if (!chain->encodebinpad) { /* Check for unused static pads */ - sinkpad = get_compatible_unlinked_pad (self->priv->encodebin, pad); + sinkpad = get_compatible_unlinked_pad (self->priv->encodebin, track); if (sinkpad == NULL) { GstCaps *caps = gst_pad_query_caps (pad, NULL); @@ -704,10 +744,9 @@ pad_added_cb (GstElement * timeline, GstPad * pad, GESPipeline * self) } tmppad = gst_element_get_request_pad (chain->tee, "src_%u"); - if (G_UNLIKELY (gst_pad_link_full (tmppad, - chain->encodebinpad, + if (G_UNLIKELY (gst_pad_link_full (tmppad, chain->encodebinpad, GST_PAD_LINK_CHECK_NOTHING) != GST_PAD_LINK_OK)) { - GST_WARNING_OBJECT (self, "Couldn't link track pad to playsink"); + GST_ERROR_OBJECT (self, "Couldn't link track pad to encodebin"); goto error; } gst_object_unref (tmppad); @@ -733,25 +772,19 @@ error: } static void -pad_removed_cb (GstElement * timeline, GstPad * pad, GESPipeline * self) +_unlink_track (GESPipeline * self, GESTrack * track) { OutputChain *chain; - GESTrack *track; - GstPad *peer; + GstPad *pad, *peer; - GST_DEBUG_OBJECT (self, "pad removed %s:%s", GST_DEBUG_PAD_NAME (pad)); - - if (G_UNLIKELY (!(track = - ges_timeline_get_track_for_pad (self->priv->timeline, pad)))) { - GST_WARNING_OBJECT (self, "Couldn't find coresponding track !"); - return; - } + GST_DEBUG_OBJECT (self, "Unlinking removed %" GST_PTR_FORMAT, track); if (G_UNLIKELY (!(chain = get_output_chain_for_track (self, track)))) { - GST_DEBUG_OBJECT (self, "Pad wasn't used"); + GST_DEBUG_OBJECT (self, "Track wasn't used"); return; } + pad = ges_timeline_get_pad_for_track (self->priv->timeline, track); /* Unlink encodebin */ if (chain->encodebinpad) { peer = gst_pad_get_peer (chain->encodebinpad); @@ -771,14 +804,6 @@ pad_removed_cb (GstElement * timeline, GstPad * pad, GESPipeline * self) gst_object_unref (chain->playsinkpad); } - if (chain->blocked_pad) { - GST_DEBUG_OBJECT (chain->blocked_pad, "unblocking pad"); - gst_pad_remove_probe (chain->blocked_pad, chain->probe_id); - gst_object_unref (chain->blocked_pad); - chain->blocked_pad = NULL; - chain->probe_id = 0; - } - /* Unlike/remove tee */ peer = gst_element_get_static_pad (chain->tee, "sink"); gst_pad_unlink (pad, peer); @@ -788,29 +813,11 @@ pad_removed_cb (GstElement * timeline, GstPad * pad, GESPipeline * self) self->priv->chains = g_list_remove (self->priv->chains, chain); g_free (chain); + gst_object_unref (pad); GST_DEBUG ("done"); } -static void -no_more_pads_cb (GstElement * timeline, GESPipeline * self) -{ - GList *tmp; - - GST_DEBUG ("received no-more-pads"); - for (tmp = self->priv->chains; tmp; tmp = g_list_next (tmp)) { - OutputChain *chain = (OutputChain *) tmp->data; - - if (chain->blocked_pad) { - GST_DEBUG_OBJECT (chain->blocked_pad, "unblocking pad"); - gst_pad_remove_probe (chain->blocked_pad, chain->probe_id); - gst_object_unref (chain->blocked_pad); - chain->blocked_pad = NULL; - chain->probe_id = 0; - } - } -} - /** * ges_pipeline_set_timeline: * @pipeline: a #GESPipeline @@ -826,6 +833,7 @@ no_more_pads_cb (GstElement * timeline, GESPipeline * self) gboolean ges_pipeline_set_timeline (GESPipeline * pipeline, GESTimeline * timeline) { + g_return_val_if_fail (GES_IS_PIPELINE (pipeline), FALSE); g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE); g_return_val_if_fail (pipeline->priv->timeline == NULL, FALSE); @@ -838,13 +846,6 @@ ges_pipeline_set_timeline (GESPipeline * pipeline, GESTimeline * timeline) } pipeline->priv->timeline = timeline; - /* Connect to pipeline */ - g_signal_connect (timeline, "pad-added", (GCallback) pad_added_cb, pipeline); - g_signal_connect (timeline, "pad-removed", (GCallback) pad_removed_cb, - pipeline); - g_signal_connect (timeline, "no-more-pads", (GCallback) no_more_pads_cb, - pipeline); - /* FIXME Check if we should rollback if we can't sync state */ gst_element_sync_state_with_parent (GST_ELEMENT (timeline)); diff --git a/ges/ges-timeline.c b/ges/ges-timeline.c index 686c9d2097..5c221a3872 100644 --- a/ges/ges-timeline.c +++ b/ges/ges-timeline.c @@ -2400,19 +2400,18 @@ _pad_probe_cb (GstPad * mixer_pad, GstPadProbeInfo * info, } static void -pad_added_cb (GESTrack * track, GstPad * pad, TrackPrivate * tr_priv) +_ghost_track_srcpad (TrackPrivate * tr_priv) { + GstPad *pad; gchar *padname; gboolean no_more; GList *tmp; + GESTrack *track = tr_priv->track; + + pad = gst_element_get_static_pad (GST_ELEMENT (track), "src"); GST_DEBUG ("track:%p, pad:%s:%s", track, GST_DEBUG_PAD_NAME (pad)); - if (G_UNLIKELY (tr_priv->pad)) { - GST_WARNING ("We are already controlling a pad for this track"); - return; - } - /* Remember the pad */ LOCK_DYN (tr_priv->timeline); GST_OBJECT_LOCK (track); @@ -2449,28 +2448,6 @@ pad_added_cb (GESTrack * track, GstPad * pad, TrackPrivate * tr_priv) UNLOCK_DYN (tr_priv->timeline); } -static void -pad_removed_cb (GESTrack * track, GstPad * pad, TrackPrivate * tr_priv) -{ - GST_DEBUG ("track:%p, pad:%s:%s", track, GST_DEBUG_PAD_NAME (pad)); - - if (G_UNLIKELY (tr_priv->pad != pad)) { - GST_WARNING ("Not the pad we're controlling"); - return; - } - - if (G_UNLIKELY (tr_priv->ghostpad == NULL)) { - GST_WARNING ("We don't have a ghostpad for this pad !"); - return; - } - - GST_DEBUG ("Removing ghostpad"); - gst_pad_set_active (tr_priv->ghostpad, FALSE); - gst_element_remove_pad (GST_ELEMENT (tr_priv->timeline), tr_priv->ghostpad); - tr_priv->ghostpad = NULL; - tr_priv->pad = NULL; -} - gboolean timeline_add_element (GESTimeline * timeline, GESTimelineElement * element) { @@ -2809,10 +2786,6 @@ ges_timeline_add_track (GESTimeline * timeline, GESTrack * track) UNLOCK_DYN (timeline); timeline->tracks = g_list_append (timeline->tracks, track); - /* Listen to pad-added/-removed */ - g_signal_connect (track, "pad-added", (GCallback) pad_added_cb, tr_priv); - g_signal_connect (track, "pad-removed", (GCallback) pad_removed_cb, tr_priv); - /* Inform the track that it's currently being used by ourself */ ges_track_set_timeline (track, timeline); @@ -2843,6 +2816,8 @@ ges_timeline_add_track (GESTimeline * timeline, GESTrack * track) g_list_free (objects); } + _ghost_track_srcpad (tr_priv); + /* FIXME Check if we should rollback if we can't sync state */ gst_element_sync_state_with_parent (GST_ELEMENT (track)); g_object_set (track, "message-forward", TRUE, NULL); @@ -2904,8 +2879,6 @@ ges_timeline_remove_track (GESTimeline * timeline, GESTrack * track) } /* Remove pad-added/-removed handlers */ - g_signal_handlers_disconnect_by_func (track, pad_added_cb, tr_priv); - g_signal_handlers_disconnect_by_func (track, pad_removed_cb, tr_priv); g_signal_handlers_disconnect_by_func (track, track_element_added_cb, timeline); g_signal_handlers_disconnect_by_func (track, track_element_removed_cb, diff --git a/ges/ges-track.c b/ges/ges-track.c index cb17037aa3..6840fde679 100644 --- a/ges/ges-track.c +++ b/ges/ges-track.c @@ -37,6 +37,12 @@ G_DEFINE_TYPE_WITH_CODE (GESTrack, ges_track, GST_TYPE_BIN, G_IMPLEMENT_INTERFACE (GES_TYPE_META_CONTAINER, NULL)); +static GstStaticPadTemplate ges_track_src_pad_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + /* Structure that represents gaps and keep knowledge * of the gaps filled in the track */ typedef struct @@ -92,9 +98,6 @@ static guint ges_track_signals[LAST_SIGNAL] = { 0 }; static GParamSpec *properties[ARG_LAST]; -static void pad_added_cb (GstElement * element, GstPad * pad, GESTrack * track); -static void -pad_removed_cb (GstElement * element, GstPad * pad, GESTrack * track); static void composition_duration_cb (GstElement * composition, GParamSpec * arg G_GNUC_UNUSED, GESTrack * obj); @@ -256,9 +259,10 @@ sort_track_elements_cb (GESTrackElement * child, static void _ghost_nlecomposition_srcpad (GESTrack * track) { - GESTrackPrivate *priv = track->priv; GstPad *capsfilter_sink; GstPad *capsfilter_src; + GESTrackPrivate *priv = track->priv; + GstPad *pad = gst_element_get_static_pad (priv->composition, "src"); capsfilter_sink = gst_element_get_static_pad (priv->capsfilter, "sink"); @@ -277,22 +281,6 @@ _ghost_nlecomposition_srcpad (GESTrack * track) GST_DEBUG ("done"); } -static void -pad_removed_cb (GstElement * element, GstPad * pad, GESTrack * track) -{ - GESTrackPrivate *priv = track->priv; - - GST_DEBUG ("track:%p, pad %s:%s", track, GST_DEBUG_PAD_NAME (pad)); - - if (G_LIKELY (priv->srcpad)) { - gst_pad_set_active (priv->srcpad, FALSE); - gst_element_remove_pad (GST_ELEMENT (track), priv->srcpad); - priv->srcpad = NULL; - } - - GST_DEBUG ("done"); -} - static void composition_duration_cb (GstElement * composition, GParamSpec * arg G_GNUC_UNUSED, GESTrack * track) @@ -423,6 +411,7 @@ ges_track_dispose (GObject * object) { GESTrack *track = (GESTrack *) object; GESTrackPrivate *priv = track->priv; + gboolean ret; /* Remove all TrackElements and drop our reference */ g_hash_table_unref (priv->trackelements_iter); @@ -430,11 +419,13 @@ ges_track_dispose (GObject * object) (GFunc) dispose_trackelements_foreach, track); g_sequence_free (priv->trackelements_by_start); g_list_free_full (priv->gaps, (GDestroyNotify) free_gap); + g_signal_emit_by_name (track->priv->composition, "commit", TRUE, &ret); if (priv->mixing_operation) gst_object_unref (priv->mixing_operation); if (priv->composition) { + gst_element_remove_pad (GST_ELEMENT (track), priv->srcpad); gst_bin_remove (GST_BIN (object), priv->composition); priv->composition = NULL; } @@ -508,6 +499,7 @@ static void ges_track_class_init (GESTrackClass * klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + GstElementClass *gstelement_class = (GstElementClass *) klass; g_type_class_add_private (klass, sizeof (GESTrackPrivate)); @@ -586,6 +578,9 @@ ges_track_class_init (GESTrackClass * klass) g_object_class_install_property (object_class, ARG_MIXING, properties[ARG_MIXING]); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&ges_track_src_pad_template)); + /** * GESTrack::track-element-added: * @object: the #GESTrack @@ -632,10 +627,6 @@ ges_track_init (GESTrack * self) g_signal_connect (G_OBJECT (self->priv->composition), "notify::duration", G_CALLBACK (composition_duration_cb), self); - g_signal_connect (self->priv->composition, "pad-added", - (GCallback) pad_added_cb, self); - g_signal_connect (self->priv->composition, "pad-removed", - (GCallback) pad_removed_cb, self); } /**