#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 */
GstPad *srcpad; /* Timeline source pad */
GstPad *playsinkpad;
GstPad *encodebinpad;
- GstPad *blocked_pad;
- gulong probe_id;
} OutputChain;
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 *
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;
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)
{
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;
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;
}
/* 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);
}
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,
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;
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 */
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");
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);
}
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);
}
static void
-pad_removed_cb (GstElement * timeline, GstPad * pad, GESPipeline * self)
+_unlink_track (GESPipeline * self, GESTrack * track)
{
OutputChain *chain;
- GESTrack *track;
- GstPad *peer;
-
- GST_DEBUG_OBJECT (self, "pad removed %s:%s", GST_DEBUG_PAD_NAME (pad));
+ GstPad *pad, *peer;
- 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);
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);
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
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);
}
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));
}
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;
- GST_DEBUG ("track:%p, pad:%s:%s", track, GST_DEBUG_PAD_NAME (pad));
+ pad = gst_element_get_static_pad (GST_ELEMENT (track), "src");
- if (G_UNLIKELY (tr_priv->pad)) {
- GST_WARNING ("We are already controlling a pad for this track");
- return;
- }
+ GST_DEBUG ("track:%p, pad:%s:%s", track, GST_DEBUG_PAD_NAME (pad));
/* Remember the pad */
LOCK_DYN (tr_priv->timeline);
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)
{
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);
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);
}
/* 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,
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
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);
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");
}
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)
{
{
GESTrack *track = (GESTrack *) object;
GESTrackPrivate *priv = track->priv;
+ gboolean ret;
/* Remove all TrackElements and drop our reference */
g_hash_table_unref (priv->trackelements_iter);
(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;
}
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));
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
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);
}
/**