From f97c9d5213f14f12dc056b9eaa2253325bc3a18c Mon Sep 17 00:00:00 2001 From: Nicola Murino Date: Tue, 12 Mar 2013 10:50:48 +0100 Subject: appsrc: fix deadlock setting pipeline in NULL state with block=true --- gst-libs/gst/app/gstappsrc.c | 1 + tests/check/elements/appsrc.c | 152 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) diff --git a/gst-libs/gst/app/gstappsrc.c b/gst-libs/gst/app/gstappsrc.c index 2db8b1853..e74a3a320 100644 --- a/gst-libs/gst/app/gstappsrc.c +++ b/gst-libs/gst/app/gstappsrc.c @@ -752,6 +752,7 @@ gst_app_src_stop (GstBaseSrc * bsrc) priv->flushing = TRUE; priv->started = FALSE; gst_app_src_flush_queued (appsrc); + g_cond_broadcast (priv->cond); g_mutex_unlock (priv->mutex); return TRUE; diff --git a/tests/check/elements/appsrc.c b/tests/check/elements/appsrc.c index b888f4802..ceb3ecaf8 100644 --- a/tests/check/elements/appsrc.c +++ b/tests/check/elements/appsrc.c @@ -20,6 +20,7 @@ #include #include +#include #define SAMPLE_CAPS "application/x-gst-check-test" @@ -170,6 +171,155 @@ GST_START_TEST (test_appsrc_non_null_caps) GST_END_TEST; +static GstAppSinkCallbacks app_callbacks; + +typedef struct +{ + GMainLoop *loop; + GstElement *source; + GstElement *sink; +} ProgramData; + +static GstFlowReturn +on_new_buffer_from_source (GstAppSink * elt, gpointer user_data) +{ + ProgramData *data = (ProgramData *) user_data; + GstBuffer *buffer; + GstElement *source; + + buffer = gst_app_sink_pull_buffer (GST_APP_SINK (elt)); + source = gst_bin_get_by_name (GST_BIN (data->sink), "testsource"); + gst_app_src_push_buffer (GST_APP_SRC (source), gst_buffer_ref (buffer)); + g_object_unref (source); + return GST_FLOW_OK; +} + +/* called when we get a GstMessage from the source pipeline when we get EOS, we + * notify the appsrc of it. */ +static gboolean +on_source_message (GstBus * bus, GstMessage * message, ProgramData * data) +{ + GstElement *source; + + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_EOS: + source = gst_bin_get_by_name (GST_BIN (data->sink), "testsource"); + fail_unless (gst_app_src_end_of_stream (GST_APP_SRC (source)) == + GST_FLOW_OK); + break; + case GST_MESSAGE_ERROR: + g_main_loop_quit (data->loop); + break; + default: + break; + } + return TRUE; +} + +static gboolean +on_sink_message (GstBus * bus, GstMessage * message, ProgramData * data) +{ + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_EOS: + g_main_loop_quit (data->loop); + break; + case GST_MESSAGE_ERROR: + ASSERT_SET_STATE (data->sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS); + ASSERT_SET_STATE (data->source, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS); + g_main_loop_quit (data->loop); + break; + default: + break; + } + return TRUE; +} + +static gboolean +error_timeout (ProgramData * data) +{ + GstBus *bus; + bus = gst_element_get_bus (data->sink); + gst_bus_post (bus, gst_message_new_error (GST_OBJECT (data->sink), NULL, + "test error")); + gst_object_unref (bus); + return FALSE; +} + +/* + * appsink => appsrc pipelines executed 100 times: + * - appsink pipeline has sync=false + * - appsrc pipeline has sync=true + * - appsrc has block=true + * after 1 second an error message is posted on appsink pipeline bus + * when the error is received the appsrc pipeline is set to NULL + * and then the appsink pipeline is + * set to NULL too, this must not deadlock + */ + +GST_START_TEST (test_appsrc_block_deadlock) +{ + int i = 0; + int num_iteration = 100; + while (i < num_iteration) { + ProgramData *data = NULL; + GstBus *bus = NULL; + GstElement *testsink = NULL; + + gint tout; + + data = g_new0 (ProgramData, 1); + + data->loop = g_main_loop_new (NULL, FALSE); + + data->source = + gst_parse_launch ("videotestsrc ! appsink sync=false name=testsink", + NULL); + + fail_unless (data->source != NULL); + + bus = gst_element_get_bus (data->source); + gst_bus_add_watch (bus, (GstBusFunc) on_source_message, data); + gst_object_unref (bus); + + app_callbacks.new_buffer = on_new_buffer_from_source; + testsink = gst_bin_get_by_name (GST_BIN (data->source), "testsink"); + gst_app_sink_set_callbacks (GST_APP_SINK_CAST (testsink), &app_callbacks, + data, NULL); + + gst_object_unref (testsink); + + data->sink = + gst_parse_launch + ("appsrc name=testsource block=1 max-bytes=1000 is-live=true ! fakesink sync=true", + NULL); + + fail_unless (data->sink != NULL); + + bus = gst_element_get_bus (data->sink); + gst_bus_add_watch (bus, (GstBusFunc) on_sink_message, data); + gst_object_unref (bus); + + tout = g_timeout_add (150, (GSourceFunc) error_timeout, data); + + ASSERT_SET_STATE (data->sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC); + ASSERT_SET_STATE (data->source, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC); + + g_main_loop_run (data->loop); + + ASSERT_SET_STATE (data->sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS); + ASSERT_SET_STATE (data->source, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS); + + g_source_remove (tout); + gst_object_unref (data->source); + gst_object_unref (data->sink); + g_main_loop_unref (data->loop); + g_free (data); + i++; + g_print ("appsrc deadlock test iteration number %d/%d\n", i, num_iteration); + } +} + +GST_END_TEST; static Suite * appsrc_suite (void) @@ -179,7 +329,9 @@ appsrc_suite (void) tcase_add_test (tc_chain, test_appsrc_null_caps); tcase_add_test (tc_chain, test_appsrc_non_null_caps); + tcase_add_test (tc_chain, test_appsrc_block_deadlock); + tcase_set_timeout (tc_chain, 20); suite_add_tcase (s, tc_chain); return s; -- cgit v1.2.3