Cleanly handle removing the last object in a composition
authorMathieu Duponchelle <mathieu.duponchelle@opencreed.com>
Tue, 3 Feb 2015 11:02:42 +0000 (12:02 +0100)
committerThibault Saunier <tsaunier@gnome.org>
Tue, 3 Feb 2015 11:03:33 +0000 (12:03 +0100)
The strategy here is to seek at the new end of the composition. And in
GES we always add a 1ns long gap at the end of the tracks so that all
track have the exact same duration, and we have black frames when the
timeline is empty

ges/ges-track.c
ges/nle/nlecomposition.c
tests/check/nle/common.c
tests/check/nle/nlecomposition.c

index e097b38..f307358 100644 (file)
@@ -218,7 +218,7 @@ update_gaps (GESTrack * track)
     duration = MAX (duration, end);
   }
 
-  /* 4- Add a gap at the end of the timeline if needed */
+  /* 3- Add a gap at the end of the timeline if needed */
   if (priv->timeline) {
     g_object_get (priv->timeline, "duration", &timeline_duration, NULL);
 
@@ -233,6 +233,10 @@ update_gaps (GESTrack * track)
     }
   }
 
+  GST_DEBUG_OBJECT (track, "Adding a one second gap at the end");
+  gap = gap_new (track, timeline_duration, 1);
+  priv->gaps = g_list_prepend (priv->gaps, gap);
+
   /* 4- Remove old gaps */
   g_list_free_full (gaps, (GDestroyNotify) free_gap);
 }
index 3f2bd08..19bf26c 100644 (file)
@@ -1328,7 +1328,9 @@ get_new_seek_event (NleComposition * comp, gboolean initial,
       GST_TIME_FORMAT, GST_TIME_ARGS (priv->segment->stop),
       GST_TIME_ARGS (priv->segment_stop));
 
-  start = MAX (priv->segment->start, priv->segment_start);
+  start = GST_CLOCK_TIME_IS_VALID (priv->segment->start)
+      ? MAX (priv->segment->start, priv->segment_start)
+      : priv->segment_start;
   stop = GST_CLOCK_TIME_IS_VALID (priv->segment->stop)
       ? MIN (priv->segment->stop, priv->segment_stop)
       : priv->segment_stop;
@@ -2801,12 +2803,19 @@ update_pipeline (NleComposition * comp, GstClockTime currenttime, gint32 seqnum,
   NleCompositionPrivate *priv = comp->priv;
   GstClockTime new_stop = GST_CLOCK_TIME_NONE;
   GstClockTime new_start = GST_CLOCK_TIME_NONE;
+  GstClockTime duration = NLE_OBJECT (comp)->duration - 1;
 
   GstState nextstate = (GST_STATE_NEXT (comp) == GST_STATE_VOID_PENDING) ?
       GST_STATE (comp) : GST_STATE_NEXT (comp);
 
   _assert_proper_thread (comp);
 
+  if (currenttime >= duration) {
+    currenttime = duration;
+    priv->segment->start = GST_CLOCK_TIME_NONE;
+    priv->segment->stop = GST_CLOCK_TIME_NONE;
+  }
+
   GST_INFO_OBJECT (comp,
       "currenttime:%" GST_TIME_FORMAT
       " Reason: %s, Seqnum: %i", GST_TIME_ARGS (currenttime),
index 01527dd..c5c582a 100644 (file)
@@ -358,7 +358,6 @@ static GCond cond;
 static void
 commited_cb (GstElement * comp, gboolean changed)
 {
-  GST_ERROR ("commited !!");
   g_mutex_lock (&lock);
   g_cond_signal (&cond);
   g_mutex_unlock (&lock);
index 7bdb098..be727d2 100644 (file)
@@ -187,6 +187,116 @@ GST_START_TEST (test_remove_invalid_object)
 
 GST_END_TEST;
 
+static GstClockTime
+_query_position_cb (GstElement * composition, GstPipeline * pipeline)
+{
+  gint64 position;
+
+  if (gst_element_query_position (GST_ELEMENT (pipeline), GST_FORMAT_TIME,
+          &position))
+    return position;
+
+  return GST_CLOCK_TIME_NONE;
+}
+
+GST_START_TEST (test_remove_last_object)
+{
+  GstBin *composition;
+  GstElement *source1, *audiotestsrc, *source2, *audiotestsrc2, *fakesink,
+      *pipeline;
+  GstBus *bus;
+  GstMessage *message;
+  gboolean ret;
+  gint64 position = 0;
+  GstClockTime duration;
+
+  pipeline = GST_ELEMENT (gst_pipeline_new (NULL));
+  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+
+  composition = GST_BIN (gst_element_factory_make ("nlecomposition",
+          "composition"));
+
+  g_signal_connect (composition, "query-position",
+      G_CALLBACK (_query_position_cb), pipeline);
+
+  gst_element_set_state (GST_ELEMENT (composition), GST_STATE_READY);
+
+  fakesink = gst_element_factory_make ("fakesink", NULL);
+  gst_bin_add_many (GST_BIN (pipeline), GST_ELEMENT (composition), fakesink,
+      NULL);
+  gst_element_link (GST_ELEMENT (composition), fakesink);
+
+  source1 = gst_element_factory_make ("nlesource", "source1");
+  audiotestsrc = gst_element_factory_make ("audiotestsrc", "audiotestsrc1");
+  gst_bin_add (GST_BIN (source1), audiotestsrc);
+  g_object_set (source1, "start", (guint64) 0 * GST_SECOND,
+      "duration", 10 * GST_SECOND, "inpoint", (guint64) 0, "priority", 1, NULL);
+
+  nle_composition_add (composition, source1);
+
+  source2 = gst_element_factory_make ("nlesource", "source1");
+  audiotestsrc2 = gst_element_factory_make ("audiotestsrc", "audiotestsrc1");
+  gst_bin_add (GST_BIN (source2), audiotestsrc2);
+  g_object_set (source2, "start", (guint64) 10 * GST_SECOND,
+      "duration", 10 * GST_SECOND, "inpoint", (guint64) 0, "priority", 1, NULL);
+
+  nle_composition_add (composition, source2);
+
+  fail_if (gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PAUSED)
+      == GST_STATE_CHANGE_FAILURE);
+  message = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
+      GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_ERROR);
+  gst_mini_object_unref (GST_MINI_OBJECT (message));
+
+  commit_and_wait (GST_ELEMENT (composition), &ret);
+
+  message = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
+      GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_ERROR);
+  gst_mini_object_unref (GST_MINI_OBJECT (message));
+
+  gst_element_seek_simple (pipeline,
+      GST_FORMAT_TIME,
+      GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, 15 * GST_SECOND);
+
+  message = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
+      GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_ERROR);
+  gst_mini_object_unref (GST_MINI_OBJECT (message));
+
+  ret =
+      gst_element_query_position (GST_ELEMENT (pipeline), GST_FORMAT_TIME,
+      &position);
+  fail_unless_equals_uint64 (position, 15 * GST_SECOND);
+
+  gst_element_seek_simple (pipeline,
+      GST_FORMAT_TIME,
+      GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, 18 * GST_SECOND);
+
+  message = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
+      GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_ERROR);
+  gst_mini_object_unref (GST_MINI_OBJECT (message));
+
+  ret =
+      gst_element_query_position (GST_ELEMENT (pipeline), GST_FORMAT_TIME,
+      &position);
+  fail_unless_equals_uint64 (position, 18 * GST_SECOND);
+
+  nle_composition_remove (composition, source2);
+
+  commit_and_wait (GST_ELEMENT (composition), &ret);
+  g_object_get (composition, "duration", &duration, NULL);
+  fail_unless_equals_uint64 (duration, 10 * GST_SECOND);
+
+  ret =
+      gst_element_query_position (GST_ELEMENT (pipeline), GST_FORMAT_TIME,
+      &position);
+  fail_unless_equals_uint64 (position, 10 * GST_SECOND - 1);
+
+  gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
+  gst_object_unref (pipeline);
+}
+
+GST_END_TEST;
+
 GST_START_TEST (test_dispose_on_commit)
 {
   GstElement *composition;
@@ -235,7 +345,6 @@ GST_START_TEST (test_simple_audiomixer)
   pipeline = GST_ELEMENT (gst_pipeline_new (NULL));
   bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
 
-  GST_ERROR ("Pipeline refcounts: %i", ((GObject *) pipeline)->ref_count);
   composition = gst_element_factory_make ("nlecomposition", "composition");
   gst_element_set_state (composition, GST_STATE_READY);
   fakesink = gst_element_factory_make ("fakesink", NULL);
@@ -250,7 +359,6 @@ GST_START_TEST (test_simple_audiomixer)
       "priority", 0, NULL);
   nle_composition_add (GST_BIN (composition), nle_audiomixer);
 
-  GST_ERROR ("Pipeline refcounts: %i", ((GObject *) pipeline)->ref_count);
   /* source 1 */
   nlesource1 = gst_element_factory_make ("nlesource", "nlesource1");
   audiotestsrc1 = gst_element_factory_make ("audiotestsrc", "audiotestsrc1");
@@ -270,8 +378,6 @@ GST_START_TEST (test_simple_audiomixer)
   GST_DEBUG ("Adding composition to pipeline");
   gst_bin_add_many (GST_BIN (pipeline), composition, fakesink, NULL);
 
-  GST_ERROR ("Pipeline refcounts: %i", ((GObject *) pipeline)->ref_count);
-
   fail_unless (nle_composition_add (GST_BIN (composition), nlesource2));
   fail_unless (gst_element_link (composition, fakesink) == TRUE);
 
@@ -336,6 +442,7 @@ gnonlin_suite (void)
 
   tcase_add_test (tc_chain, test_change_object_start_stop_in_current_stack);
   tcase_add_test (tc_chain, test_remove_invalid_object);
+  tcase_add_test (tc_chain, test_remove_last_object);
 
   tcase_add_test (tc_chain, test_dispose_on_commit);