Revert "streamsynchronizer: Consider streams having received stream-start as waiting"

This reverts commit a1a189c07cb66af06d7047c74f6421bd36e3d66c.

It breaks the uriplaylistbin tests and needs further investigation.

See https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/4506

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9309>
This commit is contained in:
Sebastian Dröge 2025-06-30 09:26:29 +03:00
parent b0d952ef53
commit 46df60ed1d
2 changed files with 46 additions and 647 deletions

View File

@ -494,7 +494,6 @@ gst_stream_synchronizer_handle_stream_start (GstStreamSynchronizer * self,
gboolean have_group_id;
GList *l;
gboolean all_wait = TRUE;
gboolean others_not_wait = TRUE;
gboolean new_stream = TRUE;
have_group_id = gst_event_parse_group_id (event, &group_id);
@ -550,36 +549,16 @@ gst_stream_synchronizer_handle_stream_start (GstStreamSynchronizer * self,
stream->wait = TRUE;
/* This variable checks if all the other streams except this one
are not waiting. There's an initial special case: If there's only
one stream and it's this one, set to FALSE */
others_not_wait = self->streams != NULL && !(self->streams->next == NULL
&& self->streams->data == stream);
for (l = self->streams; l; l = l->next) {
GstSyncStream *ostream = l->data;
/* This condition is reused for compuing both all_wait and
others_not_wait, but the former can also support sparse streams */
gboolean this_wait = ostream->wait && (!have_group_id
|| ostream->group_id == group_id);
all_wait = all_wait && ((ostream->flags & GST_STREAM_FLAG_SPARSE)
|| this_wait);
if (ostream != stream) {
others_not_wait = others_not_wait && !this_wait;
}
|| (ostream->wait && (!have_group_id
|| ostream->group_id == group_id)));
if (!all_wait)
break;
}
/* When all the streams of a media have been waiting, all of them are
unblocked for playback, but if a new stream appears after that (this is
detected when stream-start comes), it will be waiting and unaware of
all its sibling streams having already been unblocked, leading to a
stall. All_but_this_nowait detects that case and automatically sets the
new stream (this one) as not waiting. */
if (others_not_wait)
stream->wait = FALSE;
if (all_wait) {
gint64 position = 0;

View File

@ -40,19 +40,10 @@ typedef struct
{
GstPad *pad;
GList *to_push;
GMutex *push_mutex;
GCond *push_cond;
} MyPushInfo;
typedef struct
{
GList *expected;
GMutex push_mutex;
GCond push_cond;
gboolean compare_segment_base;
} MyPadPrivateData;
MyPadPrivateData private_data_video, private_data_audio;
GMutex push_mutex;
GCond push_cond;
static GstPad *
get_other_pad (GstPad * pad)
@ -73,36 +64,36 @@ get_other_pad (GstPad * pad)
static GstFlowReturn
my_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
{
MyPadPrivateData *private_data = GST_PAD_ELEMENT_PRIVATE (pad);
GList **expected = GST_PAD_ELEMENT_PRIVATE (pad);
GList *next;
GstBuffer *exp;
fail_if (private_data->expected == NULL,
fail_if (*expected == NULL,
"streamsynchronizer pushed a buffer/event but we didn't expect any");
next = (private_data->expected)->next;
next = (*expected)->next;
fail_if (GST_IS_EVENT ((private_data->expected)->data),
fail_if (GST_IS_EVENT ((*expected)->data),
"Expected an event (%s) but got a buffer instead",
GST_EVENT_TYPE_NAME (GST_EVENT ((private_data->expected)->data)));
GST_EVENT_TYPE_NAME (GST_EVENT ((*expected)->data)));
exp = GST_BUFFER ((private_data->expected)->data);
exp = GST_BUFFER ((*expected)->data);
fail_unless_equals_uint64 (GST_BUFFER_TIMESTAMP (buf),
GST_BUFFER_TIMESTAMP (exp));
GST_DEBUG ("Properly received expected buffer: %p", buf);
GST_DEBUG ("Properly received expected buffer");
gst_buffer_unref (exp);
gst_buffer_unref (buf);
g_list_free1 (private_data->expected);
private_data->expected = next;
g_list_free1 (*expected);
*expected = next;
/* When done signal main thread */
if (next == NULL) {
g_mutex_lock (&private_data->push_mutex);
g_cond_signal (&private_data->push_cond);
g_mutex_unlock (&private_data->push_mutex);
g_mutex_lock (&push_mutex);
g_cond_signal (&push_cond);
g_mutex_unlock (&push_mutex);
}
return GST_FLOW_OK;
@ -111,19 +102,19 @@ my_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
static gboolean
my_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
MyPadPrivateData *private_data = GST_PAD_ELEMENT_PRIVATE (pad);
GList **expected = GST_PAD_ELEMENT_PRIVATE (pad);
GList *next;
GstEvent *exp;
fail_if (private_data->expected == NULL,
fail_if (*expected == NULL,
"streamsynchronizer pushed a buffer/event but we didn't expect any");
next = (private_data->expected)->next;
next = (*expected)->next;
fail_unless (GST_IS_EVENT ((private_data->expected)->data),
fail_unless (GST_IS_EVENT ((*expected)->data),
"We were not expecting an event (But got an event of type %s)",
GST_EVENT_TYPE_NAME (event));
exp = GST_EVENT ((private_data->expected)->data);
exp = GST_EVENT ((*expected)->data);
fail_unless (GST_EVENT_TYPE (event) == GST_EVENT_TYPE (exp),
"Got event of type %s but expected of type %s",
GST_EVENT_TYPE_NAME (event), GST_EVENT_TYPE_NAME (exp));
@ -140,9 +131,7 @@ my_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
gst_event_parse_segment (exp, &expectseg);
fail_unless_equals_int (recvseg->format, expectseg->format);
if (private_data->compare_segment_base) {
fail_unless_equals_uint64 (recvseg->base, expectseg->base);
}
fail_unless_equals_uint64 (recvseg->base, expectseg->base);
fail_unless_equals_uint64 (recvseg->offset, expectseg->offset);
fail_unless_equals_uint64 (recvseg->start, expectseg->start);
fail_unless_equals_uint64 (recvseg->stop, expectseg->stop);
@ -155,20 +144,19 @@ my_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
break;
}
GST_DEBUG ("Properly received expected event %s: %p",
GST_EVENT_TYPE_NAME (exp), event);
GST_DEBUG ("Properly received expected event %s", GST_EVENT_TYPE_NAME (exp));
gst_event_unref (exp);
gst_event_unref (event);
g_list_free1 (private_data->expected);
private_data->expected = next;
g_list_free1 (*expected);
*expected = next;
/* When done signal main thread */
if (next == NULL) {
g_mutex_lock (&private_data->push_mutex);
g_cond_signal (&private_data->push_cond);
g_mutex_unlock (&private_data->push_mutex);
g_mutex_lock (&push_mutex);
g_cond_signal (&push_cond);
g_mutex_unlock (&push_mutex);
}
return TRUE;
@ -179,25 +167,12 @@ my_push_thread (MyPushInfo * pushinfo)
{
GList *tmp;
GST_DEBUG ("starting thread");
/* Nothing to do if the to_push list is empty in the first place. Signal main thread */
if (pushinfo->to_push == NULL) {
g_mutex_lock (pushinfo->push_mutex);
g_cond_signal (pushinfo->push_cond);
g_mutex_unlock (pushinfo->push_mutex);
}
/* FIXME : Do this in a thread */
for (tmp = pushinfo->to_push; tmp; tmp = tmp->next) {
if (GST_IS_EVENT (tmp->data)) {
GST_DEBUG ("Pushing event %s: %p",
GST_EVENT_TYPE_NAME (GST_EVENT (tmp->data)), GST_EVENT (tmp->data));
if (GST_IS_EVENT (tmp->data))
gst_pad_push_event (pushinfo->pad, GST_EVENT (tmp->data));
} else {
GST_DEBUG ("Pushing buffer: %p", GST_BUFFER (tmp->data));
else
gst_pad_push (pushinfo->pad, GST_BUFFER (tmp->data));
}
}
GST_INFO ("leaving thread");
@ -209,8 +184,7 @@ GST_START_TEST (test_basic)
GstElement *synchr;
GstPad *sinkpad, *srcpad;
GstPad *mysrcpad, *mysinkpad;
GList *to_push = NULL;
GList *to_push = NULL, *expected = NULL;
GstEvent *event;
GstBuffer *buf;
GThread *thread;
@ -240,24 +214,17 @@ GST_START_TEST (test_basic)
fail_if (mysinkpad == NULL);
fail_unless (gst_pad_link (srcpad, mysinkpad) == GST_PAD_LINK_OK);
fail_unless (gst_pad_set_active (mysinkpad, TRUE));
GST_PAD_ELEMENT_PRIVATE (mysinkpad) = &private_data_video;
/* The segment.base expected time is important for this test */
private_data_video.compare_segment_base = TRUE;
private_data_video.expected = NULL;
GST_PAD_ELEMENT_PRIVATE (mysinkpad) = &expected;
/* Start with a stream START and a new segment */
event = gst_event_new_stream_start ("lala");
to_push = g_list_append (to_push, event);
private_data_video.expected =
g_list_append (private_data_video.expected, gst_event_ref (event));
expected = g_list_append (expected, gst_event_ref (event));
gst_segment_init (&segment, GST_FORMAT_TIME);
event = gst_event_new_segment (&segment);
to_push = g_list_append (to_push, event);
private_data_video.expected =
g_list_append (private_data_video.expected, gst_event_ref (event));
expected = g_list_append (expected, gst_event_ref (event));
/* Then 10 buffers */
for (i = 0; i < 10; i++) {
@ -265,15 +232,13 @@ GST_START_TEST (test_basic)
GST_BUFFER_TIMESTAMP (buf) = i * GST_SECOND;
GST_BUFFER_DURATION (buf) = GST_SECOND;
to_push = g_list_append (to_push, buf);
private_data_video.expected =
g_list_append (private_data_video.expected, gst_buffer_ref (buf));
expected = g_list_append (expected, gst_buffer_ref (buf));
}
/* Then a new stream start */
event = gst_event_new_stream_start ("lala again");
to_push = g_list_append (to_push, event);
private_data_video.expected =
g_list_append (private_data_video.expected, gst_event_ref (event));
expected = g_list_append (expected, gst_event_ref (event));
/* This newsegment will be updated */
gst_segment_init (&segment, GST_FORMAT_TIME);
@ -285,8 +250,7 @@ GST_START_TEST (test_basic)
segment.base = 10 * GST_SECOND;
event = gst_event_new_segment (&segment);
gst_event_set_seqnum (event, seqnum);
private_data_video.expected =
g_list_append (private_data_video.expected, event);
expected = g_list_append (expected, event);
/* Then 10 buffers */
for (i = 0; i < 10; i++) {
@ -294,25 +258,22 @@ GST_START_TEST (test_basic)
GST_BUFFER_TIMESTAMP (buf) = i * GST_SECOND;
GST_BUFFER_DURATION (buf) = GST_SECOND;
to_push = g_list_append (to_push, buf);
private_data_video.expected =
g_list_append (private_data_video.expected, gst_buffer_ref (buf));
expected = g_list_append (expected, gst_buffer_ref (buf));
}
g_mutex_init (&private_data_video.push_mutex);
pushinfo.push_mutex = &private_data_video.push_mutex;
g_cond_init (&private_data_video.push_cond);
pushinfo.push_cond = &private_data_video.push_cond;
g_mutex_init (&push_mutex);
g_cond_init (&push_cond);
pushinfo.pad = mysrcpad;
pushinfo.to_push = to_push;
g_mutex_lock (&private_data_video.push_mutex);
g_mutex_lock (&push_mutex);
thread = g_thread_new ("pushthread", (GThreadFunc) my_push_thread, &pushinfo);
fail_unless (thread != NULL);
g_cond_wait (&private_data_video.push_cond, &private_data_video.push_mutex);
g_mutex_unlock (&private_data_video.push_mutex);
g_cond_wait (&push_cond, &push_mutex);
g_mutex_unlock (&push_mutex);
fail_if (private_data_video.expected != NULL);
fail_if (expected != NULL);
/* wait for thread to exit before freeing things */
g_thread_join (thread);
@ -330,545 +291,6 @@ GST_START_TEST (test_basic)
GST_END_TEST;
GST_START_TEST (test_stream_start_wait)
{
GstElement *synchr;
GstPad *sinkpad_video, *srcpad_video, *sinkpad_audio, *srcpad_audio;
GstPad *mysrcpad_video, *mysinkpad_video, *mysrcpad_audio, *mysinkpad_audio;
GList *to_push_video = NULL, *to_push_audio = NULL;
GstEvent *event;
GstBuffer *buf;
GThread *thread_video, *thread_audio;
MyPushInfo pushinfo_video, pushinfo_audio;
GstSegment segment;
synchr = gst_element_factory_make ("streamsynchronizer", NULL);
GST_DEBUG ("Get sinkpad/srcpad for a first V0 stream");
sinkpad_video = gst_element_request_pad_simple (synchr, "sink_%u");
fail_unless (sinkpad_video != NULL);
srcpad_video = get_other_pad (sinkpad_video);
fail_unless (srcpad_video != NULL);
gst_element_set_state (synchr, GST_STATE_PLAYING);
mysrcpad_video = gst_pad_new_from_static_template (&mysrctemplate, "src");
fail_if (mysrcpad_video == NULL);
fail_unless (gst_pad_link (mysrcpad_video, sinkpad_video) == GST_PAD_LINK_OK);
fail_unless (gst_pad_set_active (mysrcpad_video, TRUE));
mysinkpad_video = gst_pad_new_from_static_template (&mysinktemplate, "sink");
gst_pad_set_chain_function (mysinkpad_video, my_sink_chain);
gst_pad_set_event_function (mysinkpad_video, my_sink_event);
fail_if (mysinkpad_video == NULL);
fail_unless (gst_pad_link (srcpad_video, mysinkpad_video) == GST_PAD_LINK_OK);
fail_unless (gst_pad_set_active (mysinkpad_video, TRUE));
GST_PAD_ELEMENT_PRIVATE (mysinkpad_video) = &private_data_video;
/* The segment.base expected time is important for this part of the test */
private_data_video.compare_segment_base = TRUE;
private_data_video.expected = NULL;
GST_DEBUG ("Start with a stream-start and a segment event");
event = gst_event_new_stream_start ("mse/V0");
to_push_video = g_list_append (to_push_video, event);
private_data_video.expected =
g_list_append (private_data_video.expected, gst_event_ref (event));
gst_segment_init (&segment, GST_FORMAT_TIME);
event = gst_event_new_segment (&segment);
to_push_video = g_list_append (to_push_video, event);
private_data_video.expected =
g_list_append (private_data_video.expected, gst_event_ref (event));
buf = gst_buffer_new ();
GST_BUFFER_TIMESTAMP (buf) = 0 * GST_SECOND;
GST_BUFFER_DURATION (buf) = GST_SECOND;
GST_DEBUG ("Then 1 video buffer %p", buf);
to_push_video = g_list_append (to_push_video, buf);
private_data_video.expected =
g_list_append (private_data_video.expected, gst_buffer_ref (buf));
GST_DEBUG ("Simulate the effect of a seek to 6s with basic events with...");
GST_DEBUG ("...a flush-start event");
event = gst_event_new_flush_start ();
to_push_video = g_list_append (to_push_video, event);
private_data_video.expected =
g_list_append (private_data_video.expected, gst_event_ref (event));
GST_DEBUG ("...a flush-stop event");
event = gst_event_new_flush_stop (TRUE);
to_push_video = g_list_append (to_push_video, event);
private_data_video.expected =
g_list_append (private_data_video.expected, gst_event_ref (event));
GST_DEBUG ("...a stream-start event");
event = gst_event_new_stream_start ("mse/V0");
to_push_video = g_list_append (to_push_video, event);
private_data_video.expected =
g_list_append (private_data_video.expected, gst_event_ref (event));
GST_DEBUG ("...and a segment event");
gst_segment_init (&segment, GST_FORMAT_TIME);
segment.format = GST_FORMAT_TIME;
segment.start = 6 * GST_SECOND;
segment.time = 6 * GST_SECOND;
event = gst_event_new_segment (&segment);
to_push_video = g_list_append (to_push_video, event);
private_data_video.expected =
g_list_append (private_data_video.expected, gst_event_ref (event));
/* Run all these steps until completion before continuing */
GST_DEBUG ("Run all these steps until completion before continuing");
g_mutex_init (&private_data_video.push_mutex);
pushinfo_video.push_mutex = &private_data_video.push_mutex;
g_cond_init (&private_data_video.push_cond);
pushinfo_video.push_cond = &private_data_video.push_cond;
pushinfo_video.pad = mysrcpad_video;
pushinfo_video.to_push = to_push_video;
g_mutex_lock (&private_data_video.push_mutex);
GST_DEBUG ("Creating video thread");
thread_video =
g_thread_new ("pushthread_video", (GThreadFunc) my_push_thread,
&pushinfo_video);
fail_unless (thread_video != NULL);
GST_DEBUG
("Waiting for all expected video events/buffers to be processed and join the video thread");
g_cond_wait (&private_data_video.push_cond, &private_data_video.push_mutex);
g_mutex_unlock (&private_data_video.push_mutex);
GST_DEBUG ("Wait completed");
fail_if (private_data_video.expected != NULL);
g_thread_join (thread_video);
GST_DEBUG ("Now create a second stream, A0");
sinkpad_audio = gst_element_request_pad_simple (synchr, "sink_%u");
fail_unless (sinkpad_audio != NULL);
srcpad_audio = get_other_pad (sinkpad_audio);
fail_unless (srcpad_audio != NULL);
mysrcpad_audio = gst_pad_new_from_static_template (&mysrctemplate, "src");
fail_if (mysrcpad_audio == NULL);
fail_unless (gst_pad_link (mysrcpad_audio, sinkpad_audio) == GST_PAD_LINK_OK);
fail_unless (gst_pad_set_active (mysrcpad_audio, TRUE));
mysinkpad_audio = gst_pad_new_from_static_template (&mysinktemplate, "sink");
gst_pad_set_chain_function (mysinkpad_audio, my_sink_chain);
gst_pad_set_event_function (mysinkpad_audio, my_sink_event);
fail_if (mysinkpad_audio == NULL);
fail_unless (gst_pad_link (srcpad_audio, mysinkpad_audio) == GST_PAD_LINK_OK);
fail_unless (gst_pad_set_active (mysinkpad_audio, TRUE));
GST_PAD_ELEMENT_PRIVATE (mysinkpad_audio) = &private_data_audio;
/* The segment.base expected time is not important for this part of the test,
* because it changes depending on the stream and sometimes is 1s for audio
* and 0s for video and some other times it's the opposite. It's not
* predictable. */
private_data_video.compare_segment_base = FALSE;
private_data_audio.expected = NULL;
/* Start with a stream START and a new segment like the one used for the simulated seek */
GST_DEBUG
("Start with a stream-start and a new segment like the one used for the simulated seek");
event = gst_event_new_stream_start ("mse/A0");
to_push_audio = g_list_append (to_push_audio, event);
private_data_audio.expected =
g_list_append (private_data_audio.expected, gst_event_ref (event));
gst_segment_init (&segment, GST_FORMAT_TIME);
segment.format = GST_FORMAT_TIME;
segment.start = 6 * GST_SECOND;
segment.time = 6 * GST_SECOND;
event = gst_event_new_segment (&segment);
to_push_audio = g_list_append (to_push_audio, event);
private_data_audio.expected =
g_list_append (private_data_audio.expected, gst_event_ref (event));
g_mutex_lock (&private_data_video.push_mutex);
buf = gst_buffer_new ();
GST_BUFFER_TIMESTAMP (buf) = 6 * GST_SECOND;
GST_BUFFER_DURATION (buf) = GST_SECOND;
GST_DEBUG ("Send one video buffer at 6 sec: %p", buf);
/* Discard old events/buffers from the list and start from scratch */
g_list_free (to_push_video);
to_push_video = NULL;
to_push_video = g_list_append (to_push_video, buf);
private_data_video.expected =
g_list_append (private_data_video.expected, gst_buffer_ref (buf));
buf = gst_buffer_new ();
GST_BUFFER_TIMESTAMP (buf) = 6 * GST_SECOND;
GST_BUFFER_DURATION (buf) = GST_SECOND;
GST_DEBUG ("Send one audio buffer at 6 sec: %p", buf);
to_push_audio = g_list_append (to_push_audio, buf);
private_data_audio.expected =
g_list_append (private_data_audio.expected, gst_buffer_ref (buf));
g_mutex_init (&private_data_video.push_mutex);
pushinfo_video.push_mutex = &private_data_video.push_mutex;
g_cond_init (&private_data_video.push_cond);
pushinfo_video.push_cond = &private_data_video.push_cond;
pushinfo_video.pad = mysrcpad_video;
pushinfo_video.to_push = to_push_video;
g_mutex_lock (&private_data_video.push_mutex);
GST_DEBUG ("Creating video thread again");
thread_video =
g_thread_new ("pushthread_video", (GThreadFunc) my_push_thread,
&pushinfo_video);
fail_unless (thread_video != NULL);
g_mutex_init (&private_data_audio.push_mutex);
pushinfo_audio.push_mutex = &private_data_audio.push_mutex;
g_cond_init (&private_data_audio.push_cond);
pushinfo_audio.push_cond = &private_data_audio.push_cond;
pushinfo_audio.pad = mysrcpad_audio;
pushinfo_audio.to_push = to_push_audio;
g_mutex_lock (&private_data_audio.push_mutex);
GST_DEBUG ("Creating audio thread");
thread_audio =
g_thread_new ("pushthread_audio", (GThreadFunc) my_push_thread,
&pushinfo_audio);
fail_unless (thread_audio != NULL);
GST_DEBUG ("Waiting for all expected video events/buffers to be processed");
g_cond_wait (&private_data_video.push_cond, &private_data_video.push_mutex);
g_mutex_unlock (&private_data_video.push_mutex);
GST_DEBUG ("Video wait completed");
fail_if (private_data_video.expected != NULL);
GST_DEBUG ("Waiting for all expected audio events/buffers to be processed");
g_cond_wait (&private_data_audio.push_cond, &private_data_audio.push_mutex);
g_mutex_unlock (&private_data_audio.push_mutex);
GST_DEBUG ("Audio wait completed");
fail_if (private_data_audio.expected != NULL);
g_thread_join (thread_video);
g_thread_join (thread_audio);
GST_DEBUG ("Cleanup");
g_list_free (to_push_video);
g_list_free (to_push_audio);
gst_element_release_request_pad (synchr, sinkpad_video);
gst_element_release_request_pad (synchr, sinkpad_audio);
gst_object_unref (srcpad_video);
gst_object_unref (sinkpad_video);
gst_object_unref (mysinkpad_video);
gst_object_unref (mysrcpad_video);
gst_object_unref (srcpad_audio);
gst_object_unref (sinkpad_audio);
gst_object_unref (mysinkpad_audio);
gst_object_unref (mysrcpad_audio);
gst_element_set_state (synchr, GST_STATE_NULL);
gst_object_unref (synchr);
}
GST_END_TEST;
GST_START_TEST (test_stream_start_wait_sparse)
{
GstElement *synchr;
GstPad *sinkpad_video, *srcpad_video, *sinkpad_audio, *srcpad_audio;
GstPad *mysrcpad_video, *mysinkpad_video, *mysrcpad_audio, *mysinkpad_audio;
GList *to_push_video = NULL, *to_push_audio = NULL;
GstEvent *event;
GstBuffer *buf;
GThread *thread_video, *thread_audio;
MyPushInfo pushinfo_video, pushinfo_audio;
GstSegment segment;
synchr = gst_element_factory_make ("streamsynchronizer", NULL);
GST_DEBUG ("Get sinkpad/srcpad for a first V0 stream");
sinkpad_video = gst_element_request_pad_simple (synchr, "sink_%u");
fail_unless (sinkpad_video != NULL);
srcpad_video = get_other_pad (sinkpad_video);
fail_unless (srcpad_video != NULL);
gst_element_set_state (synchr, GST_STATE_PLAYING);
mysrcpad_video = gst_pad_new_from_static_template (&mysrctemplate, "src");
fail_if (mysrcpad_video == NULL);
fail_unless (gst_pad_link (mysrcpad_video, sinkpad_video) == GST_PAD_LINK_OK);
fail_unless (gst_pad_set_active (mysrcpad_video, TRUE));
mysinkpad_video = gst_pad_new_from_static_template (&mysinktemplate, "sink");
gst_pad_set_chain_function (mysinkpad_video, my_sink_chain);
gst_pad_set_event_function (mysinkpad_video, my_sink_event);
fail_if (mysinkpad_video == NULL);
fail_unless (gst_pad_link (srcpad_video, mysinkpad_video) == GST_PAD_LINK_OK);
fail_unless (gst_pad_set_active (mysinkpad_video, TRUE));
GST_PAD_ELEMENT_PRIVATE (mysinkpad_video) = &private_data_video;
/* The segment.base expected time is important for this part of the test */
private_data_video.compare_segment_base = TRUE;
private_data_video.expected = NULL;
GST_DEBUG ("Start with a stream-start (sparse) and a segment event");
event = gst_event_new_stream_start ("mse/V0");
gst_event_set_stream_flags (event, GST_STREAM_FLAG_SPARSE);
to_push_video = g_list_append (to_push_video, event);
private_data_video.expected =
g_list_append (private_data_video.expected, gst_event_ref (event));
gst_segment_init (&segment, GST_FORMAT_TIME);
event = gst_event_new_segment (&segment);
to_push_video = g_list_append (to_push_video, event);
private_data_video.expected =
g_list_append (private_data_video.expected, gst_event_ref (event));
buf = gst_buffer_new ();
/* The stream is sparse. The segment starts at 0s but the first buffer
comes at 1s */
GST_BUFFER_TIMESTAMP (buf) = 1 * GST_SECOND;
GST_BUFFER_DURATION (buf) = GST_SECOND;
GST_DEBUG ("Then 1 video buffer %p", buf);
to_push_video = g_list_append (to_push_video, buf);
private_data_video.expected =
g_list_append (private_data_video.expected, gst_buffer_ref (buf));
GST_DEBUG ("Simulate the effect of a seek to 6s with basic events with...");
GST_DEBUG ("...a flush-start event");
event = gst_event_new_flush_start ();
to_push_video = g_list_append (to_push_video, event);
private_data_video.expected =
g_list_append (private_data_video.expected, gst_event_ref (event));
GST_DEBUG ("...a flush-stop event");
event = gst_event_new_flush_stop (TRUE);
to_push_video = g_list_append (to_push_video, event);
private_data_video.expected =
g_list_append (private_data_video.expected, gst_event_ref (event));
GST_DEBUG ("...a stream-start (sparse) event");
event = gst_event_new_stream_start ("mse/V0");
gst_event_set_stream_flags (event, GST_STREAM_FLAG_SPARSE);
to_push_video = g_list_append (to_push_video, event);
private_data_video.expected =
g_list_append (private_data_video.expected, gst_event_ref (event));
GST_DEBUG ("...and a segment event");
gst_segment_init (&segment, GST_FORMAT_TIME);
segment.format = GST_FORMAT_TIME;
segment.start = 6 * GST_SECOND;
segment.time = 6 * GST_SECOND;
event = gst_event_new_segment (&segment);
to_push_video = g_list_append (to_push_video, event);
private_data_video.expected =
g_list_append (private_data_video.expected, gst_event_ref (event));
/* Run all these steps until completion before continuing */
GST_DEBUG ("Run all these steps until completion before continuing");
g_mutex_init (&private_data_video.push_mutex);
pushinfo_video.push_mutex = &private_data_video.push_mutex;
g_cond_init (&private_data_video.push_cond);
pushinfo_video.push_cond = &private_data_video.push_cond;
pushinfo_video.pad = mysrcpad_video;
pushinfo_video.to_push = to_push_video;
g_mutex_lock (&private_data_video.push_mutex);
GST_DEBUG ("Creating video thread");
thread_video =
g_thread_new ("pushthread_video", (GThreadFunc) my_push_thread,
&pushinfo_video);
fail_unless (thread_video != NULL);
GST_DEBUG
("Waiting for all expected video events/buffers to be processed and join the video thread");
g_cond_wait (&private_data_video.push_cond, &private_data_video.push_mutex);
g_mutex_unlock (&private_data_video.push_mutex);
GST_DEBUG ("Wait completed");
fail_if (private_data_video.expected != NULL);
g_thread_join (thread_video);
GST_DEBUG ("Now create a second stream, A0");
sinkpad_audio = gst_element_request_pad_simple (synchr, "sink_%u");
fail_unless (sinkpad_audio != NULL);
srcpad_audio = get_other_pad (sinkpad_audio);
fail_unless (srcpad_audio != NULL);
mysrcpad_audio = gst_pad_new_from_static_template (&mysrctemplate, "src");
fail_if (mysrcpad_audio == NULL);
fail_unless (gst_pad_link (mysrcpad_audio, sinkpad_audio) == GST_PAD_LINK_OK);
fail_unless (gst_pad_set_active (mysrcpad_audio, TRUE));
mysinkpad_audio = gst_pad_new_from_static_template (&mysinktemplate, "sink");
gst_pad_set_chain_function (mysinkpad_audio, my_sink_chain);
gst_pad_set_event_function (mysinkpad_audio, my_sink_event);
fail_if (mysinkpad_audio == NULL);
fail_unless (gst_pad_link (srcpad_audio, mysinkpad_audio) == GST_PAD_LINK_OK);
fail_unless (gst_pad_set_active (mysinkpad_audio, TRUE));
GST_PAD_ELEMENT_PRIVATE (mysinkpad_audio) = &private_data_audio;
/* The segment.base expected time is not important for this part of the test,
* because it changes depending on the stream and sometimes is 1s for audio
* and 0s for video and some other times it's the opposite. It's not
* predictable. */
private_data_video.compare_segment_base = FALSE;
private_data_audio.expected = NULL;
/* Start with a stream START and a new segment like the one used for the simulated seek */
GST_DEBUG
("Start with a stream-start (not sparse) and a new segment like the one used for the simulated seek");
event = gst_event_new_stream_start ("mse/A0");
to_push_audio = g_list_append (to_push_audio, event);
private_data_audio.expected =
g_list_append (private_data_audio.expected, gst_event_ref (event));
gst_segment_init (&segment, GST_FORMAT_TIME);
segment.format = GST_FORMAT_TIME;
segment.start = 6 * GST_SECOND;
segment.time = 6 * GST_SECOND;
event = gst_event_new_segment (&segment);
to_push_audio = g_list_append (to_push_audio, event);
private_data_audio.expected =
g_list_append (private_data_audio.expected, gst_event_ref (event));
/* Discard old events/buffers from the list and start from scratch */
g_list_free (to_push_video);
to_push_video = NULL;
g_list_free (private_data_video.expected);
private_data_video.expected = NULL;
buf = gst_buffer_new ();
GST_BUFFER_TIMESTAMP (buf) = 6 * GST_SECOND;
GST_BUFFER_DURATION (buf) = GST_SECOND;
GST_DEBUG ("Send one audio buffer at 6 sec: %p", buf);
to_push_audio = g_list_append (to_push_audio, buf);
private_data_audio.expected =
g_list_append (private_data_audio.expected, gst_buffer_ref (buf));
g_mutex_init (&private_data_video.push_mutex);
pushinfo_video.push_mutex = &private_data_video.push_mutex;
g_cond_init (&private_data_video.push_cond);
pushinfo_video.push_cond = &private_data_video.push_cond;
pushinfo_video.pad = mysrcpad_video;
pushinfo_video.to_push = to_push_video;
g_mutex_lock (&private_data_video.push_mutex);
GST_DEBUG ("Creating video thread again");
thread_video =
g_thread_new ("pushthread_video", (GThreadFunc) my_push_thread,
&pushinfo_video);
fail_unless (thread_video != NULL);
g_mutex_init (&private_data_audio.push_mutex);
pushinfo_audio.push_mutex = &private_data_audio.push_mutex;
g_cond_init (&private_data_audio.push_cond);
pushinfo_audio.push_cond = &private_data_audio.push_cond;
pushinfo_audio.pad = mysrcpad_audio;
pushinfo_audio.to_push = to_push_audio;
g_mutex_lock (&private_data_audio.push_mutex);
GST_DEBUG ("Creating audio thread");
thread_audio =
g_thread_new ("pushthread_audio", (GThreadFunc) my_push_thread,
&pushinfo_audio);
fail_unless (thread_audio != NULL);
GST_DEBUG ("Waiting for all expected audio events/buffers to be processed");
g_cond_wait (&private_data_audio.push_cond, &private_data_audio.push_mutex);
g_mutex_unlock (&private_data_audio.push_mutex);
GST_DEBUG ("Audio wait completed");
fail_if (private_data_audio.expected != NULL);
GST_DEBUG ("Waiting for all expected video events/buffers to be processed");
g_cond_wait (&private_data_video.push_cond, &private_data_video.push_mutex);
g_mutex_unlock (&private_data_video.push_mutex);
GST_DEBUG ("Video wait completed");
fail_if (private_data_video.expected != NULL);
g_thread_join (thread_video);
g_thread_join (thread_audio);
GST_DEBUG ("Cleanup");
g_list_free (to_push_video);
g_list_free (to_push_audio);
gst_element_release_request_pad (synchr, sinkpad_video);
gst_element_release_request_pad (synchr, sinkpad_audio);
gst_object_unref (srcpad_video);
gst_object_unref (sinkpad_video);
gst_object_unref (mysinkpad_video);
gst_object_unref (mysrcpad_video);
gst_object_unref (srcpad_audio);
gst_object_unref (sinkpad_audio);
gst_object_unref (mysinkpad_audio);
gst_object_unref (mysrcpad_audio);
gst_element_set_state (synchr, GST_STATE_NULL);
gst_object_unref (synchr);
}
GST_END_TEST;
static Suite *
streamsynchronizer_suite (void)
{
@ -877,8 +299,6 @@ streamsynchronizer_suite (void)
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_basic);
tcase_add_test (tc_chain, test_stream_start_wait);
tcase_add_test (tc_chain, test_stream_start_wait_sparse);
return s;
}