From c7ca9b74eb18f1a27dfcd1b5f7bf1b174de89acb Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 5 Mar 2010 17:51:26 +0100 Subject: [PATCH] media: avoid doing _get_state() for state changes When preparing, use the ASYNC_DONE and ERROR messages in the bus handler to wait until the media is prerolled or in error. This avoids doing a blocking call of gst_element_get_state() that can cause lockups when there is an error. Fixes #611899 --- gst/rtsp-server/rtsp-media.c | 63 ++++++++++++++++++++++++++++++++++-------- gst/rtsp-server/rtsp-media.h | 57 +++++++++++++++++++++++++------------- gst/rtsp-server/rtsp-session.c | 2 +- 3 files changed, 90 insertions(+), 32 deletions(-) diff --git a/gst/rtsp-server/rtsp-media.c b/gst/rtsp-server/rtsp-media.c index 986e6d7..a2fe5d3 100644 --- a/gst/rtsp-server/rtsp-media.c +++ b/gst/rtsp-server/rtsp-media.c @@ -109,6 +109,8 @@ static void gst_rtsp_media_init (GstRTSPMedia * media) { media->streams = g_array_new (FALSE, TRUE, sizeof (GstRTSPMediaStream *)); + media->lock = g_mutex_new (); + media->cond = g_cond_new (); } static void @@ -168,6 +170,8 @@ gst_rtsp_media_finalize (GObject * obj) g_source_destroy (media->source); g_source_unref (media->source); } + g_mutex_free (media->lock); + g_cond_free (media->cond); G_OBJECT_CLASS (gst_rtsp_media_parent_class)->finalize (obj); } @@ -467,8 +471,6 @@ gst_rtsp_media_seek (GstRTSPMedia * media, GstRTSPTimeRange * range) GST_INFO ("done seeking %d", res); gst_element_get_state (media->pipeline, NULL, NULL, -1); GST_INFO ("prerolled again"); - - collect_media_stats (media); } else { GST_INFO ("no seek needed"); res = TRUE; @@ -1097,6 +1099,37 @@ unlock_streams (GstRTSPMedia * media) } } +static void +gst_rtsp_media_set_status (GstRTSPMedia *media, GstRTSPMediaStatus status) +{ + g_mutex_lock (media->lock); + /* never overwrite the error status */ + if (media->status != GST_RTSP_MEDIA_STATUS_ERROR) + media->status = status; + GST_DEBUG ("setting new status to %d", status); + g_cond_broadcast (media->cond); + g_mutex_unlock (media->lock); +} + +static GstRTSPMediaStatus +gst_rtsp_media_get_status (GstRTSPMedia *media) +{ + GstRTSPMediaStatus result; + + g_mutex_lock (media->lock); + /* while we are preparing, wait */ + while (media->status == GST_RTSP_MEDIA_STATUS_PREPARING) { + GST_DEBUG ("waiting for status change"); + g_cond_wait (media->cond, media->lock); + } + /* could be success or error */ + result = media->status; + GST_DEBUG ("got status %d", result); + g_mutex_unlock (media->lock); + + return result; +} + static gboolean default_handle_message (GstRTSPMedia * media, GstMessage * message) { @@ -1156,6 +1189,8 @@ default_handle_message (GstRTSPMedia * media, GstMessage * message) GST_WARNING ("%p: got error %s (%s)", media, gerror->message, debug); g_error_free (gerror); g_free (debug); + + gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_ERROR); break; } case GST_MESSAGE_WARNING: @@ -1173,6 +1208,12 @@ default_handle_message (GstRTSPMedia * media, GstMessage * message) break; case GST_MESSAGE_STREAM_STATUS: break; + case GST_MESSAGE_ASYNC_DONE: + GST_INFO ("%p: got ASYNC_DONE", media); + collect_media_stats (media); + + gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARED); + break; default: GST_INFO ("%p: got message type %s", media, gst_message_type_get_name (type)); @@ -1263,12 +1304,13 @@ gboolean gst_rtsp_media_prepare (GstRTSPMedia * media) { GstStateChangeReturn ret; + GstRTSPMediaStatus status; guint i, n_streams; GstRTSPMediaClass *klass; GstBus *bus; GList *walk; - if (media->prepared) + if (media->status == GST_RTSP_MEDIA_STATUS_PREPARED) goto was_prepared; if (!media->reusable && media->reused) @@ -1279,6 +1321,8 @@ gst_rtsp_media_prepare (GstRTSPMedia * media) /* reset some variables */ media->is_live = FALSE; media->buffering = FALSE; + /* we're preparing now */ + media->status = GST_RTSP_MEDIA_STATUS_PREPARING; bus = gst_pipeline_get_bus (GST_PIPELINE_CAST (media->pipeline)); @@ -1344,17 +1388,12 @@ gst_rtsp_media_prepare (GstRTSPMedia * media) } /* now wait for all pads to be prerolled */ - ret = gst_element_get_state (media->pipeline, NULL, NULL, -1); - if (ret == GST_STATE_CHANGE_FAILURE) + status = gst_rtsp_media_get_status (media); + if (status == GST_RTSP_MEDIA_STATUS_ERROR) goto state_failed; - /* collect stats about the media */ - collect_media_stats (media); - GST_INFO ("object %p is prerolled", media); - media->prepared = TRUE; - return TRUE; /* OK */ @@ -1393,7 +1432,7 @@ gst_rtsp_media_unprepare (GstRTSPMedia * media) GstRTSPMediaClass *klass; gboolean success; - if (!media->prepared) + if (media->status == GST_RTSP_MEDIA_STATUS_UNPREPARED) return TRUE; GST_INFO ("unprepare media %p", media); @@ -1405,7 +1444,7 @@ gst_rtsp_media_unprepare (GstRTSPMedia * media) else success = TRUE; - media->prepared = FALSE; + media->status = GST_RTSP_MEDIA_STATUS_UNPREPARED; media->reused = TRUE; /* when the media is not reusable, this will effectively unref the media and diff --git a/gst/rtsp-server/rtsp-media.h b/gst/rtsp-server/rtsp-media.h index aa58aef..b4c75cd 100644 --- a/gst/rtsp-server/rtsp-media.h +++ b/gst/rtsp-server/rtsp-media.h @@ -140,12 +140,28 @@ struct _GstRTSPMediaStream { }; /** + * GstRTSPMediaStatus: + * @GST_RTSP_MEDIA_STATUS_UNPREPARED: media pipeline not prerolled + * @GST_RTSP_MEDIA_STATUS_PREPARING: media pipeline is prerolling + * @GST_RTSP_MEDIA_STATUS_PREPARED: media pipeline is prerolled + * @GST_RTSP_MEDIA_STATUS_ERROR: media pipeline is in error + * + * The state of the media pipeline. + */ +typedef enum { + GST_RTSP_MEDIA_STATUS_UNPREPARED = 0, + GST_RTSP_MEDIA_STATUS_PREPARING = 1, + GST_RTSP_MEDIA_STATUS_PREPARED = 2, + GST_RTSP_MEDIA_STATUS_ERROR = 3 +} GstRTSPMediaStatus; + +/** * GstRTSPMedia: * @shared: if this media can be shared between clients * @reusable: if this media can be reused after an unprepare * @element: the data providing element * @streams: the different streams provided by @element - * @prepared: if the media is prepared for streaming + * @status: the status of the media pipeline * @pipeline: the toplevel pipeline * @source: the bus watch for pipeline messages. * @id: the id of the watch @@ -161,33 +177,36 @@ struct _GstRTSPMediaStream { * This object is usually created from a #GstRTSPMediaFactory. */ struct _GstRTSPMedia { - GObject parent; + GObject parent; - gboolean shared; - gboolean reusable; - gboolean reused; + GMutex *lock; + GCond *cond; - GstElement *element; - GArray *streams; - GList *dynamic; - gboolean prepared; - gint active; + gboolean shared; + gboolean reusable; + gboolean reused; + + GstElement *element; + GArray *streams; + GList *dynamic; + GstRTSPMediaStatus status; + gint active; /* the pipeline for the media */ - GstElement *pipeline; - GstElement *fakesink; - GSource *source; - guint id; + GstElement *pipeline; + GstElement *fakesink; + GSource *source; + guint id; - gboolean is_live; - gboolean buffering; - GstState target_state; + gboolean is_live; + gboolean buffering; + GstState target_state; /* RTP session manager */ - GstElement *rtpbin; + GstElement *rtpbin; /* the range of media */ - GstRTSPTimeRange range; + GstRTSPTimeRange range; }; /** diff --git a/gst/rtsp-server/rtsp-session.c b/gst/rtsp-server/rtsp-session.c index b45530b..31a371a 100644 --- a/gst/rtsp-server/rtsp-session.c +++ b/gst/rtsp-server/rtsp-session.c @@ -197,7 +197,7 @@ gst_rtsp_session_manage_media (GstRTSPSession *sess, const GstRTSPUrl *uri, g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL); g_return_val_if_fail (uri != NULL, NULL); g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL); - g_return_val_if_fail (media->prepared, NULL); + g_return_val_if_fail (media->status == GST_RTSP_MEDIA_STATUS_PREPARED, NULL); result = g_new0 (GstRTSPSessionMedia, 1); result->media = media; -- 2.7.4