rtsp-media: Unblock all streams
[platform/upstream/gstreamer.git] / gst / rtsp-server / rtsp-media.c
index bd3fcaa..345559d 100644 (file)
@@ -152,6 +152,7 @@ struct _GstRTSPMediaPrivate
   /* Dynamic element handling */
   guint nb_dynamic_elements;
   guint no_more_pads_pending;
+  gboolean expected_async_done;
 };
 
 #define DEFAULT_SHARED          FALSE
@@ -235,7 +236,7 @@ static gboolean default_handle_sdp (GstRTSPMedia * media, GstSDPMessage * sdp);
 
 static gboolean wait_preroll (GstRTSPMedia * media);
 
-static GstElement *find_payload_element (GstElement * payloader);
+static GstElement *find_payload_element (GstElement * payloader, GstPad * pad);
 
 static guint gst_rtsp_media_signals[SIGNAL_LAST] = { 0 };
 
@@ -243,6 +244,8 @@ static gboolean check_complete (GstRTSPMedia * media);
 
 #define C_ENUM(v) ((gint) v)
 
+#define TRICKMODE_FLAGS (GST_SEEK_FLAG_TRICKMODE | GST_SEEK_FLAG_TRICKMODE_KEY_UNITS | GST_SEEK_FLAG_TRICKMODE_FORWARD_PREDICTED)
+
 GType
 gst_rtsp_suspend_mode_get_type (void)
 {
@@ -474,6 +477,7 @@ gst_rtsp_media_init (GstRTSPMedia * media)
   priv->max_mcast_ttl = DEFAULT_MAX_MCAST_TTL;
   priv->bind_mcast_address = DEFAULT_BIND_MCAST_ADDRESS;
   priv->do_rate_control = DEFAULT_DO_RATE_CONTROL;
+  priv->expected_async_done = FALSE;
 }
 
 static void
@@ -2046,7 +2050,7 @@ gst_rtsp_media_collect_streams (GstRTSPMedia * media)
       pad = gst_element_get_static_pad (elem, "src");
 
       /* find the real payload element in case elem is a GstBin */
-      pay = find_payload_element (elem);
+      pay = find_payload_element (elem, pad);
 
       /* create the stream */
       if (pay == NULL) {
@@ -2213,7 +2217,8 @@ gst_rtsp_media_create_stream (GstRTSPMedia * media, GstElement * payloader,
   g_mutex_lock (&priv->lock);
   idx = priv->streams->len;
 
-  GST_DEBUG ("media %p: creating stream with index %d", media, idx);
+  GST_DEBUG ("media %p: creating stream with index %d and payloader %"
+      GST_PTR_FORMAT, media, idx, payloader);
 
   if (GST_PAD_IS_SRC (pad))
     name = g_strdup_printf ("src_%u", idx);
@@ -2559,7 +2564,10 @@ gst_rtsp_media_get_rates (GstRTSPMedia * media, gdouble * rate,
 {
   GstRTSPMediaPrivate *priv;
   GstRTSPStream *stream;
+  gdouble save_rate, save_applied_rate;
   gboolean result = TRUE;
+  gboolean first_stream = TRUE;
+  gint i;
 
   g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
 
@@ -2573,11 +2581,34 @@ gst_rtsp_media_get_rates (GstRTSPMedia * media, gdouble * rate,
   g_mutex_lock (&priv->lock);
 
   g_assert (priv->streams->len > 0);
-  stream = g_ptr_array_index (priv->streams, 0);
-  if (!gst_rtsp_stream_get_rates (stream, rate, applied_rate)) {
+  for (i = 0; i < priv->streams->len; i++) {
+    stream = g_ptr_array_index (priv->streams, i);
+    if (gst_rtsp_stream_is_complete (stream)) {
+      if (gst_rtsp_stream_get_rates (stream, rate, applied_rate)) {
+        if (first_stream) {
+          save_rate = *rate;
+          save_applied_rate = *applied_rate;
+          first_stream = FALSE;
+        } else {
+          if (save_rate != *rate || save_applied_rate != *applied_rate) {
+            /* diffrent rate or applied_rate, weird */
+            g_assert (FALSE);
+            result = FALSE;
+            break;
+          }
+        }
+      } else {
+        /* complete stream withot rate and applied_rate, weird */
+        g_assert (FALSE);
+        result = FALSE;
+        break;
+      }
+    }
+  }
+
+  if (!result) {
     GST_WARNING_OBJECT (media,
-        "failed to obtain rate and applied_rate from first stream");
-    result = FALSE;
+        "failed to obtain consistent rate and applied_rate");
   }
 
   g_mutex_unlock (&priv->lock);
@@ -2604,15 +2635,15 @@ media_streams_set_blocked (GstRTSPMedia * media, gboolean blocked)
 static void
 stream_unblock (GstRTSPStream * stream, GstRTSPMedia * media)
 {
-  gst_rtsp_stream_unblock_linked (stream);
+  gst_rtsp_stream_set_blocked (stream, FALSE);
 }
 
 static void
-media_unblock_linked (GstRTSPMedia * media)
+media_unblock (GstRTSPMedia * media)
 {
   GstRTSPMediaPrivate *priv = media->priv;
 
-  GST_DEBUG ("media %p unblocking linked streams", media);
+  GST_DEBUG ("media %p unblocking streams", media);
   /* media is not blocked any longer, as it contains active streams,
    * streams that are complete */
   priv->blocked = FALSE;
@@ -2756,13 +2787,11 @@ gst_rtsp_media_seek_trickmode (GstRTSPMedia * media,
   if (stop != GST_CLOCK_TIME_NONE)
     stop_type = GST_SEEK_TYPE_SET;
 
-  /* we force a seek if any seek flag is set, or if the the rate
+  /* we force a seek if any trickmode flag is set, or if the rate
    * is non-standard, i.e. not 1.0 */
-  force_seek = flags != GST_SEEK_FLAG_NONE || rate != 1.0;
+  force_seek = (flags & TRICKMODE_FLAGS) || rate != 1.0;
 
   if (start != GST_CLOCK_TIME_NONE || stop != GST_CLOCK_TIME_NONE || force_seek) {
-    gboolean had_flags = flags != GST_SEEK_FLAG_NONE;
-
     GST_INFO ("seeking to %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT,
         GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
 
@@ -2781,14 +2810,7 @@ gst_rtsp_media_seek_trickmode (GstRTSPMedia * media,
             GST_TIME_ARGS (current_position));
         start = current_position;
         start_type = GST_SEEK_TYPE_SET;
-        if (!had_flags)
-          flags |= GST_SEEK_FLAG_ACCURATE;
       }
-    } else {
-      /* only set keyframe flag when modifying start */
-      if (start_type != GST_SEEK_TYPE_NONE)
-        if (!had_flags)
-          flags |= GST_SEEK_FLAG_KEY_UNIT;
     }
 
     if (start == current_position && stop_type == GST_SEEK_TYPE_NONE &&
@@ -2799,6 +2821,30 @@ gst_rtsp_media_seek_trickmode (GstRTSPMedia * media,
       GstEvent *seek_event;
       gboolean unblock = FALSE;
 
+      /* Handle expected async-done before waiting on next async-done.
+       * 
+       * Since the seek further down in code will cause a preroll and
+       * a async-done will be generated it's important to wait on async-done
+       * if that is expected. Otherwise there is the risk that the waiting
+       * for async-done after the seek is detecting the expected async-done
+       * instead of the one that corresponds to the seek. Then execution
+       * continue and act as if the pipeline is prerolled, but it's not.
+       * 
+       * During wait_preroll message GST_MESSAGE_ASYNC_DONE will come
+       * and then the state will change from preparing to prepared */
+      if (priv->expected_async_done) {
+        GST_DEBUG (" expected to get async-done, waiting ");
+        gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARING);
+        g_rec_mutex_unlock (&priv->state_lock);
+
+        /* wait until pipeline is prerolled  */
+        if (!wait_preroll (media))
+          goto preroll_failed_expected_async_done;
+
+        g_rec_mutex_lock (&priv->state_lock);
+        GST_DEBUG (" got expected async-done");
+      }
+
       gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARING);
 
       if (rate < 0.0) {
@@ -2890,6 +2936,11 @@ preroll_failed:
     GST_WARNING ("failed to preroll after seek");
     return FALSE;
   }
+preroll_failed_expected_async_done:
+  {
+    GST_WARNING ("failed to preroll");
+    return FALSE;
+  }
 }
 
 /**
@@ -2902,6 +2953,7 @@ preroll_failed:
  * @media must be prepared with gst_rtsp_media_prepare().
  *
  * Returns: %TRUE on success.
+ * Since: 1.18
  */
 gboolean
 gst_rtsp_media_seek_full (GstRTSPMedia * media, GstRTSPTimeRange * range,
@@ -3097,6 +3149,8 @@ default_handle_message (GstRTSPMedia * media, GstMessage * message)
     case GST_MESSAGE_STREAM_STATUS:
       break;
     case GST_MESSAGE_ASYNC_DONE:
+      if (priv->expected_async_done)
+        priv->expected_async_done = FALSE;
       if (priv->complete) {
         /* receive the final ASYNC_DONE, that is posted by the media pipeline
          * after all the transport parts have been successfully added to
@@ -3148,27 +3202,57 @@ watch_destroyed (GstRTSPMedia * media)
   g_object_unref (media);
 }
 
+static gboolean
+is_payloader (GstElement * element)
+{
+  GstElementClass *eclass = GST_ELEMENT_GET_CLASS (element);
+  const gchar *klass;
+
+  klass = gst_element_class_get_metadata (eclass, GST_ELEMENT_METADATA_KLASS);
+  if (klass == NULL)
+    return FALSE;
+
+  if (strstr (klass, "Payloader") && strstr (klass, "RTP")) {
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
 static GstElement *
-find_payload_element (GstElement * payloader)
+find_payload_element (GstElement * payloader, GstPad * pad)
 {
   GstElement *pay = NULL;
 
   if (GST_IS_BIN (payloader)) {
     GstIterator *iter;
     GValue item = { 0 };
+    gchar *pad_name, *payloader_name;
+    GstElement *element;
+
+    if ((element = gst_bin_get_by_name (GST_BIN (payloader), "pay"))) {
+      if (is_payloader (element))
+        return element;
+      gst_object_unref (element);
+    }
+
+    pad_name = gst_object_get_name (GST_OBJECT (pad));
+    payloader_name = g_strdup_printf ("pay_%s", pad_name);
+    g_free (pad_name);
+    if ((element = gst_bin_get_by_name (GST_BIN (payloader), payloader_name))) {
+      g_free (payloader_name);
+      if (is_payloader (element))
+        return element;
+      gst_object_unref (element);
+    } else {
+      g_free (payloader_name);
+    }
 
     iter = gst_bin_iterate_recurse (GST_BIN (payloader));
     while (gst_iterator_next (iter, &item) == GST_ITERATOR_OK) {
-      GstElement *element = (GstElement *) g_value_get_object (&item);
-      GstElementClass *eclass = GST_ELEMENT_GET_CLASS (element);
-      const gchar *klass;
+      element = (GstElement *) g_value_get_object (&item);
 
-      klass =
-          gst_element_class_get_metadata (eclass, GST_ELEMENT_METADATA_KLASS);
-      if (klass == NULL)
-        continue;
-
-      if (strstr (klass, "Payloader") && strstr (klass, "RTP")) {
+      if (is_payloader (element)) {
         pay = gst_object_ref (element);
         g_value_unset (&item);
         break;
@@ -3192,7 +3276,7 @@ pad_added_cb (GstElement * element, GstPad * pad, GstRTSPMedia * media)
   GstElement *pay;
 
   /* find the real payload element */
-  pay = find_payload_element (element);
+  pay = find_payload_element (element, pad);
   stream = gst_rtsp_media_create_stream (media, pay, pad);
   gst_object_unref (pay);
 
@@ -4309,8 +4393,8 @@ default_unsuspend (GstRTSPMedia * media)
         gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARING);
         /* at this point the media pipeline has been updated and contain all
          * specific transport parts: all active streams contain at least one sink
-         * element and it's safe to unblock any blocked streams that are active */
-        media_unblock_linked (media);
+         * element and it's safe to unblock all blocked streams */
+        media_unblock (media);
       } else {
         /* streams are not blocked and media is suspended from PAUSED */
         gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARED);
@@ -4330,8 +4414,8 @@ default_unsuspend (GstRTSPMedia * media)
       gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARING);
       /* at this point the media pipeline has been updated and contain all
        * specific transport parts: all active streams contain at least one sink
-       * element and it's safe to unblock any blocked streams that are active */
-      media_unblock_linked (media);
+       * element and it's safe to unblock all blocked streams */
+      media_unblock (media);
       if (!start_preroll (media))
         goto start_failed;
 
@@ -4408,6 +4492,8 @@ static void
 media_set_pipeline_state_locked (GstRTSPMedia * media, GstState state)
 {
   GstRTSPMediaPrivate *priv = media->priv;
+  GstStateChangeReturn set_state_ret;
+  priv->expected_async_done = FALSE;
 
   if (state == GST_STATE_NULL) {
     gst_rtsp_media_unprepare (media);
@@ -4421,13 +4507,17 @@ media_set_pipeline_state_locked (GstRTSPMedia * media, GstState state)
     } else {
       if (state == GST_STATE_PLAYING)
         /* make sure pads are not blocking anymore when going to PLAYING */
-        media_unblock_linked (media);
-
-      set_state (media, state);
+        media_unblock (media);
 
-      /* and suspend after pause */
-      if (state == GST_STATE_PAUSED)
+      if (state == GST_STATE_PAUSED) {
+        set_state_ret = set_state (media, state);
+        if (set_state_ret == GST_STATE_CHANGE_ASYNC)
+          priv->expected_async_done = TRUE;
+        /* and suspend after pause */
         gst_rtsp_media_suspend (media);
+      } else {
+        set_state (media, state);
+      }
     }
   }
 }
@@ -4757,6 +4847,36 @@ gst_rtsp_media_is_receive_only (GstRTSPMedia * media)
 }
 
 /**
+ * gst_rtsp_media_has_completed_sender:
+ *
+ * See gst_rtsp_stream_is_complete(), gst_rtsp_stream_is_sender().
+ *    
+ * Returns: whether @media has at least one complete sender stream.
+ * Since: 1.18
+ */
+gboolean
+gst_rtsp_media_has_completed_sender (GstRTSPMedia * media)
+{
+  GstRTSPMediaPrivate *priv = media->priv;
+  gboolean sender = FALSE;
+  guint i;
+
+  g_mutex_lock (&priv->lock);
+  for (i = 0; i < priv->streams->len; i++) {
+    GstRTSPStream *stream = g_ptr_array_index (priv->streams, i);
+    if (gst_rtsp_stream_is_complete (stream))
+      if (gst_rtsp_stream_is_sender (stream) ||
+          !gst_rtsp_stream_is_receiver (stream)) {
+        sender = TRUE;
+        break;
+      }
+  }
+  g_mutex_unlock (&priv->lock);
+
+  return sender;
+}
+
+/**
  * gst_rtsp_media_set_rate_control:
  *
  * Define whether @media will follow the Rate-Control=no behaviour as specified