Implement and use the GstStream API
authorThibault Saunier <tsaunier@igalia.com>
Fri, 7 Jun 2019 03:19:38 +0000 (23:19 -0400)
committerThibault Saunier <tsaunier@igalia.com>
Fri, 5 Jul 2019 21:53:15 +0000 (17:53 -0400)
ges/ges-timeline.c
ges/ges-track.c
plugins/ges/gesbasebin.c
plugins/ges/gesdemux.c
plugins/nle/nlecomposition.c

index ef437bf..f2fd616 100644 (file)
@@ -135,6 +135,8 @@ struct _GESTimelinePrivate
   GCond commited_cond;
 
   GThread *valid_thread;
+
+  GstStreamCollection *stream_collection;
 };
 
 /* private structure to contain our track-related information */
@@ -147,6 +149,7 @@ typedef struct
   GstPad *ghostpad;
 
   gulong probe_id;
+  GstStream *stream;
 } TrackPrivate;
 
 enum
@@ -302,6 +305,7 @@ ges_timeline_dispose (GObject * object)
   g_list_free_full (priv->auto_transitions, gst_object_unref);
 
   g_hash_table_unref (priv->all_elements);
+  gst_object_unref (priv->stream_collection);
 
   G_OBJECT_CLASS (ges_timeline_parent_class)->dispose (object);
 }
@@ -374,6 +378,63 @@ forward:
   gst_element_post_message (GST_ELEMENT_CAST (bin), message);
 }
 
+static GstStateChangeReturn
+ges_timeline_change_state (GstElement * element, GstStateChange transition)
+{
+  GstStateChangeReturn res;
+  GESTimeline *timeline = GES_TIMELINE (element);
+
+  res = GST_ELEMENT_CLASS (ges_timeline_parent_class)->change_state (element,
+      transition);
+
+  if (transition == GST_STATE_CHANGE_READY_TO_PAUSED)
+    gst_element_post_message ((GstElement *) timeline,
+        gst_message_new_stream_collection ((GstObject *) timeline,
+            timeline->priv->stream_collection));
+  return res;
+}
+
+static gboolean
+ges_timeline_send_event (GstElement * element, GstEvent * event)
+{
+  GESTimeline *timeline = GES_TIMELINE (element);
+
+  if (GST_EVENT_TYPE (event) == GST_EVENT_SELECT_STREAMS) {
+    GList *stream_ids = NULL, *tmp, *to_remove =
+        ges_timeline_get_tracks (timeline);
+
+    gst_event_parse_select_streams (event, &stream_ids);
+    for (tmp = stream_ids; tmp; tmp = tmp->next) {
+      GList *trackit;
+      gchar *stream_id = tmp->data;
+
+      LOCK_DYN (timeline);
+      for (trackit = timeline->priv->priv_tracks; trackit;
+          trackit = trackit->next) {
+        TrackPrivate *tr_priv = trackit->data;
+
+        if (!g_strcmp0 (gst_stream_get_stream_id (tr_priv->stream), stream_id)) {
+          to_remove = g_list_remove (to_remove, tr_priv->track);
+        }
+      }
+      UNLOCK_DYN (timeline);
+    }
+    for (tmp = to_remove; tmp; tmp = tmp->next) {
+      GST_INFO_OBJECT (timeline, "Removed unselected track: %" GST_PTR_FORMAT,
+          tmp->data);
+      ges_timeline_remove_track (timeline, tmp->data);
+    }
+
+    g_list_free_full (stream_ids, g_free);
+    g_list_free (to_remove);
+
+    return TRUE;
+  }
+
+  return GST_ELEMENT_CLASS (ges_timeline_parent_class)->send_event (element,
+      event);
+}
+
 /* we collect the first result */
 static gboolean
 _gst_array_accumulator (GSignalInvocationHint * ihint,
@@ -392,6 +453,7 @@ static void
 ges_timeline_class_init (GESTimelineClass * klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GstElementClass *element_class = (GstElementClass *) klass;
   GstBinClass *bin_class = GST_BIN_CLASS (klass);
 
   GST_DEBUG_CATEGORY_INIT (ges_timeline_debug, "gestimeline",
@@ -405,6 +467,9 @@ ges_timeline_class_init (GESTimelineClass * klass)
   object_class->dispose = ges_timeline_dispose;
   object_class->finalize = ges_timeline_finalize;
 
+  element_class->change_state = GST_DEBUG_FUNCPTR (ges_timeline_change_state);
+  element_class->send_event = GST_DEBUG_FUNCPTR (ges_timeline_send_event);
+
   bin_class->handle_message = GST_DEBUG_FUNCPTR (ges_timeline_handle_message);
 
   /**
@@ -597,6 +662,7 @@ ges_timeline_init (GESTimeline * self)
       g_hash_table_new_full (g_str_hash, g_str_equal, g_free, gst_object_unref);
 
   priv->stream_start_group_id = -1;
+  priv->stream_collection = gst_stream_collection_new (NULL);
 
   g_signal_connect_after (self, "select-tracks-for-object",
       G_CALLBACK (select_tracks_for_object_default), NULL);
@@ -1405,6 +1471,34 @@ layer_object_removed_cb (GESLayer * layer, GESClip * clip,
   GST_DEBUG ("Done");
 }
 
+static gboolean
+update_stream_object (TrackPrivate * tr_priv)
+{
+  gboolean res = FALSE;
+  GstStreamType type = GST_STREAM_TYPE_UNKNOWN;
+  gchar *stream_id;
+
+  g_object_get (tr_priv->track, "id", &stream_id, NULL);
+  if (tr_priv->track->type == GES_TRACK_TYPE_VIDEO)
+    type = GST_STREAM_TYPE_VIDEO;
+  if (tr_priv->track->type == GES_TRACK_TYPE_AUDIO)
+    type = GST_STREAM_TYPE_AUDIO;
+
+  if (!tr_priv->stream ||
+      g_strcmp0 (stream_id, gst_stream_get_stream_id (tr_priv->stream))) {
+    res = TRUE;
+    gst_object_replace ((GstObject **) & tr_priv->stream,
+        (GstObject *) gst_stream_new (stream_id,
+            (GstCaps *) ges_track_get_caps (tr_priv->track),
+            type, GST_STREAM_FLAG_NONE)
+        );
+  }
+
+  g_free (stream_id);
+
+  return res;
+}
+
 static void
 trackelement_start_changed_cb (GESTrackElement * child,
     GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
@@ -1436,7 +1530,6 @@ _pad_probe_cb (GstPad * mixer_pad, GstPadProbeInfo * info,
 {
   GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
   GESTimeline *timeline = tr_priv->timeline;
-  gchar *stream_id;
 
   if (GST_EVENT_TYPE (event) == GST_EVENT_STREAM_START) {
     LOCK_DYN (timeline);
@@ -1447,10 +1540,10 @@ _pad_probe_cb (GstPad * mixer_pad, GstPadProbeInfo * info,
     }
 
     gst_event_unref (event);
-    g_object_get (tr_priv->track, "id", &stream_id, NULL);
-    info->data = gst_event_new_stream_start (stream_id);
-    gst_event_set_group_id (GST_PAD_PROBE_INFO_EVENT (info),
-        timeline->priv->stream_start_group_id);
+    event = info->data =
+        gst_event_new_stream_start (gst_stream_get_stream_id (tr_priv->stream));
+    gst_event_set_stream (event, tr_priv->stream);
+    gst_event_set_group_id (event, timeline->priv->stream_start_group_id);
     UNLOCK_DYN (timeline);
 
     return GST_PAD_PROBE_REMOVE;
@@ -1898,6 +1991,10 @@ ges_timeline_add_track (GESTimeline * timeline, GESTrack * track)
   tr_priv->timeline = timeline;
   tr_priv->track = track;
 
+  update_stream_object (tr_priv);
+  gst_stream_collection_add_stream (timeline->priv->stream_collection,
+      gst_object_ref (tr_priv->stream));
+
   /* Add the track to the list of tracks we track */
   LOCK_DYN (timeline);
   timeline->priv->priv_tracks = g_list_append (timeline->priv->priv_tracks,
@@ -2174,12 +2271,24 @@ ges_timeline_commit_unlocked (GESTimeline * timeline)
   if (timeline->priv->expected_commited == 0) {
     g_signal_emit (timeline, ges_timeline_signals[COMMITED], 0);
   } else {
+    GstStreamCollection *collection = gst_stream_collection_new (NULL);
+
     for (tmp = timeline->tracks; tmp; tmp = tmp->next) {
+      TrackPrivate *tr_priv =
+          g_list_find_custom (timeline->priv->priv_tracks, tmp->data,
+          (GCompareFunc) custom_find_track)->data;
+
+      update_stream_object (tr_priv);
+      gst_stream_collection_add_stream (collection,
+          gst_object_ref (tr_priv->stream));
       g_signal_connect (tmp->data, "commited", G_CALLBACK (track_commited_cb),
           timeline);
       if (!ges_track_commit (GES_TRACK (tmp->data)))
         res = FALSE;
     }
+
+    gst_object_unref (timeline->priv->stream_collection);
+    timeline->priv->stream_collection = collection;
   }
 
   return res;
@@ -2218,6 +2327,7 @@ gboolean
 ges_timeline_commit (GESTimeline * timeline)
 {
   gboolean ret;
+  GstStreamCollection *pcollection = timeline->priv->stream_collection;
 
   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
 
@@ -2225,6 +2335,12 @@ ges_timeline_commit (GESTimeline * timeline)
   ret = ges_timeline_commit_unlocked (timeline);
   UNLOCK_DYN (timeline);
 
+  if (pcollection != timeline->priv->stream_collection) {
+    gst_element_post_message ((GstElement *) timeline,
+        gst_message_new_stream_collection ((GstObject *) timeline,
+            timeline->priv->stream_collection));
+  }
+
   ges_timeline_emit_snapping (timeline, NULL, NULL, GST_CLOCK_TIME_NONE);
   return ret;
 }
index 789b420..2cbbb4b 100644 (file)
@@ -402,6 +402,43 @@ ges_track_change_state (GstElement * element, GstStateChange transition)
       transition);
 }
 
+static void
+ges_track_handle_message (GstBin * bin, GstMessage * message)
+{
+  GESTrack *track = GES_TRACK (bin);
+
+  if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_STREAM_COLLECTION) {
+    gint i;
+    GList *selected_streams = NULL;
+    GstStreamCollection *collection;
+
+    gst_message_parse_stream_collection (message, &collection);
+
+    for (i = 0; i < gst_stream_collection_get_size (collection); i++) {
+      GstStream *stream = gst_stream_collection_get_stream (collection, i);
+      GstStreamType stype = gst_stream_get_stream_type (stream);
+
+      if ((track->type == GES_TRACK_TYPE_VIDEO
+              && stype == GST_STREAM_TYPE_VIDEO)
+          || (track->type == GES_TRACK_TYPE_AUDIO
+              && stype == GST_STREAM_TYPE_AUDIO)
+          || (stype == GST_STREAM_TYPE_UNKNOWN)) {
+
+        selected_streams =
+            g_list_append (selected_streams,
+            (gchar *) gst_stream_get_stream_id (stream));
+      }
+    }
+
+    if (selected_streams) {
+      gst_element_send_event (GST_ELEMENT (GST_MESSAGE_SRC (message)),
+          gst_event_new_select_streams (selected_streams));
+      g_list_free (selected_streams);
+    }
+  }
+  gst_element_post_message (GST_ELEMENT_CAST (bin), message);
+}
+
 /* GObject virtual methods */
 static void
 ges_track_get_property (GObject * object, guint property_id,
@@ -567,9 +604,12 @@ ges_track_class_init (GESTrackClass * klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GstElementClass *gstelement_class = (GstElementClass *) klass;
+  GstBinClass *bin_class = GST_BIN_CLASS (klass);
 
   gstelement_class->change_state = GST_DEBUG_FUNCPTR (ges_track_change_state);
 
+  bin_class->handle_message = GST_DEBUG_FUNCPTR (ges_track_handle_message);
+
   object_class->get_property = ges_track_get_property;
   object_class->set_property = ges_track_set_property;
   object_class->dispose = ges_track_dispose;
index e39e4f3..d878bf6 100644 (file)
@@ -144,7 +144,7 @@ ges_base_bin_init (GESBaseBin * self)
 }
 
 static GstFlowReturn
-gst_bin_src_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
+ges_base_bin_src_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
 {
   GstFlowReturn result, chain_result;
   GESBaseBin *self = GES_BASE_BIN (GST_OBJECT_PARENT (parent));
@@ -186,6 +186,7 @@ ges_base_bin_set_timeline (GESBaseBin * self, GESTimeline * timeline)
     return FALSE;
   }
 
+  ges_timeline_commit (timeline);
   for (tmp = timeline->tracks; tmp; tmp = tmp->next) {
     GstPad *gpad;
     gchar *name = NULL;
@@ -238,7 +239,7 @@ ges_base_bin_set_timeline (GESBaseBin * self, GESTimeline * timeline)
 
     proxy_pad = GST_PAD (gst_proxy_pad_get_internal (GST_PROXY_PAD (gpad)));
     gst_flow_combiner_add_pad (priv->flow_combiner, proxy_pad);
-    gst_pad_set_chain_function (proxy_pad, gst_bin_src_chain);
+    gst_pad_set_chain_function (proxy_pad, ges_base_bin_src_chain);
     gst_object_unref (proxy_pad);
     GST_DEBUG_OBJECT (sbin, "Adding pad: %" GST_PTR_FORMAT, gpad);
   }
index 62ff2c0..5718bea 100644 (file)
@@ -142,6 +142,7 @@ typedef struct
   GCond cond;
   gulong loaded_sigid;
   gulong error_sigid;
+  GESDemux *self;
 } TimelineConstructionData;
 
 static void
@@ -237,6 +238,9 @@ done:
 
   gst_clear_object (&project);
 
+  GST_INFO_OBJECT (data->self, "Timeline properly loaded: %" GST_PTR_FORMAT,
+      data->timeline);
+  ges_base_bin_set_timeline (GES_BASE_BIN (data->self), data->timeline);
   g_cond_broadcast (&data->cond);
   g_mutex_unlock (&data->lock);
 
@@ -290,6 +294,7 @@ ges_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
 
         uri = gst_filename_to_uri (filename, NULL);
         data.uri = uri;
+        data.self = self;
 
         g_main_context_invoke (main_context,
             (GSourceFunc) ges_timeline_new_from_uri_from_main_thread, &data);
@@ -309,9 +314,6 @@ ges_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
           goto error;
         }
 
-        GST_INFO_OBJECT (self, "Timeline properly loaded: %" GST_PTR_FORMAT,
-            data.timeline);
-        ges_base_bin_set_timeline (GES_BASE_BIN (self), data.timeline);
       done:
         g_free (filename);
         g_free (uri);
index 1e471b5..ebee470 100644 (file)
@@ -1325,7 +1325,7 @@ ghost_event_probe_handler (GstPad * ghostpad G_GNUC_UNUSED,
               FALSE)) {
 
         gst_event_unref (event);
-        event = info->data = gst_event_new_stream_start (g_strdup (priv->id));
+        event = info->data = gst_event_new_stream_start (priv->id);
         GST_INFO_OBJECT (comp, "forward stream-start %p (%s)", event, priv->id);
       } else {
         GST_DEBUG_OBJECT (comp, "dropping stream-start %p", event);