From 1bb45a61a6a037ad465914aed92819e796ae6976 Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Wed, 19 May 2010 12:36:11 +0200 Subject: [PATCH] GESTimeline: Use GstDiscoverer for incomplete filesources If a GESTimelineFileSource is added to a layer and: * It doesn't have specified supported formats * OR it doesn't have a specified maximum duration * OR it doesn't have a specifed duration Then we asynchronously send it to the GstDiscoverer. If this happens, the state change of the timeline from READY to PAUSED will happen asynchronously and be completed when everything has been properly discovered. Part 2 of GstDiscoverer integration --- ges/ges-timeline.c | 181 +++++++++++++++++++++++++++++++++++++++++++-- ges/ges-timeline.h | 8 ++ 2 files changed, 184 insertions(+), 5 deletions(-) diff --git a/ges/ges-timeline.c b/ges/ges-timeline.c index 34ddd76575..c14254658a 100644 --- a/ges/ges-timeline.c +++ b/ges/ges-timeline.c @@ -60,9 +60,19 @@ enum LAST_SIGNAL }; +static GstBinClass *parent_class; + static guint ges_timeline_signals[LAST_SIGNAL] = { 0 }; gint custom_find_track (TrackPrivate * priv, GESTrack * track); +static GstStateChangeReturn +ges_timeline_change_state (GstElement * element, GstStateChange transition); +static void +discoverer_ready_cb (GstDiscoverer * discoverer, GESTimeline * timeline); +static void +discoverer_discovered_cb (GstDiscoverer * discoverer, + GstDiscovererInformation * info, GError * err, GESTimeline * timeline); + static void ges_timeline_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec) @@ -86,6 +96,13 @@ ges_timeline_set_property (GObject * object, guint property_id, static void ges_timeline_dispose (GObject * object) { + GESTimeline *timeline = GES_TIMELINE (object); + + if (timeline->discoverer) { + gst_discoverer_stop (timeline->discoverer); + g_object_unref (timeline->discoverer); + timeline->discoverer = NULL; + } G_OBJECT_CLASS (ges_timeline_parent_class)->dispose (object); } @@ -99,13 +116,17 @@ static void ges_timeline_class_init (GESTimelineClass * klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + element_class->change_state = ges_timeline_change_state; object_class->get_property = ges_timeline_get_property; object_class->set_property = ges_timeline_set_property; object_class->dispose = ges_timeline_dispose; object_class->finalize = ges_timeline_finalize; - /** * GESTimeline::track-added * @timeline: the #GESTimeline @@ -161,6 +182,14 @@ ges_timeline_init (GESTimeline * self) { self->layers = NULL; self->tracks = NULL; + + /* New discoverer with a 15s timeout */ + self->discoverer = gst_discoverer_new (15 * GST_SECOND); + g_signal_connect (self->discoverer, "ready", G_CALLBACK (discoverer_ready_cb), + self); + g_signal_connect (self->discoverer, "discovered", + G_CALLBACK (discoverer_discovered_cb), self); + gst_discoverer_start (self->discoverer); } /** @@ -216,13 +245,10 @@ ges_timeline_save (GESTimeline * timeline, gchar * uri) } static void -layer_object_added_cb (GESTimelineLayer * layer, GESTimelineObject * object, - GESTimeline * timeline) +add_object_to_tracks (GESTimeline * timeline, GESTimelineObject * object) { GList *tmp; - GST_DEBUG ("New TimelineObject %p added to layer %p", object, layer); - for (tmp = timeline->tracks; tmp; tmp = g_list_next (tmp)) { TrackPrivate *priv = (TrackPrivate *) tmp->data; GESTrack *track = priv->track; @@ -239,6 +265,151 @@ layer_object_added_cb (GESTimelineLayer * layer, GESTimelineObject * object, GST_LOG ("Got new TrackObject %p, adding it to track", trobj); ges_track_add_object (track, trobj); } +} + +static void +do_async_start (GESTimeline * timeline) +{ + GstMessage *message; + + timeline->async_pending = TRUE; + + message = gst_message_new_async_start (GST_OBJECT_CAST (timeline), FALSE); + parent_class->handle_message (GST_BIN_CAST (timeline), message); +} + +static void +do_async_done (GESTimeline * timeline) +{ + GstMessage *message; + + if (timeline->async_pending) { + GST_DEBUG_OBJECT (timeline, "Emitting async-done"); + message = gst_message_new_async_done (GST_OBJECT_CAST (timeline)); + parent_class->handle_message (GST_BIN_CAST (timeline), message); + + timeline->async_pending = FALSE; + } +} + +static void +discoverer_ready_cb (GstDiscoverer * discoverer, GESTimeline * timeline) +{ + do_async_done (timeline); +} + +static void +discoverer_discovered_cb (GstDiscoverer * discoverer, + GstDiscovererInformation * info, GError * err, GESTimeline * timeline) +{ + GList *tmp; + gboolean found = FALSE; + GESTimelineFileSource *tfs = NULL; + + GST_DEBUG ("Discovered uri %s", info->uri); + + /* Find corresponding TimelineFileSource in the sources */ + for (tmp = timeline->pendingobjects; tmp; tmp = tmp->next) { + tfs = (GESTimelineFileSource *) tmp->data; + + if (!g_strcmp0 (tfs->uri, info->uri)) { + found = TRUE; + break; + } + } + + if (found) { + /* Remove object from list */ + timeline->pendingobjects = + g_list_delete_link (timeline->pendingobjects, tmp); + + /* FIXME : Handle errors in discovery */ + + /* Update timelinefilesource properties based on info */ + for (tmp = info->stream_list; tmp; tmp = tmp->next) { + GstStreamInformation *sinf = (GstStreamInformation *) tmp->data; + + if (sinf->streamtype == GST_STREAM_AUDIO) + tfs->supportedformats |= GES_TRACK_TYPE_AUDIO; + else if ((sinf->streamtype == GST_STREAM_VIDEO) || + (sinf->streamtype == GST_STREAM_IMAGE)) + tfs->supportedformats |= GES_TRACK_TYPE_VIDEO; + } + + g_object_set (tfs, "max-duration", info->duration, NULL); + + /* Continue the processing on tfs */ + add_object_to_tracks (timeline, GES_TIMELINE_OBJECT (tfs)); + } +} + +static GstStateChangeReturn +ges_timeline_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + GESTimeline *timeline = GES_TIMELINE (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + if (timeline->pendingobjects) { + do_async_start (timeline); + ret = GST_STATE_CHANGE_ASYNC; + } + break; + default: + break; + } + + { + GstStateChangeReturn bret; + + bret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (G_UNLIKELY (bret == GST_STATE_CHANGE_NO_PREROLL)) { + do_async_done (timeline); + ret = bret; + } + } + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + do_async_done (timeline); + break; + default: + break; + } + + return ret; + +} + +static void +layer_object_added_cb (GESTimelineLayer * layer, GESTimelineObject * object, + GESTimeline * timeline) +{ + GList *tmp; + + GST_DEBUG ("New TimelineObject %p added to layer %p", object, layer); + + if (GES_IS_TIMELINE_FILE_SOURCE (object)) { + GESTimelineFileSource *tfs = GES_TIMELINE_FILE_SOURCE (object); + + /* Send the filesource to the discoverer if: + * * it doesn't have specified supported formats + * * OR it doesn't have a specified max-duration + * * OR it doesn't have a valid duration */ + + if (tfs->supportedformats == GES_TRACK_TYPE_UNKNOWN || + tfs->maxduration == GST_CLOCK_TIME_NONE || object->duration == 0) { + GST_LOG ("Incomplete TimelineFileSource, discovering it"); + timeline->pendingobjects = + g_list_append (timeline->pendingobjects, object); + gst_discoverer_append_uri (timeline->discoverer, + GES_TIMELINE_FILE_SOURCE (object)->uri); + } else + add_object_to_tracks (timeline, object); + } else { + add_object_to_tracks (timeline, object); + } GST_DEBUG ("done"); } diff --git a/ges/ges-timeline.h b/ges/ges-timeline.h index aed30b9504..e62cc52fd1 100644 --- a/ges/ges-timeline.h +++ b/ges/ges-timeline.h @@ -23,6 +23,7 @@ #include #include +#include #include G_BEGIN_DECLS @@ -54,6 +55,13 @@ struct _GESTimeline { /*< private >*/ GList *layers; /* A list of GESTimelineLayer sorted by priority */ GList *tracks; /* A list of private track data */ + + /* discoverer used for virgin sources */ + GstDiscoverer *discoverer; + /* Objects that are being discovered FIXME : LOCK ! */ + GList *pendingobjects; + /* Whether we are changing state asynchronously or not */ + gboolean async_pending; }; struct _GESTimelineClass {