media: avoid doing _get_state() for state changes
authorWim Taymans <wim.taymans@collabora.co.uk>
Fri, 5 Mar 2010 16:51:26 +0000 (17:51 +0100)
committerWim Taymans <wim.taymans@collabora.co.uk>
Fri, 5 Mar 2010 16:54:09 +0000 (17:54 +0100)
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
gst/rtsp-server/rtsp-media.h
gst/rtsp-server/rtsp-session.c

index 986e6d7..a2fe5d3 100644 (file)
@@ -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
index aa58aef..b4c75cd 100644 (file)
@@ -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;
 };
 
 /**
index b45530b..31a371a 100644 (file)
@@ -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;