Commit ece392e9 authored by Alicia Boya García's avatar Alicia Boya García

[WIP] playsink: Avoid race with flush before first frame

parent 940c9998
......@@ -2192,9 +2192,18 @@ gst_play_sink_sink_event (GstPad * pad, GstObject * parent, GstEvent * event,
g_free (custom_flush_finish);
}
if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_START) {
GST_DEBUG_OBJECT (pad,
"Received flush, locking playsink during propagation to ensure no race with inner element instantiation.");
GST_PLAY_SINK_LOCK (playsink);
}
GST_DEBUG_OBJECT (pad, "Forwarding event %" GST_PTR_FORMAT, event);
ret = gst_pad_event_default (pad, parent, gst_event_ref (event));
if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_START)
GST_PLAY_SINK_UNLOCK (playsink);
gst_event_unref (event);
gst_object_unref (playsink);
return ret;
......@@ -3029,7 +3038,7 @@ setup_audio_chain (GstPlaySink * playsink, gboolean raw)
} else if (conv) {
/* no volume, we need to add a volume element when we can */
g_object_set (chain->conv, "use-volume",
! !(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
!!(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
......@@ -4345,6 +4354,12 @@ sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
GST_PLAY_SINK_LOCK (playsink);
pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
if (GST_PAD_IS_FLUSHING (pad)) {
gst_object_unref (pad);
GST_PLAY_SINK_UNLOCK (playsink);
return GST_PAD_PROBE_DROP;
}
if (pad == playsink->video_pad) {
playsink->video_pad_blocked = TRUE;
GST_DEBUG_OBJECT (pad, "Video pad blocked");
......@@ -4408,14 +4423,14 @@ caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
if (pad == playsink->audio_pad) {
raw = is_raw_pad (pad);
reconfigure = (! !playsink->audio_pad_raw != ! !raw)
reconfigure = (!!playsink->audio_pad_raw != !!raw)
&& playsink->audiochain;
GST_DEBUG_OBJECT (pad,
"Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
reconfigure, caps);
} else if (pad == playsink->video_pad) {
raw = is_raw_pad (pad);
reconfigure = (! !playsink->video_pad_raw != ! !raw)
reconfigure = (!!playsink->video_pad_raw != !!raw)
&& playsink->videochain;
GST_DEBUG_OBJECT (pad,
"Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
......
......@@ -71,6 +71,218 @@ GST_START_TEST (test_volume_in_sink)
GST_END_TEST;
static GstSample *
make_simple_sample (const gchar * element)
{
GError *error = NULL;
GstElement *pipeline, *appsink;
GstSample *sample;
const gchar *launch_args[] = { element, "!", "appsink", "name=sink", NULL };
pipeline = gst_parse_launchv (launch_args, &error);
g_assert_no_error (error);
appsink = gst_bin_get_by_name (GST_BIN (pipeline), "sink");
gst_element_set_state (pipeline, GST_STATE_PLAYING);
g_signal_emit_by_name (appsink, "pull-sample", &sample);
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (appsink);
gst_object_unref (pipeline);
return sample;
}
typedef struct _FlushyTaskData
{
GstPad *pad;
GstElement *playsink;
GstSample *sample;
const gchar *stream_id;
guint group_id;
} FlushyTaskData;
static void
flushy_pad_task (gpointer user_data)
{
FlushyTaskData *data = (FlushyTaskData *) user_data;
GstPad *pad = data->pad;
GstEvent *stream_start;
GstSegment segment;
gboolean found_stream_synchronizer = FALSE;
stream_start = gst_event_new_stream_start (data->stream_id);
gst_event_set_group_id (stream_start, data->group_id);
gst_pad_push_event (pad, stream_start);
gst_pad_push_event (pad,
gst_event_new_caps (gst_sample_get_caps (data->sample)));
gst_segment_init (&segment, GST_FORMAT_TIME);
gst_pad_push_event (pad, gst_event_new_segment (&segment));
gst_pad_push (pad, gst_buffer_ref (gst_sample_get_buffer (data->sample)));
while (!found_stream_synchronizer) {
GstIterator *it = gst_bin_iterate_elements (GST_BIN (data->playsink));
GValue item = G_VALUE_INIT;
gboolean done = FALSE;
while (!done) {
switch (gst_iterator_next (it, &item)) {
case GST_ITERATOR_OK:{
GstElement *element = GST_ELEMENT (g_value_get_object (&item));
const gchar *element_name = gst_element_get_name (element);
if (g_strstr_len (element_name, strlen (element_name),
"streamsynchronizer") == element_name) {
found_stream_synchronizer = TRUE;
}
g_value_reset (&item);
break;
}
case GST_ITERATOR_RESYNC:
gst_iterator_resync (it);
break;
case GST_ITERATOR_ERROR:
fail ();
break;
case GST_ITERATOR_DONE:
done = TRUE;
break;
}
}
g_value_unset (&item);
gst_iterator_free (it);
}
gst_pad_push_event (pad, gst_event_new_flush_start ());
gst_pad_push_event (pad, gst_event_new_flush_stop (TRUE));
gst_pad_push_event (pad, gst_event_new_segment (&segment));
gst_pad_push (pad, gst_buffer_ref (gst_sample_get_buffer (data->sample)));
gst_pad_push_event (pad, gst_event_new_eos ());
gst_pad_pause_task (pad);
g_free (data);
}
static gboolean
flushy_pad_activate (GstPad * pad, GstObject * object)
{
return gst_pad_activate_mode (pad, GST_PAD_MODE_PUSH, TRUE);
}
static gboolean
flushy_pad_activatemode (GstPad * pad, GstObject * object, GstPadMode mode,
gboolean active)
{
g_assert (mode == GST_PAD_MODE_PUSH);
if (active)
gst_pad_start_task (pad, flushy_pad_task, gst_pad_get_element_private (pad),
NULL);
else
gst_pad_stop_task (pad);
return TRUE;
}
const gchar *PAD_KIND_AUDIO_RAW = "audio_raw";
const gchar *PAD_KIND_VIDEO_RAW = "video_raw";
static void
test_race_add_stream (GstElement * playsink, const gchar * element_name,
const gchar * pad_template_name, const gchar * pad_kind,
const gchar * stream_id, guint group_id)
{
GstBin *playsink_parent = GST_BIN (gst_element_get_parent (playsink));
FlushyTaskData *data = g_new0 (FlushyTaskData, 1);
GstPad *playsink_sink, *queue_src;
GstElement *fakesrc, *queue;
GstElement *audio_fakesink, *video_fakesink;
gchar *fakesrc_name, *queue_name;
data->playsink = playsink;
data->stream_id = stream_id;
data->group_id = group_id;
data->sample = make_simple_sample (element_name);
playsink_sink =
gst_element_request_pad (playsink, gst_element_get_pad_template (playsink,
pad_template_name), pad_template_name,
gst_sample_get_caps (data->sample));
audio_fakesink = gst_element_factory_make ("fakesink", "audio_fakesink");
video_fakesink = gst_element_factory_make ("fakesink", "video_fakesink");
g_object_set (playsink, "audio-sink", audio_fakesink, "video-sink",
video_fakesink, NULL);
fakesrc_name = g_strdup_printf ("fakesrc_%s", pad_kind);
fakesrc = gst_element_factory_make ("fakesrc", fakesrc_name);
queue_name = g_strdup_printf ("queue_%s", pad_kind);
queue = gst_element_factory_make ("queue", queue_name);
gst_bin_add_many (GST_BIN (playsink_parent), fakesrc, queue, NULL);
gst_element_link (fakesrc, queue);
queue_src = gst_element_get_static_pad (queue, "src");
gst_pad_link (queue_src, playsink_sink);
data->pad = gst_element_get_static_pad (fakesrc, "src");
gst_pad_set_element_private (data->pad, data);
gst_pad_set_activate_function_full (data->pad, flushy_pad_activate, NULL,
NULL);
gst_pad_set_activatemode_function_full (data->pad, flushy_pad_activatemode,
NULL, NULL);
g_free (fakesrc_name);
g_free (queue_name);
gst_object_unref (playsink_parent);
gst_object_unref (queue_src);
}
GST_START_TEST (test_race_with_flush)
{
GstElement *pipeline, *playsink;
GstMessage *msg;
guint group_id = gst_util_group_id_next ();
pipeline = gst_pipeline_new (NULL);
playsink = gst_element_factory_make ("playsink", NULL);
gst_bin_add (GST_BIN (pipeline), playsink);
test_race_add_stream (playsink, "videotestsrc", "video_raw_sink", "video",
"video", group_id);
test_race_add_stream (playsink, "audiotestsrc", "audio_raw_sink", "audio",
"audio", group_id);
GST_DEBUG_BIN_TO_DOT_FILE (GST_BIN (pipeline), GST_DEBUG_GRAPH_SHOW_ALL,
"pipeline-null");
gst_element_set_state (pipeline, GST_STATE_PLAYING);
GST_DEBUG_BIN_TO_DOT_FILE (GST_BIN (pipeline), GST_DEBUG_GRAPH_SHOW_ALL,
"pipeline-playing");
/* wait for eos */
msg =
gst_bus_timed_pop_filtered (GST_ELEMENT_BUS (pipeline),
GST_CLOCK_TIME_NONE, GST_MESSAGE_EOS | GST_MESSAGE_ERROR);
if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
GError *err = NULL;
gchar *dbg_info = NULL;
gst_message_parse_error (msg, &err, &dbg_info);
g_printerr ("ERROR from element %s: %s\n",
GST_OBJECT_NAME (msg->src), err->message);
g_printerr ("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");
g_error_free (err);
g_free (dbg_info);
fail ();
}
gst_message_unref (msg);
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (pipeline);
}
GST_END_TEST;
static Suite *
playsink_suite (void)
......@@ -81,6 +293,7 @@ playsink_suite (void)
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_volume_in_sink);
// tcase_add_test (tc_chain, test_race_with_flush);
return s;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment