mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-04-23 17:14:23 +00:00
Cleanly handle removing the last object in a composition
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
This commit is contained in:
parent
f3f27bd636
commit
1bfe0b7ef3
4 changed files with 126 additions and 7 deletions
|
@ -218,7 +218,7 @@ update_gaps (GESTrack * track)
|
||||||
duration = MAX (duration, end);
|
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) {
|
if (priv->timeline) {
|
||||||
g_object_get (priv->timeline, "duration", &timeline_duration, NULL);
|
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 */
|
/* 4- Remove old gaps */
|
||||||
g_list_free_full (gaps, (GDestroyNotify) free_gap);
|
g_list_free_full (gaps, (GDestroyNotify) free_gap);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1328,7 +1328,9 @@ get_new_seek_event (NleComposition * comp, gboolean initial,
|
||||||
GST_TIME_FORMAT, GST_TIME_ARGS (priv->segment->stop),
|
GST_TIME_FORMAT, GST_TIME_ARGS (priv->segment->stop),
|
||||||
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)
|
stop = GST_CLOCK_TIME_IS_VALID (priv->segment->stop)
|
||||||
? MIN (priv->segment->stop, priv->segment_stop)
|
? MIN (priv->segment->stop, 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;
|
NleCompositionPrivate *priv = comp->priv;
|
||||||
GstClockTime new_stop = GST_CLOCK_TIME_NONE;
|
GstClockTime new_stop = GST_CLOCK_TIME_NONE;
|
||||||
GstClockTime new_start = 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) ?
|
GstState nextstate = (GST_STATE_NEXT (comp) == GST_STATE_VOID_PENDING) ?
|
||||||
GST_STATE (comp) : GST_STATE_NEXT (comp);
|
GST_STATE (comp) : GST_STATE_NEXT (comp);
|
||||||
|
|
||||||
_assert_proper_thread (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,
|
GST_INFO_OBJECT (comp,
|
||||||
"currenttime:%" GST_TIME_FORMAT
|
"currenttime:%" GST_TIME_FORMAT
|
||||||
" Reason: %s, Seqnum: %i", GST_TIME_ARGS (currenttime),
|
" Reason: %s, Seqnum: %i", GST_TIME_ARGS (currenttime),
|
||||||
|
|
|
@ -358,7 +358,6 @@ static GCond cond;
|
||||||
static void
|
static void
|
||||||
commited_cb (GstElement * comp, gboolean changed)
|
commited_cb (GstElement * comp, gboolean changed)
|
||||||
{
|
{
|
||||||
GST_ERROR ("commited !!");
|
|
||||||
g_mutex_lock (&lock);
|
g_mutex_lock (&lock);
|
||||||
g_cond_signal (&cond);
|
g_cond_signal (&cond);
|
||||||
g_mutex_unlock (&lock);
|
g_mutex_unlock (&lock);
|
||||||
|
|
|
@ -187,6 +187,116 @@ GST_START_TEST (test_remove_invalid_object)
|
||||||
|
|
||||||
GST_END_TEST;
|
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)
|
GST_START_TEST (test_dispose_on_commit)
|
||||||
{
|
{
|
||||||
GstElement *composition;
|
GstElement *composition;
|
||||||
|
@ -235,7 +345,6 @@ GST_START_TEST (test_simple_audiomixer)
|
||||||
pipeline = GST_ELEMENT (gst_pipeline_new (NULL));
|
pipeline = GST_ELEMENT (gst_pipeline_new (NULL));
|
||||||
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
|
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
|
||||||
|
|
||||||
GST_ERROR ("Pipeline refcounts: %i", ((GObject *) pipeline)->ref_count);
|
|
||||||
composition = gst_element_factory_make ("nlecomposition", "composition");
|
composition = gst_element_factory_make ("nlecomposition", "composition");
|
||||||
gst_element_set_state (composition, GST_STATE_READY);
|
gst_element_set_state (composition, GST_STATE_READY);
|
||||||
fakesink = gst_element_factory_make ("fakesink", NULL);
|
fakesink = gst_element_factory_make ("fakesink", NULL);
|
||||||
|
@ -250,7 +359,6 @@ GST_START_TEST (test_simple_audiomixer)
|
||||||
"priority", 0, NULL);
|
"priority", 0, NULL);
|
||||||
nle_composition_add (GST_BIN (composition), nle_audiomixer);
|
nle_composition_add (GST_BIN (composition), nle_audiomixer);
|
||||||
|
|
||||||
GST_ERROR ("Pipeline refcounts: %i", ((GObject *) pipeline)->ref_count);
|
|
||||||
/* source 1 */
|
/* source 1 */
|
||||||
nlesource1 = gst_element_factory_make ("nlesource", "nlesource1");
|
nlesource1 = gst_element_factory_make ("nlesource", "nlesource1");
|
||||||
audiotestsrc1 = gst_element_factory_make ("audiotestsrc", "audiotestsrc1");
|
audiotestsrc1 = gst_element_factory_make ("audiotestsrc", "audiotestsrc1");
|
||||||
|
@ -270,8 +378,6 @@ GST_START_TEST (test_simple_audiomixer)
|
||||||
GST_DEBUG ("Adding composition to pipeline");
|
GST_DEBUG ("Adding composition to pipeline");
|
||||||
gst_bin_add_many (GST_BIN (pipeline), composition, fakesink, NULL);
|
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 (nle_composition_add (GST_BIN (composition), nlesource2));
|
||||||
fail_unless (gst_element_link (composition, fakesink) == TRUE);
|
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_change_object_start_stop_in_current_stack);
|
||||||
tcase_add_test (tc_chain, test_remove_invalid_object);
|
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);
|
tcase_add_test (tc_chain, test_dispose_on_commit);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue